fix: global-edit rules for every library entry that pages show (L2)
Divi renders any referenced et_pb_layout, whatever its scope term. A normal entry with usages now gets the dry run, confirm, P8a, backup and the site-wide GlobalItem scope; lit-divi-delete's notice and the library-list detail name its pages too. The usage scan is the same single query. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -113,10 +113,10 @@ final class LibraryFixtures
|
||||
}
|
||||
|
||||
/**
|
||||
* GlobalUsages whose query answers $rows (a closure is asked on every call); the rights checks
|
||||
* GlobalUsages whose query answers $rows (a closure gets the entry ID on every call); the rights checks
|
||||
* are the real ones, with the post-cache functions stubbed.
|
||||
*
|
||||
* @param list<array{id: int, status: string, type: string}>|\Closure(): list<array{id: int, status: string, type: string}> $rows
|
||||
* @param list<array{id: int, status: string, type: string}>|\Closure(int): list<array{id: int, status: string, type: string}> $rows
|
||||
*/
|
||||
public static function usages(array|\Closure $rows, ?DiviRoles $roles = null): GlobalUsages
|
||||
{
|
||||
@@ -124,7 +124,7 @@ final class LibraryFixtures
|
||||
Functions\when('_prime_post_caches')->justReturn(null);
|
||||
Functions\when('wp_cache_delete')->justReturn(true);
|
||||
$usages = Mockery::mock(GlobalUsages::class, [null, $roles ?? self::roles()])->makePartial();
|
||||
$usages->allows('rows')->andReturnUsing(static fn (): array => is_array($rows) ? $rows : $rows());
|
||||
$usages->allows('rows')->andReturnUsing(static fn (int $id): array => is_array($rows) ? $rows : $rows($id));
|
||||
|
||||
return $usages;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,17 @@ final class GlobalEditsTest extends TestCase
|
||||
Functions\when('_prime_post_caches')->justReturn(null);
|
||||
}
|
||||
|
||||
/** The live posts ($this->live; 1053 published, the others private) and draft 1070. */
|
||||
/** @var list<int> Normal (non-global) entries that pages nevertheless show (audit L2). */
|
||||
private array $referenced = [];
|
||||
|
||||
/**
|
||||
* For the global entries and $this->referenced: the live posts ($this->live; 1053 published, the
|
||||
* others private) and draft 1070. Other entries are shown nowhere.
|
||||
*/
|
||||
private function usages(): GlobalUsages
|
||||
{
|
||||
return LibraryFixtures::usages(fn (): array => array_merge(
|
||||
return LibraryFixtures::usages(fn (int $id): array => ! LibraryFixtures::ITEMS[$id][2]
|
||||
&& ! in_array($id, $this->referenced, true) ? [] : array_merge(
|
||||
array_map(
|
||||
static fn (int $id): array => ['id' => $id, 'status' => $id === 1053 ? 'publish' : 'private', 'type' => 'page'],
|
||||
$this->live
|
||||
@@ -133,6 +140,45 @@ final class GlobalEditsTest extends TestCase
|
||||
self::assertArrayNotHasKey('affected', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit L2: Divi renders every referenced entry, global scope or not – a normal entry that pages
|
||||
* show gets the dry run, P8a and the site-wide scope of a global one.
|
||||
*/
|
||||
public function testAReferencedNormalEntryIsEditedLikeAGlobalOne(): void
|
||||
{
|
||||
LibraryFixtures::given([LibraryFixtures::item(1057)]);
|
||||
$this->referenced = [1057];
|
||||
$this->live = [1053, 1080];
|
||||
|
||||
$dryRun = (new DiviUpdateAbility($this->writer()))->execute(self::relabel(1057));
|
||||
self::denyOn('edit_post', [1080]);
|
||||
$blocked = (new DiviUpdateAbility($this->writer()))->execute(self::relabel(1057, true));
|
||||
LibraryFixtures::given([LibraryFixtures::item(1057)]);
|
||||
$done = (new DiviUpdateAbility($this->writer()))->execute(self::relabel(1057, true));
|
||||
|
||||
self::assertToolError($dryRun, 'confirm_required');
|
||||
self::assertStringContainsString('Bibliothekseintrag 1057', $dryRun->get_error_message());
|
||||
self::assertStringContainsString('als globales Modul', $dryRun->get_error_message());
|
||||
self::assertSame(3, $dryRun->get_error_data()['affected_count']);
|
||||
self::assertToolError($blocked, 'forbidden');
|
||||
self::assertTrue($done['ok']);
|
||||
self::assertCount(1, $this->saved);
|
||||
self::assertSame(SaveScope::GlobalItem, $this->saved[0][1]);
|
||||
}
|
||||
|
||||
public function testDeleteInAReferencedNormalEntryNamesTheAffectedPages(): void
|
||||
{
|
||||
LibraryFixtures::given([LibraryFixtures::item(1055)]);
|
||||
$this->referenced = [1055];
|
||||
|
||||
$result = (new DiviDeleteAbility($this->writer()))->execute(
|
||||
['post_id' => 1055, 'tree_rev' => self::rev(1055), 'ids' => ['s0.r0.c0.m0']]
|
||||
);
|
||||
|
||||
self::assertToolError($result, 'confirm_required');
|
||||
self::assertSame(2, $result->get_error_data()['affected_count']);
|
||||
}
|
||||
|
||||
public function testGlobalItemDryRunNamesTheAffectedPagesAndSavesNothing(): void
|
||||
{
|
||||
LibraryFixtures::given([LibraryFixtures::item(1059)]);
|
||||
|
||||
@@ -38,7 +38,9 @@ final class LibraryListAbilityTest extends TestCase
|
||||
));
|
||||
$access = LibraryFixtures::access($deniedRoles, true, $store);
|
||||
$usages = LibraryFixtures::usages(
|
||||
static fn (): array => [['id' => 1053, 'status' => 'draft', 'type' => 'page']]
|
||||
static fn (int $id): array => in_array($id, [1058, 1056], true)
|
||||
? [['id' => 1053, 'status' => 'draft', 'type' => 'page']]
|
||||
: []
|
||||
);
|
||||
|
||||
return new LibraryListAbility($access, LibraryFixtures::loader($access), DiviWriterFixture::designReader(), $usages);
|
||||
@@ -185,6 +187,18 @@ final class LibraryListAbilityTest extends TestCase
|
||||
self::assertToolError($this->ability()->permission(['id' => 1058]), 'forbidden');
|
||||
}
|
||||
|
||||
/** Audit L2: a normal entry that a page shows as an instance names that page too. */
|
||||
public function testDetailOfAReferencedNormalEntryNamesItsPages(): void
|
||||
{
|
||||
LibraryFixtures::given([LibraryFixtures::item(1056)]);
|
||||
|
||||
$result = $this->ability([], [1056])->execute(['id' => 1056]);
|
||||
|
||||
self::assertSame('normal', $result['items'][0]['scope']);
|
||||
self::assertSame(1, $result['affected_count']);
|
||||
self::assertSame(1053, $result['affected'][0]['post_id']);
|
||||
}
|
||||
|
||||
public function testDetailOfANormalEntryHasNoAffectedPages(): void
|
||||
{
|
||||
LibraryFixtures::given([LibraryFixtures::item(1055)]);
|
||||
|
||||
Reference in New Issue
Block a user