Skip to content

Unified Snippets: implement Tier 3 scanner - Divi#393

Open
louiswol94 wants to merge 5 commits into
feature/unified-snippetsfrom
unified-snippets-pr5
Open

Unified Snippets: implement Tier 3 scanner - Divi#393
louiswol94 wants to merge 5 commits into
feature/unified-snippetsfrom
unified-snippets-pr5

Conversation

@louiswol94
Copy link
Copy Markdown
Contributor

@louiswol94 louiswol94 commented May 20, 2026

Summary

Adds the Tier 3 Divi Theme Options scanner to the Unified Snippets feature. Surfaces the five code-input fields that Divi exposes under Theme Options — Custom CSS and the four "Integration" textareas (head, body, single top, single bottom) — as read-only Discovered_Snippets so they show up alongside the rest of a site's custom code in the Unified view.

This is Phase 1 (discovery only): each snippet carries the metadata a later Phase 3 importer needs (accurate type, scanner-specific import_notes, and a deterministic source_path), but no code generation happens inside the scanner.

What's in the box

  • Divi_Theme_Options_Scanner — extends Scanner_Base, id = 'divi-theme-options', risk = medium, supports_import = true, supports_editing = false (Phase 4)
  • Registered in Plugin::init_unified_snippets() alongside the existing Tier 1/2 scanners
  • 12 PHPUnit cases covering populated/empty/disabled scenarios, both Divi storage modes, the Extra sibling theme, and hash/checksum stability

Key design decisions

Storage read path mirrors Divi's et_get_option(). Divi stores theme options either as a single serialised array under et_<shortname> (one-row mode) or as per-key options named after the full field id e.g. divi_integration_head (per-row mode), decided per-install by et_options_stored_in_one_row(). Rather than depend on Divi's globals, the scanner checks both layouts and returns whichever yields a value. Both modes have dedicated tests.

is_active honours Divi's enable toggles, strictly. The four Integration fields each have a companion _integrate_*_enable checkbox that Divi checks at runtime before emitting the code. Divi's runtime test is et_get_option(...) === 'on', which means a missing toggle (boolean false from et_get_option()) does NOT emit — the checkbox std => 'on' in options_divi.php only controls the admin UI default-checked state, not the runtime default. The scanner mirrors that strict check, so the Unified view reflects what Divi is actually emitting. Tests cover the three real states: 'on', missing, and the literal string 'false' that Divi writes on save-with-unchecked.

Availability is gated on the active template (Divi or Extra). We deliberately do not scan when Divi is merely installed-but-inactive — its hooks are not running, so the code is dormant.

"Body code" maps to footer-content, not wp_body_open. Despite the Divi UI labelling it "body", Divi actually emits this field on wp_footer at priority 12 (see Divi/epanel/custom_functions.php). Importing into Code Snippets' footer-content scope reproduces the runtime behaviour. The priority delta (Divi: 12 vs footer-content: default 10 per Evaluate_Content::init()) is surfaced in import_notes — the imported snippet executes slightly earlier than Divi did, which is usually harmless but may matter for order-sensitive scripts. The head field has the same delta (Divi: 12 vs head-content: 10) and the same note.

Single top / single bottom flag the Divi dependency. These fields fire on Divi's own et_before_post / et_after_post hooks. import_notes makes it explicit that the imported snippet will only run while Divi is the active theme — we don't block the import, but the user knows what they're signing up for.

Per-field metadata only; no wrapping in the scanner. Code generation lives in the (separate) Phase 3 importer, which will switch on scanner_id === 'divi-theme-options' + type. Keeps the scanner small and read-only, per the architecture doc.

Field mapping (for Phase 3)

Divi field Type Target scope Notes
custom_css css site-css Clean 1:1 import
integration_head html head-content Toggle-aware; Divi priority 12 vs CS 10
integration_body html footer-content Toggle-aware; Divi priority 12 vs CS 10
integration_single_top html global (PHP wrap) Wrap in add_action( 'et_before_post', ... )
integration_single_bottom html global (PHP wrap) Wrap in add_action( 'et_after_post', ... )

Testability

The production class has no test-only seams — no override arrays, no injected collaborators, no protected hooks. Tests drive it through the same WordPress surface production uses:

  • wp_options fixtures are written via real update_option() calls (both one-row et_divi bundles and per-row divi_* options).
  • is_available() is exercised by short-circuiting get_template() through the documented pre_option_template / pre_option_stylesheet filters — no theme activation required.
  • Each test cleans up its own filters and option rows in tear_down(); WP_UnitTestCase's transactional isolation backs that up.

This means the tests double as integration coverage for the read_option() two-storage-mode logic, rather than bypassing it.

Out of scope

  • Phase 3 import transformer (the et_before_post / et_after_post wrap generator)
  • Phase 4 in-place editing via Divi's option API
  • Per-page Divi module _et_pb_custom_css postmeta (the Elementor-equivalent "per-element CSS" problem — warrants its own scanner with is_importable = false)
  • Divi Theme Builder template code areas
  • Extra-specific Module CSS fields

Test plan

  • PHPUnit: npm run test:php — 12 cases pass under the unified-snippets group
  • PHPCS clean on changed files (npm run lint:php)
  • Manual: activate Divi, populate all five Theme Options fields, hit the scan REST endpoint, verify 5 results with expected type, is_active, and import_notes
  • Manual: disable one of the _integrate_*_enable toggles, rescan, verify the corresponding snippet returns with is_active = false
  • Manual: switch to a non-Divi theme, verify is_available() returns false and the scanner is skipped

@louiswol94 louiswol94 added the run-tests Trigger automated tests label May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-tests Trigger automated tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant