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:
2026-09-27 04:14:58 +02:00
co-authored by Claude Opus 5.5
parent 8aacc5dc4f
commit 395049ec97
6 changed files with 117 additions and 36 deletions
@@ -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)]);