From a90cd7f5bdb0a1b6fd84345519049d9a65f33ac4 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Mon, 19 Oct 2026 17:44:57 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20F=C3=A4higkeiten=20theme-option-read/-w?= =?UTF-8?q?rite=20und=20font-upload;=20ThemeOptionsAdapter=20und=20FontCat?= =?UTF-8?q?alog=20(Manifest,=20Nachbau=20customizer-typography,=20Signatur?= =?UTF-8?q?-Referenzen)=20und=20DesignRights::themeOptions()/fonts()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- src/Integrations/Divi/Design/DesignRights.php | 69 +- src/Integrations/Divi/Design/FontCatalog.php | 95 + .../Divi/Design/ThemeOptionsAdapter.php | 187 + .../Divi/Release/DiviAdapters.php | 4 + src/Integrations/Divi/Release/DiviFeature.php | 12 +- .../Divi/Release/fingerprints.json | 13 + .../divi/reference/customizer-typography.txt | 3962 +++++++++++++++++ tests/Integration/divi-release-smoke.sh | 12 +- .../Divi/Cli/DiviCheckCommandTest.php | 9 +- .../Divi/Design/DesignRightsTest.php | 44 + .../Divi/Design/FontCatalogTest.php | 98 + .../Divi/Design/ThemeOptionsAdapterTest.php | 246 + .../Release/DiviFeatureAssignmentTest.php | 24 +- .../Divi/Release/DiviFeatureTest.php | 16 +- 14 files changed, 4765 insertions(+), 26 deletions(-) create mode 100644 src/Integrations/Divi/Design/FontCatalog.php create mode 100644 src/Integrations/Divi/Design/ThemeOptionsAdapter.php create mode 100644 tests/Fixtures/divi/reference/customizer-typography.txt create mode 100644 tests/Unit/Integrations/Divi/Design/FontCatalogTest.php create mode 100644 tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php diff --git a/src/Integrations/Divi/Design/DesignRights.php b/src/Integrations/Divi/Design/DesignRights.php index bf6f484..1ae3869 100644 --- a/src/Integrations/Divi/Design/DesignRights.php +++ b/src/Integrations/Divi/Design/DesignRights.php @@ -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 + )); + } } diff --git a/src/Integrations/Divi/Design/FontCatalog.php b/src/Integrations/Divi/Design/FontCatalog.php new file mode 100644 index 0000000..e51154f --- /dev/null +++ b/src/Integrations/Divi/Design/FontCatalog.php @@ -0,0 +1,95 @@ +|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 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 */ + public function uploaded(): array + { + return self::namesOf(et_builder_get_custom_fonts()); + } + + /** @return list 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 + */ + 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)); + } +} diff --git a/src/Integrations/Divi/Design/ThemeOptionsAdapter.php b/src/Integrations/Divi/Design/ThemeOptionsAdapter.php new file mode 100644 index 0000000..837ad82 --- /dev/null +++ b/src/Integrations/Divi/Design/ThemeOptionsAdapter.php @@ -0,0 +1,187 @@ + 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 + */ + 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 $set Checked values (ThemeOptionSpec, sanitize()) or raw values + * of a snapshot. + * @param list $unset + * @return array 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'); + } +} diff --git a/src/Integrations/Divi/Release/DiviAdapters.php b/src/Integrations/Divi/Release/DiviAdapters.php index 5e09753..293e76f 100644 --- a/src/Integrations/Divi/Release/DiviAdapters.php +++ b/src/Integrations/Divi/Release/DiviAdapters.php @@ -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, ]; diff --git a/src/Integrations/Divi/Release/DiviFeature.php b/src/Integrations/Divi/Release/DiviFeature.php index 2880001..4ed32b3 100644 --- a/src/Integrations/Divi/Release/DiviFeature.php +++ b/src/Integrations/Divi/Release/DiviFeature.php @@ -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', diff --git a/src/Integrations/Divi/Release/fingerprints.json b/src/Integrations/Divi/Release/fingerprints.json index 16da6fa..67bf1c9 100644 --- a/src/Integrations/Divi/Release/fingerprints.json +++ b/src/Integrations/Divi/Release/fingerprints.json @@ -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", diff --git a/tests/Fixtures/divi/reference/customizer-typography.txt b/tests/Fixtures/divi/reference/customizer-typography.txt new file mode 100644 index 0000000..777070d --- /dev/null +++ b/tests/Fixtures/divi/reference/customizer-typography.txt @@ -0,0 +1,3962 @@ +function et_divi_customizer_theme_settings( $wp_customize ) { + $site_domain = get_locale(); + + $google_fonts = et_builder_get_fonts( + array( + 'prepend_standard_fonts' => false, + ) + ); + + $user_fonts = et_builder_get_custom_fonts(); + + // combine google fonts with custom user fonts. + $google_fonts = array_merge( $user_fonts, $google_fonts ); + + $et_domain_fonts = array( + 'ru_RU' => 'cyrillic', + 'uk' => 'cyrillic', + 'bg_BG' => 'cyrillic', + 'vi' => 'vietnamese', + 'el' => 'greek', + 'ar' => 'arabic', + 'he_IL' => 'hebrew', + 'th' => 'thai', + 'si_lk' => 'sinhala', + 'bn_bd' => 'bengali', + 'ta_lk' => 'tamil', + 'te' => 'telegu', + 'km' => 'khmer', + 'kn' => 'kannada', + 'ml_in' => 'malayalam', + 'ja' => 'japanese', + 'ko_KR' => 'korean', + 'ml_IN' => 'malayalam', + 'zh_CN' => 'chinese-simplified', + ); + + $font_choices = array(); + $font_choices['none'] = array( + 'label' => 'Default Theme Font', + ); + + $removed_fonts_mapping = et_builder_old_fonts_mapping(); + + foreach ( $google_fonts as $google_font_name => $google_font_properties ) { + $use_parent_font = false; + + if ( isset( $removed_fonts_mapping[ $google_font_name ] ) ) { + $parent_font = $removed_fonts_mapping[ $google_font_name ]['parent_font']; + $google_font_properties['character_set'] = $google_fonts[ $parent_font ]['character_set']; + $use_parent_font = true; + } + + if ( '' !== $site_domain && isset( $et_domain_fonts[ $site_domain ] ) && isset( $google_font_properties['character_set'] ) && false === strpos( $google_font_properties['character_set'], $et_domain_fonts[ $site_domain ] ) ) { + continue; + } + + $font_choices[ $google_font_name ] = array( + 'label' => $google_font_name, + 'data' => array( + 'parent_font' => $use_parent_font ? $google_font_properties['parent_font'] : '', + 'parent_styles' => $use_parent_font ? $google_fonts[ $parent_font ]['styles'] : $google_font_properties['styles'], + 'current_styles' => $use_parent_font && isset( $google_fonts[ $parent_font ]['styles'] ) && isset( $google_font_properties['styles'] ) ? $google_font_properties['styles'] : '', + 'parent_subset' => $use_parent_font && isset( $google_fonts[ $parent_font ]['character_set'] ) ? $google_fonts[ $parent_font ]['character_set'] : '', + 'standard' => isset( $google_font_properties['standard'] ) && $google_font_properties['standard'] ? 'on' : 'off', + ), + ); + } + + $wp_customize->add_panel( + 'et_divi_general_settings', + array( + 'title' => esc_html__( 'General Settings', 'Divi' ), + 'priority' => 1, + ) + ); + + $wp_customize->add_section( + 'title_tagline', + array( + 'title' => esc_html__( 'Site Identity', 'Divi' ), + 'panel' => 'et_divi_general_settings', + ) + ); + + $wp_customize->add_section( + 'et_divi_general_layout', + array( + 'title' => esc_html__( 'Layout Settings', 'Divi' ), + 'panel' => 'et_divi_general_settings', + ) + ); + + $wp_customize->add_section( + 'et_divi_general_typography', + array( + 'title' => esc_html__( 'Typography', 'Divi' ), + 'panel' => 'et_divi_general_settings', + ) + ); + + $wp_customize->add_panel( + 'et_divi_mobile', + array( + 'title' => esc_html__( 'Mobile Styles', 'Divi' ), + 'priority' => 6, + ) + ); + + $wp_customize->add_section( + 'et_divi_mobile_tablet', + array( + 'title' => esc_html__( 'Tablet', 'Divi' ), + 'panel' => 'et_divi_mobile', + ) + ); + + $wp_customize->add_section( + 'et_divi_mobile_phone', + array( + 'title' => esc_html__( 'Phone', 'Divi' ), + 'panel' => 'et_divi_mobile', + ) + ); + + $wp_customize->add_section( + 'et_divi_mobile_menu', + array( + 'title' => esc_html__( 'Mobile Menu', 'Divi' ), + 'panel' => 'et_divi_mobile', + ) + ); + + $wp_customize->add_section( + 'et_divi_general_background', + array( + 'title' => esc_html__( 'Background', 'Divi' ), + 'panel' => 'et_divi_general_settings', + ) + ); + + $wp_customize->add_panel( + 'et_divi_header_panel', + array( + 'title' => esc_html__( 'Header & Navigation', 'Divi' ), + 'priority' => 2, + ) + ); + + $wp_customize->add_section( + 'et_divi_header_layout', + array( + 'title' => esc_html__( 'Header Format', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_header_primary', + array( + 'title' => esc_html__( 'Primary Menu Bar', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_header_secondary', + array( + 'title' => esc_html__( 'Secondary Menu Bar', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_header_slide', + array( + 'title' => esc_html__( 'Slide In & Fullscreen Header Settings', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_header_fixed', + array( + 'title' => esc_html__( 'Fixed Navigation Settings', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_header_information', + array( + 'title' => esc_html__( 'Header Elements', 'Divi' ), + 'panel' => 'et_divi_header_panel', + ) + ); + + $wp_customize->add_panel( + 'et_divi_footer_panel', + array( + 'title' => esc_html__( 'Footer', 'Divi' ), + 'priority' => 3, + ) + ); + + $wp_customize->add_section( + 'et_divi_footer_layout', + array( + 'title' => esc_html__( 'Layout', 'Divi' ), + 'panel' => 'et_divi_footer_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_footer_widgets', + array( + 'title' => esc_html__( 'Widgets', 'Divi' ), + 'panel' => 'et_divi_footer_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_footer_elements', + array( + 'title' => esc_html__( 'Footer Elements', 'Divi' ), + 'panel' => 'et_divi_footer_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_footer_menu', + array( + 'title' => esc_html__( 'Footer Menu', 'Divi' ), + 'panel' => 'et_divi_footer_panel', + ) + ); + + $wp_customize->add_section( + 'et_divi_bottom_bar', + array( + 'title' => esc_html__( 'Bottom Bar', 'Divi' ), + 'panel' => 'et_divi_footer_panel', + ) + ); + + $wp_customize->add_section( + 'et_color_schemes', + array( + 'title' => esc_html__( 'Color Schemes', 'Divi' ), + 'priority' => 7, + 'description' => esc_html__( 'Note: Color settings set above should be applied to the Default color scheme.', 'Divi' ), + ) + ); + + $wp_customize->add_panel( + 'et_divi_buttons_settings', + array( + 'title' => esc_html__( 'Buttons', 'Divi' ), + 'priority' => 4, + ) + ); + + $wp_customize->add_section( + 'et_divi_buttons', + array( + 'title' => esc_html__( 'Buttons Style', 'Divi' ), + 'panel' => 'et_divi_buttons_settings', + ) + ); + + $wp_customize->add_section( + 'et_divi_buttons_hover', + array( + 'title' => esc_html__( 'Buttons Hover Style', 'Divi' ), + 'panel' => 'et_divi_buttons_settings', + ) + ); + + $wp_customize->add_panel( + 'et_divi_blog_settings', + array( + 'title' => esc_html__( 'Blog', 'Divi' ), + 'priority' => 5, + ) + ); + + $wp_customize->add_section( + 'et_divi_blog_post', + array( + 'title' => esc_html__( 'Post', 'Divi' ), + 'panel' => 'et_divi_blog_settings', + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_meta_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->get_setting( 'blogname' )->transport = 'postMessage'; + + $wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage'; + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_meta_font_size]', + array( + 'label' => esc_html__( 'Meta Text Size', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_meta_height]', + array( + 'default' => '1', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_float_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_meta_height]', + array( + 'label' => esc_html__( 'Meta Line Height', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => .8, + 'max' => 3, + 'step' => .1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_meta_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_meta_spacing]', + array( + 'label' => esc_html__( 'Meta Letter Spacing', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -2, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_meta_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[post_meta_style]', + array( + 'label' => esc_html__( 'Meta Font Style', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_header_font_size]', + array( + 'default' => '30', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_header_font_size]', + array( + 'label' => esc_html__( 'Header Text Size', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 72, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_header_height]', + array( + 'default' => '1', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_float_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_header_height]', + array( + 'label' => esc_html__( 'Header Line Height', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0.8, + 'max' => 3, + 'step' => 0.1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_header_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[post_header_spacing]', + array( + 'label' => esc_html__( 'Header Letter Spacing', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -2, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[post_header_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[post_header_style]', + array( + 'label' => esc_html__( 'Header Font Style', 'Divi' ), + 'section' => 'et_divi_blog_post', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[boxed_layout]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[boxed_layout]', + array( + 'label' => esc_html__( 'Enable Boxed Layout', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[content_width]', + array( + 'default' => '1080', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[content_width]', + array( + 'label' => esc_html__( 'Website Content Width', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 960, + 'max' => 1920, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[gutter_width]', + array( + 'default' => '3', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[gutter_width]', + array( + 'label' => esc_html__( 'Website Gutter Width', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 1, + 'max' => 4, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[use_sidebar_width]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[use_sidebar_width]', + array( + 'label' => esc_html__( 'Use Custom Sidebar Width', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[sidebar_width]', + array( + 'default' => '21', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[sidebar_width]', + array( + 'label' => esc_html__( 'Sidebar Width', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 19, + 'max' => 33, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[section_padding]', + array( + 'default' => '4', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[section_padding]', + array( + 'label' => esc_html__( 'Section Height', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[phone_section_height]', + array( + 'default' => et_get_option( 'tablet_section_height', '50' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[phone_section_height]', + array( + 'label' => esc_html__( 'Section Height', 'Divi' ), + 'section' => 'et_divi_mobile_phone', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 150, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[tablet_section_height]', + array( + 'default' => '50', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[tablet_section_height]', + array( + 'label' => esc_html__( 'Section Height', 'Divi' ), + 'section' => 'et_divi_mobile_tablet', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 150, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[row_padding]', + array( + 'default' => '2', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[row_padding]', + array( + 'label' => esc_html__( 'Row Height', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[phone_row_height]', + array( + 'default' => et_get_option( 'tablet_row_height', '30' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[phone_row_height]', + array( + 'label' => esc_html__( 'Row Height', 'Divi' ), + 'section' => 'et_divi_mobile_phone', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 150, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[tablet_row_height]', + array( + 'default' => '30', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[tablet_row_height]', + array( + 'label' => esc_html__( 'Row Height', 'Divi' ), + 'section' => 'et_divi_mobile_tablet', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 150, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[cover_background]', + array( + 'default' => 'on', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[cover_background]', + array( + 'label' => esc_html__( 'Stretch Background Image', 'Divi' ), + 'section' => 'et_divi_general_background', + 'type' => 'checkbox', + ) + ); + + if ( ! is_null( $wp_customize->get_setting( 'background_color' ) ) ) { + $wp_customize->add_control( + new WP_Customize_Color_Control( + $wp_customize, + 'background_color', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_general_background', + ) + ) + ); + } + + if ( ! is_null( $wp_customize->get_setting( 'background_image' ) ) ) { + $wp_customize->add_control( + new WP_Customize_Image_Control( + $wp_customize, + 'background_image', + array( + 'label' => esc_html__( 'Background Image', 'Divi' ), + 'section' => 'et_divi_general_background', + ) + ) + ); + } + + // Remove default background_repeat setting and control since native + // background_repeat field has different different settings. + $wp_customize->remove_setting( 'background_repeat' ); + $wp_customize->remove_control( 'background_repeat' ); + + // Re-defined Divi specific background repeat option. + $wp_customize->add_setting( + 'background_repeat', + array( + 'default' => apply_filters( 'et_divi_background_repeat_default', 'repeat' ), + 'sanitize_callback' => 'et_sanitize_background_repeat', + 'theme_supports' => 'custom-background', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + ) + ); + + $wp_customize->add_control( + 'background_repeat', + array( + 'label' => esc_html__( 'Background Repeat', 'Divi' ), + 'section' => 'et_divi_general_background', + 'type' => 'radio', + 'choices' => et_divi_background_repeat_choices(), + ) + ); + + $wp_customize->add_control( + 'background_position_x', + array( + 'label' => esc_html__( 'Background Position', 'Divi' ), + 'section' => 'et_divi_general_background', + 'type' => 'radio', + 'choices' => array( + 'left' => esc_html__( 'Left', 'Divi' ), + 'center' => esc_html__( 'Center', 'Divi' ), + 'right' => esc_html__( 'Right', 'Divi' ), + ), + ) + ); + + // Remove default background_attachment setting and control since native + // background_attachment field has different different settings. + $wp_customize->remove_setting( 'background_attachment' ); + $wp_customize->remove_control( 'background_attachment' ); + + $wp_customize->add_setting( + 'background_attachment', + array( + 'default' => apply_filters( 'et_sanitize_background_attachment_default', 'scroll' ), + 'sanitize_callback' => 'et_sanitize_background_attachment', + 'theme_supports' => 'custom-background', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + ) + ); + + $wp_customize->add_control( + 'background_attachment', + array( + 'label' => esc_html__( 'Background Position', 'Divi' ), + 'section' => 'et_divi_general_background', + 'type' => 'radio', + 'choices' => et_divi_background_attachment_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[body_font_size]', + array( + 'label' => esc_html__( 'Body Text Size', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_font_height]', + array( + 'default' => '1.7', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_float_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[body_font_height]', + array( + 'label' => esc_html__( 'Body Line Height', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0.8, + 'max' => 3, + 'step' => 0.1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[phone_body_font_size]', + array( + 'default' => et_get_option( 'tablet_body_font_size', '14' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[phone_body_font_size]', + array( + 'label' => esc_html__( 'Body Text Size', 'Divi' ), + 'section' => 'et_divi_mobile_phone', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[tablet_body_font_size]', + array( + 'default' => et_get_option( 'body_font_size', '14' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[tablet_body_font_size]', + array( + 'label' => esc_html__( 'Body Text Size', 'Divi' ), + 'section' => 'et_divi_mobile_tablet', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_header_size]', + array( + 'default' => '30', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[body_header_size]', + array( + 'label' => esc_html__( 'Header Text Size', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 22, + 'max' => 72, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_header_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[body_header_spacing]', + array( + 'label' => esc_html__( 'Header Letter Spacing', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -2, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_header_height]', + array( + 'default' => '1', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_float_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[body_header_height]', + array( + 'label' => esc_html__( 'Header Line Height', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0.8, + 'max' => 3, + 'step' => 0.1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_header_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_divi_global_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[body_header_style]', + array( + 'label' => esc_html__( 'Header Font Style', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'type' => 'font_style', + 'choices' => et_divi_global_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[phone_header_font_size]', + array( + 'default' => et_get_option( 'tablet_header_font_size', '30' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[phone_header_font_size]', + array( + 'label' => esc_html__( 'Header Text Size', 'Divi' ), + 'section' => 'et_divi_mobile_phone', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 22, + 'max' => 72, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[tablet_header_font_size]', + array( + 'default' => et_get_option( 'body_header_size', '30' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[tablet_header_font_size]', + array( + 'label' => esc_html__( 'Header Text Size', 'Divi' ), + 'section' => 'et_divi_mobile_tablet', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 22, + 'max' => 72, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[heading_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[heading_font]', + array( + 'label' => esc_html__( 'Header Font', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[heading_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[heading_font_weight]', + array( + 'default' => '500', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_global_font_weight', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Custom_Select_Option( + $wp_customize, + 'et_divi[heading_font_weight]', + array( + 'label' => esc_html__( 'Header Font Weight', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[heading_font_weight]', + 'type' => 'select', + 'choices' => et_builder_get_font_weight_list(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[header_color]', + array( + 'default' => '#666666', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[header_color]', + array( + 'label' => esc_html__( 'Heading Text Color', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[header_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[body_font]', + array( + 'label' => esc_html__( 'Body Font', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[body_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[body_font_weight]', + array( + 'default' => '500', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_global_font_weight', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Custom_Select_Option( + $wp_customize, + 'et_divi[body_font_weight]', + array( + 'label' => esc_html__( 'Body Font Weight', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[body_font_weight]', + 'type' => 'select', + 'choices' => et_builder_get_font_weight_list(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[font_color]', + array( + 'default' => '#666666', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[font_color]', + array( + 'label' => esc_html__( 'Body Text Color', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[font_color]', + ) + ) + ); + + + $wp_customize->add_setting( + 'et_divi[link_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[link_color]', + array( + 'label' => esc_html__( 'Link Color', 'Divi' ), + 'section' => 'et_divi_general_typography', + 'settings' => 'et_divi[link_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[accent_color]', + array( + 'default' => '#2ea3f2', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_accent_color]', + array( + 'default' => '#8800FF', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[accent_color]', + array( + 'label' => esc_html__( 'Primary Color', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'settings' => 'et_divi[accent_color]', + ) + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[secondary_accent_color]', + array( + 'label' => esc_html__( 'Secondary Color', 'Divi' ), + 'section' => 'et_divi_general_layout', + 'settings' => 'et_divi[secondary_accent_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[color_schemes]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_color_scheme', + ) + ); + + $wp_customize->add_control( + 'et_divi[color_schemes]', + array( + 'label' => esc_html__( 'Color Schemes', 'Divi' ), + 'section' => 'et_color_schemes', + 'settings' => 'et_divi[color_schemes]', + 'type' => 'select', + 'choices' => et_divi_color_scheme_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[header_style]', + array( + 'default' => 'left', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_header_style', + ) + ); + + $wp_customize->add_control( + 'et_divi[header_style]', + array( + 'label' => esc_html__( 'Header Style', 'Divi' ), + 'section' => 'et_divi_header_layout', + 'type' => 'select', + 'choices' => et_divi_header_style_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[vertical_nav]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[vertical_nav]', + array( + 'label' => esc_html__( 'Enable Vertical Navigation', 'Divi' ), + 'section' => 'et_divi_header_layout', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[vertical_nav_orientation]', + array( + 'default' => 'left', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_left_right', + ) + ); + + $wp_customize->add_control( + 'et_divi[vertical_nav_orientation]', + array( + 'label' => esc_html__( 'Vertical Menu Orientation', 'Divi' ), + 'section' => 'et_divi_header_layout', + 'type' => 'select', + 'choices' => et_divi_left_right_choices(), + ) + ); + + if ( 'on' === et_get_option( 'divi_fixed_nav', 'on' ) ) { + + $wp_customize->add_setting( + 'et_divi[hide_nav]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[hide_nav]', + array( + 'label' => esc_html__( 'Hide Navigation Until Scroll', 'Divi' ), + 'section' => 'et_divi_header_layout', + 'type' => 'checkbox', + ) + ); + + } + + $wp_customize->add_setting( + 'et_divi[show_header_social_icons]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[show_header_social_icons]', + array( + 'label' => esc_html__( 'Show Social Icons', 'Divi' ), + 'section' => 'et_divi_header_information', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[show_search_icon]', + array( + 'default' => 'on', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[show_search_icon]', + array( + 'label' => esc_html__( 'Show Search Icon', 'Divi' ), + 'section' => 'et_divi_header_information', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_show_top_bar]', + array( + 'default' => 'on', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[slide_nav_show_top_bar]', + array( + 'label' => esc_html__( 'Show Top Bar', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_width]', + array( + 'default' => '320', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[slide_nav_width]', + array( + 'label' => esc_html__( 'Menu Width', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 280, + 'max' => 600, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[slide_nav_font_size]', + array( + 'label' => esc_html__( 'Menu Text Size', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 24, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_top_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[slide_nav_top_font_size]', + array( + 'label' => esc_html__( 'Top Bar Text Size', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 24, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fullscreen_nav_font_size]', + array( + 'default' => '30', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[fullscreen_nav_font_size]', + array( + 'label' => esc_html__( 'Menu Text Size', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 50, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fullscreen_nav_top_font_size]', + array( + 'default' => '18', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[fullscreen_nav_top_font_size]', + array( + 'label' => esc_html__( 'Top Bar Text Size', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 40, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_font_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[slide_nav_font_spacing]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -1, + 'max' => 8, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[slide_nav_font]', + array( + 'label' => esc_html__( 'Font', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_font_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[slide_nav_font_style]', + array( + 'label' => esc_html__( 'Font Style', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_bg]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_bg]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_links_color]', + array( + 'default' => '#ffffff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_links_color]', + array( + 'label' => esc_html__( 'Menu Link Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_links_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_links_color_active]', + array( + 'default' => '#ffffff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_links_color_active]', + array( + 'label' => esc_html__( 'Active Link Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_links_color_active]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_top_color]', + array( + 'default' => 'rgba(255,255,255,0.6)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_top_color]', + array( + 'label' => esc_html__( 'Top Bar Text Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_top_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_search]', + array( + 'default' => 'rgba(255,255,255,0.6)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_search]', + array( + 'label' => esc_html__( 'Search Bar Text Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_search]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[slide_nav_search_bg]', + array( + 'default' => 'rgba(0,0,0,0.2)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[slide_nav_search_bg]', + array( + 'label' => esc_html__( 'Search Bar Background Color', 'Divi' ), + 'section' => 'et_divi_header_slide', + 'settings' => 'et_divi[slide_nav_search_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[nav_fullwidth]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[nav_fullwidth]', + array( + 'label' => esc_html__( 'Make Full Width', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[hide_primary_logo]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[hide_primary_logo]', + array( + 'label' => esc_html__( 'Hide Logo Image', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[menu_height]', + array( + 'default' => '66', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[menu_height]', + array( + 'label' => esc_html__( 'Menu Height', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 30, + 'max' => 300, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[logo_height]', + array( + 'default' => '54', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[logo_height]', + array( + 'label' => esc_html__( 'Logo Max Height', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 30, + 'max' => 100, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[menu_margin_top]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[menu_margin_top]', + array( + 'label' => esc_html__( 'Menu Top Margin', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 300, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[primary_nav_font_size]', + array( + 'label' => esc_html__( 'Text Size', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 24, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_font_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[primary_nav_font_spacing]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -1, + 'max' => 8, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[primary_nav_font]', + array( + 'label' => esc_html__( 'Font', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[primary_nav_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_font_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[primary_nav_font_style]', + array( + 'label' => esc_html__( 'Font Style', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_font_size]', + array( + 'default' => '12', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_fullwidth]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[secondary_nav_fullwidth]', + array( + 'label' => esc_html__( 'Make Full Width', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[secondary_nav_font_size]', + array( + 'label' => esc_html__( 'Text Size', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 20, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_font_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[secondary_nav_font_spacing]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -1, + 'max' => 8, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[secondary_nav_font]', + array( + 'label' => esc_html__( 'Font', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'settings' => 'et_divi[secondary_nav_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_font_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[secondary_nav_font_style]', + array( + 'label' => esc_html__( 'Font Style', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[menu_link]', + array( + 'default' => 'rgba(0,0,0,0.6)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[menu_link]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[menu_link]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[hide_mobile_logo]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[hide_mobile_logo]', + array( + 'label' => esc_html__( 'Hide Logo Image', 'Divi' ), + 'section' => 'et_divi_mobile_menu', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[mobile_menu_link]', + array( + 'default' => et_get_option( 'menu_link', 'rgba(0,0,0,0.6)' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[mobile_menu_link]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_mobile_menu', + 'settings' => 'et_divi[mobile_menu_link]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[menu_link_active]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[menu_link_active]', + array( + 'label' => esc_html__( 'Active Link Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[menu_link_active]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_bg]', + array( + 'default' => '#ffffff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[primary_nav_bg]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[primary_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_dropdown_bg]', + array( + 'default' => et_get_option( 'primary_nav_bg', '#ffffff' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[primary_nav_dropdown_bg]', + array( + 'label' => esc_html__( 'Dropdown Menu Background Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[primary_nav_dropdown_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_dropdown_line_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[primary_nav_dropdown_line_color]', + array( + 'label' => esc_html__( 'Dropdown Menu Line Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[primary_nav_dropdown_line_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_dropdown_link_color]', + array( + 'default' => et_get_option( 'menu_link', 'rgba(0,0,0,0.7)' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[primary_nav_dropdown_link_color]', + array( + 'label' => esc_html__( 'Dropdown Menu Text Color', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'settings' => 'et_divi[primary_nav_dropdown_link_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[primary_nav_dropdown_animation]', + array( + 'default' => 'fade', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_dropdown_animation', + ) + ); + + $wp_customize->add_control( + 'et_divi[primary_nav_dropdown_animation]', + array( + 'label' => esc_html__( 'Dropdown Menu Animation', 'Divi' ), + 'section' => 'et_divi_header_primary', + 'type' => 'select', + 'choices' => et_divi_dropdown_animation_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[mobile_primary_nav_bg]', + array( + 'default' => et_get_option( 'primary_nav_bg', '#ffffff' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[mobile_primary_nav_bg]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_mobile_menu', + 'settings' => 'et_divi[mobile_primary_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_bg]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[secondary_nav_bg]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'settings' => 'et_divi[secondary_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_text_color_new]', + array( + 'default' => '#ffffff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[secondary_nav_text_color_new]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'settings' => 'et_divi[secondary_nav_text_color_new]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_dropdown_bg]', + array( + 'default' => et_get_option( 'secondary_nav_bg', et_get_option( 'accent_color', '#2ea3f2' ) ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[secondary_nav_dropdown_bg]', + array( + 'label' => esc_html__( 'Dropdown Menu Background Color', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'settings' => 'et_divi[secondary_nav_dropdown_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_dropdown_link_color]', + array( + 'default' => et_get_option( 'secondary_nav_text_color_new', '#ffffff' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[secondary_nav_dropdown_link_color]', + array( + 'label' => esc_html__( 'Dropdown Menu Text Color', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'settings' => 'et_divi[secondary_nav_dropdown_link_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[secondary_nav_dropdown_animation]', + array( + 'default' => 'fade', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_dropdown_animation', + ) + ); + + $wp_customize->add_control( + 'et_divi[secondary_nav_dropdown_animation]', + array( + 'label' => esc_html__( 'Dropdown Menu Animation', 'Divi' ), + 'section' => 'et_divi_header_secondary', + 'type' => 'select', + 'choices' => et_divi_dropdown_animation_choices(), + ) + ); + + // Setting with no control kept for backwards compatbility. + $wp_customize->add_setting( + 'et_divi[primary_nav_text_color]', + array( + 'default' => 'dark', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'sanitize_hex_color', + ) + ); + + // Setting with no control kept for backwards compatbility. + $wp_customize->add_setting( + 'et_divi[secondary_nav_text_color]', + array( + 'default' => 'light', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'sanitize_hex_color', + ) + ); + + if ( 'on' === et_get_option( 'divi_fixed_nav', 'on' ) ) { + $wp_customize->add_setting( + 'et_divi[hide_fixed_logo]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[hide_fixed_logo]', + array( + 'label' => esc_html__( 'Hide Logo Image', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[minimized_menu_height]', + array( + 'default' => '40', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[minimized_menu_height]', + array( + 'label' => esc_html__( 'Fixed Menu Height', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 30, + 'max' => 300, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_primary_nav_font_size]', + array( + 'default' => et_get_option( 'primary_nav_font_size', '14' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[fixed_primary_nav_font_size]', + array( + 'label' => esc_html__( 'Text Size', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 24, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_primary_nav_bg]', + array( + 'default' => et_get_option( 'primary_nav_bg', '#ffffff' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[fixed_primary_nav_bg]', + array( + 'label' => esc_html__( 'Primary Menu Background Color', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'settings' => 'et_divi[fixed_primary_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_secondary_nav_bg]', + array( + 'default' => et_get_option( 'secondary_nav_bg', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[fixed_secondary_nav_bg]', + array( + 'label' => esc_html__( 'Secondary Menu Background Color', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'settings' => 'et_divi[fixed_secondary_nav_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_menu_link]', + array( + 'default' => et_get_option( 'menu_link', 'rgba(0,0,0,0.6)' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[fixed_menu_link]', + array( + 'label' => esc_html__( 'Primary Menu Link Color', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'settings' => 'et_divi[fixed_menu_link]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_secondary_menu_link]', + array( + 'default' => et_get_option( 'secondary_nav_text_color_new', '#ffffff' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[fixed_secondary_menu_link]', + array( + 'label' => esc_html__( 'Secondary Menu Link Color', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'settings' => 'et_divi[fixed_secondary_menu_link]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[fixed_menu_link_active]', + array( + 'default' => et_get_option( 'menu_link_active', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[fixed_menu_link_active]', + array( + 'label' => esc_html__( 'Active Primary Menu Link Color', 'Divi' ), + 'section' => 'et_divi_header_fixed', + 'settings' => 'et_divi[fixed_menu_link_active]', + ) + ) + ); + } + + $wp_customize->add_setting( + 'et_divi[phone_number]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_html_input_text', + ) + ); + + $wp_customize->add_control( + 'et_divi[phone_number]', + array( + 'label' => esc_html__( 'Phone Number', 'Divi' ), + 'section' => 'et_divi_header_information', + 'type' => 'text', + ) + ); + + $wp_customize->add_setting( + 'et_divi[header_email]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'sanitize_email', + ) + ); + + $wp_customize->add_control( + 'et_divi[header_email]', + array( + 'label' => esc_html__( 'Email', 'Divi' ), + 'section' => 'et_divi_header_information', + 'type' => 'text', + ) + ); + + $wp_customize->add_setting( + 'et_divi[show_footer_social_icons]', + array( + 'default' => 'on', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[show_footer_social_icons]', + array( + 'label' => esc_html__( 'Show Social Icons', 'Divi' ), + 'section' => 'et_divi_footer_elements', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_columns]', + array( + 'default' => '4', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_footer_column', + ) + ); + + $wp_customize->add_control( + 'et_divi[footer_columns]', + array( + 'label' => esc_html__( 'Column Layout', 'Divi' ), + 'section' => 'et_divi_footer_layout', + 'settings' => 'et_divi[footer_columns]', + 'type' => 'select', + 'choices' => et_divi_footer_column_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_bg]', + array( + 'default' => '#222222', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_bg]', + array( + 'label' => esc_html__( 'Footer Background Color', 'Divi' ), + 'section' => 'et_divi_footer_layout', + 'settings' => 'et_divi[footer_bg]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[widget_header_font_size]', + array( + 'default' => absint( et_get_option( 'body_header_size', '30' ) ) * .6, + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[widget_header_font_size]', + array( + 'label' => esc_html__( 'Header Text Size', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 72, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[widget_header_font_style]', + array( + 'default' => et_get_option( 'widget_header_font_style', '' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[widget_header_font_style]', + array( + 'label' => esc_html__( 'Header Font Style', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[widget_body_font_size]', + array( + 'default' => et_get_option( 'body_font_size', '14' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[widget_body_font_size]', + array( + 'label' => esc_html__( 'Body/Link Text Size', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[widget_body_line_height]', + array( + 'default' => '1.7', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_float_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[widget_body_line_height]', + array( + 'label' => esc_html__( 'Body/Link Line Height', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0.8, + 'max' => 3, + 'step' => 0.1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[widget_body_font_style]', + array( + 'default' => et_get_option( 'footer_widget_body_font_style', '' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[widget_body_font_style]', + array( + 'label' => esc_html__( 'Body Font Style', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_widget_text_color]', + array( + 'default' => '#fff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_widget_text_color]', + array( + 'label' => esc_html__( 'Widget Text Color', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'settings' => 'et_divi[footer_widget_text_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_widget_link_color]', + array( + 'default' => '#fff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_widget_link_color]', + array( + 'label' => esc_html__( 'Widget Link Color', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'settings' => 'et_divi[footer_widget_link_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_widget_header_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_widget_header_color]', + array( + 'label' => esc_html__( 'Widget Header Color', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'settings' => 'et_divi[footer_widget_header_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_widget_bullet_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_widget_bullet_color]', + array( + 'label' => esc_html__( 'Widget Bullet Color', 'Divi' ), + 'section' => 'et_divi_footer_widgets', + 'settings' => 'et_divi[footer_widget_bullet_color]', + ) + ) + ); + + /* Footer Menu */ + $wp_customize->add_setting( + 'et_divi[footer_menu_background_color]', + array( + 'default' => 'rgba(255,255,255,0.05)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_menu_background_color]', + array( + 'label' => esc_html__( 'Footer Menu Background Color', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'settings' => 'et_divi[footer_menu_background_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_menu_text_color]', + array( + 'default' => '#bbbbbb', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_menu_text_color]', + array( + 'label' => esc_html__( 'Footer Menu Text Color', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'settings' => 'et_divi[footer_menu_text_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_menu_active_link_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[footer_menu_active_link_color]', + array( + 'label' => esc_html__( 'Footer Menu Active Link Color', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'settings' => 'et_divi[footer_menu_active_link_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_menu_letter_spacing]', + array( + 'default' => '0', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[footer_menu_letter_spacing]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 20, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_menu_font_style]', + array( + 'default' => et_get_option( 'footer_footer_menu_font_style', '' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[footer_menu_font_style]', + array( + 'label' => esc_html__( 'Font Style', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[footer_menu_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[footer_menu_font_size]', + array( + 'label' => esc_html__( 'Font Size', 'Divi' ), + 'section' => 'et_divi_footer_menu', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + /* Bottom Bar */ + $wp_customize->add_setting( + 'et_divi[bottom_bar_background_color]', + array( + 'default' => 'rgba(0,0,0,0.32)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[bottom_bar_background_color]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'settings' => 'et_divi[bottom_bar_background_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[bottom_bar_text_color]', + array( + 'default' => '#666666', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[bottom_bar_text_color]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'settings' => 'et_divi[bottom_bar_text_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[bottom_bar_font_style]', + array( + 'default' => et_get_option( 'footer_bottom_bar_font_style', '' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[bottom_bar_font_style]', + array( + 'label' => esc_html__( 'Font Style', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[bottom_bar_font_size]', + array( + 'default' => '14', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[bottom_bar_font_size]', + array( + 'label' => esc_html__( 'Font Size', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[bottom_bar_social_icon_size]', + array( + 'default' => '24', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[bottom_bar_social_icon_size]', + array( + 'label' => esc_html__( 'Social Icon Size', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 10, + 'max' => 32, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[bottom_bar_social_icon_color]', + array( + 'default' => '#666666', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[bottom_bar_social_icon_color]', + array( + 'label' => esc_html__( 'Social Icon Color', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'settings' => 'et_divi[bottom_bar_social_icon_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[disable_custom_footer_credits]', + array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + 'et_divi[disable_custom_footer_credits]', + array( + 'label' => esc_html__( 'Disable Footer Credits', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'type' => 'checkbox', + ) + ); + + $wp_customize->add_setting( + 'et_divi[custom_footer_credits]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_html_input_text', + ) + ); + + $wp_customize->add_control( + 'et_divi[custom_footer_credits]', + array( + 'label' => esc_html__( 'Edit Footer Credits', 'Divi' ), + 'section' => 'et_divi_bottom_bar', + 'settings' => 'et_divi[custom_footer_credits]', + 'type' => 'textarea', + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_font_size]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_font_size', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_font_size]', + array( + 'label' => esc_html__( 'Text Size', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 12, + 'max' => 30, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_text_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_text_color]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_buttons', + 'settings' => 'et_divi[all_buttons_text_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_bg_color]', + array( + 'default' => 'rgba(0,0,0,0)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_bg_color]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_buttons', + 'settings' => 'et_divi[all_buttons_bg_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_border_width]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_border_width', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_border_width]', + array( + 'label' => esc_html__( 'Border Width', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_border_color]', + array( + 'default' => et_get_option( 'accent_color', '#2ea3f2' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_border_color]', + array( + 'label' => esc_html__( 'Border Color', 'Divi' ), + 'section' => 'et_divi_buttons', + 'settings' => 'et_divi[all_buttons_border_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_border_radius]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_border_radius', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_border_radius]', + array( + 'label' => esc_html__( 'Border Radius', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 100, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_spacing]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_spacing', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_spacing]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -2, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_font_style]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_style', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Font_Style_Option( + $wp_customize, + 'et_divi[all_buttons_font_style]', + array( + 'label' => esc_html__( 'Button Font Style', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'font_style', + 'choices' => et_divi_font_style_choices(), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_font]', + array( + 'default' => 'none', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_choices', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Select_Option( + $wp_customize, + 'et_divi[all_buttons_font]', + array( + 'label' => esc_html__( 'Buttons Font', 'Divi' ), + 'section' => 'et_divi_buttons', + 'settings' => 'et_divi[all_buttons_font]', + 'type' => 'select', + 'choices' => $font_choices, + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_icon]', + array( + 'default' => 'yes', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'sanitize_callback' => 'et_sanitize_yes_no', + ) + ); + + $wp_customize->add_control( + 'et_divi[all_buttons_icon]', + array( + 'label' => esc_html__( 'Add Button Icon', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'select', + 'choices' => et_divi_yes_no_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_selected_icon]', + array( + 'default' => '5', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_font_icon', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Icon_Picker_Option( + $wp_customize, + 'et_divi[all_buttons_selected_icon]', + array( + 'label' => esc_html__( 'Select Icon', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'icon_picker', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_icon_color]', + array( + 'default' => '#ffffff', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_icon_color]', + array( + 'label' => esc_html__( 'Icon Color', 'Divi' ), + 'section' => 'et_divi_buttons', + 'settings' => 'et_divi[all_buttons_icon_color]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_icon_placement]', + array( + 'default' => 'right', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_left_right', + ) + ); + + $wp_customize->add_control( + 'et_divi[all_buttons_icon_placement]', + array( + 'label' => esc_html__( 'Icon Placement', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'select', + 'choices' => et_divi_left_right_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_icon_hover]', + array( + 'default' => 'yes', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_yes_no', + ) + ); + + $wp_customize->add_control( + 'et_divi[all_buttons_icon_hover]', + array( + 'label' => esc_html__( 'Only Show Icon on Hover', 'Divi' ), + 'section' => 'et_divi_buttons', + 'type' => 'select', + 'choices' => et_divi_yes_no_choices(), + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_text_color_hover]', + array( + 'default' => '', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_text_color_hover]', + array( + 'label' => esc_html__( 'Text Color', 'Divi' ), + 'section' => 'et_divi_buttons_hover', + 'settings' => 'et_divi[all_buttons_text_color_hover]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_bg_color_hover]', + array( + 'default' => 'rgba(255,255,255,0.2)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_bg_color_hover]', + array( + 'label' => esc_html__( 'Background Color', 'Divi' ), + 'section' => 'et_divi_buttons_hover', + 'settings' => 'et_divi[all_buttons_bg_color_hover]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_border_color_hover]', + array( + 'default' => 'rgba(0,0,0,0)', + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_alpha_color', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Customize_Color_Alpha_Control( + $wp_customize, + 'et_divi[all_buttons_border_color_hover]', + array( + 'label' => esc_html__( 'Border Color', 'Divi' ), + 'section' => 'et_divi_buttons_hover', + 'settings' => 'et_divi[all_buttons_border_color_hover]', + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_border_radius_hover]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_border_radius_hover', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'absint', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_border_radius_hover]', + array( + 'label' => esc_html__( 'Border Radius', 'Divi' ), + 'section' => 'et_divi_buttons_hover', + 'type' => 'range', + 'input_attrs' => array( + 'min' => 0, + 'max' => 50, + 'step' => 1, + ), + ) + ) + ); + + $wp_customize->add_setting( + 'et_divi[all_buttons_spacing_hover]', + array( + 'default' => ET_Global_Settings::get_value( 'all_buttons_spacing_hover', 'default' ), + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'postMessage', + 'sanitize_callback' => 'et_sanitize_int_number', + ) + ); + + $wp_customize->add_control( + new ET_Divi_Range_Option( + $wp_customize, + 'et_divi[all_buttons_spacing_hover]', + array( + 'label' => esc_html__( 'Letter Spacing', 'Divi' ), + 'section' => 'et_divi_buttons_hover', + 'type' => 'range', + 'input_attrs' => array( + 'min' => -2, + 'max' => 10, + 'step' => 1, + ), + ) + ) + ); + } diff --git a/tests/Integration/divi-release-smoke.sh b/tests/Integration/divi-release-smoke.sh index 35dd803..084ec5f 100755 --- a/tests/Integration/divi-release-smoke.sh +++ b/tests/Integration/divi-release-smoke.sh @@ -27,8 +27,8 @@ trap 'rc=$?; cleanup; guard_exit "$rc" || exit $?' EXIT check_pages() { wp post list --post_type=page --post_status=any --meta_key=_lit_mcp_check_page --format=count; } -# The ids of the thirteen features (plan decision D1, plan 4c V2), sorted. -FEATURE_IDS='["catalog","color-write","design-read","dynamic-sources","library-read","library-write","page-read","page-write","preset-write","roles","tb-read","tb-write","variable-write"]' +# The ids of the sixteen features (plan decision D1, plan 4c V2, plan 4d W1), sorted. +FEATURE_IDS='["catalog","color-write","design-read","dynamic-sources","font-upload","library-read","library-write","page-read","page-write","preset-write","roles","tb-read","tb-write","theme-option-read","theme-option-write","variable-write"]' # Adds a problem to one feature in the cached state, as if its mirror check had failed. Needs the cache entry of # ReleaseResolver (format 2: ["format" => 2, "key" => stateKey, "problems" => list of Problem::toArray()]); the key @@ -68,7 +68,7 @@ echo "$STATUS" | jq -e --argjson ids "$FEATURE_IDS" ' .components[] | select(.id == "divi") | .features | ([.[].id] | sort) == $ids and all(.state == "released" and .source == "code" and .problems == [])' >/dev/null \ - || fail "all thirteen features must be released from code without problems: $STATUS" + || fail "all sixteen features must be released from code without problems: $STATUS" echo "$STATUS" | jq -e ' .components[] | select(.id == "divi") | .features | (.[] | select(.id == "page-write") | .tools | any(. == "lit-divi-insert")) @@ -76,7 +76,7 @@ echo "$STATUS" | jq -e ' and (.[] | select(.id == "preset-write") | .tools | any(startswith("lit/divi-presets-"))) and (.[] | select(.id == "page-read") | .label == "Seiten lesen")' >/dev/null \ || fail "features must name their tools (direct lit-, indirect lit/): $STATUS" -pass "lit-site-status: thirteen features released (code), each with its tools" +pass "lit-site-status: sixteen features released (code), each with its tools" # 2. Admin check through the REST route (application password instead of the cookie nonce). # X-Forwarded-Proto: https is required here: without it the request to http://localhost with the @@ -212,8 +212,8 @@ simulate_problem page-write "Smoke: Cache vergiftet" CLI=$(wp --user="$ADMIN_LOGIN" lit-mcp divi-check 2>&1) || fail "wp lit-mcp divi-check must succeed (exit 0): $CLI" echo "$CLI" | grep -q '^Fähigkeiten:' || fail "divi-check must print the matrix: $CLI" echo "$CLI" | grep -q '✓ Seiten schreiben – freigegeben (code)' || fail "divi-check must show page-write released: $CLI" -[ "$(echo "$CLI" | grep -c ' – freigegeben (')" = "13" ] || fail "divi-check must show thirteen released features: $CLI" +[ "$(echo "$CLI" | grep -c ' – freigegeben (')" = "16" ] || fail "divi-check must show sixteen released features: $CLI" [ "$(check_pages)" = "0" ] || fail "the CLI check must delete its page" -pass "wp lit-mcp divi-check prints thirteen released features, recomputes the state and leaves no page" +pass "wp lit-mcp divi-check prints sixteen released features, recomputes the state and leaves no page" finish diff --git a/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php b/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php index 3098480..6c914bd 100644 --- a/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php +++ b/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php @@ -228,14 +228,14 @@ final class DiviCheckCommandTest extends TestCase self::assertSame(0, $code); self::assertSame( [ - 'library-submit-layout', 'preset-sync', 'sig:A::b', 'sig:et_pb_is_allowed', 'tb-api-save', - 'tb-load-setting-options', 'tb-store-template', 'vb-sync-flags', + 'customizer-typography', 'library-submit-layout', 'preset-sync', 'sig:A::b', 'sig:et_pb_is_allowed', + 'tb-api-save', 'tb-load-setting-options', 'tb-store-template', 'vb-sync-flags', ], array_keys(DiviReference::expected($this->expectedFile)) ); self::assertSame(str_repeat('b', 64), DiviReference::expected($this->expectedFile)['sig:A::b']); self::assertContains( - 'Referenz aktualisiert (6 Fingerabdrücke, 2 Signaturen). Smoke-Skripte laufen lassen, dann committen.', + 'Referenz aktualisiert (7 Fingerabdrücke, 2 Signaturen). Smoke-Skripte laufen lassen, dann committen.', $this->lines ); self::assertStringStartsWith( @@ -244,10 +244,11 @@ final class DiviCheckCommandTest extends TestCase ); } - /** Minimal stand-ins for the six mirrored Divi functions at their real paths. */ + /** Minimal stand-ins for the seven mirrored Divi functions at their real paths. */ private function writeDivi(): void { $files = [ + 'functions.php' => "function et_divi_customizer_theme_settings( \$wp_customize ) {\n}", 'includes/builder/shortcode-core.php' => "function et_pb_submit_layout( \$args ) {\n\treturn 1;\n}", 'includes/builder/frontend-builder/theme-builder/api.php' => "function et_theme_builder_api_save() {\n}", 'includes/builder/frontend-builder/theme-builder/theme-builder.php' => diff --git a/tests/Unit/Integrations/Divi/Design/DesignRightsTest.php b/tests/Unit/Integrations/Divi/Design/DesignRightsTest.php index 0714c59..7857e7b 100644 --- a/tests/Unit/Integrations/Divi/Design/DesignRightsTest.php +++ b/tests/Unit/Integrations/Divi/Design/DesignRightsTest.php @@ -7,7 +7,9 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Design\DesignRights; use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter; +use LIT\MCP\Integrations\Divi\Design\FontCatalog; use LIT\MCP\Integrations\Divi\Design\GlobalVariablesAdapter; +use LIT\MCP\Integrations\Divi\Design\ThemeOptionsAdapter; use LIT\MCP\Tests\Unit\TestCase; use Mockery; @@ -73,4 +75,46 @@ final class DesignRightsTest extends TestCase $refusal = self::assertToolError(DesignRights::variables($writer, $variables), 'forbidden'); self::assertStringContainsString('Variablen ändern erfordert', $refusal['message']); } + + public function testThemeOptionsNeedEditThemeOptionsAndTheThemeCustomizerRight(): void + { + $writer = Mockery::mock(DesignSystemWriter::class); + // Divi's customizer does not need the Visual Builder (plan 4d, rights). + $writer->expects('canUseVisualBuilder')->never(); + $options = Mockery::mock(ThemeOptionsAdapter::class); + $options->allows('canCustomize')->andReturn(true, false); + Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'edit_theme_options'); + + self::assertNull(DesignRights::themeOptions($writer, $options)); + $refusal = self::assertToolError(DesignRights::themeOptions($writer, $options), 'forbidden'); + self::assertStringContainsString('Theme-Anpassung', $refusal['message']); + self::assertStringContainsString('keine Theme-Optionen', $refusal['message']); + self::assertStringContainsString('Rollen-Editor', $refusal['hint']); + + Functions\when('current_user_can')->justReturn(false); + $refusal = self::assertToolError(DesignRights::themeOptions($writer, $options), 'forbidden'); + self::assertStringContainsString('Theme-Optionen ändern erfordert', $refusal['message']); + self::assertStringContainsString('edit_theme_options', $refusal['message']); + } + + public function testFontsNeedUploadFilesEditThemeOptionsAndTheFontsRight(): void + { + $fonts = Mockery::mock(FontCatalog::class); + $fonts->allows('canManage')->andReturn(true, false); + Functions\when('current_user_can')->justReturn(true); + + self::assertNull(DesignRights::fonts($fonts)); + $refusal = self::assertToolError(DesignRights::fonts($fonts), 'forbidden'); + self::assertStringContainsString('Eigene Schriften verwalten', $refusal['message']); + self::assertStringContainsString('Rollen-Editor', $refusal['hint']); + + Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'edit_theme_options'); + $refusal = self::assertToolError(DesignRights::fonts($fonts), 'forbidden'); + self::assertStringContainsString('upload_files', $refusal['message']); + + Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'upload_files'); + $refusal = self::assertToolError(DesignRights::fonts($fonts), 'forbidden'); + self::assertStringContainsString('Schriften hochladen erfordert', $refusal['message']); + self::assertStringContainsString('edit_theme_options', $refusal['message']); + } } diff --git a/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php b/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php new file mode 100644 index 0000000..f35dcf6 --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php @@ -0,0 +1,98 @@ +area); + self::assertSame( + [ + 'et_builder_get_fonts()', 'et_builder_get_custom_fonts()', 'et_builder_get_google_fonts()', + 'et_builder_get_websafe_fonts()', 'et_pb_is_allowed()', + ], + array_map(static fn (Call $call): string => $call->label(), $manifest->calls) + ); + self::assertSame([0, 0, 0, 0, 1], array_map(static fn (Call $call): ?int => $call->args, $manifest->calls)); + self::assertSame(['et_uploaded_fonts', 'et_google_fonts_cache', 'et_google_api_settings'], $manifest->keys); + // Plan 4d W1: a broken font list locks every tool that checks font names against it. + self::assertSame( + [DiviFeature::ThemeOptionWrite, DiviFeature::FontUpload, DiviFeature::VariableWrite], + $manifest->features + ); + self::assertSame([DiviFeature::FontUpload], $manifest->featuresOf($manifest->calls[4])); + } + + /** @runInSeparateProcess */ + public function testNamesAreTheExactFamiliesOfDivisSanitizerLoadedOnce(): void + { + Functions\expect('et_builder_get_custom_fonts')->once()->andReturn(['Hausschrift' => [], '1942' => []]); + Functions\expect('et_builder_get_fonts')->once() + ->andReturn(['Arial' => [], 'Open Sans' => [], 'Roboto' => []]); + $catalog = new FontCatalog(); + + self::assertSame(['Hausschrift', '1942', 'Arial', 'Open Sans', 'Roboto'], $catalog->names()); + self::assertTrue($catalog->has('Roboto')); + self::assertTrue($catalog->has('1942'), 'a numeric family name stays a string'); + self::assertFalse($catalog->has('roboto'), 'exact, like Divi'); + self::assertFalse($catalog->has('none'), 'Divi stores the default font as false, never as a name'); + self::assertSame($catalog->names(), $catalog->names()); + } + + /** @runInSeparateProcess */ + public function testUploadedAndReservedAreReadFreshEachTime(): void + { + Functions\expect('et_builder_get_custom_fonts')->twice() + ->andReturn(['Hausschrift' => []], ['Hausschrift' => [], 'Neu' => []]); + Functions\when('et_builder_get_google_fonts')->justReturn(['Roboto' => []]); + Functions\when('et_builder_get_websafe_fonts')->justReturn(['Arial' => [], 'Roboto' => []]); + $catalog = new FontCatalog(); + + self::assertSame(['Hausschrift'], $catalog->uploaded()); + self::assertSame(['Roboto', 'Arial', 'Hausschrift', 'Neu'], $catalog->reserved(), 'after an upload'); + } + + /** @runInSeparateProcess */ + public function testABrokenListCountsAsEmpty(): void + { + // A filter of et_builder_google_fonts that returns no array (Divi passes it on unchecked). + Functions\when('et_builder_get_google_fonts')->justReturn(null); + Functions\when('et_builder_get_websafe_fonts')->justReturn(['Arial' => []]); + Functions\when('et_builder_get_custom_fonts')->justReturn([]); + + self::assertSame(['Arial'], (new FontCatalog())->reserved()); + } + + public function testWithoutDiviNobodyManagesFonts(): void + { + // Unit tests have no et_pb_is_allowed(): a missing Divi function never means "allowed". + self::assertFalse((new FontCatalog())->canManage()); + } + + /** @runInSeparateProcess */ + public function testCanManageAsksTheCustomFontsRight(): void + { + Functions\when('et_pb_is_allowed')->alias(static fn (string $cap): bool => $cap === 'custom_fonts_management'); + + self::assertTrue((new FontCatalog())->canManage()); + } +} diff --git a/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php b/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php new file mode 100644 index 0000000..951151e --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php @@ -0,0 +1,246 @@ +area); + self::assertSame( + [ + 'et_sanitize_font_choices()', 'et_sanitize_global_font_weight()', 'et_sanitize_float_number()', + 'et_sanitize_int_number()', 'et_divi_global_font_style()', 'et_update_option()', 'et_delete_option()', + 'ET_Core_PageResource::remove_static_resources()', 'et_pb_force_regenerate_templates()', + 'et_pb_is_allowed()', + ], + array_map(static fn (Call $call): string => $call->label(), $manifest->calls) + ); + self::assertSame( + [1, 1, 1, 1, 1, 2, 1, 2, 0, 1], + array_map(static fn (Call $call): ?int => $call->args, $manifest->calls) + ); + self::assertSame(['et_divi'], $manifest->keys); + self::assertSame([DiviFeature::ThemeOptionWrite], $manifest->features); + + self::assertCount(1, $manifest->mirrors); + $mirror = $manifest->mirrors[0]; + self::assertSame('customizer-typography', $mirror->id); + self::assertSame('functions.php', $mirror->file); + self::assertSame('et_divi_customizer_theme_settings', $mirror->function); + self::assertNull($mirror->class); + self::assertSame( + [DiviFeature::ThemeOptionRead, DiviFeature::ThemeOptionWrite], + $manifest->featuresOf($mirror) + ); + } + + public function testTheSanitizerTableCoversTheWhitelistWithDivisCallsOfTheManifest(): void + { + self::assertSame(self::KEYS, array_keys(ThemeOptionsAdapter::SANITIZERS)); + + $divi = array_values(array_unique(array_diff(ThemeOptionsAdapter::SANITIZERS, ['absint']))); + sort($divi); + self::assertSame( + [ + 'et_divi_global_font_style', 'et_sanitize_float_number', 'et_sanitize_font_choices', + 'et_sanitize_global_font_weight', 'et_sanitize_int_number', + ], + $divi + ); + $calls = array_map( + static fn (Call $call): string => $call->symbol . '/' . $call->args, + ThemeOptionsAdapter::manifest()->calls + ); + foreach ($divi as $sanitizer) { + self::assertContains($sanitizer . '/1', $calls, 'every Divi sanitizer is a call of the manifest'); + } + } + + public function testSanitizeRefusesAKeyOutsideTheWhitelist(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('not a theme option of the whitelist: accent_color'); + + (new ThemeOptionsAdapter())->sanitize('accent_color', '#ff0000'); + } + + public function testSizesGoThroughWordPressAbsintAndNonScalarsAreRefused(): void + { + $adapter = new ThemeOptionsAdapter(); + + self::assertSame(15, $adapter->sanitize('body_font_size', '15.7'), 'absint rounds, the caller compares'); + self::assertSame(1200, $adapter->sanitize('content_width', 1200)); + self::assertFalse($adapter->sanitize('row_padding', ['2'])); + } + + /** @runInSeparateProcess */ + public function testSanitizeCallsDivisCustomizerSanitizerOfTheKey(): void + { + Functions\expect('et_sanitize_font_choices')->once()->with('Roboto')->andReturn('Roboto'); + Functions\expect('et_sanitize_global_font_weight')->once()->with('600')->andReturn('600'); + Functions\expect('et_sanitize_float_number')->once()->with('1.8')->andReturn(1.8); + Functions\expect('et_sanitize_int_number')->once()->with('-1')->andReturn(-1); + Functions\expect('et_divi_global_font_style')->once()->with('italic|uppercase')->andReturn(false); + $adapter = new ThemeOptionsAdapter(); + + self::assertSame('Roboto', $adapter->sanitize('secondary_nav_font', 'Roboto')); + self::assertSame('600', $adapter->sanitize('body_font_weight', '600')); + self::assertSame(1.8, $adapter->sanitize('body_header_height', '1.8')); + self::assertSame(-1, $adapter->sanitize('body_header_spacing', '-1')); + self::assertFalse($adapter->sanitize('body_header_style', 'italic|uppercase'), 'Divi refuses combinations'); + } + + public function testReadReturnsThePresentWhitelistKeysWithTheirStoredTypes(): void + { + Functions\when('get_option')->alias(static fn (string $name): mixed => $name === 'et_divi' + ? [ + 'row_padding' => null, + 'accent_color' => '#ff0000', + 'body_font' => 'Roboto', + 'body_font_height' => 1.8, + 'gutter_width' => ['3'], + 'heading_font' => false, + 'body_font_size' => 15, + ] + : false); + + self::assertSame( + ['heading_font' => false, 'body_font' => 'Roboto', 'body_font_size' => 15, 'body_font_height' => 1.8], + (new ThemeOptionsAdapter())->read(), + 'whitelist only, in W2 order; values of another type are not theme option values' + ); + } + + public function testReadWithoutThemeOptionsIsEmpty(): void + { + Functions\when('get_option')->justReturn(false); + + self::assertSame([], (new ThemeOptionsAdapter())->read()); + } + + public function testWritesNeedAReleasedDivi(): void + { + ReleaseGate::useResolver( + static fn (): ReleaseStatus => ReleaseMatrices::uniform(ReleaseState::Untested, '5.16.0') + ); + + $this->expectException(DiviLocked::class); + + (new ThemeOptionsAdapter())->write(['body_font_size' => 16], []); + } + + public function testWriteRefusesAKeyOutsideTheWhitelistBeforeWriting(): void + { + // Unit tests have no et_update_option(): reaching it would be an Error, not this exception. + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('not a theme option of the whitelist: accent_color, divi_logo'); + + (new ThemeOptionsAdapter())->write(['body_font_size' => 16, 'accent_color' => '#f00'], ['divi_logo']); + } + + /** @runInSeparateProcess */ + public function testWriteUpdatesAndDeletesPerKeyClearsTheCachesAndReadsBack(): void + { + $stored = ['body_font_size' => 14, 'heading_font' => 'Roboto', 'accent_color' => '#ff0000']; + $log = []; + Functions\when('get_option')->alias(static function (string $name) use (&$stored): mixed { + return $name === 'et_divi' ? $stored : false; + }); + Functions\when('et_update_option')->alias(static function (string $key, mixed $value) use ( + &$stored, + &$log + ): void { + $stored[$key] = $value; + $log[] = 'update ' . $key; + }); + Functions\when('et_delete_option')->alias(static function (string $key) use (&$stored, &$log): void { + unset($stored[$key]); + $log[] = 'delete ' . $key; + }); + Functions\when('et_pb_force_regenerate_templates')->alias(static function () use (&$log): void { + $log[] = 'regenerate templates after ' . count(\ET_Core_PageResource::$removed) . ' static CSS clear'; + }); + \ET_Core_PageResource::$removed = []; + + $result = (new ThemeOptionsAdapter())->write( + ['body_font_size' => 16, 'body_font_height' => 1.8], + ['heading_font'] + ); + + self::assertSame(['body_font_size' => 16, 'body_font_height' => 1.8], $result); + self::assertSame( + [ + 'update body_font_size', 'update body_font_height', 'delete heading_font', + 'regenerate templates after 1 static CSS clear', + ], + $log + ); + self::assertSame([['all', 'all', false]], \ET_Core_PageResource::$removed, 'like a customizer save'); + self::assertSame('#ff0000', $stored['accent_color'], 'other theme options stay'); + } + + /** @runInSeparateProcess */ + public function testWriteThrowsWhenTheReadBackDiffers(): void + { + $stored = ['body_font_size' => 14, 'heading_font' => 'Roboto']; + Functions\when('get_option')->alias(static fn (string $name): mixed => $name === 'et_divi' ? $stored : false); + // Divi silently saves nothing (e.g. a filter or another process). + Functions\when('et_update_option')->justReturn(null); + Functions\when('et_delete_option')->justReturn(null); + Functions\when('et_pb_force_regenerate_templates')->justReturn(null); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('theme options not written: body_font_size, heading_font'); + + (new ThemeOptionsAdapter())->write(['body_font_size' => 16], ['heading_font']); + } + + public function testWithoutDiviNobodyCustomizesTheTheme(): void + { + // Unit tests have no et_pb_is_allowed(): a missing Divi function never means "allowed". + self::assertFalse((new ThemeOptionsAdapter())->canCustomize()); + } + + /** @runInSeparateProcess */ + public function testCanCustomizeAsksTheThemeCustomizerRight(): void + { + Functions\when('et_pb_is_allowed')->alias(static fn (string $cap): bool => $cap === 'theme_customizer'); + + self::assertTrue((new ThemeOptionsAdapter())->canCustomize()); + } +} diff --git a/tests/Unit/Integrations/Divi/Release/DiviFeatureAssignmentTest.php b/tests/Unit/Integrations/Divi/Release/DiviFeatureAssignmentTest.php index 5033b0f..1b32ee5 100644 --- a/tests/Unit/Integrations/Divi/Release/DiviFeatureAssignmentTest.php +++ b/tests/Unit/Integrations/Divi/Release/DiviFeatureAssignmentTest.php @@ -6,8 +6,10 @@ namespace LIT\MCP\Tests\Unit\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; @@ -82,6 +84,26 @@ final class DiviFeatureAssignmentTest extends TestCase 'GlobalData::set_global_variables()' => ['variable-write'], 'et_pb_is_allowed()' => ['variable-write'], ], + ThemeOptionsAdapter::class => [ + 'et_sanitize_font_choices()' => ['theme-option-write'], + 'et_sanitize_global_font_weight()' => ['theme-option-write'], + 'et_sanitize_float_number()' => ['theme-option-write'], + 'et_sanitize_int_number()' => ['theme-option-write'], + 'et_divi_global_font_style()' => ['theme-option-write'], + 'et_update_option()' => ['theme-option-write'], + 'et_delete_option()' => ['theme-option-write'], + 'ET_Core_PageResource::remove_static_resources()' => ['theme-option-write'], + 'et_pb_force_regenerate_templates()' => ['theme-option-write'], + 'et_pb_is_allowed()' => ['theme-option-write'], + 'Nachbau et_divi_customizer_theme_settings()' => ['theme-option-read', 'theme-option-write'], + ], + FontCatalog::class => [ + 'et_builder_get_fonts()' => ['font-upload', 'theme-option-write', 'variable-write'], + 'et_builder_get_custom_fonts()' => ['font-upload', 'theme-option-write', 'variable-write'], + 'et_builder_get_google_fonts()' => ['font-upload', 'theme-option-write', 'variable-write'], + 'et_builder_get_websafe_fonts()' => ['font-upload', 'theme-option-write', 'variable-write'], + 'et_pb_is_allowed()' => ['font-upload'], + ], DiviModules::class => [ 'ModuleRegistration::get_all_core_modules_metadata()' => ['catalog'], 'ModuleRegistration::get_default_attrs()' => ['catalog'], @@ -143,7 +165,7 @@ final class DiviFeatureAssignmentTest extends TestCase } self::assertSame(self::EXPECTED, $actual); - self::assertSame(array_keys(self::EXPECTED), DiviAdapters::CLASSES, 'all ten adapters, in report order'); + self::assertSame(array_keys(self::EXPECTED), DiviAdapters::CLASSES, 'all twelve adapters, in report order'); } public function testReadingAdaptersCarryNoWriteFeature(): void diff --git a/tests/Unit/Integrations/Divi/Release/DiviFeatureTest.php b/tests/Unit/Integrations/Divi/Release/DiviFeatureTest.php index f02476a..bfa0aee 100644 --- a/tests/Unit/Integrations/Divi/Release/DiviFeatureTest.php +++ b/tests/Unit/Integrations/Divi/Release/DiviFeatureTest.php @@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase; final class DiviFeatureTest extends TestCase { - public function testTheThirteenFeaturesAndTheirValues(): void + public function testTheSixteenFeaturesAndTheirValues(): void { self::assertSame( [ @@ -21,6 +21,9 @@ final class DiviFeatureTest extends TestCase 'PresetWrite' => 'preset-write', 'ColorWrite' => 'color-write', 'VariableWrite' => 'variable-write', + 'ThemeOptionRead' => 'theme-option-read', + 'ThemeOptionWrite' => 'theme-option-write', + 'FontUpload' => 'font-upload', 'LibraryRead' => 'library-read', 'LibraryWrite' => 'library-write', 'Roles' => 'roles', @@ -34,7 +37,7 @@ final class DiviFeatureTest extends TestCase ); } - public function testOnlyTheSixWriteFeaturesWrite(): void + public function testOnlyTheEightWriteFeaturesWrite(): void { $writing = array_values(array_map( static fn (DiviFeature $feature): string => $feature->value, @@ -42,7 +45,10 @@ final class DiviFeatureTest extends TestCase )); self::assertSame( - ['page-write', 'preset-write', 'color-write', 'variable-write', 'library-write', 'tb-write'], + [ + 'page-write', 'preset-write', 'color-write', 'variable-write', 'theme-option-write', 'font-upload', + 'library-write', 'tb-write', + ], $writing ); } @@ -56,6 +62,9 @@ final class DiviFeatureTest extends TestCase self::assertSame('Theme Builder lesen', DiviFeature::ThemeBuilderRead->label()); self::assertSame('Farben schreiben', DiviFeature::ColorWrite->label()); self::assertSame('Variablen schreiben', DiviFeature::VariableWrite->label()); + self::assertSame('Theme-Optionen lesen', DiviFeature::ThemeOptionRead->label()); + self::assertSame('Theme-Optionen schreiben', DiviFeature::ThemeOptionWrite->label()); + self::assertSame('Schriften hochladen', DiviFeature::FontUpload->label()); foreach ($labels as $label) { self::assertNotSame('', trim($label)); } @@ -64,6 +73,7 @@ final class DiviFeatureTest extends TestCase public function testValuesResolveBackToTheFeature(): void { self::assertSame(DiviFeature::ThemeBuilderWrite, DiviFeature::from('tb-write')); + self::assertSame(DiviFeature::ThemeOptionWrite, DiviFeature::from('theme-option-write')); self::assertNull(DiviFeature::tryFrom('page')); } }