Skip to content

Commit d2b62a8

Browse files
committed
1 parent ec9dc5e commit d2b62a8

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
## Unreleased
44

5+
- Added `craft\base\NestedElementInterface::getOwners()`.
6+
- Added `craft\base\NestedElementTrait::getOwners()`.
57
- Fixed a bug where entries’ and categories’ descendants’ URIs weren’t always getting updated after a parent’s URI changed. ([#17804](https://github.com/craftcms/cms/discussions/17804))
68
- Fixed a bug where addresses weren’t saving `firstName`, `lastName`, and `fullName` values properly. ([#17807](https://github.com/craftcms/cms/issues/17807))
79
- Fixed an n+1 query bug when working with Content Block fields. ([#17801](https://github.com/craftcms/cms/issues/17801))
810
- Fixed a bug where element selector modals’ sidebars would go out of view if the browser had a custom font size. ([#17809](https://github.com/craftcms/cms/issues/17809))
911
- Fixed a bug where relation field values weren’t getting updated properly when two elements were merged together. ([#17817](https://github.com/craftcms/cms/issues/17817))
1012
- Fixed an authorization error that could occur when editing a provisional draft of a nested element.
1113
- Fixed a bug where custom field values could be lost when changing an entry type. ([#17821](https://github.com/craftcms/cms/issues/17821))
14+
- Fixed a bug where non-admin users couldn’t edit recursively-nested elements if they didn’t have save permissions for the top-level element.
1215
- Fixed a styling issue.
1316

1417
## 5.8.16 - 2025-08-29

src/base/NestedElementInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ public function getOwner(): ?ElementInterface;
8282
*/
8383
public function setOwner(?ElementInterface $owner): void;
8484

85+
/**
86+
* Returns each of the element’s owners
87+
*
88+
* @param array $criteria
89+
* @return ElementInterface[]
90+
* @throws InvalidConfigException if the element is misconfigured
91+
* @since 5.8.17
92+
*/
93+
public function getOwners(array $criteria = []): array;
94+
8595
/**
8696
* Returns the field that contains the element.
8797
*

src/base/NestedElementTrait.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public static function eagerLoadingMap(array $sourceElements, string $handle): a
113113
*/
114114
private ElementInterface|false|null $_owner = null;
115115

116+
/**
117+
* @var ElementInterface[]
118+
* @see getOwners()
119+
*/
120+
private array $_owners;
121+
116122
public function __clone(): void
117123
{
118124
parent::__clone();
@@ -301,6 +307,32 @@ public function getOwner(): ?ElementInterface
301307
return $this->_owner ?: null;
302308
}
303309

310+
/**
311+
* @@inheritdoc
312+
*/
313+
public function getOwners(array $criteria = []): array
314+
{
315+
if (!isset($this->_owners)) {
316+
$this->_owners = [];
317+
$ownerType = $this->ownerType();
318+
if ($ownerType) {
319+
$ownerIds = (new Query())
320+
->select('ownerId')
321+
->from(Table::ELEMENTS_OWNERS)
322+
->where(['elementId' => $this->id])
323+
->column();
324+
if (!empty($ownerIds)) {
325+
$query = $ownerType::find()
326+
->id($ownerIds);
327+
Craft::configure($query, $criteria + $this->ownerCriteria());
328+
$this->_owners = $query->all();
329+
}
330+
}
331+
}
332+
333+
return $this->_owners;
334+
}
335+
304336
private function ownerCriteria(): array
305337
{
306338
return [

src/fields/ContentBlock.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,26 @@ public function canViewElement(NestedElementInterface $element, User $user): ?bo
344344
public function canSaveElement(NestedElementInterface $element, User $user): ?bool
345345
{
346346
$owner = $element->getOwner();
347-
return $owner && Craft::$app->getElements()->canSave($owner, $user);
347+
348+
if (!$owner) {
349+
return false;
350+
}
351+
352+
if (Craft::$app->getElements()->canSave($owner, $user)) {
353+
return true;
354+
}
355+
356+
// Check all the owners. Maybe the user can save one of the other ones?
357+
/** @phpstan-ignore-next-line */
358+
if (!Craft::$app->getElements()->canSave($owner, $user) && !$owner->getIsRevision()) {
359+
foreach ($element->getOwners(['revisions' => false]) as $o) {
360+
if ($o->id !== $owner->id && Craft::$app->getElements()->canSave($o, $user)) {
361+
return true;
362+
}
363+
}
364+
}
365+
366+
return false;
348367
}
349368

350369
/**

src/fields/Matrix.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,26 @@ public function canViewElement(NestedElementInterface $element, User $user): ?bo
564564
public function canSaveElement(NestedElementInterface $element, User $user): ?bool
565565
{
566566
$owner = $element->getOwner();
567-
return $owner && Craft::$app->getElements()->canSave($owner, $user);
567+
568+
if (!$owner) {
569+
return false;
570+
}
571+
572+
if (Craft::$app->getElements()->canSave($owner, $user)) {
573+
return true;
574+
}
575+
576+
// Check all the owners. Maybe the user can save one of the other ones?
577+
/** @phpstan-ignore-next-line */
578+
if (!Craft::$app->getElements()->canSave($owner, $user) && !$owner->getIsRevision()) {
579+
foreach ($element->getOwners(['revisions' => false]) as $o) {
580+
if ($o->id !== $owner->id && Craft::$app->getElements()->canSave($o, $user)) {
581+
return true;
582+
}
583+
}
584+
}
585+
586+
return false;
568587
}
569588

570589
/**

0 commit comments

Comments
 (0)