feat: Fähigkeiten theme-option-read/-write und font-upload; ThemeOptionsAdapter und FontCatalog (Manifest, Nachbau customizer-typography, Signatur-Referenzen) und DesignRights::themeOptions()/fonts()
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
8379c55c89
commit
a90cd7f5bd
@@ -13,11 +13,15 @@ defined('ABSPATH') || exit;
|
||||
/**
|
||||
* Who may change the design system (spec §10): presets and colors need edit_theme_options and Divi's role right
|
||||
* "Visual Builder verwenden" – what Divi's own preset sync asks since 5.15 (formats "Nachtrag 5.15.1"); variables
|
||||
* also the role right "Variables Manager". Without unfiltered_html a write is refused while presets it saves along
|
||||
* hold HTML/CSS that Divi would strip (final review I-2).
|
||||
* also the role right "Variables Manager". Theme options need edit_theme_options and the role right
|
||||
* "Theme-Anpassung" (what Divi's customizer asks), font uploads upload_files, edit_theme_options and the role right
|
||||
* "Eigene Schriften verwalten" (Divi's font route plus the plugin's rule, plan 4d). Without unfiltered_html a write is
|
||||
* refused while presets it saves along hold HTML/CSS that Divi would strip (final review I-2).
|
||||
*/
|
||||
final class DesignRights
|
||||
{
|
||||
private const ROLE_EDITOR = 'Einen Administrator bitten, das Recht unter Divi → Rollen-Editor freizugeben.';
|
||||
|
||||
public const OTHER_MARKUP = 'Andere Presets enthalten HTML/CSS, das beim Speichern ohne das Recht unfiltered_html '
|
||||
. 'verloren ginge; nichts wurde gespeichert.';
|
||||
|
||||
@@ -70,18 +74,52 @@ final class DesignRights
|
||||
return Errors::forbidden(
|
||||
'Die Divi-Rolle dieses Kontos erlaubt den Variablen-Manager nicht; ohne ihn speichert Divi keine '
|
||||
. 'Variablen.',
|
||||
'Einen Administrator bitten, das Recht unter Divi → Rollen-Editor freizugeben.'
|
||||
self::ROLE_EDITOR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme options (writing and restoring): edit_theme_options and Divi's role right "Theme-Anpassung". $writer is
|
||||
* not asked – Divi's customizer needs no Visual Builder; it keeps the signature of the other design rights.
|
||||
*/
|
||||
public static function themeOptions(DesignSystemWriter $writer, ThemeOptionsAdapter $options): ?WP_Error
|
||||
{
|
||||
$refusal = self::themeRight('Theme-Optionen ändern');
|
||||
if ($refusal !== null || $options->canCustomize()) {
|
||||
return $refusal;
|
||||
}
|
||||
|
||||
return Errors::forbidden(
|
||||
'Die Divi-Rolle dieses Kontos erlaubt die Theme-Anpassung nicht; ohne sie ändert LIT MCP keine '
|
||||
. 'Theme-Optionen.',
|
||||
self::ROLE_EDITOR
|
||||
);
|
||||
}
|
||||
|
||||
/** Font uploads: upload_files, edit_theme_options and Divi's role right "Eigene Schriften verwalten". */
|
||||
public static function fonts(FontCatalog $fonts): ?WP_Error
|
||||
{
|
||||
if (! current_user_can('upload_files')) {
|
||||
return Errors::forbidden('Schriften hochladen erfordert das Recht, Dateien hochzuladen (upload_files).');
|
||||
}
|
||||
$refusal = self::themeRight('Schriften hochladen');
|
||||
if ($refusal !== null || $fonts->canManage()) {
|
||||
return $refusal;
|
||||
}
|
||||
|
||||
return Errors::forbidden(
|
||||
'Die Divi-Rolle dieses Kontos erlaubt „Eigene Schriften verwalten“ nicht; ohne das Recht lädt LIT MCP '
|
||||
. 'keine Schriften hoch.',
|
||||
self::ROLE_EDITOR
|
||||
);
|
||||
}
|
||||
|
||||
/** edit_theme_options and Divi's role right "Visual Builder verwenden"; $what names the area in the message. */
|
||||
private static function designer(DesignSystemWriter $writer, string $what): ?WP_Error
|
||||
{
|
||||
if (! current_user_can('edit_theme_options')) {
|
||||
return Errors::forbidden(sprintf(
|
||||
'%s ändern erfordert das Recht, das Design der Website anzupassen (edit_theme_options).',
|
||||
$what
|
||||
));
|
||||
$refusal = self::themeRight($what . ' ändern');
|
||||
if ($refusal !== null) {
|
||||
return $refusal;
|
||||
}
|
||||
if (! $writer->canUseVisualBuilder()) {
|
||||
return Errors::forbidden(
|
||||
@@ -89,10 +127,23 @@ final class DesignRights
|
||||
'Die Divi-Rolle dieses Kontos erlaubt den Visual Builder nicht; ohne ihn ändert LIT MCP keine %s.',
|
||||
$what
|
||||
),
|
||||
'Einen Administrator bitten, das Recht unter Divi → Rollen-Editor freizugeben.'
|
||||
self::ROLE_EDITOR
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** edit_theme_options; $activity starts the message ("Presets ändern", "Schriften hochladen"). */
|
||||
private static function themeRight(string $activity): ?WP_Error
|
||||
{
|
||||
if (current_user_can('edit_theme_options')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Errors::forbidden(sprintf(
|
||||
'%s erfordert das Recht, das Design der Website anzupassen (edit_theme_options).',
|
||||
$activity
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Design;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Release\Call;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
use LIT\MCP\Integrations\Divi\Release\Manifest;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Adapter "Designsystem" (spec §3) for Divi's font lists (plan 4d W5, W9): the families Divi's customizer sanitizer
|
||||
* accepts (uploaded fonts + et_builder_get_fonts(), exact names), the uploaded ones, and every reserved name for an
|
||||
* upload, plus Divi's role right "Eigene Schriften verwalten". et_builder_get_google_fonts() parses about 1.3 MB and
|
||||
* may fetch from Google once a day, so names() is loaded once per instance – DiviIntegration builds one per request;
|
||||
* uploaded() and reserved() are read fresh (an upload changes them). Not final so the tools can be tested with a mock.
|
||||
*/
|
||||
class FontCatalog implements DiviAdapter
|
||||
{
|
||||
/** @var list<string>|null */
|
||||
private ?array $names = null;
|
||||
|
||||
public static function manifest(): Manifest
|
||||
{
|
||||
return new Manifest(
|
||||
Manifest::DESIGN,
|
||||
calls: [
|
||||
new Call('et_builder_get_fonts', 0),
|
||||
new Call('et_builder_get_custom_fonts', 0),
|
||||
new Call('et_builder_get_google_fonts', 0),
|
||||
new Call('et_builder_get_websafe_fonts', 0),
|
||||
new Call('et_pb_is_allowed', 1, [DiviFeature::FontUpload]),
|
||||
],
|
||||
// The options behind Divi's lists; read only through the functions above.
|
||||
keys: ['et_uploaded_fonts', 'et_google_fonts_cache', 'et_google_api_settings'],
|
||||
// Plan 4d W1: font variables are checked against the same list, so a break locks all three on purpose.
|
||||
features: [DiviFeature::ThemeOptionWrite, DiviFeature::FontUpload, DiviFeature::VariableWrite]
|
||||
);
|
||||
}
|
||||
|
||||
/** @return list<string> Exact family names Divi's sanitizer accepts: uploaded, then web-safe and Google. */
|
||||
public function names(): array
|
||||
{
|
||||
return $this->names ??= self::namesOf(et_builder_get_custom_fonts(), et_builder_get_fonts());
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
public function uploaded(): array
|
||||
{
|
||||
return self::namesOf(et_builder_get_custom_fonts());
|
||||
}
|
||||
|
||||
/** @return list<string> Google, web-safe and uploaded names – an upload must not reuse one (W9). */
|
||||
public function reserved(): array
|
||||
{
|
||||
return self::namesOf(
|
||||
et_builder_get_google_fonts(),
|
||||
et_builder_get_websafe_fonts(),
|
||||
et_builder_get_custom_fonts()
|
||||
);
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return in_array($name, $this->names(), true);
|
||||
}
|
||||
|
||||
/** Divi's role right "Eigene Schriften verwalten" (custom_fonts_management); fail closed without Divi. */
|
||||
public function canManage(): bool
|
||||
{
|
||||
return function_exists('et_pb_is_allowed') && (bool) et_pb_is_allowed('custom_fonts_management');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed ...$lists Divi's lists (family => settings); anything else (a broken filter) is skipped.
|
||||
* @return list<string>
|
||||
*/
|
||||
private static function namesOf(mixed ...$lists): array
|
||||
{
|
||||
$names = [];
|
||||
foreach ($lists as $list) {
|
||||
if (! is_array($list)) {
|
||||
continue;
|
||||
}
|
||||
foreach (array_keys($list) as $name) {
|
||||
$names[] = (string) $name;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($names));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Design;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use LIT\MCP\Integrations\Divi\Release\Call;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviLocked;
|
||||
use LIT\MCP\Integrations\Divi\Release\Manifest;
|
||||
use LIT\MCP\Integrations\Divi\Release\Mirror;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
||||
use RuntimeException;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Adapter "Designsystem" (spec §3) for the whitelisted theme options of Divi's customizer (plan 4d W2, W6): reads
|
||||
* them raw from get_option('et_divi'), sends a value through the customizer's own sanitizer of its setting, and
|
||||
* writes the way a customizer save ends – et_update_option()/et_delete_option() per key, then the static CSS and the
|
||||
* builder caches (research A.2/A.3). Limits (W13): et_update_option() writes the whole et_divi array from Divi's
|
||||
* request global, so a change another process made after this request loaded it is lost; design_rev and the lock of
|
||||
* the pipeline narrow that window. remove_static_resources() does nothing without a logged-in user. Not final so the
|
||||
* theme option area can be tested with a mock.
|
||||
*/
|
||||
class ThemeOptionsAdapter implements DiviAdapter
|
||||
{
|
||||
/** Divi's theme options (one array option). */
|
||||
private const OPTIONS = 'et_divi';
|
||||
|
||||
/**
|
||||
* The 20 whitelist keys (plan 4d W2, in this order) => the sanitize_callback of their setting in
|
||||
* et_divi_customizer_theme_settings() (mirror customizer-typography). sanitize() calls it by this name; every
|
||||
* Divi name here is a call of the manifest (ThemeOptionsAdapterTest), absint is WordPress core.
|
||||
*/
|
||||
public const SANITIZERS = [
|
||||
'heading_font' => 'et_sanitize_font_choices',
|
||||
'body_font' => 'et_sanitize_font_choices',
|
||||
'primary_nav_font' => 'et_sanitize_font_choices',
|
||||
'secondary_nav_font' => 'et_sanitize_font_choices',
|
||||
'heading_font_weight' => 'et_sanitize_global_font_weight',
|
||||
'body_font_weight' => 'et_sanitize_global_font_weight',
|
||||
'body_font_size' => 'absint',
|
||||
'tablet_body_font_size' => 'absint',
|
||||
'phone_body_font_size' => 'absint',
|
||||
'body_header_size' => 'absint',
|
||||
'tablet_header_font_size' => 'absint',
|
||||
'phone_header_font_size' => 'absint',
|
||||
'body_font_height' => 'et_sanitize_float_number',
|
||||
'body_header_height' => 'et_sanitize_float_number',
|
||||
'body_header_spacing' => 'et_sanitize_int_number',
|
||||
'body_header_style' => 'et_divi_global_font_style',
|
||||
'content_width' => 'absint',
|
||||
'gutter_width' => 'absint',
|
||||
'section_padding' => 'absint',
|
||||
'row_padding' => 'absint',
|
||||
];
|
||||
|
||||
public static function manifest(): Manifest
|
||||
{
|
||||
return new Manifest(
|
||||
Manifest::DESIGN,
|
||||
calls: [
|
||||
new Call('et_sanitize_font_choices', 1),
|
||||
new Call('et_sanitize_global_font_weight', 1),
|
||||
new Call('et_sanitize_float_number', 1),
|
||||
new Call('et_sanitize_int_number', 1),
|
||||
new Call('et_divi_global_font_style', 1),
|
||||
new Call('et_update_option', 2),
|
||||
new Call('et_delete_option', 1),
|
||||
new Call('ET_Core_PageResource::remove_static_resources', 2),
|
||||
new Call('et_pb_force_regenerate_templates', 0),
|
||||
new Call('et_pb_is_allowed', 1),
|
||||
],
|
||||
keys: [self::OPTIONS],
|
||||
mirrors: [
|
||||
// shortcut: the fingerprint covers the whole customizer function (about 4,000 lines) and changes with
|
||||
// every customizer edit; switch to a narrower excerpt when it fires too often.
|
||||
new Mirror(
|
||||
'customizer-typography',
|
||||
'functions.php',
|
||||
'et_divi_customizer_theme_settings',
|
||||
null,
|
||||
'Standards, Bereiche und Sanitizer der 20 Theme-Optionen (ThemeOptionSpec)',
|
||||
[DiviFeature::ThemeOptionRead, DiviFeature::ThemeOptionWrite]
|
||||
),
|
||||
],
|
||||
features: [DiviFeature::ThemeOptionWrite]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The whitelist keys present in et_divi with their raw value, in the order of SANITIZERS; a missing key (Divi's
|
||||
* default) is missing here too. A value of another type (array, null, true) is no customizer value and left out.
|
||||
*
|
||||
* @return array<string, int|float|string|false>
|
||||
*/
|
||||
public function read(): array
|
||||
{
|
||||
$options = get_option(self::OPTIONS);
|
||||
if (! is_array($options)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stored = [];
|
||||
foreach (array_keys(self::SANITIZERS) as $key) {
|
||||
$value = $options[$key] ?? null;
|
||||
if (is_int($value) || is_float($value) || is_string($value) || $value === false) {
|
||||
$stored[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divi's customizer sanitizer of $key applied to $value; false = Divi refuses it (Divi itself would store false).
|
||||
* Non-scalars never reach Divi's sanitizers.
|
||||
*
|
||||
* @throws InvalidArgumentException When $key is not a whitelist key.
|
||||
*/
|
||||
public function sanitize(string $key, mixed $value): mixed
|
||||
{
|
||||
$sanitizer = self::SANITIZERS[$key] ?? throw new InvalidArgumentException(
|
||||
'not a theme option of the whitelist: ' . $key
|
||||
);
|
||||
|
||||
return is_scalar($value) ? $sanitizer($value) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes like a customizer save: $set per key with et_update_option(), $unset (back to Divi's default) with
|
||||
* et_delete_option(), then the static CSS and the builder caches; reads back from et_divi.
|
||||
*
|
||||
* @param array<string, int|float|string|false> $set Checked values (ThemeOptionSpec, sanitize()) or raw values
|
||||
* of a snapshot.
|
||||
* @param list<string> $unset
|
||||
* @return array<string, int|float|string|false> read() after writing.
|
||||
* @throws DiviLocked When theme-option-write is not released – before anything is written.
|
||||
* @throws InvalidArgumentException When a key is not a whitelist key – before anything is written.
|
||||
* @throws RuntimeException When the read-back differs from what was written.
|
||||
*/
|
||||
public function write(array $set, array $unset): array
|
||||
{
|
||||
ReleaseGate::assertFeature(DiviFeature::ThemeOptionWrite);
|
||||
$unknown = array_diff([...array_keys($set), ...$unset], array_keys(self::SANITIZERS));
|
||||
if ($unknown !== []) {
|
||||
throw new InvalidArgumentException('not a theme option of the whitelist: ' . implode(', ', $unknown));
|
||||
}
|
||||
|
||||
foreach ($set as $key => $value) {
|
||||
et_update_option($key, $value);
|
||||
}
|
||||
foreach ($unset as $key) {
|
||||
et_delete_option($key);
|
||||
}
|
||||
// What Divi runs on customize_save_after (research A.2): the customizer CSS lives in the static resources.
|
||||
\ET_Core_PageResource::remove_static_resources('all', 'all');
|
||||
et_pb_force_regenerate_templates();
|
||||
|
||||
$stored = $this->read();
|
||||
$wrong = [];
|
||||
foreach ($set as $key => $value) {
|
||||
if (! array_key_exists($key, $stored) || $stored[$key] !== $value) {
|
||||
$wrong[] = $key;
|
||||
}
|
||||
}
|
||||
foreach ($unset as $key) {
|
||||
if (array_key_exists($key, $stored)) {
|
||||
$wrong[] = $key;
|
||||
}
|
||||
}
|
||||
if ($wrong !== []) {
|
||||
throw new RuntimeException('theme options not written: ' . implode(', ', $wrong));
|
||||
}
|
||||
|
||||
return $stored;
|
||||
}
|
||||
|
||||
/** Divi's role right "Theme-Anpassung" (theme_customizer); fail closed without Divi. */
|
||||
public function canCustomize(): bool
|
||||
{
|
||||
return function_exists('et_pb_is_allowed') && (bool) et_pb_is_allowed('theme_customizer');
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,10 @@ namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
|
||||
use LIT\MCP\Integrations\Divi\Design\FontCatalog;
|
||||
use LIT\MCP\Integrations\Divi\Design\GlobalColorsAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Design\GlobalVariablesAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Design\ThemeOptionsAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
|
||||
use LIT\MCP\Integrations\Divi\Library\DiviRoles;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviPages;
|
||||
@@ -31,6 +33,8 @@ final class DiviAdapters
|
||||
DesignSystemWriter::class,
|
||||
GlobalColorsAdapter::class,
|
||||
GlobalVariablesAdapter::class,
|
||||
ThemeOptionsAdapter::class,
|
||||
FontCatalog::class,
|
||||
DiviModules::class,
|
||||
DynamicContentMap::class,
|
||||
];
|
||||
|
||||
@@ -23,18 +23,21 @@ enum DiviFeature: string
|
||||
case PresetWrite = 'preset-write';
|
||||
case ColorWrite = 'color-write';
|
||||
case VariableWrite = 'variable-write';
|
||||
case ThemeOptionRead = 'theme-option-read';
|
||||
case ThemeOptionWrite = 'theme-option-write';
|
||||
case FontUpload = 'font-upload';
|
||||
case LibraryRead = 'library-read';
|
||||
case LibraryWrite = 'library-write';
|
||||
case Roles = 'roles';
|
||||
case ThemeBuilderRead = 'tb-read';
|
||||
case ThemeBuilderWrite = 'tb-write';
|
||||
|
||||
/** Whether the feature changes data (the six *Write cases). */
|
||||
/** Whether the feature changes data (the seven *Write cases and FontUpload). */
|
||||
public function isWrite(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::PageWrite, self::PresetWrite, self::ColorWrite, self::VariableWrite, self::LibraryWrite,
|
||||
self::ThemeBuilderWrite => true,
|
||||
self::PageWrite, self::PresetWrite, self::ColorWrite, self::VariableWrite, self::ThemeOptionWrite,
|
||||
self::FontUpload, self::LibraryWrite, self::ThemeBuilderWrite => true,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
@@ -51,6 +54,9 @@ enum DiviFeature: string
|
||||
self::PresetWrite => 'Presets schreiben',
|
||||
self::ColorWrite => 'Farben schreiben',
|
||||
self::VariableWrite => 'Variablen schreiben',
|
||||
self::ThemeOptionRead => 'Theme-Optionen lesen',
|
||||
self::ThemeOptionWrite => 'Theme-Optionen schreiben',
|
||||
self::FontUpload => 'Schriften hochladen',
|
||||
self::LibraryRead => 'Bibliothek lesen',
|
||||
self::LibraryWrite => 'Bibliothek schreiben',
|
||||
self::Roles => 'Divi-Rollen',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"customizer-typography": "0264fa5a2ba604542e1e1ad0c0339f04c8f4f67297087a0f92954ee63d343fd0",
|
||||
"library-submit-layout": "594b3843d4c42e1a91e5eda06f108c79583a0036e46c92dd749f4f541e362d02",
|
||||
"preset-sync": "9586f8005b8065893c0350977a58ee9171bf715fff21ac0c84663cc512f45522",
|
||||
"sig:ET\\Builder\\Framework\\UserRole\\UserRole::can_current_user_use_visual_builder": "76d199c13adc6600c8a456225a8eb9db8037064f20eca9db597c4e45ccecf8d5",
|
||||
@@ -24,8 +25,19 @@
|
||||
"sig:ET_Core_PageResource::remove_static_resources": "eaa39fd2c037c2d399578f71862da5c04b68d58a30657916482f95e94e5d8a96",
|
||||
"sig:et_": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_builder_enabled_for_post_type": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_builder_get_custom_fonts": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_builder_get_fonts": "3ac3132305f3818c13df78416ae13e34c36af17a0ba9b66d652fe9babc6c051d",
|
||||
"sig:et_builder_get_google_fonts": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_builder_get_public_post_types": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_builder_get_websafe_fonts": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_delete_option": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_divi_global_font_style": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_pb_force_regenerate_templates": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_pb_is_allowed": "b7561d91c3e696079a598474517118c3e9809cbf1f08b1321babe7af11d15799",
|
||||
"sig:et_sanitize_float_number": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_sanitize_font_choices": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_sanitize_global_font_weight": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_sanitize_int_number": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_theme_builder_clear_wp_cache": "3ac3132305f3818c13df78416ae13e34c36af17a0ba9b66d652fe9babc6c051d",
|
||||
"sig:et_theme_builder_get_flat_template_settings_options": "ae7b0ec701e388b03d29d972195cb727fe1c8fd75dba4657ad71f1a963a32966",
|
||||
"sig:et_theme_builder_get_template": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
@@ -33,6 +45,7 @@
|
||||
"sig:et_theme_builder_get_theme_builder_template_ids": "b7561d91c3e696079a598474517118c3e9809cbf1f08b1321babe7af11d15799",
|
||||
"sig:et_theme_builder_insert_layout": "a6aed8043b2c07ef1082488fde706816cf0146847f5bac8acca79fb3fe5275fc",
|
||||
"sig:et_theme_builder_store_template": "6c84ec472739a2dbb87bcf21777c4dc977fd2233142eacfefe60eba462a4c219",
|
||||
"sig:et_update_option": "b6b87f370cc0a0e01df5899074045bc8646b05855d4cd7103d9fc1ca0ac09799",
|
||||
"tb-api-save": "7ba452f4d73c9b676281ca6ddcfddd669502965db868bf478ef7804937e5bfc4",
|
||||
"tb-load-setting-options": "2ea0a6925a1da5522489a417104b384b2bd77a16b25f4122b9503873a4c8a24e",
|
||||
"tb-store-template": "3bf4ac6028ed99ed8ac3523c659392a5fce7818ef8a80ea226e01f5404eb441d",
|
||||
|
||||
Reference in New Issue
Block a user