Skip to content

Instantly share code, notes, and snippets.

@Pavracer
Created March 16, 2026 02:31
Show Gist options
  • Select an option

  • Save Pavracer/c4af8fbc2a0150e3066a20752eb60261 to your computer and use it in GitHub Desktop.

Select an option

Save Pavracer/c4af8fbc2a0150e3066a20752eb60261 to your computer and use it in GitHub Desktop.
Variable Colors Removing Issue
# Bug: Deleted global colors cannot be re-imported — "inactive" status persists through portability import
## Summary
When custom global colors are deleted through the Divi 5 Visual Builder UI, they are not removed from the database. Instead, their `status` is set to `"inactive"`. Subsequent portability imports of the same colors (same `gcid-*` IDs) fail to restore them because the `set_global_colors()` merge logic does not overwrite the `"inactive"` status with the incoming `"active"` status from the import file.
## Environment
- Divi 5 (Visual Builder)
- WordPress (standard hosting, WP CLI available)
- Import via Portability (JSON file upload through the builder)
## Steps to Reproduce
1. Create a Divi portability export JSON file containing custom global colors in the `global_colors` array (standard tuple format with `gcid-*` IDs and `"status": "active"`).
2. Import the file via the Visual Builder portability modal. All custom colors appear correctly.
3. Delete all custom colors through the Visual Builder Variables Manager UI.
4. Confirm colors no longer appear in the builder.
5. Import the **same** JSON file again via the same portability modal.
**Expected:** All custom colors reappear with `"status": "active"`.
**Actual:** No custom colors reappear. The UI shows no change.
## Root Cause
### Deletion sets `status: "inactive"` rather than removing the record
When colors are deleted in the UI, the builder sends the updated color list (without deleted colors) to `GlobalDataController::update_global_colors()`, which calls `GlobalData::set_global_colors($data, true)`.
In `set_global_colors()` (`GlobalData.php` ~line 664), when `$already_sanitized` is `true`, the method uses `array_merge`:
```php
// GlobalData.php, set_global_colors()
if ( $already_sanitized && ! empty( $global_data['global_colors'] ) && is_array( $global_data['global_colors'] ) ) {
$global_data['global_colors'] = array_merge( $global_data['global_colors'], $data );
} else {
$global_data['global_colors'] = $data;
}
```
The UI save sends only the system/customizer colors (the custom ones were "deleted"). `array_merge` preserves the existing custom `gcid-*` keys from the left side (database) because they aren't present in the right side (incoming UI payload). The custom colors remain in the database indefinitely.
At some point (likely during the same save cycle or a subsequent one), the `status` field on these orphaned records is set to `"inactive"`.
### Re-import fails to overwrite inactive status
The portability import follows the same `set_global_colors($data, true)` path, which also uses `array_merge`. The incoming import data has `"status": "active"` for these colors, and PHP's `array_merge` on associative arrays should overwrite matching string keys (right side wins). However, the colors remain `"inactive"` in the database after re-import.
### Database evidence
After performing steps 1-5, querying the `et_divi` option shows every custom color still present with `"status": "inactive"`:
```json
{
"gcid-5453514f46": {
"id": "gcid-5453514f46",
"label": "neon-carrot-500",
"color": "#ff9e3d",
"status": "inactive",
"folder": "",
"usedInPosts": []
}
}
```
This pattern repeats for every custom color that was imported, deleted, and re-imported.
### Likely contributing factor: Visual Builder overwrites on save
If the Visual Builder is open during re-import, its Redux state does not include the re-imported colors. When the builder subsequently saves (auto-save or manual), it sends its stale state to `update_global_colors`, which overwrites the freshly-imported `"active"` statuses back to `"inactive"`. This creates a race condition where the import succeeds momentarily but is immediately undone by the builder's save.
## Suggested Fix
Two issues should be addressed:
### 1. Deletion should remove records, not just mark them inactive
When the UI sends an updated color list that omits previously-existing `gcid-*` keys, those keys should be removed from `global_data['global_colors']` rather than preserved via `array_merge`. This could be a targeted replacement for the specific save context (UI save vs. import merge):
```php
// For UI saves (not import), replace entirely rather than merging:
if ( $is_ui_save ) {
$global_data['global_colors'] = $data;
} else {
// For imports, merge to preserve existing colors not in the import file:
$global_data['global_colors'] = array_merge( $global_data['global_colors'], $data );
}
```
### 2. Import should force-overwrite status for matching IDs
When `set_global_colors` is called from the portability import path, any incoming color with a matching `gcid-*` key should fully replace the existing record, including `status`. The current `array_merge` should do this for matching keys, but the Visual Builder save race condition (see above) may undo it. Consider refreshing the builder's global color state from the server after a portability import completes.
## Files Referenced
- `includes/builder-5/server/Packages/GlobalData/GlobalData.php` — `set_global_colors()` (~line 637), `get_imported_global_colors()` (~line 450)
- `includes/builder-5/server/Packages/GlobalData/GlobalDataController.php` — `update_global_colors()` (~line 71)
- `includes/builder-5/server/Framework/Portability/PortabilityPost.php` — `import_global_colors()` (~line 1426), import flow (~line 2182)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment