- 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
- library_id + targets (post_id, tree_rev, ids from lit/library-find-similar):
only nodes with the entry's content fingerprint, pages and posts only
- every target checked first (DiviWriter::page: edit_post + tree_rev, P8a;
StoredNodeGuard of the page on the instance); the entry must be an active
global entry with one readable root and no reference in any spelling (Q7)
- dry run lists pages with tree_rev, status and live flag; confirm writes each
page through DiviWriter::commit (revision, change log); live pages without
revisions get VisibleEdits' backup, refused up front where not faithful
- failure on page N: earlier pages stay replaced, N rolled back, later pages
untouched; the error names changed_posts/pages, failed_post, unchanged_posts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
- NewLibraryEntry: spec and creation of a new library entry, shared by
lit/library-save and lit/library-make-global (NewDraft, P7, StoredNodeGuard)
- UsageMatcher::referencesAny() (ruling Q7): a global entry never names any
entry in any spelling Divi renders (P23/P24 forms, Divi 4 shortcodes inside
Divi 5 values, values casting to 0); used by library-save scope global and
make-global
- make-global: entry created, node replaced by GlobalInstance::of, page through
DiviWriter::commit; refused or rolled-back page deletes the entry, a partial
page write keeps it (the page may show it); sources are pages and posts only
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
SimilarSections compares inserted sections (not global instances) with
at most 200 editable library section entries by structure fingerprint,
read lazily once per request, only while library IDs may be named (P13).
lit/library-insert is not asked.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fingerprint::structure()/content() (E11) and lit/library-find-similar
(indirect, group Divi-Bibliothek): at most 200 posts per call (cursor),
editable entries only (own posts without edit_others_posts), contents
primed in chunks of 50, stops at 200 matches or 5000 compared nodes with
a note; library entries and Theme Builder layouts are never scanned.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Both forms Divi writes are recognized (ruling Q11): custom_meta_<key> and the
dialog form post_meta_key + select_meta_key. Choice fields get a note about the
stored value, relation fields name IDs (F4). Only templates the user may edit
are checked or named; others are counted (Q21 pattern). Warnings only, also in
the dry run of a live body. DynamicFields extracted from lit/divi-dynamic-sources.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ThemeBuilderAccess::editableLiveTemplates() shared by tb-list and the tb-assign/tb-activate dry runs;
uneditable live templates only as a count (hidden_overlaps, hidden_live)
- tb-activate enabled:false works while the Theme Builder has a draft, with a warning
- possible_overlaps skips identical conditions; reattach records the previous enabled state;
exclude_from must be a list; tb-activate live from the read-back
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- only _et_enabled; dry run in both directions lists conditions, exclusions,
layouts, overlaps and possible overlaps (B9); confirm: true switches
- enabling needs conditions and published layouts (B10); a detached
template needs reattach first; the default template is refused
- the off-switch reaches an attached, enabled template outside publish,
e.g. in the trash (ruling Q16); result names the fast way back
- every switch in the change log; failed switch rolled back
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ThemeBuilderAccess::changeableTemplate(): rights (Q18 not_found), group gate,
no TB draft, never the default template, current template_rev
- omitted exclude_from stays, [] clears (Q3); live dry run shows use_on and
exclude_from before/after, overlaps and possible overlaps (B9)
- reattach only with published layouts (B10) and a live TB post; always
disabled, unmarks template and layouts; failure restores everything
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TB layouts load through ThemeBuilderAccess (P12), live layouts (attached +
enabled, Q8) need a dry run and confirm (Q9) and get the shared restore point
of VisibleEdits, extracted from GlobalEdits (Q2: backup when revisions are
off, P9 refusals, P14 naming). divi/post-content only in body layouts (F22),
also for nested stored nodes. SaveScope::ThemeBuilderLayout sets Divi flags
and clears site CSS + TB cache.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Divi's Theme Builder page is gated by edit_theme_options; without it every
TB ID answers like a missing one. Docblocks (Q16): the wp_die guard cannot
restore WP_Hook nesting after a third-party wp_die inside a hook, and
canEditTemplate expects IDs from Divi's list.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
DiviThemeBuilder wraps Divi's Theme Builder: reads through Divi's functions,
writes layouts/templates through insert_layout/store_template behind a
wp_die guard (edit_others_posts and function checks first, every handler
filter and output buffer restored in finally), list/conditions/enabled
mirrored from Divi's own save. Template: live = attached and enabled,
whatever the post status (ruling Q8/B8). ThemeBuilderAccess: edit_others_posts
plus Divi's theme_builder role, hidden IDs answer like missing ones,
canEditTemplate (B11) and layoutsRefusal (B10). DiviCompatibility checks the
Theme Builder functions and post types (E15).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WpLinkTargets answers the library, internal "_" types and every type
without a Divi post_link_url option like a missing ID; replace-text strips
library IDs only from global subtrees.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Outline, node-get, write outlines, the render summary and replace-text's
opaque-node match name no library ID unless the library group is visible
and the user has divi_library; read-only still names them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One PostIdParser for {{link:}} and select_post; post_link_url_<type> needs a
living, readable, non-attachment target of exactly that type.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Own fixture pod lit_fixture_person (ruling Q1), template and body layout
built through Divi's Theme Builder and Visual Builder UI, brief render with
the template limited to one person (Q5), findings 10-12 in the README.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- G1: every decoded string value of a Divi 5 opener is scanned for shortcodes;
outside the openers only the remaining content is.
- G2: attributes are parsed like shortcode_parse_atts() and global_module is
cast with (int), as Divi does.
- G3: a non-empty global_module other than "0" that casts to 0 makes Divi
render the newest layout, so it counts for every entry (fail closed).
- The prefilter LIKEs use LOWER(post_content), so they are case-insensitive
under any collation.
- The guide describes the fail-closed rule without an absolute claim.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Candidates come from one ID/status/type query: content with globalModule,
global_module or a JSON-escaped letter. Their content is read in chunks of
50 and parsed by UsageMatcher: Divi 5 openers are json-decoded (escaped keys
and values, leading zeros, any depth), and Divi 4 global_module attributes
are read in any quoting. An undecodable escaped opener counts as a possible
reference. Beyond 5000 candidates the rest count unread. The guide names
the formats and the wp_block parity. Also fixes the grammar of the
blocked-hidden count.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
synthetic-divi-filters.html now stores its paragraph as Divi does (unicode
escapes, bytes built with chr(92) and checked with od). A data-provider test
inserts a section as a user without unfiltered_html into every Divi 5 fixture
page read from disk and asserts that every other byte stays.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Per-run editor login and role, deleted only when this run created them (and
refused when they exist); the allowed-roles option is deleted again when it
was absent; the run-token fallback covers pages too; readOnlyHint must be
false, not just missing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The header says "mit OAuth für KI-Assistenten" like the readme; the spec names
the client neutrally and keeps claude.ai only where the connector is meant.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
search uses search_columns post_title; users without edit_others_posts on
et_pb_layout query their own entries only (PostGuard::ownPostsOnly); the
cursor counts the entries shown, never the rows left out.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
StoredNodeGuard accepts a zero-padded library id (Divi intval()s it) but
refuses instances whose opener holds more than one or an escaped
globalModule, so the entry Divi renders is the one checked. GlobalUsages
matches "globalModule":"0*<id>" with REGEXP behind a cheap LIKE prefilter.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
et_header_layout, et_body_layout, et_footer_layout and et_template showing a
global entry now block a confirmed edit unless et_pb_is_allowed('theme_builder'),
like Divi's own check for TB layouts.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GlobalUsages runs one query per request (ID, status, type – no content),
checks read_post/edit_post in chunks of 200 with a primed and released post
cache, lists at most 50 readable posts and refuses a confirmed global edit
above 2000 live references (unsupported, edit in the Divi builder). Posts the
caller may not read appear only as hidden_count / blocked_hidden_count;
blocked_ids names readable posts only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With the library group disabled the entries stay unreadable and unwritable, but
page outline, node-get and render summary still name global module IDs; full
hiding follows in 3b.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A library entry as source now requires LibraryAccess::canInsert() (add_library,
writable library group) and follows lit-library-insert's Tools-tab state
(disabled -> unsupported). DiviPageLoader::library() gets its caller.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reuses NewDraft for insert and rollback (P6), requires edit_global_library
for scope global (P7), refuses opaque and global roots before wrapping (P11)
and checks the node with StoredNodeGuard. The source page is never written.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lottie.innerContent (src → data-lottie-src) makes the frontend load an
animation whose expressions lottie-web runs with eval; it joins the S7 gate
in ValueCleaner, which covers removals and StoredNodeGuard.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A link without scheme and without whitespace, quotes, <, >, backslashes or
control characters passes although esc_url_raw() would prefix http://; every
value with a scheme, and protocol-relative //host, must survive url() unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>