1 | <?php |
||
2 | |||
3 | namespace DNADesign\Elemental\Models; |
||
4 | |||
5 | use DNADesign\Elemental\Controllers\ElementController; |
||
6 | use DNADesign\Elemental\Forms\ElementalGridFieldHistoryButton; |
||
7 | use DNADesign\Elemental\Forms\HistoricalVersionedGridFieldItemRequest; |
||
8 | use DNADesign\Elemental\Forms\TextCheckboxGroupField; |
||
9 | use Exception; |
||
10 | use SilverStripe\CMS\Controllers\CMSPageEditController; |
||
11 | use SilverStripe\CMS\Model\SiteTree; |
||
12 | use SilverStripe\Control\Controller; |
||
13 | use SilverStripe\Control\Director; |
||
14 | use SilverStripe\Core\ClassInfo; |
||
15 | use SilverStripe\Core\Injector\Injector; |
||
16 | use SilverStripe\Forms\CheckboxField; |
||
17 | use SilverStripe\Forms\DropdownField; |
||
18 | use SilverStripe\Forms\FieldList; |
||
19 | use SilverStripe\Forms\GridField\GridField; |
||
20 | use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer; |
||
21 | use SilverStripe\Forms\GridField\GridFieldDataColumns; |
||
22 | use SilverStripe\Forms\GridField\GridFieldDetailForm; |
||
23 | use SilverStripe\Forms\GridField\GridFieldPageCount; |
||
24 | use SilverStripe\Forms\GridField\GridFieldSortableHeader; |
||
25 | use SilverStripe\Forms\GridField\GridFieldToolbarHeader; |
||
26 | use SilverStripe\Forms\GridField\GridFieldViewButton; |
||
27 | use SilverStripe\Forms\HiddenField; |
||
28 | use SilverStripe\Forms\NumericField; |
||
29 | use SilverStripe\Forms\TextField; |
||
30 | use SilverStripe\ORM\CMSPreviewable; |
||
31 | use SilverStripe\ORM\DataObject; |
||
32 | use SilverStripe\ORM\FieldType\DBField; |
||
33 | use SilverStripe\ORM\FieldType\DBHTMLText; |
||
34 | use SilverStripe\Security\Member; |
||
35 | use SilverStripe\Security\Permission; |
||
36 | use SilverStripe\Versioned\Versioned; |
||
37 | use SilverStripe\View\ArrayData; |
||
38 | use SilverStripe\View\Parsers\URLSegmentFilter; |
||
39 | use SilverStripe\View\Requirements; |
||
40 | use Symbiote\GridFieldExtensions\GridFieldTitleHeader; |
||
41 | |||
42 | /** |
||
43 | * Class BaseElement |
||
44 | * @package DNADesign\Elemental\Models |
||
45 | * |
||
46 | * @property string $Title |
||
47 | * @property bool $ShowTitle |
||
48 | * @property int $Sort |
||
49 | * @property string $ExtraClass |
||
50 | * @property string $Style |
||
51 | * |
||
52 | * @method ElementalArea Parent() |
||
53 | */ |
||
54 | class BaseElement extends DataObject implements CMSPreviewable |
||
55 | { |
||
56 | /** |
||
57 | * Override this on your custom elements to specify a CSS icon class |
||
58 | * |
||
59 | * @var string |
||
60 | */ |
||
61 | private static $icon = 'font-icon-block-layout'; |
||
62 | |||
63 | /** |
||
64 | * Describe the purpose of this element |
||
65 | * |
||
66 | * @config |
||
67 | * @var string |
||
68 | */ |
||
69 | private static $description = 'Base element class'; |
||
70 | |||
71 | private static $db = [ |
||
72 | 'Title' => 'Varchar(255)', |
||
73 | 'ShowTitle' => 'Boolean', |
||
74 | 'Sort' => 'Int', |
||
75 | 'ExtraClass' => 'Varchar(255)', |
||
76 | 'Style' => 'Varchar(255)' |
||
77 | ]; |
||
78 | |||
79 | private static $has_one = [ |
||
80 | 'Parent' => ElementalArea::class |
||
81 | ]; |
||
82 | |||
83 | private static $extensions = [ |
||
84 | Versioned::class |
||
85 | ]; |
||
86 | |||
87 | private static $versioned_gridfield_extensions = true; |
||
88 | |||
89 | private static $table_name = 'Element'; |
||
90 | |||
91 | /** |
||
92 | * @var string |
||
93 | */ |
||
94 | private static $controller_class = ElementController::class; |
||
95 | |||
96 | /** |
||
97 | * @var string |
||
98 | */ |
||
99 | private static $controller_template = 'ElementHolder'; |
||
100 | |||
101 | /** |
||
102 | * @var ElementController |
||
103 | */ |
||
104 | protected $controller; |
||
105 | |||
106 | private static $default_sort = 'Sort'; |
||
107 | |||
108 | private static $singular_name = 'block'; |
||
109 | |||
110 | private static $plural_name = 'blocks'; |
||
111 | |||
112 | private static $summary_fields = [ |
||
113 | 'EditorPreview' => 'Summary' |
||
114 | ]; |
||
115 | |||
116 | /** |
||
117 | * @config |
||
118 | * @var array |
||
119 | */ |
||
120 | private static $styles = []; |
||
121 | |||
122 | private static $searchable_fields = [ |
||
123 | 'ID' => [ |
||
124 | 'field' => NumericField::class, |
||
125 | ], |
||
126 | 'Title', |
||
127 | 'LastEdited' |
||
128 | ]; |
||
129 | |||
130 | /** |
||
131 | * Enable for backwards compatibility |
||
132 | * |
||
133 | * @var boolean |
||
134 | */ |
||
135 | private static $disable_pretty_anchor_name = false; |
||
136 | |||
137 | /** |
||
138 | * Store used anchor names, this is to avoid title clashes |
||
139 | * when calling 'getAnchor' |
||
140 | * |
||
141 | * @var array |
||
142 | */ |
||
143 | protected static $_used_anchors = []; |
||
144 | |||
145 | /** |
||
146 | * For caching 'getAnchor' |
||
147 | * |
||
148 | * @var string |
||
149 | */ |
||
150 | protected $_anchor = null; |
||
151 | |||
152 | /** |
||
153 | * Basic permissions, defaults to page perms where possible. |
||
154 | * |
||
155 | * @param Member $member |
||
156 | * @return boolean |
||
157 | */ |
||
158 | public function canView($member = null) |
||
159 | { |
||
160 | $extended = $this->extendedCan(__FUNCTION__, $member); |
||
161 | if ($extended !== null) { |
||
162 | return $extended; |
||
163 | } |
||
164 | |||
165 | if ($this->hasMethod('getPage')) { |
||
166 | if ($page = $this->getPage()) { |
||
167 | return $page->canView($member); |
||
168 | } |
||
169 | } |
||
170 | |||
171 | return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Basic permissions, defaults to page perms where possible. |
||
176 | * |
||
177 | * @param Member $member |
||
178 | * |
||
179 | * @return boolean |
||
180 | */ |
||
181 | public function canEdit($member = null) |
||
182 | { |
||
183 | $extended = $this->extendedCan(__FUNCTION__, $member); |
||
184 | if ($extended !== null) { |
||
185 | return $extended; |
||
186 | } |
||
187 | |||
188 | if ($this->hasMethod('getPage')) { |
||
189 | if ($page = $this->getPage()) { |
||
190 | return $page->canEdit($member); |
||
191 | } |
||
192 | } |
||
193 | |||
194 | return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Basic permissions, defaults to page perms where possible. |
||
199 | * |
||
200 | * Uses archive not delete so that current stage is respected i.e if a |
||
201 | * element is not published, then it can be deleted by someone who doesn't |
||
202 | * have publishing permissions. |
||
203 | * |
||
204 | * @param Member $member |
||
205 | * |
||
206 | * @return boolean |
||
207 | */ |
||
208 | public function canDelete($member = null) |
||
209 | { |
||
210 | $extended = $this->extendedCan(__FUNCTION__, $member); |
||
211 | if ($extended !== null) { |
||
212 | return $extended; |
||
213 | } |
||
214 | |||
215 | if ($this->hasMethod('getPage')) { |
||
216 | if ($page = $this->getPage()) { |
||
217 | return $page->canArchive($member); |
||
218 | } |
||
219 | } |
||
220 | |||
221 | return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Basic permissions, defaults to page perms where possible. |
||
226 | * |
||
227 | * @param Member $member |
||
228 | * @param array $context |
||
229 | * |
||
230 | * @return boolean |
||
231 | */ |
||
232 | public function canCreate($member = null, $context = array()) |
||
233 | { |
||
234 | $extended = $this->extendedCan(__FUNCTION__, $member); |
||
235 | if ($extended !== null) { |
||
236 | return $extended; |
||
237 | } |
||
238 | |||
239 | return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * @throws \SilverStripe\ORM\ValidationException |
||
244 | */ |
||
245 | public function onBeforeWrite() |
||
246 | { |
||
247 | parent::onBeforeWrite(); |
||
248 | |||
249 | if ($areaID = $this->ParentID) { |
||
250 | if ($elementalArea = ElementalArea::get()->byID($areaID)) { |
||
251 | $elementalArea->write(); |
||
252 | } |
||
253 | } |
||
254 | |||
255 | if (!$this->Sort) { |
||
256 | $parentID = ($this->ParentID) ? $this->ParentID : 0; |
||
257 | |||
258 | $this->Sort = static::get()->max('Sort') + 1; |
||
259 | } |
||
260 | } |
||
261 | |||
262 | public function getCMSFields() |
||
263 | { |
||
264 | $this->beforeUpdateCMSFields(function (FieldList $fields) { |
||
265 | // Remove relationship fields |
||
266 | $fields->removeByName('ParentID'); |
||
267 | $fields->removeByName('Sort'); |
||
268 | |||
269 | $fields->addFieldToTab( |
||
270 | 'Root.Settings', |
||
271 | TextField::create('ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes')) |
||
272 | ->setAttribute( |
||
273 | 'placeholder', |
||
274 | _t(__CLASS__ . '.ExtraCssClassesPlaceholder', 'my_class another_class') |
||
275 | ) |
||
276 | ); |
||
277 | |||
278 | // Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group |
||
279 | $fields->removeByName('ShowTitle'); |
||
280 | $fields->replaceField( |
||
281 | 'Title', |
||
282 | TextCheckboxGroupField::create( |
||
283 | TextField::create('Title', _t(__CLASS__ . '.TitleLabel', 'Title (displayed if checked)')), |
||
284 | CheckboxField::create('ShowTitle', _t(__CLASS__ . '.ShowTitleLabel', 'Displayed')) |
||
285 | ) |
||
286 | ->setName('TitleAndDisplayed') |
||
287 | ); |
||
288 | |||
289 | // Rename the "Main" tab |
||
290 | $fields->fieldByName('Root.Main') |
||
291 | ->setTitle(_t(__CLASS__ . '.MainTabLabel', 'Content')); |
||
292 | |||
293 | $fields->addFieldsToTab('Root.Main', [ |
||
294 | HiddenField::create('AbsoluteLink', false, Director::absoluteURL($this->PreviewLink())), |
||
295 | HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())), |
||
296 | HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())), |
||
297 | ]); |
||
298 | |||
299 | $styles = $this->config()->get('styles'); |
||
300 | |||
301 | if ($styles && count($styles) > 0) { |
||
302 | $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles); |
||
303 | |||
304 | $fields->insertBefore($styleDropdown, 'ExtraClass'); |
||
305 | |||
306 | $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..')); |
||
307 | } else { |
||
308 | $fields->removeByName('Style'); |
||
309 | } |
||
310 | |||
311 | $history = $this->getHistoryFields(); |
||
312 | |||
313 | if ($history) { |
||
314 | $fields->addFieldsToTab('Root.History', $history); |
||
315 | } |
||
316 | }); |
||
317 | |||
318 | return parent::getCMSFields(); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Returns the history fields for this element. |
||
323 | * |
||
324 | * @param bool $checkLatestVersion Whether to check if this is the latest version. Prevents recursion, but can be |
||
325 | * overridden to get the history GridField if required. |
||
326 | * @return FieldList |
||
327 | */ |
||
328 | public function getHistoryFields($checkLatestVersion = true) |
||
329 | { |
||
330 | if ($checkLatestVersion && !$this->isLatestVersion()) { |
||
331 | // if viewing the history of the of page then don't show the history |
||
332 | // fields as then we have recursion. |
||
333 | return null; |
||
334 | } |
||
335 | |||
336 | Requirements::javascript('dnadesign/silverstripe-elemental:client/dist/js/bundle.js'); |
||
337 | |||
338 | $config = GridFieldConfig_RecordViewer::create(); |
||
339 | $config->removeComponentsByType(GridFieldPageCount::class); |
||
340 | $config->removeComponentsByType(GridFieldToolbarHeader::class); |
||
341 | // Replace the sortable ID column with a static header component |
||
342 | $config->removeComponentsByType(GridFieldSortableHeader::class); |
||
343 | $config->addComponent(new GridFieldTitleHeader); |
||
344 | |||
345 | $config |
||
346 | ->getComponentByType(GridFieldDetailForm::class) |
||
347 | ->setItemRequestClass(HistoricalVersionedGridFieldItemRequest::class); |
||
348 | |||
349 | $config->getComponentByType(GridFieldDataColumns::class) |
||
350 | ->setDisplayFields([ |
||
351 | 'Version' => '#', |
||
352 | 'RecordStatus' => _t(__CLASS__ . '.Record', 'Record'), |
||
353 | 'getAuthor.Name' => _t(__CLASS__ . '.Author', 'Author') |
||
354 | ]) |
||
355 | ->setFieldFormatting([ |
||
356 | 'RecordStatus' => '$VersionedStateNice <span class=\"element-history__date--small\">on $LastEditedNice</span>', |
||
357 | ]); |
||
358 | |||
359 | $config->removeComponentsByType(GridFieldViewButton::class); |
||
360 | $config->addComponent(new ElementalGridFieldHistoryButton()); |
||
361 | |||
362 | $history = Versioned::get_all_versions(__CLASS__, $this->ID) |
||
363 | ->sort('Version', 'DESC'); |
||
364 | |||
365 | return FieldList::create( |
||
366 | GridField::create('History', '', $history, $config) |
||
367 | ->addExtraClass('elemental-block__history') |
||
368 | ); |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Get the type of the current block, for use in GridField summaries, block |
||
373 | * type dropdowns etc. Examples are "Content", "File", "Media", etc. |
||
374 | * |
||
375 | * @return string |
||
376 | */ |
||
377 | public function getType() |
||
378 | { |
||
379 | return _t(__CLASS__ . '.BlockType', 'Block'); |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * @param ElementController $controller |
||
384 | * |
||
385 | * @return $this |
||
386 | */ |
||
387 | public function setController($controller) |
||
388 | { |
||
389 | $this->controller = $controller; |
||
390 | |||
391 | return $this; |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * @throws Exception If the specified controller class doesn't exist |
||
396 | * |
||
397 | * @return ElementController |
||
398 | */ |
||
399 | public function getController() |
||
400 | { |
||
401 | if ($this->controller) { |
||
402 | return $this->controller; |
||
403 | } |
||
404 | |||
405 | $controllerClass = self::config()->controller_class; |
||
406 | |||
407 | if (!class_exists($controllerClass)) { |
||
408 | throw new Exception('Could not find controller class ' . $controllerClass . ' as defined in ' . static::class); |
||
409 | } |
||
410 | |||
411 | $this->controller = Injector::inst()->create($controllerClass, $this); |
||
412 | $this->controller->doInit(); |
||
413 | |||
414 | return $this->controller; |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * @return Controller |
||
419 | */ |
||
420 | public function Top() |
||
421 | { |
||
422 | return (Controller::has_curr()) ? Controller::curr() : null; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Default way to render element in templates. Note that all blocks should |
||
427 | * be rendered through their {@link ElementController} class as this |
||
428 | * contains the holder styles. |
||
429 | * |
||
430 | * @return string|null HTML |
||
431 | */ |
||
432 | public function forTemplate($holder = true) |
||
433 | { |
||
434 | $templates = $this->getRenderTemplates(); |
||
435 | |||
436 | if ($templates) { |
||
0 ignored issues
–
show
|
|||
437 | return $this->renderWith($templates); |
||
438 | } |
||
439 | |||
440 | return null; |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * @param string $suffix |
||
445 | * |
||
446 | * @return array |
||
447 | */ |
||
448 | public function getRenderTemplates($suffix = '') |
||
449 | { |
||
450 | $classes = ClassInfo::ancestry($this->ClassName); |
||
451 | $classes[static::class] = static::class; |
||
452 | $classes = array_reverse($classes); |
||
453 | $templates = array(); |
||
454 | |||
455 | foreach ($classes as $key => $value) { |
||
456 | if ($value == BaseElement::class) { |
||
457 | continue; |
||
458 | } |
||
459 | |||
460 | if ($value == DataObject::class) { |
||
461 | break; |
||
462 | } |
||
463 | |||
464 | $templates[] = $value . $suffix . '_'. $this->getAreaRelationName(); |
||
465 | $templates[] = $value . $suffix; |
||
466 | } |
||
467 | |||
468 | return $templates; |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Strip all namespaces from class namespace. |
||
473 | * |
||
474 | * @param string $classname e.g. "\Fully\Namespaced\Class" |
||
475 | * |
||
476 | * @return string following the param example, "Class" |
||
477 | */ |
||
478 | protected function stripNamespacing($classname) |
||
479 | { |
||
480 | $classParts = explode('\\', $classname); |
||
481 | return array_pop($classParts); |
||
482 | } |
||
483 | |||
484 | /** |
||
485 | * @return string |
||
486 | */ |
||
487 | public function getSimpleClassName() |
||
488 | { |
||
489 | return strtolower($this->sanitiseClassName($this->ClassName, '__')); |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * @return null|DataObject |
||
494 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
495 | * @throws \SilverStripe\ORM\ValidationException |
||
496 | */ |
||
497 | public function getPage() |
||
498 | { |
||
499 | $area = $this->Parent(); |
||
500 | |||
501 | if ($area instanceof ElementalArea && $area->exists()) { |
||
502 | return $area->getOwnerPage(); |
||
503 | } |
||
504 | |||
505 | return null; |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * Get a unique anchor name |
||
510 | * |
||
511 | * @return string |
||
512 | */ |
||
513 | public function getAnchor() |
||
514 | { |
||
515 | if ($this->_anchor !== null) { |
||
516 | return $this->_anchor; |
||
517 | } |
||
518 | |||
519 | $anchorTitle = ''; |
||
520 | |||
521 | if (!$this->config()->disable_pretty_anchor_name) { |
||
522 | if ($this->hasMethod('getAnchorTitle')) { |
||
523 | $anchorTitle = $this->getAnchorTitle(); |
||
524 | } elseif ($this->config()->enable_title_in_template) { |
||
525 | $anchorTitle = $this->getField('Title'); |
||
526 | } |
||
527 | } |
||
528 | |||
529 | if (!$anchorTitle) { |
||
530 | $anchorTitle = 'e'.$this->ID; |
||
531 | } |
||
532 | |||
533 | $filter = URLSegmentFilter::create(); |
||
534 | $titleAsURL = $filter->filter($anchorTitle); |
||
535 | |||
536 | // Ensure that this anchor name isn't already in use |
||
537 | // ie. If two elemental blocks have the same title, it'll append '-2', '-3' |
||
538 | $result = $titleAsURL; |
||
539 | $count = 1; |
||
540 | while (isset(self::$_used_anchors[$result]) && self::$_used_anchors[$result] !== $this->ID) { |
||
541 | ++$count; |
||
542 | $result = $titleAsURL.'-'.$count; |
||
543 | } |
||
544 | self::$_used_anchors[$result] = $this->ID; |
||
545 | return $this->_anchor = $result; |
||
546 | } |
||
547 | |||
548 | /** |
||
549 | * @param string|null $action |
||
550 | * @return string|null |
||
551 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
552 | * @throws \SilverStripe\ORM\ValidationException |
||
553 | */ |
||
554 | public function AbsoluteLink($action = null) |
||
555 | { |
||
556 | if ($page = $this->getPage()) { |
||
557 | $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor(); |
||
558 | |||
559 | return $link; |
||
560 | } |
||
561 | |||
562 | return null; |
||
563 | } |
||
564 | |||
565 | /** |
||
566 | * @param string|null $action |
||
567 | * @return string |
||
568 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
569 | * @throws \SilverStripe\ORM\ValidationException |
||
570 | */ |
||
571 | public function Link($action = null) |
||
572 | { |
||
573 | if ($page = $this->getPage()) { |
||
574 | $link = $page->Link($action) . '#' . $this->getAnchor(); |
||
575 | |||
576 | $this->extend('updateLink', $link); |
||
577 | |||
578 | return $link; |
||
579 | } |
||
580 | |||
581 | return null; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * @param string|null $action |
||
586 | * @return string |
||
587 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
588 | * @throws \SilverStripe\ORM\ValidationException |
||
589 | */ |
||
590 | public function PreviewLink($action = null) |
||
591 | { |
||
592 | $action = $action . '?ElementalPreview=' . mt_rand(); |
||
593 | $link = $this->Link($action); |
||
594 | $this->extend('updatePreviewLink', $link); |
||
595 | |||
596 | return $link; |
||
597 | } |
||
598 | |||
599 | /** |
||
600 | * @return boolean |
||
601 | */ |
||
602 | public function isCMSPreview() |
||
603 | { |
||
604 | if (Controller::has_curr()) { |
||
605 | $controller = Controller::curr(); |
||
606 | |||
607 | if ($controller->getRequest()->requestVar('CMSPreview')) { |
||
608 | return true; |
||
609 | } |
||
610 | } |
||
611 | |||
612 | return false; |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * @return null|string |
||
617 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
618 | * @throws \SilverStripe\ORM\ValidationException |
||
619 | */ |
||
620 | public function CMSEditLink() |
||
621 | { |
||
622 | $relationName = $this->getAreaRelationName(); |
||
623 | $page = $this->getPage(true); |
||
624 | |||
625 | if (!$page) { |
||
626 | return null; |
||
627 | } |
||
628 | |||
629 | $editLinkPrefix = ''; |
||
630 | if (!$page instanceof SiteTree && method_exists($page, 'CMSEditLink')) { |
||
631 | $editLinkPrefix = Controller::join_links($page->CMSEditLink(), 'ItemEditForm'); |
||
632 | } else { |
||
633 | $editLinkPrefix = Controller::join_links(singleton(CMSPageEditController::class)->Link('EditForm'), $page->ID); |
||
634 | } |
||
635 | |||
636 | $link = Controller::join_links( |
||
637 | $editLinkPrefix, |
||
638 | 'field/' . $relationName . '/item/', |
||
639 | $this->ID |
||
640 | ); |
||
641 | |||
642 | $link = Controller::join_links( |
||
643 | $link, |
||
644 | 'edit' |
||
645 | ); |
||
646 | |||
647 | $this->extend('updateCMSEditLink', $link); |
||
648 | |||
649 | return $link; |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Retrieve a elemental area relation for creating cms links |
||
654 | * |
||
655 | * @return int|string The name of a valid elemental area relation |
||
656 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
657 | * @throws \SilverStripe\ORM\ValidationException |
||
658 | */ |
||
659 | public function getAreaRelationName() |
||
660 | { |
||
661 | $page = $this->getPage(); |
||
662 | |||
663 | if ($page) { |
||
664 | $has_one = $page->config()->get('has_one'); |
||
665 | $area = $this->Parent(); |
||
666 | |||
667 | foreach ($has_one as $relationName => $relationClass) { |
||
668 | if ($page instanceof BaseElement && $relationName === 'Parent') { |
||
669 | continue; |
||
670 | } |
||
671 | if ($relationClass === $area->ClassName) { |
||
672 | return $relationName; |
||
673 | } |
||
674 | } |
||
675 | } |
||
676 | |||
677 | return 'ElementalArea'; |
||
678 | } |
||
679 | |||
680 | /** |
||
681 | * Sanitise a model class' name for inclusion in a link. |
||
682 | * |
||
683 | * @return string |
||
684 | */ |
||
685 | public function sanitiseClassName($class, $delimiter = '-') |
||
686 | { |
||
687 | return str_replace('\\', $delimiter, $class); |
||
688 | } |
||
689 | |||
690 | public function unsanitiseClassName($class, $delimiter = '-') |
||
691 | { |
||
692 | return str_replace($delimiter, '\\', $class); |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * @return null|string |
||
697 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
698 | * @throws \SilverStripe\ORM\ValidationException |
||
699 | */ |
||
700 | public function getEditLink() |
||
701 | { |
||
702 | return $this->CMSEditLink(); |
||
703 | } |
||
704 | |||
705 | /** |
||
706 | * @return DBField|null |
||
707 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
708 | * @throws \SilverStripe\ORM\ValidationException |
||
709 | */ |
||
710 | public function PageCMSEditLink() |
||
711 | { |
||
712 | if ($page = $this->getPage()) { |
||
713 | return DBField::create_field('HTMLText', sprintf( |
||
714 | '<a href="%s">%s</a>', |
||
715 | $page->CMSEditLink(), |
||
716 | $page->Title |
||
717 | )); |
||
718 | } |
||
719 | |||
720 | return null; |
||
721 | } |
||
722 | |||
723 | /** |
||
724 | * @return string |
||
725 | */ |
||
726 | public function getMimeType() |
||
727 | { |
||
728 | return 'text/html'; |
||
729 | } |
||
730 | |||
731 | /** |
||
732 | * This can be overridden on child elements to create a summary for display |
||
733 | * in GridFields. |
||
734 | * |
||
735 | * @return string |
||
736 | */ |
||
737 | public function getSummary() |
||
738 | { |
||
739 | return ''; |
||
740 | } |
||
741 | |||
742 | |||
743 | /** |
||
744 | * Generate markup for element type icons suitable for use in GridFields. |
||
745 | * |
||
746 | * @return null|DBHTMLText |
||
747 | */ |
||
748 | public function getIcon() |
||
749 | { |
||
750 | $data = ArrayData::create([]); |
||
751 | |||
752 | $iconClass = $this->config()->get('icon'); |
||
753 | if ($iconClass) { |
||
754 | $data->IconClass = $iconClass; |
||
755 | |||
756 | // Add versioned states (rendered as a circle over the icon) |
||
757 | if ($this->hasExtension(Versioned::class)) { |
||
758 | $data->IsVersioned = true; |
||
759 | if ($this->isOnDraftOnly()) { |
||
760 | $data->VersionState = 'draft'; |
||
761 | $data->VersionStateTitle = _t( |
||
762 | 'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.ADDEDTODRAFTHELP', |
||
763 | 'Item has not been published yet' |
||
764 | ); |
||
765 | } elseif ($this->isModifiedOnDraft()) { |
||
766 | $data->VersionState = 'modified'; |
||
767 | $data->VersionStateTitle = $data->VersionStateTitle = _t( |
||
768 | 'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.MODIFIEDONDRAFTHELP', |
||
769 | 'Item has unpublished changes' |
||
770 | ); |
||
771 | } |
||
772 | } |
||
773 | |||
774 | return $data->renderWith(__CLASS__ . '/PreviewIcon'); |
||
775 | } |
||
776 | |||
777 | return null; |
||
778 | } |
||
779 | |||
780 | /** |
||
781 | * Get a description for this content element, if available |
||
782 | * |
||
783 | * @return string |
||
784 | */ |
||
785 | public function getDescription() |
||
786 | { |
||
787 | $description = $this->config()->uninherited('description'); |
||
788 | if ($description) { |
||
789 | return _t(__CLASS__ . '.Description', $description); |
||
790 | } |
||
791 | return ''; |
||
792 | } |
||
793 | |||
794 | /** |
||
795 | * Generate markup for element type, with description suitable for use in |
||
796 | * GridFields. |
||
797 | * |
||
798 | * @return DBField |
||
799 | */ |
||
800 | public function getTypeNice() |
||
801 | { |
||
802 | $description = $this->getDescription(); |
||
803 | $desc = ($description) ? ' <span class="element__note"> — ' . $description . '</span>' : ''; |
||
804 | |||
805 | return DBField::create_field( |
||
806 | 'HTMLVarchar', |
||
807 | $this->getType() . $desc |
||
808 | ); |
||
809 | } |
||
810 | |||
811 | /** |
||
812 | * @return \SilverStripe\ORM\FieldType\DBHTMLText |
||
813 | */ |
||
814 | public function getEditorPreview() |
||
815 | { |
||
816 | $templates = $this->getRenderTemplates('_EditorPreview'); |
||
817 | $templates[] = BaseElement::class . '_EditorPreview'; |
||
818 | |||
819 | return $this->renderWith($templates); |
||
820 | } |
||
821 | |||
822 | /** |
||
823 | * @return Member |
||
824 | */ |
||
825 | public function getAuthor() |
||
826 | { |
||
827 | if ($this->AuthorID) { |
||
828 | return Member::get()->byId($this->AuthorID); |
||
829 | } |
||
830 | |||
831 | return null; |
||
832 | } |
||
833 | |||
834 | /** |
||
835 | * Get a user defined style variant for this element, if available |
||
836 | * |
||
837 | * @return string |
||
838 | */ |
||
839 | public function getStyleVariant() |
||
840 | { |
||
841 | $style = $this->Style; |
||
842 | $styles = $this->config()->get('styles'); |
||
843 | |||
844 | if (isset($styles[$style])) { |
||
845 | $style = strtolower($style); |
||
846 | } else { |
||
847 | $style = ''; |
||
848 | } |
||
849 | |||
850 | $this->extend('updateStyleVariant', $style); |
||
851 | |||
852 | return $style; |
||
853 | } |
||
854 | |||
855 | /** |
||
856 | * @return mixed|null |
||
857 | * @throws \Psr\Container\NotFoundExceptionInterface |
||
858 | * @throws \SilverStripe\ORM\ValidationException |
||
859 | */ |
||
860 | public function getPageTitle() |
||
861 | { |
||
862 | $page = $this->getPage(); |
||
863 | |||
864 | if ($page) { |
||
865 | return $page->Title; |
||
866 | } |
||
867 | |||
868 | return null; |
||
869 | } |
||
870 | |||
871 | /** |
||
872 | * Get a "nice" label for use in the block history GridField |
||
873 | * |
||
874 | * @return string |
||
875 | */ |
||
876 | public function getVersionedStateNice() |
||
877 | { |
||
878 | if ($this->WasPublished) { |
||
879 | return _t(__CLASS__ . '.Published', 'Published'); |
||
880 | } |
||
881 | |||
882 | return _t(__CLASS__ . '.Modified', 'Modified'); |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * Return a formatted date for use in the block history GridField |
||
887 | * |
||
888 | * @return string |
||
889 | */ |
||
890 | public function getLastEditedNice() |
||
891 | { |
||
892 | return $this->dbObject('LastEdited')->Nice(); |
||
893 | } |
||
894 | } |
||
895 |
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.