Files
WP-LIT-MCP/docs/superpowers/notes/2026-09-28-etappe-4-divi-formate.md
T

29 KiB
Raw Blame History

Etappe 4 – Datenformate des Divi-Designsystems (Divi 5.2.1)

Stand 2026-09-28. Grundlage: Quelltext von Divi 5.2.1 und echte Daten, die über Divis eigene Speicherwege angelegt wurden (Seed-Skript tests/Integration/divi-seed-stage4-design.php, Fixtures tests/Fixtures/divi/stage4/). Erfüllt Spec §14 („Datenformate Designsystem nur aus Code abgeleitet“) und §15 („Format der Presets/Variablen an echten Daten verifizieren“).

Pfadangaben relativ zu wp-content/themes/Divi/. Abkürzung B5 = includes/builder-5/server/. Minifizierte Builder-Dateien liegen in includes/builder-5/visual-builder/build/; dort steht statt einer Zeile ein Suchbegriff.

Nachtrag Divi 5.15.1 (Etappe 4a, 2026-10-16)

Geprüft gegen Divi 5.15.1 (installiert, freigegeben):

  • GlobalPreset::save_data() hat den zweiten Parameter $skip_external_cache = false; das Plugin übergibt ihn nicht. Der Parameter wird mit einem internen Flag ($_skip_external_cache_nested) verodert und an ET_Core_PageResource::remove_static_resources() weitergereicht.
  • GlobalPresetController::sync_permission() verlangt jetzt zusätzlich edit_theme_options (5.2.1: nur Visual-Builder-Recht). Die Rechte in Spec §10 entsprechen damit Divi.
  • Der Sync nimmt Chunks an (requestId, chunkIndex, totalChunks, Transient et_global_preset_chunks_<user>_<id>); das Plugin schickt nie Chunks.
  • Schrittfolge des Sync (ohne Chunks) unverändert: prepare_data → get_data → validate_preset_count → bei Löschaktionen get_deleted_preset_ids + remove_preset_from_legacy_option → save_data. Das Plugin wiederholt sie in DesignSystemWriter::savePresets() (Mirror preset-sync); diese Klasse existiert noch nicht (geplant für Etappe 4a).
  • Signaturen aus Plan Step 1 bestätigt (prepare_data, validate_preset_count, save_data, get_deleted_preset_ids, remove_preset_from_legacy_option, can_current_user_use_visual_builder); option_name, get_data, get_preset_attrs, get_preset_attrs_mapping vorhanden.
  • Abweichungen bei der Formatprüfung am lebenden Bestand (Seed-Skript lief erfolgreich, Seite 170):
    • Zeilennummern in den Abschnitten 1–8 beziehen sich auf 5.2.1 und stimmen nicht mehr (z. B. save_data jetzt GlobalPreset.php:548, sync_permission jetzt GlobalPresetController.php:465).
    • Gruppen-Preset-Einträge haben groupName und groupId (wie in Abschnitt 3), aber kein renderAttrs im Seed-Bestand; renderAttrs ist optional.
    • Das Mapping (Conversion::get_preset_attrs_mapping("divi/button")) liefert attrName, preset und bei Bedarf subName. preset ist nicht immer eine Liste: gesehen wurden "meta", "content" (String) und ["html"] (Array).
  • Keine weiteren Abweichungen zu §3, außer den oben genannten.

Nachtrag Divi 5.15.1 – Farben und Variablen (Etappe 4c, 2026-10-19)

Geprüft gegen den installierten Quelltext includes/builder-5/server/Packages/GlobalData/GlobalData.php (5.15.1):

  • Farben unverändert: set_global_colors(array $data, $already_sanitized = false): void mischt mit true (array_merge), ersetzt mit false und bereinigt dann selbst; die fünf Hauptfarben gehen über et_update_option() in et_divi und werden aus den Daten entfernt; danach remove_static_resources('all', 'all', true). sanitize_global_colors_data(array $data): array ist öffentlich und verlangt, dass usedInPosts ein Array ist.
  • get_customizer_colors() hat einen static-Cache ohne Reset. Nach einer Änderung der Hauptfarbe liefert get_global_colors() im selben Request noch den alten Wert ($_cached_data wird zurückgesetzt, der static nicht). LIT MCP liest die fünf Hauptfarben deshalb für das Zurücklesen aus et_divi (GlobalColorsAdapter::read()).
  • Variablen: neue Signatur und Prüfungen. set_global_variables(array $data, ?array $expected_variables = null): ?WP_Error kehrt weiterhin still zurück, wenn edit_theme_options oder variables_manager fehlt; sonst läuft es unter einer MySQL-Sperre (GET_LOCK), prüft optional den erwarteten Bestand (global_variables_stale, 409), verlangt, dass jede gespeicherte gvid--ID im neuen Bestand vorkommt (validate_global_variable_ids() → global_variable_ids_missing, 400) und prüft geänderte Einträge (validate_global_variable_changes(): status active|archived, aktive Variablen mit nicht leerem value). Folge: Variablen lassen sich über Divis Funktionen nicht löschen, nur archivieren (Ruling V1 des Plans 4c).
  • Neuer Typ gradients (GLOBAL_VARIABLE_TYPES = numbers, strings, images, links, fonts, gradients); strings und gradients behalten ihren Wert zeichengetreu. get_global_variables() liefert zusätzlich die leere Gruppe colors, wandelt Objekt-Gruppen in Arrays und fügt allowedActions und die Customizer-Schriften hinzu (jede Gruppe als stdClass); beim Zurückschreiben bleiben nur Speicherformat-Felder.
  • Archivierte Variablen: _sort_global_variable_snapshot() behandelt order archivierter Variablen als ''; der Builder setzt beim Archivieren order: null, das Sanitize macht daraus ''.

0. Grundlagen: wo Divi speichert

et_get_option()/et_update_option() (epanel/custom_functions.php:211, :272) haben drei Ziele:

  • normale Theme-Option → Schlüssel im Array der Option et_divi (autoload);
  • „Product Setting“ (letzter Parameter true) → eigene Option et_divi_<name>, autoload aus (:224, :288-292);
  • et_delete_option() (:316) kennt keine Product Settings und löscht immer nur den Schlüssel in et_divi.

Folge: GlobalPreset::delete_data() (B5 Packages/GlobalData/GlobalPreset.php:149-158) ruft et_delete_option(option_name()) auf und entfernt die Option et_divi_builder_global_presets_d5 nicht – nur den statischen Cache. Für Etappe 4 nicht verwenden.

et_update_option() aktualisiert zusätzlich das globale $et_theme_options; direktes update_option('et_divi', …) lässt diesen Cache im selben Request veralten.

1. Globale Farben

Speicherort: et_divi['et_global_data']['global_colors'] (normale Theme-Option, kein Product Setting). Die fünf Customizer-Farben liegen nicht dort, sondern als et_divi['accent_color'], secondary_accent_color, header_color, font_color, link_color (Zuordnung GlobalData::$customizer_colors, B5 Packages/GlobalData/GlobalData.php:58-83).

Format eines Eintrags (Schlüssel = ID):

"gcid-q9swzy97df": {
  "color": "#5b3a29",
  "folder": "",
  "label": "[Fixture] Holz dunkel",
  "lastUpdated": "2026-09-27T22:21:39.000Z",
  "status": "active",
  "usedInPosts": []
}
  • ID: gcid- + 10 Zeichen [0-9a-z] (Builder: generateVariableId/generateNanoUuid, global-data.js, Suche S=(e,t)=>e||("colors"===t?`gcid-${_()}). Ältere Daten können UUIDs haben (gcid-79d0acb1-…, Beispiele GlobalData.php:700-770). Prüfung serverseitig nur substr($id, 0, 5) === 'gcid-' (:597-603).
  • status: active | inactive | temporary. Der Builder löscht nie: „Löschen“ setzt status: inactive (global-data.js, Suche case"DELETE_GLOBAL_COLOR").
  • label/folder werden gespeichert, obwohl der Konverter-Kommentar „not until D6“ sagt (:159-160); der Builder liest label (Suche label:t.label||"").
  • lastUpdated: Format Y-m-d\TH:i:s.v\Z.
  • Relative Farbe (Farbe aus anderer Farbe): color enthält eine Referenz
    $variable({"type":"color","value":{"name":"gcid-q9swzy97df","settings":{"lightness":30}}})$
    mit settings aus hue, saturation, lightness, opacity (GlobalData::transform_state_into_global_color_value, :181-206). CSS-Ausgabe: --gcid-989stx15zc: hsl(from var(--gcid-q9swzy97df) calc(h + 0) calc(s + 0) calc(l + 30)); (FrontEnd/Module/Style.php:910-947 über StyleLibrary/Utils/Utils.php:86-130). Abhängigkeiten werden rekursiv aufgelöst (GlobalData.php:220-310, Tiefe max. 10 in :1336ff).

Lesen: GlobalData::get_global_colors() (:333-354) liefert Customizer-Farben (neu erzeugt, folder: customizer, übersetztes Label, lastUpdated = jetzt, :370-398) vor den gespeicherten Farben; gleichnamige gespeicherte Customizer-IDs werden verworfen. Ergebnis wird statisch gecacht ($_cached_data), set_global_colors() setzt den Cache zurück, get_customizer_colors() hat einen eigenen static-Cache ohne Reset.

Schreiben: GlobalData::set_global_colors(array $data, $already_sanitized = false) (:650-691):

  1. ohne $already_sanitized: sanitize_global_colors_data() (:585-616) – verwirft Einträge ohne color oder ohne gcid-, wendet sanitize_text_field auf jeden skalaren Wert an; usedInPosts wird mit array_map behandelt und muss ein Array sein (sonst TypeError).
  2. Customizer-IDs im Payload → et_update_option(<option_name>, color) und aus dem Payload entfernt (:664-674).
  3. $already_sanitized = false ersetzt alle gespeicherten Farben, true mischt (array_merge) mit den vorhandenen (:678-684).
  4. et_update_option('et_global_data', …), dann ET_Core_PageResource::remove_static_resources('all', 'all', true) (:687-690).

Builder-Speicherweg: POST divi/v1/global-data/global-colors (VisualBuilder/REST/RESTRegistration.php:1001-1008) → Schema-Sanitize sanitize_global_colors_data (GlobalDataController.php:100-108) → set_global_colors($colors, true) (:71-78), also Mischen. Der Builder schickt dabei die komplette Farbliste einschließlich der fünf Customizer-Farben (global-data.js, Suche restRoute:"/divi/v1/global-data/global-colors"); Entfernen einer Farbe ist über diesen Weg nicht möglich.

Seiteneffekte:

  • Customizer-Sync ist direkt eingebaut (Schritt 2); ein separater Hook existiert nur für Divi 4 (includes/builder/functions.php:13205-13237, et_global_colors_saved, mit veralteten IDs gcid-header-color/gcid-font-color).
  • usedInPosts pflegt nur der Builder im Browser (UPDATE_GLOBAL_COLORS_USED_IN_POSTS); serverseitig wird es nie berechnet. Über PHP angelegte Farben behalten [].
  • Einmalige Migration Divi 4 → 5 (maybe_convert_global_colors_data, :526-551) läuft beim Laden der Builder-Einstellungen (Framework/Settings/Settings.php:159-162).

Rechte: REST-Route nur current_user_can('edit_posts') (GlobalDataController.php:124-130); set_global_colors() selbst prüft nichts. Zusätzlich verlangt jede Divi-REST-Route den Nonce X-ET-Nonce für die Aktion /divi/v1/<route>--POST (Framework/Route/RESTRoute.php:98, Framework/Controllers/RESTController.php:158-178).

2. Globale Variablen

Speicherort: Product Setting et_divi_global_variables (eigene Option, autoload aus), gespeichert mit et_update_option('global_variables', $data, false, '', '', true) (GlobalData.php:1016).

Gespeichertes Format (PHP-Arrays):

{
  "numbers": { "gvid-f6yq6mayfy": { "id": "gvid-f6yq6mayfy", "label": "[Fixture] Eckenradius", "value": "8px",
                                     "order": "1", "status": "active", "lastUpdated": "…", "variableType": "numbers" } },
  "strings": { … }, "images": { … }, "links": { … }, "fonts": { … }
}
  • Typen: nur numbers, strings, images, links, fonts (sanitize_global_variables_data, :804-850). colors wird beim Speichern verworfen – „Farb-Variablen“ im Variablen-Manager sind die globalen Farben aus Abschnitt 1.
  • ID: gvid- + 10 Zeichen [0-9a-z]; Pflicht: gvid--Präfix und nicht-leeres label.
  • Das Feld id im Eintrag ist nötig: die CSS-Ausgabe benutzt $value['id'], nicht den Schlüssel (FrontEnd/Module/Style.php:1002).
  • Sanitize: value bei strings zeichengetreu (wp_check_invalid_utf8 + wp_kses_no_null), bei links esc_url_raw, sonst sanitize_text_field – dadurch wird order zum String ("1"). allowedActions wird verworfen. Der Builder speichert außerdem lastUpdated und variableType mit (global-data.js, Suche case"ADD_GLOBAL_VARIABLE").
  • status: active | archived. Builder-„Löschen“ setzt archived und order: null (Suche case"DELETE_GLOBAL_VARIABLE"), echtes Entfernen nur ohne Label/Wert.
  • numbers erlaubt beliebige CSS-Ausdrücke (clamp(1.5rem, 4vw, 2.5rem) bleibt erhalten).

Lesen: GlobalData::get_global_variables() (:896-963) liefert ein Array, jede Gruppe als stdClass ((object) $items, :959-962), die Einträge darin als Arrays; ergänzt allowedActions und stellt die Customizer-Schriften --et_global_heading_font / --et_global_body_font (aus et_divi['heading_font']/['body_font'], Standard „Open Sans“, :93-104, :415-437) an den Anfang von fonts. Leere Gruppen sind leere stdClass (JSON {}). Eine beschädigte fonts-Gruppe wird still auf [] gesetzt (:925).

Schreiben: GlobalData::set_global_variables(array $data) (:1002-1020) ersetzt die komplette Option. Vorher also vollständig lesen, stdClass → Array wandeln, allowedActions und die beiden Customizer-Schriften herausnehmen (sonst landen sie in der Option), dann schreiben. import_global_variables() (:1087-1137) mischt, arbeitet aber mit stdClass-Gruppen und schreibt nur id/label/value/status. Danach remove_static_resources('all', 'all', true).

Rechte: set_global_variables() prüft current_user_can('edit_theme_options') && et_pb_is_allowed('variables_manager') und kehrt sonst ohne Fehler zurück (:1003-1005). Die REST-Route prüft nur edit_posts (GlobalDataController.php:350-356) und meldet danach Erfolg mit den unveränderten Daten – ein stiller Fehlschlag. variables_manager ist ein Recht des Divi-Rollen-Editors (includes/builder/functions.php:9219-9222, Auswertung includes/builder/core.php:1128ff, Option et_pb_role_settings; ohne Eintrag gilt „an“).

Referenzen in Modulen und Presets: $variable({"type":"content","value":{"name":"gvid-…","settings":{}}})$ – für Zahlen/Schriften wird daraus var(--gvid-…) (StyleLibrary/Utils/Utils.php:86-130), für Strings/Links/Bilder der Wert beim Rendern (Dynamic Content, Packages/Module/Layout/Components/DynamicContent/DynamicContentGlobalVariableOptions.php). :root enthält nur numbers und fonts (Style.php:991-1030), Schriftwerte in Anführungszeichen. In post_content ist " innerhalb der Referenz als " kodiert (WordPress-Block-Serialisierung).

Schriften der Customizer-Seite schreibt der Builder separat: POST divi/v1/global-data/global-fonts → et_update_option('heading_font'|'body_font') + Cache leeren (GlobalDataController.php:166-190, Recht edit_posts).

3. Presets (Modul- und Optionsgruppen-Presets)

Speicherort: Product Setting et_divi_builder_global_presets_d5 (GlobalPreset::option_name() = builder_global_presets_d5, GlobalPreset.php:129), autoload aus. Divi-4-Presets: et_divi_builder_global_presets_ng (wird bei Bedarf importiert, Flag builder_is_legacy_presets_imported_to_d5).

Gespeichertes Format:

{
  "module": {
    "divi/button": { "default": "", "items": { "3gbmxlurey": { …Modul-Preset… } } },
    "divi/divider": { "default": "g7kvfq5uh8", "items": { "g7kvfq5uh8": { … } } }
  },
  "group": {
    "divi/font-body": { "default": "", "items": { "ja6o54jtlc": { …Gruppen-Preset… } } }
  }
}

Modul-Preset-Eintrag: type: "module", id, name, moduleName, version (Builder-Version, hier "5.2.1"), created/updated (Millisekunden, Builder Date.now(); PHP-Hilfen _generate_new_preset nutzen time() in Sekunden, :1695-1707), attrs, optional styleAttrs, renderAttrs, groupPresets, priority, order. Gruppen-Preset-Eintrag zusätzlich groupName (z. B. divi/font-body), groupId (Attributpfad im Quellmodul, z. B. content.decoration.bodyFont), moduleName (Quellmodul), optional primaryAttrName (nur über JS-Filter gesetzt, module-utils.js, Suche divi.optionGroupPresetPrimaryAttrNameResolver).

  • IDs 10 Zeichen [0-9a-z], ohne Präfix (Builder generateDefaultPresetName, global-data.js). Namen sind nicht eindeutig – der Builder zählt „Button 1, Button 2 …“.
  • attrs = normale Modulattribute (Pfad wie im Block, z. B. button.decoration.background.desktop.value.color); Gruppen-Presets tragen die vollen Pfade des Quellmoduls und werden beim Anwenden auf andere Module umgerechnet (GlobalPresetItemGroup::_attrs_resolver, GlobalPresetItemGroup.php:245ff, GlobalPresetItemGroupAttrNameResolver).
  • styleAttrs/renderAttrs: Teilmengen von attrs für CSS bzw. HTML/Skript. Serverseitig berechenbar mit Conversion::get_preset_attrs($attrs, ['style'] | ['html','script'], Conversion::get_preset_attrs_mapping($moduleName)) (B5 Packages/Conversion/Conversion.php:2242, :2479; genutzt in convert_preset, :2144-2145). Das Frontend benutzt renderAttrs/styleAttrs der Gruppen-Presets direkt (Packages/ModuleLibrary/ModuleRegistration.php:326-340).
  • groupPresets im Modul-Preset (verschachtelte Gruppen-Presets): { "<groupId>": { "presetId": ["<id>"], "groupName": "divi/…" } }.
  • default je Modul/Gruppe: ID des Standard-Presets oder "". Der Builder legt beim Öffnen des Preset-Managers leere Standard-Presets an (ADD_DEFAULT_MODULE_PRESET); ein Standard-Preset ohne attrs erzeugt keine Klasse (get_module_preset_class_names, GlobalPreset.php:730-800).
  • priority fehlt meist und gilt dann als 10 (:676, :2078, :2761); das REST-Schema-Default 10 (GlobalPresetController.php, preset_type_properties) wird für verschachtelte Einträge nicht eingesetzt.

Verweise in Blöcken (post_content):

  • Modul-Preset: "modulePreset": ["3gbmxlurey"] – Array (Stapel); ein String ist Altformat und wird noch akzeptiert (normalize_preset_stack, :2627-2680). "", "default", "_initial" bedeuten „Standard-Preset“ (is_preset_id_as_default, :2691). Fehlt modulePreset, gilt das Standard-Preset des Moduls.
  • Gruppen-Preset: "groupPreset": { "module.decoration.spacing": { "presetId": ["apzj7e17ok"], "groupName": "divi/spacing" } } (get_selected_group_presets, :2021-2320; Überlagerung :2148).
  • Stapel: letztes Element gewinnt bei get_selected_preset (:522-600); renderAttrs werden nach priority aufsteigend gemischt (:626-712). Verschachtelte und explizite Gruppen-Presets werden gestapelt, nicht ersetzt (:2148-2163).
  • Welche Gruppen ein Modul hat und wie sie heißen: GlobalPreset::get_group_preset_default_attr(ModuleRegistration::get_module_settings($name)) (:2332-2465), Namensliste get_default_group_name (:2475-2511). Beispiel divi/text: content.decoration.bodyFont → divi/font-body, module.decoration.spacing → divi/spacing; divi/button: button.decoration.font → divi/font, button.decoration.button → divi/button.

CSS-Klassen im Frontend (GlobalPresetItemUtils::generate_preset_class_name, GlobalPresetItemUtils.php:43-78): preset--module--divi-button--3gbmxlurey, Standard-Preset preset--module--divi-divider--default, Gruppe preset--group--divi-blurb--divi-spacing--apzj7e17ok, verschachtelt preset--group--divi-text--divi-font-body--nested--ja6o54jtlc. Keine Klassen für divi/id-classes, divi/animation. Am Fixture geprüft (README in tests/Fixtures/divi/stage4/).

Schreiben: GlobalPreset::save_data(array $data) (:419-431) schreibt die komplette Option ungeprüft, leert Caches (remove_static_resources('all','all',true,'all',true) – Builder-CSS bleibt erhalten) und setzt self::$_data zurück. Sanitisierung gibt es nur in GlobalPreset::prepare_data() (:213-268): wandelt das REST-Format ({module: [{default, items: [ … ]}], group: …}, Listen statt ID-Schlüssel) ins Speicherformat, entfernt leere Attribut-Arrays und sanitisiert attrs/renderAttrs/styleAttrs mit SavingUtility::sanitize_block_attrs bzw. sanitize_group_attrs (VisualBuilder/Saving/SavingUtility.php:2153, :2296; ohne unfiltered_html wird gefiltert).

Builder-Speicherweg: POST divi/v1/global-data/global-preset/sync (RESTRegistration.php:1035-1045) → GlobalPresetController::sync (GlobalPresetController.php:45-187): optional in Blöcken (Transient 5 min), prepare_data, validate_preset_count (GlobalPreset.php:335-410: weniger Presets als gespeichert → Fehler 400 preset_count_decreased, außer actionType DELETE_MODULE_PRESET/DELETE_OPTION_GROUP_PRESET), bei Löschaktionen Bereinigung der Divi-4-Option (remove_preset_from_legacy_option), dann save_data. Der Builder schickt immer den vollständigen Bestand.

Rechte: sync_permission() = UserRole::can_current_user_use_visual_builder() (GlobalPresetController.php:460, Framework/UserRole/UserRole.php:38-70) – nur das Rollen-Recht „Visual Builder verwenden“ (Standard „an“), kein edit_theme_options. Dazu der Routen-Nonce wie in Abschnitt 1.

Preset-Historie: et_divi_builder_presets_history_meta ({index, size}) und et_divi_builder_presets_history_item_<n> gehören zu Divi 4 (includes/builder/feature/global-presets/History.php:4-10, Schreiben nur per AJAX et_builder_save_global_presets_history mit switch_themes, :75-139). Der Divi-5-Sync schreibt dort nichts; Rückgängig im Builder ist rein clientseitig. Lokal: {index: -1, size: 0}. Für Etappe 4 heißt das: eine Wiederherstellung gibt es nur über eigene Snapshots (Spec §7).

4. Theme-Optionen (et_divi)

Lokal ist keiner der Whitelist-Schlüssel gesetzt (Fixture et-divi-subset.json) – überall gelten Divi-Standards.

Gruppe Schlüssel in et_divi Schreibweg in Divi Sanitize / Standard (Customizer, functions.php)
Schriften heading_font, body_font, primary_nav_font, secondary_nav_font, all_buttons_font Customizer, global-fonts-REST (nur heading/body) et_sanitize_font_choices, Standard none (:2484, :2557, :3398, :3522, :4996)
heading_font_weight, body_font_weight Customizer et_sanitize_global_font_weight, 500 (:2509, :2582)
Größen body_font_size (14), tablet_body_font_size, phone_body_font_size, body_header_size (30), tablet_header_font_size, phone_header_font_size Customizer absint (:2208-2456); Tablet/Phone-Standard = nächstgrößerer Wert
body_font_height (1.7), body_header_height (1) Customizer et_sanitize_float_number (:2236, :2376)
body_header_spacing (0), body_header_style ('') Customizer et_sanitize_int_number, et_divi_global_font_style (:2348, :2404)
Layout content_width (1080), gutter_width (3), section_padding (4), row_padding (2) Customizer absint (:1824, :1852, :1927, :2011)
Farben accent_color, secondary_accent_color, header_color, font_color, link_color Customizer und set_global_colors (Abschnitt 1) et_sanitize_alpha_color (:2534-2665)
Buttons all_buttons_* (19 Schlüssel inkl. *_hover, z. B. all_buttons_bg_color, all_buttons_border_radius_hover) Customizer je Feld (:4843ff)
Logo divi_logo ePanel „Theme-Optionen“ Typ upload, URL-String (options_divi.php:81)
SEO divi_seo_home_*, divi_seo_single_*, divi_seo_index_* ePanel Checkboxen 'on'/'false', Texte, Selects (options_divi.php:721-894; Speichern epanel/core_functions.php:868ff, Checkbox :969-970/:1049)
  • Customizer-Einstellungen sind type => 'option' mit Schlüssel et_divi[<name>] und capability => 'edit_theme_options'; Divi prüft zusätzlich das Rollen-Recht theme_customizer (functions.php:1278). ePanel: edit_theme_options (epanel/core_functions.php:873) und Rollen-Recht theme_options (functions.php:8834-8846).
  • Standardwerte weichen voneinander ab: secondary_accent_color hat im Customizer #8800FF (functions.php:2665), in GlobalData::$customizer_colors und im Builder #2ea3f2 (GlobalData.php:64-68, includes/builder/core.php:7293). Wer die Builder-Sicht (get_global_colors()) zurückschreibt, setzt die Customizer-Farbe damit fest auf #2ea3f2. Ebenso Schriften: Customizer-Standard none, Builder-Sicht „Open Sans“.
  • Caches: nach Customizer-Speichern remove_static_resources('all','all') (core/components/PageResource.php:944, :1139-1141) und et_pb_force_regenerate_templates (includes/builder/core.php:3771). Für eigene Schreibzugriffe gilt dasselbe.

5. Caches

ET_Core_PageResource::remove_static_resources($post_id, $owner, $force, $slug, $preserve_vb_files, $delete_files) (core/components/PageResource.php:1213-1232) läuft nur, wenn wp_doing_cron() oder et_core_security_check_passed('edit_posts') – ohne angemeldeten Benutzer (z. B. WP-CLI ohne --user) ist der Aufruf wirkungslos. Farben und Variablen rufen ('all','all',true), Presets ('all','all',true,'all',true) bereits selbst auf; der in Spec §7 vorgesehene Zusatzaufruf ist harmlos, aber redundant.

In-Request-Caches: GlobalData::$_cached_data (Reset bei set_*), GlobalPreset::$_data und $_normalized_preset_stack_cache (Reset in save_data/delete_data), static in get_customizer_colors()/get_customizer_fonts() und get_group_preset_default_attr() (kein Reset).

6. Rechte im Überblick

Weg Prüfung in Divi
REST global-colors, global-fonts, global-variables edit_posts + Routen-Nonce
GlobalData::set_global_variables() edit_theme_options und et_pb_is_allowed('variables_manager'), sonst stilles return
GlobalData::set_global_colors(), GlobalPreset::save_data() keine
REST global-preset/sync Rollen-Recht use_visual_builder + Routen-Nonce
Customizer edit_theme_options + Rollen-Recht theme_customizer
ePanel edit_theme_options + Rollen-Recht theme_options
remove_static_resources edit_posts (oder Cron), sonst wirkungslos

7. Abweichungen und Ergänzungen zu Spec §7

  1. „Variablen … über GlobalData::set_global_variables“ – stimmt, aber die Funktion ersetzt die ganze Option, liefert keinen Fehler bei fehlenden Rechten und kennt keinen Typ colors. Das Tool muss vorher lesen, stdClass → Array wandeln, allowedActions und die zwei Customizer-Schriften entfernen und nach dem Schreiben per Lesen prüfen.
  2. „globale Farben … inkl. der fünf Hauptfarben (über set_global_colors)“ – die Hauptfarben landen in et_divi['accent_color'] usw.; der zweite Parameter entscheidet zwischen Ersetzen (false) und Mischen (true). Für Änderungen einzelner Farben: Mischen; Löschen wie der Builder als status: inactive. Hauptfarben nur schicken, wenn sie wirklich geändert werden sollen (Standardwert-Abweichung, Abschnitt 4).
  3. „Presets … per Name“ – Namen sind nicht eindeutig; IDs sind 10-stellige Zeichenketten. Schreiben immer als vollständiger Bestand; save_data() sanitisiert nicht → vorher prepare_data() (REST-Format) und validate_preset_count() wie der Controller verwenden. styleAttrs/renderAttrs müssen mitgeschrieben werden (Frontend liest sie).
  4. „Voraussetzung: edit_theme_options; für Variablen zusätzlich variables_manager“ – strenger als Divi selbst (Farben: edit_posts; Presets: nur Visual-Builder-Recht). Als bewusste Plugin-Entscheidung beibehalten; für Variablen ist die Doppelprüfung zwingend, weil Divi sonst still nichts tut.
  5. „danach remove_static_resources('all','all',true)“ – Divis Setter tun das schon; wirkungslos ohne angemeldeten Benutzer. Für Presets nutzt Divi preserve_vb_files = true.
  6. Snapshots sind die einzige Wiederherstellung: Divi 5 führt für Farben, Variablen und Presets keine serverseitige Historie; die Option et_divi_builder_presets_history_meta gehört zu Divi 4.
  7. Theme-Optionen: divi_logo und divi_seo_* sind ePanel-Felder (andere Sanitize-Regeln, Checkbox-Werte 'on'/'false'), keine Customizer-Einstellungen. Die übrigen Whitelist-Felder haben die Customizer-Sanitize-Callbacks aus Abschnitt 4 – diese sollten wiederverwendet werden.
  8. Lesepfad des Plugins (src/Integrations/Divi/Design/DesignSystem.php): Farb-„Variablen“ (variables['colors']) sind in 5.2.1 immer leer – globale Farben tragen Label und ID selbst. fonts() liefert die Builder-Sicht („Open Sans“), nicht den Customizer-Standard none.

8. Stolpersteine (Kurzliste)

  • get_global_variables() liefert stdClass je Gruppe, gespeichert sind Arrays; leere Gruppe = {} im JSON.
  • Variableneinträge brauchen das Feld id; order wird zum String.
  • usedInPosts muss ein Array sein; wird nur vom Builder gepflegt.
  • created/updated in Millisekunden (Builder) vs. Sekunden (PHP-Hilfen).
  • modulePreset ist ein Array; Standard-Preset wirkt nur mit attrs.
  • Sync-Endpunkt verweigert kleinere Preset-Anzahl ohne DELETE_*-actionType.
  • GlobalPreset::delete_data() löscht die Product-Setting-Option nicht.
  • Divi-REST-Routen verlangen X-ET-Nonce pro Route; direkte PHP-Aufrufe umgehen Nonce, Schema und Rechteprüfung.
  • secondary_accent_color: Customizer-Standard #8800FF, Builder-Standard #2ea3f2.