fix: tb-assign reattaches only templates no Theme Builder post lists (audit L4)

"Detached" now means for lit/tb-assign what lit/tb-list shows as detached:
DiviThemeBuilder::isListedAnywhere() looks for any _et_template row (live,
draft or Theme Builder library post). A template another Theme Builder post
lists is refused with or without reattach; nothing is written.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-27 23:06:35 +02:00
co-authored by Claude Opus 5.5
parent dcae2d4b91
commit e64d32b1f1
6 changed files with 95 additions and 3 deletions
@@ -180,6 +180,27 @@ final class TbAssignAbilityTest extends TestCase
self::assertTrue($result['previous']['enabled']);
}
/**
* Audit L4: reattach takes only templates no Theme Builder post lists (tb-list's detached) – not one that a
* Theme Builder library item or draft lists; nothing is written.
*/
public function testReattachRefusesATemplateAnotherThemeBuilderPostLists(): void
{
$listedElsewhere = ThemeBuilderFixtures::template(1066, ['attached' => false]);
$store = ThemeBuilderFixtures::store([], 0, [], [$listedElsewhere]);
$store->allows('isListedAnywhere')->with(1066)->andReturn(true);
foreach (['setEnabled', 'attach', 'unmarkUnused', 'setConditions'] as $method) {
$store->shouldReceive($method)->never();
}
foreach ([['reattach' => true], []] as $extra) {
$refused = self::ability($store, FakeChangeLog::forbidden())->execute(self::input($listedElsewhere, $extra));
self::assertToolError($refused, 'validation_failed');
self::assertStringContainsString('keine entfernte Vorlage', self::clientError($refused)['message']);
self::assertArrayHasKey('template_id', self::errorFields($refused));
}
}
/** Ruling Q8/B10: a layout Divi trashed meanwhile blocks the reattach; nothing is written. */
public function testReattachNeedsPublishedLayouts(): void
{