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) { |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||||
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')) |
||||||
0 ignored issues
–
show
'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
Loading history...
|
|||||||
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())), |
||||||
0 ignored issues
–
show
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
Loading history...
|
|||||||
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'); |
||||||
0 ignored issues
–
show
'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
Loading history...
|
|||||||
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); |
||||||
0 ignored issues
–
show
$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
Loading 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()) { |
||||||
0 ignored issues
–
show
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
Loading history...
|
|||||||
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) |
||||||
0 ignored issues
–
show
'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
Loading history...
$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
Loading history...
|
|||||||
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
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
Loading history...
|
|||||||
437 | return $this->renderWith($templates); |
||||||
0 ignored issues
–
show
|
|||||||
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); |
||||||
0 ignored issues
–
show
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
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...
|
|||||||
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()) { |
||||||
0 ignored issues
–
show
The method
isOnDraftOnly() does not exist on DNADesign\Elemental\Models\BaseElement . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
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()) { |
||||||
0 ignored issues
–
show
The method
isModifiedOnDraft() does not exist on DNADesign\Elemental\Models\BaseElement . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
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) { |
||||||
0 ignored issues
–
show
The property
AuthorID does not exist on DNADesign\Elemental\Models\BaseElement . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||||||
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) { |
||||||
0 ignored issues
–
show
The property
WasPublished does not exist on DNADesign\Elemental\Models\BaseElement . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||||||
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 |