-
Notifications
You must be signed in to change notification settings - Fork 821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API Move logic from silverstripe/cms into central place #11460
API Move logic from silverstripe/cms into central place #11460
Conversation
2f4001e
to
b57b8e2
Compare
2a11c1e
to
ade38d7
Compare
/** | ||
* Get localised description for this class | ||
*/ | ||
public function i18n_classDescription(): ?string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know new method names shouldn't have underscore, but I've kept this one in for two reasons:
- This method is being moved from
SiteTree::i18n_classDescription()
- keeping the same name reduces upgrade pain. - This method name is consistent with the other
i18n_*
methods on this class.
* @var string | ||
* @config | ||
*/ | ||
private static $singular_name = null; | ||
private static ?string $singular_name = null; | ||
|
||
/** | ||
* Human-readable plural name | ||
* @var string | ||
* @config | ||
*/ | ||
private static $plural_name = null; | ||
private static ?string $plural_name = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated tidy-up and strong typing for these config
src/ORM/DataObject.php
Outdated
/** | ||
* Description of the class. | ||
* Unlike most configuration, this is usually used uninherited, meaning it should be defined | ||
* on each subclass. | ||
* | ||
* Used in some areas of the CMS, e.g. when selecting what type of record to create. | ||
*/ | ||
private static ?string $class_description = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the description
config on SiteTree
.
Renamed this because description
is very vague (are we describing the class or the record?) and class_description
is very clear in its purpose.
*/ | ||
public function classDescription(): ?string | ||
{ | ||
return static::config()->get('class_description', Config::UNINHERITED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentionally uninherited because the two classes that use this right now (SiteTree
and BaseElement
) both want the config to be uninherited.
Makes sense - the subclass should have a more specific description so it's clear how it's different from the superclass.
* Also flush the cached results for all relations (has_one, has_many, many_many) | ||
* | ||
* @param boolean $persistent When true will also clear persistent data stored in the Cache system. | ||
* @param bool $persistent When true will also clear persistent data stored in the Cache system. | ||
* When false will just clear session-local cached data | ||
* @return static $this | ||
*/ | ||
public function flushCache($persistent = true) | ||
public function flushCache(bool $persistent = true): static |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a little more description and hardcoding while I'm here. See ModelData::flushCache()
.
// "Can be root" validation | ||
if (!$owner::config()->get('can_be_root') && !$owner->ParentID) { | ||
$validationResult->addError( | ||
_t( | ||
__CLASS__ . '.TypeOnRootNotAllowed', | ||
'Model type "{type}" is not allowed on the root level', | ||
['type' => $owner->i18n_singular_name()] | ||
), | ||
ValidationResult::TYPE_ERROR, | ||
'CAN_BE_ROOT' | ||
); | ||
} | ||
|
||
// Allowed children validation | ||
$parent = $owner->getParent(); | ||
if ($parent && $parent->exists()) { | ||
// No need to check for subclasses or instanceof, as allowedChildren() already | ||
// deconstructs any inheritance trees already. | ||
$allowed = $parent->allowedChildren(); | ||
$subject = $owner->hasMethod('getRecordForAllowedChildrenValidation') | ||
? $owner->getRecordForAllowedChildrenValidation() | ||
: $owner; | ||
if (!in_array($subject->ClassName, $allowed ?? [])) { | ||
$validationResult->addError( | ||
_t( | ||
__CLASS__ . '.ChildTypeNotAllowed', | ||
'Model type "{type}" not allowed as child of this parent record', | ||
['type' => $subject->i18n_singular_name()] | ||
), | ||
ValidationResult::TYPE_ERROR, | ||
'ALLOWED_CHILDREN' | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New validation comes from SiteTree
- it's validating against the new can_be_root
and allowed_children
* Duplicates each child of this record recursively and returns the top-level duplicate record. | ||
* If there is a sort field, new sort values are set for the duplicates to retain their sort order. | ||
*/ | ||
public function duplicateWithChildren(): DataObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CMSMain needs this, so moving it from SiteTree
to Hierarchy
protected function extendCanAddChildren() | ||
{ | ||
// Prevent canAddChildren from extending itself | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See Versioned
for other cases where we do this. This method allows us to have $extendedCan()
above without getting into an infinite recursion loop.
protected function canCreate(?Member $member, array $context): ?bool | ||
{ | ||
// Parent is added to context through CMSMain | ||
// Note that not having a parent doesn't necessarily mean this record is being | ||
// created at the root, so we can't check against can_be_root here. | ||
$parent = isset($context['Parent']) ? $context['Parent'] : null; | ||
$parentInHierarchy = ($parent && is_a($parent, $this->getHierarchyBaseClass())); | ||
if ($parentInHierarchy && !in_array(get_class($this->getOwner()), $parent->allowedChildren())) { | ||
return false; | ||
} | ||
if ($parent?->exists() && $parentInHierarchy && !$parent->canAddChildren($member)) { | ||
return false; | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is moved here from SiteTree::canCreate()
and checks against allowed_children
config.
$subject = $owner->hasMethod('getRecordForAllowedChildrenValidation') | ||
? $owner->getRecordForAllowedChildrenValidation() | ||
: $owner; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In SiteTree
there was a hardcoded reference to VirtualPage
and one of its relations here. Instead, we check for the presence of a new method which allowed VirtualPage
to say "use this instead of me"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not very tidy code, though I also can't think of a better way to do it given that Hierarchy is an extension
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
990e4bc
to
43bdde5
Compare
43bdde5
to
75ac6cc
Compare
* Example (with optional title attribute): | ||
* "deletedonlive" => ['text' => "Deleted", 'title' => 'This page has been deleted'] | ||
*/ | ||
public function getStatusFlags(bool $cached = true): array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems ... kind of out of scope?
I don't like the idea of adding generic functionality that we might use in the future, but we're not actually planning on implementing straight away
Also the method name for this is wrong for a generic method as 'status flags' is clearly something for versioning
It feels like this method should just live on the Versioned extension instead of on ModelData?
src/ORM/Hierarchy/Hierarchy.php
Outdated
* The name of the dedicated sort field, if there is one. | ||
* Note that this does not automatically apply to default_sort, | ||
* you still have to set that configuration separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* The name of the dedicated sort field, if there is one. | |
* Note that this does not automatically apply to default_sort, | |
* you still have to set that configuration separately. | |
* The name of the dedicated sort field, set to null if there is not one. | |
* Does not affect default_sort which needs to be configured separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"set to null" sounds like we're instructing developers to set it to null, rather than what I think you intend which is to indicate it will be null if there isn't one.
That feels like it's implied already (both through the "if there is one" language and the fact that it's literally set to null here) but I'll add an explicit note of it to this comment to avoid ping pong.
$subject = $owner->hasMethod('getRecordForAllowedChildrenValidation') | ||
? $owner->getRecordForAllowedChildrenValidation() | ||
: $owner; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not very tidy code, though I also can't think of a better way to do it given that Hierarchy is an extension
75ac6cc
to
113a2e4
Compare
f3393da
to
d4c320a
Compare
@@ -6,14 +6,12 @@ | |||
use InvalidArgumentException; | |||
use LogicException; | |||
use SilverStripe\Model\ModelData; | |||
use SilverStripe\Dev\Deprecation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated but since it's not used anymore I removed it
private array $columnsForStatusFlag = [ | ||
'Title', | ||
'Name', | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were the defaults in VersionedGridFieldState
(see https://github.com/silverstripe/silverstripe-versioned/pull/479/files#diff-c3dbb0835d959d49ae741afb170b84162d26849c69dce38c55530e9338980b46)
All of the new status flag code in this class is adapted from there.
// Add on status flags | ||
if ($columnName === $this->statusFlagColumn) { | ||
$value .= $record->getStatusFlagMarkup('ss-gridfield-badge badge'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds the status flags to rows in the gridfield.
$statusFlags = $record->getStatusFlagMarkup('badge--breadcrumbs badge'); | ||
if ($statusFlags) { | ||
$items->last()->setField('Extra', DBField::create_field('HTMLFragment', $statusFlags)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds the status flags to the breadcrumbs in a gridfield's edit form
if ($this->record && $this->record->ID) { | ||
$title = ($this->record->Title) ? $this->record->Title : "#{$this->record->ID}"; | ||
$record = $this->getRecord(); | ||
if ($record && $record->ID) { | ||
$title = ($record->Title) ? $record->Title : "#{$record->ID}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring slightly so I can use the $record
variable lower down - also updating this to use best practice (public getter better than protected property)
src/Model/ModelData.php
Outdated
/** | ||
* Get the HTML markup for rendering status flags for this model. | ||
*/ | ||
public function getStatusFlagMarkup(string $cssClasses = 'badge'): string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing this instead of a template because it's run a lot e.g. over every record in a gridfield, and rendering templates is relatively slow. This also retains the same code and functionality as previously, as closely as I can, which means less work for me right now since this is on the very edge of "in scope" for this card.
Putting it here because I need it in both LeftAndMain
templates and two places for gridfields - I didn't want to have the exact same code three times.
d4c320a
to
cfae487
Compare
This logic is used by CMSMain but needs to be callable on any hierarchical model. In some cases this logic is generic enough it could be on any model or any DataObject
cfae487
to
346cbd9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works great
This logic is used by
CMSMain
but needs to be callable on any hierarchical model.In some cases this logic is generic enough it could be on any model or any
DataObject
.See the corresponding CMS PR to see where this code came from.
Issue
SiteTree
inCMSMain
silverstripe-cms#2947