test: Divi 5 design system fixtures and format notes for Etappe 4
Seed script writes four global colors (one relative), one variable per type, four module presets (one default) and two option group presets plus the draft page "[Fixture] Designsystem" through Divi's own builder REST routes; export script saves them to tests/Fixtures/divi/stage4/. Notes document storage, write functions, side effects and permissions. 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:
@@ -0,0 +1,198 @@
|
||||
# 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.
|
||||
|
||||
## 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):
|
||||
|
||||
```json
|
||||
"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):
|
||||
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
|
||||
```json
|
||||
{
|
||||
"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`.
|
||||
Reference in New Issue
Block a user