Passed
Pull Request — master (#123)
by Robbie
01:48
created

BaseElement::PreviewLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
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\GridFieldDetailForm;
28
use SilverStripe\Forms\GridField\GridFieldPageCount;
29
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
30
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
31
use SilverStripe\Forms\GridField\GridFieldVersionedState;
32
use SilverStripe\Forms\GridField\GridFieldViewButton;
33
use SilverStripe\ORM\ArrayList;
34
use SilverStripe\ORM\CMSPreviewable;
35
use SilverStripe\ORM\DataObject;
36
use SilverStripe\ORM\DB;
37
use SilverStripe\ORM\FieldType\DBField;
38
use SilverStripe\ORM\Search\SearchContext;
39
use SilverStripe\Security\Permission;
40
use SilverStripe\Security\Member;
41
use SilverStripe\SiteConfig\SiteConfig;
42
use SilverStripe\Versioned\Versioned;
43
use SilverStripe\View\Parsers\URLSegmentFilter;
44
use SilverStripe\View\Requirements;
45
use SilverStripe\View\SSViewer;
46
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
47
48
class BaseElement extends DataObject implements CMSPreviewable
49
{
50
    /**
51
     * Override this on your custom elements to specify a cms icon
52
     *
53
     * @var string
54
     */
55
    private static $icon = 'dnadesign/silverstripe-elemental:images/base.svg';
0 ignored issues
show
Unused Code introduced by
The property $icon is not used and could be removed.

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

Loading history...
56
57
    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...
58
        'Title' => 'Varchar(255)',
59
        'ShowTitle' => 'Boolean',
60
        'Sort' => 'Int',
61
        'ExtraClass' => 'Varchar(255)',
62
        'Style' => 'Varchar(255)'
63
    ];
64
65
    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...
66
        'Parent' => ElementalArea::class
67
    ];
68
69
    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...
70
        Versioned::class
71
    ];
72
73
    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...
74
75
    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...
76
77
    /**
78
     * @var string
79
     */
80
    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...
81
82
    /**
83
     * @var string
84
     */
85
    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...
86
87
    /**
88
     * @var ElementController
89
     */
90
    protected $controller;
91
92
    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...
93
94
    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...
95
96
    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...
97
98
    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...
99
        'EditorPreview' => 'Summary'
100
    ];
101
102
    /**
103
     * @var array
104
     */
105
    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...
106
107
    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...
108
        'ID' => [
109
            'field' => NumericField::class,
110
        ],
111
        'Title',
112
        'LastEdited'
113
    ];
114
115
    /**
116
     * Enable for backwards compatibility
117
     *
118
     * @var boolean
119
     */
120
    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...
121
122
    /**
123
     * Store used anchor names, this is to avoid title clashes
124
     * when calling 'getAnchor'
125
     *
126
     * @var array
127
     */
128
    protected static $_used_anchors = [];
129
130
    /**
131
     * For caching 'getAnchor'
132
     *
133
     * @var string
134
     */
135
    protected $_anchor = null;
136
137
    /**
138
     * Basic permissions, defaults to page perms where possible.
139
     *
140
     * @param Member $member
141
     *
142
     * @return boolean
143
     */
144 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...
145
    {
146
        if ($this->hasMethod('getPage')) {
147
            if ($page = $this->getPage()) {
148
                return $page->canView($member);
149
            }
150
        }
151
152
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
153
    }
154
155
    /**
156
     * Basic permissions, defaults to page perms where possible.
157
     *
158
     * @param Member $member
159
     *
160
     * @return boolean
161
     */
162 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...
163
    {
164
        if ($this->hasMethod('getPage')) {
165
            if ($page = $this->getPage()) {
166
                return $page->canEdit($member);
167
            }
168
        }
169
170
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
171
    }
172
173
    /**
174
     * Basic permissions, defaults to page perms where possible.
175
     *
176
     * Uses archive not delete so that current stage is respected i.e if a
177
     * element is not published, then it can be deleted by someone who doesn't
178
     * have publishing permissions.
179
     *
180
     * @param Member $member
181
     *
182
     * @return boolean
183
     */
184 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...
185
    {
186
        if ($this->hasMethod('getPage')) {
187
            if ($page = $this->getPage()) {
188
                return $page->canArchive($member);
189
            }
190
        }
191
192
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
193
    }
194
195
    /**
196
     * Basic permissions, defaults to page perms where possible.
197
     *
198
     * @param Member $member
199
     * @param array $context
200
     *
201
     * @return boolean
202
     */
203
    public function canCreate($member = null, $context = array())
204
    {
205
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
206
    }
207
208
    /**
209
     *
210
     */
211
    public function onBeforeWrite()
212
    {
213
        parent::onBeforeWrite();
214
215
        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...
216
            if ($elementalArea = ElementalArea::get()->byID($areaID)) {
217
                $elementalArea->write();
218
            }
219
        }
220
221
        if (!$this->Sort) {
222
            $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...
223
224
            $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...
225
        }
226
    }
227
228
    public function getCMSFields()
229
    {
230
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
231
            // Remove relationship fields
232
            $fields->removeByName('ParentID');
233
            $fields->removeByName('Sort');
234
235
            $fields->addFieldToTab(
236
                'Root.Settings',
237
                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

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

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

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

284
                $fields->addFieldsToTab('Root.History', /** @scrutinizer ignore-type */ $history);
Loading history...
285
            }
286
        });
287
288
        return parent::getCMSFields();
289
    }
290
291
    /**
292
     * Returns the history fields for this element.
293
     *
294
     * @return FieldList
295
     */
296
    public function getHistoryFields()
297
    {
298
        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

298
        if (!$this->/** @scrutinizer ignore-call */ isLatestVersion()) {
Loading history...
299
            // if viewing the history of the of page then don't show the history
300
            // fields as then we have recursion.
301
            return null;
302
        }
303
304
        Requirements::javascript('dnadesign/silverstripe-elemental:javascript/block-history.js');
305
306
        $config = GridFieldConfig_RecordViewer::create();
307
        $config->removeComponentsByType(GridFieldPageCount::class);
308
        $config->removeComponentsByType(GridFieldToolbarHeader::class);
309
        // Replace the sortable ID column with a static header component
310
        $config->removeComponentsByType(GridFieldSortableHeader::class);
311
        $config->addComponent(new GridFieldTitleHeader);
312
313
        $config
314
            ->getComponentByType(GridFieldDetailForm::class)
315
            ->setItemRequestClass(HistoricalVersionedGridFieldItemRequest::class);
316
317
        $config->getComponentByType(GridFieldDataColumns::class)
318
            ->setDisplayFields([
319
                'Version' => '#',
320
                'RecordStatus' => _t(__CLASS__ . '.Record', 'Record'),
321
                'getAuthor.Name' => _t(__CLASS__ . '.Author', 'Author')
322
            ])
323
            ->setFieldFormatting([
324
                'RecordStatus' => '$VersionedStateNice <span class=\"element-history__date--small\">on $LastEditedNice</span>',
325
            ]);
326
327
        $config->removeComponentsByType(GridFieldViewButton::class);
328
        $config->addComponent(new ElementalGridFieldHistoryButton());
329
330
        $history = Versioned::get_all_versions(__CLASS__, $this->ID)
331
            ->sort('Version', 'DESC');
332
333
        return FieldList::create(
334
            GridField::create('History', '', $history, $config)
0 ignored issues
show
Bug introduced by
'History' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

334
            GridField::create(/** @scrutinizer ignore-type */ 'History', '', $history, $config)
Loading history...
Bug introduced by
$history of type SilverStripe\ORM\DataList is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

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

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

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

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

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