Skip to content
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

Merged
merged 1 commit into from
Nov 26, 2024

Conversation

GuySartorelli
Copy link
Member

@GuySartorelli GuySartorelli commented Nov 10, 2024

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

@GuySartorelli GuySartorelli marked this pull request as draft November 10, 2024 22:39
@GuySartorelli GuySartorelli force-pushed the pulls/6/move-from-cms branch 2 times, most recently from 2a11c1e to ade38d7 Compare November 11, 2024 03:20
/**
* Get localised description for this class
*/
public function i18n_classDescription(): ?string
Copy link
Member Author

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:

  1. This method is being moved from SiteTree::i18n_classDescription() - keeping the same name reduces upgrade pain.
  2. This method name is consistent with the other i18n_* methods on this class.

Comment on lines -119 to +125
* @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;
Copy link
Member Author

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

Comment on lines 127 to 134
/**
* 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;
Copy link
Member Author

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);
Copy link
Member Author

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.

Comment on lines +3545 to +3550
* 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
Copy link
Member Author

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().

Comment on lines +170 to +203
// "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'
);
}
}
Copy link
Member Author

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
Copy link
Member Author

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

Comment on lines +818 to +822
protected function extendCanAddChildren()
{
// Prevent canAddChildren from extending itself
return null;
}
Copy link
Member Author

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.

Comment on lines +838 to +852
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;
}
Copy link
Member Author

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.

Comment on lines +189 to +191
$subject = $owner->hasMethod('getRecordForAllowedChildrenValidation')
? $owner->getRecordForAllowedChildrenValidation()
: $owner;
Copy link
Member Author

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"

Copy link
Member

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

@GuySartorelli GuySartorelli force-pushed the pulls/6/move-from-cms branch 3 times, most recently from 990e4bc to 43bdde5 Compare November 14, 2024 04:31
@GuySartorelli GuySartorelli marked this pull request as ready for review November 14, 2024 04:40
* Example (with optional title attribute):
* "deletedonlive" => ['text' => "Deleted", 'title' => 'This page has been deleted']
*/
public function getStatusFlags(bool $cached = true): array
Copy link
Member

@emteknetnz emteknetnz Nov 14, 2024

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?

Comment on lines 35 to 37
* 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 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.

Copy link
Member Author

@GuySartorelli GuySartorelli Nov 14, 2024

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.

Comment on lines +189 to +191
$subject = $owner->hasMethod('getRecordForAllowedChildrenValidation')
? $owner->getRecordForAllowedChildrenValidation()
: $owner;
Copy link
Member

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

@@ -6,14 +6,12 @@
use InvalidArgumentException;
use LogicException;
use SilverStripe\Model\ModelData;
use SilverStripe\Dev\Deprecation;
Copy link
Member Author

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

Comment on lines +34 to +37
private array $columnsForStatusFlag = [
'Title',
'Name',
];
Copy link
Member Author

@GuySartorelli GuySartorelli Nov 20, 2024

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.

Comment on lines 236 to 239
// Add on status flags
if ($columnName === $this->statusFlagColumn) {
$value .= $record->getStatusFlagMarkup('ss-gridfield-badge badge');
}
Copy link
Member Author

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.

Comment on lines 958 to 961
$statusFlags = $record->getStatusFlagMarkup('badge--breadcrumbs badge');
if ($statusFlags) {
$items->last()->setField('Extra', DBField::create_field('HTMLFragment', $statusFlags));
}
Copy link
Member Author

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

Comment on lines -936 to +940
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}";
Copy link
Member Author

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)

/**
* Get the HTML markup for rendering status flags for this model.
*/
public function getStatusFlagMarkup(string $cssClasses = 'badge'): string
Copy link
Member Author

@GuySartorelli GuySartorelli Nov 20, 2024

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.

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
Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works great

@emteknetnz emteknetnz merged commit 26d5b11 into silverstripe:6 Nov 26, 2024
4 of 12 checks passed
@emteknetnz emteknetnz deleted the pulls/6/move-from-cms branch November 26, 2024 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants