diff --git a/_config.php b/_config.php index 9e519d4a..71c7914c 100644 --- a/_config.php +++ b/_config.php @@ -5,5 +5,4 @@ // Avoid creating global variables call_user_func(function () { - }); diff --git a/_config/config.yml b/_config/config.yml index 553ede50..c3ad1284 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -21,3 +21,7 @@ SilverStripe\CMS\Forms\AnchorSelectorField: SilverStripe\LinkField\Form\FormFactory: extensions: - SilverStripe\LinkField\Extensions\FormFactoryExtension + +SilverStripe\ORM\DataObject: + extensions: + - SilverStripe\LinkField\Extensions\DataObjectExtension diff --git a/src/Extensions/DataObjectExtension.php b/src/Extensions/DataObjectExtension.php new file mode 100644 index 00000000..34c60e2a --- /dev/null +++ b/src/Extensions/DataObjectExtension.php @@ -0,0 +1,47 @@ +owner->ID is not zero when creating new objects + parent::onAfterWrite(); + foreach ($this->owner->hasOne() as $relation => $relationClassName) { + $relationField = $relation . 'ID'; + $relationID = $this->owner->$relationField; + if (!$relationID) { + continue; + } + if (!is_a($relationClassName, Link::class, true) && !is_a($relationClassName, LinkArea::class, true)) { + continue; + } + // skip for the has_one:LinkArea relation on Link + if (is_a($this->owner, Link::class) && $relation === 'LinkArea') { + continue; + } + $relationObj = $relationClassName::get()->byID($relationID); + if ($relationObj === null) { + // could throw an Exception here, though not sure how if user would be able to fix it + continue; + } + $doWrite = false; + if ($relationObj->OwnerID !== $this->owner->ID) { + $relationObj->OwnerID = $this->owner->ID; + $doWrite = true; + } + if ($relationObj->OwnerClassName !== $this->owner->ClassName) { + $relationObj->OwnerClassName = $this->owner->ClassName; + $doWrite = true; + } + if ($doWrite) { + $relationObj->write(); + } + } + } +} diff --git a/src/Extensions/LinkObjectExtension.php b/src/Extensions/LinkObjectExtension.php new file mode 100644 index 00000000..1c330f53 --- /dev/null +++ b/src/Extensions/LinkObjectExtension.php @@ -0,0 +1,76 @@ + 'Int', + 'OwnerClassName' => 'Varchar', + ]; + + public function canView($member = null) + { + return $this->canCheck('canView', $member); + } + + public function canCreate($member = null) + { + return $this->canCheck('canCreate', $member); + } + + public function canEdit($member = null) + { + return $this->canCheck('canEdit', $member); + } + + public function canDelete($member = null) + { + return $this->canCheck('canDelete', $member); + } + + private function canCheck(string $canMethod, $member) + { + if (!$member) { + $member = Security::getCurrentUser(); + } + $extended = $this->extendedCan($canMethod, $member); + if ($extended !== null) { + return $extended; + } + $owner = $this->getOwningDataObject(); + if ($owner) { + return $owner->$canMethod($member); + } + return parent::$canMethod($member); + } + + private function getOwningDataObject(): ?DataObject + { + // Thismethod is not called getOwner() because of existing Extension::getOwner() method + // + // If this is a Link, and LinkArea is set on it return that + if (is_a($this->owner, Link::class, true)) { + $linkArea = $this->owner->LinkArea(); + if ($linkArea && $linkArea->exists()) { + return $linkArea; + } + } + // Otherwise look for the ownerID + ownerClassName + // These are set in DataObjectExtension::onAfterWrite() + $ownerID = $this->owner->OwnerID; + $ownerClassName = $this->owner->OwnerClassName; + if ($ownerID === 0 || $ownerClassName === '') { + return null; + } + return $ownerClassName::get()->byID($ownerID); + } +} diff --git a/src/Models/Link.php b/src/Models/Link.php index 87d2de0f..3ad255fc 100644 --- a/src/Models/Link.php +++ b/src/Models/Link.php @@ -16,6 +16,9 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\View\Requirements; +use SilverStripe\LinkField\Extensions\LinkObjectExtension; +use SilverStripe\Versioned\Versioned; +use SilverStripe\LinkField\Models\LinkArea; /** * A Link Data Object. This class should be a subclass, and you should never directly interact with a plain Link @@ -33,6 +36,15 @@ class Link extends DataObject implements JsonData, Type 'OpenInNew' => 'Boolean', ]; + private static array $has_one = [ + 'LinkArea' => LinkArea::class + ]; + + private static array $extensions = [ + Versioned::class, + LinkObjectExtension::class, + ]; + /** * In-memory only property used to change link type * This case is relevant for CMS edit form which doesn't use React driven UI diff --git a/src/Models/LinkArea.php b/src/Models/LinkArea.php new file mode 100644 index 00000000..eb968940 --- /dev/null +++ b/src/Models/LinkArea.php @@ -0,0 +1,34 @@ + Link::class, + ]; + + private static array $owns = [ + 'Links', + ]; + + private static array $cascade_deletes = [ + 'Links', + ]; + + private static array $cascade_duplicates = [ + 'Links', + ]; + + private static array $extensions = [ + Versioned::class, + LinkObjectExtension::class, + ]; +}