fix: identical means identical bytes – text parts count, pages and posts only (ruling Q25)

- Fingerprint::content() includes non-whitespace text parts (raw HTML between
  blocks, inner HTML of core blocks) and opaque blocks with all their bytes;
  structure() unchanged; find-similar "identical" follows
- make-global mode 2 also compares Fingerprint::canonical() bytes of each node
- sources and targets: post types page and post only (POST_TYPES)
- a rolled-back page N>1 keeps DiviSaver's "Revision X bleibt erhalten"
- guide: escaped divi/global-layout re-expanded by Divi's do_blocks in
  shortcode-module and WooCommerce text output documented as residual risk

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 21:59:20 +02:00
co-authored by Claude Opus 5.5
parent e733065b60
commit 4e959833b9
6 changed files with 238 additions and 21 deletions
@@ -212,4 +212,33 @@ final class LibraryFindSimilarAbilityTest extends TestCase
self::assertCount(5, $rest['matches']);
self::assertArrayNotHasKey('note', $rest);
}
/** Ruling Q25: raw HTML next to the blocks is visible – such a copy is only "structure", never "identical". */
public function testExtraVisibleTextIsNotIdentical(): void
{
$root = LibraryContent::roots(LibraryContent::load(
new Parser(),
DiviFixtures::markup('stage3/library--library-abschnitt.html')
))[0];
$copy = Serializer::block($root);
$at = strrpos($copy, '<!-- /wp:divi/column -->');
$changed = substr($copy, 0, (int) $at) . '<p>ONLY ON 8</p>' . substr($copy, (int) $at);
LibraryFixtures::given([
LibraryFixtures::item(1055),
new WP_Post(['ID' => 8, 'post_type' => 'page', 'post_status' => 'publish', 'post_title' => 'Über uns',
'post_content' => '<!-- wp:divi/placeholder -->' . $changed . '<!-- /wp:divi/placeholder -->']),
]);
Functions\when('get_post_type_object')->justReturn((object) ['cap' => (object) [
'edit_posts' => 'edit_posts',
'edit_others_posts' => 'edit_others_posts',
]]);
Functions\when('get_posts')->justReturn([8]);
$result = self::ability()->execute(['library_id' => 1055, 'post_type' => 'page']);
self::assertSame(['8 s0 structure'], array_map(
static fn (array $m): string => $m['post_id'] . ' ' . $m['id'] . ' ' . $m['match'],
$result['matches']
));
}
}