diff --git a/src/Integrations/Divi/DiviCompatibility.php b/src/Integrations/Divi/DiviCompatibility.php index 73c96e1..0910d6e 100644 --- a/src/Integrations/Divi/DiviCompatibility.php +++ b/src/Integrations/Divi/DiviCompatibility.php @@ -8,6 +8,7 @@ use LIT\MCP\Integrations\Divi\BlockTree\GlobalInstance; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap; use LIT\MCP\Integrations\Divi\Schema\DiviModules; +use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder; use Throwable; defined('ABSPATH') || exit; @@ -64,6 +65,15 @@ final class DiviCompatibility static fn (): bool => self::knowsGlobalLayout(), 'Taxonomien layout_category, layout_tag (Bibliothek)' => static fn (): bool => taxonomy_exists('layout_category') && taxonomy_exists('layout_tag'), + 'Theme Builder: Funktionen et_theme_builder_* und et_()' => + static fn (): bool => (new DiviThemeBuilder())->missing() === [], + 'Theme Builder: Post-Typen et_template und Layouts' => static fn (): bool => array_filter( + array_merge( + [DiviThemeBuilder::THEME_BUILDER, DiviThemeBuilder::TEMPLATE], + array_values(DiviThemeBuilder::LAYOUT_TYPES) + ), + static fn (string $type): bool => ! post_type_exists($type) + ) === [], ]; } diff --git a/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php b/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php new file mode 100644 index 0000000..e3f3c88 --- /dev/null +++ b/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php @@ -0,0 +1,367 @@ + layout post type (theme-builder.php:7-23). */ + public const LAYOUT_TYPES = [ + 'header' => 'et_header_layout', + 'body' => 'et_body_layout', + 'footer' => 'et_footer_layout', + ]; + + public const TEMPLATE_LIST = '_et_template'; + public const USE_ON = '_et_use_on'; + public const EXCLUDE_FROM = '_et_exclude_from'; + public const ENABLED = '_et_enabled'; + public const UNUSED = '_et_theme_builder_marked_as_unused'; + public const BACKUP_OPTION = 'et_tb_templates_backup_'; + + /** Divi functions this adapter calls; DiviCompatibility reports missing ones. */ + public const FUNCTIONS = [ + 'et_theme_builder_get_theme_builder_post_id', + 'et_theme_builder_get_theme_builder_template_ids', + 'et_theme_builder_get_template', + 'et_theme_builder_insert_layout', + 'et_theme_builder_store_template', + 'et_theme_builder_get_flat_template_settings_options', + 'et_theme_builder_clear_wp_cache', + 'et_', + ]; + + private const DIE_FILTERS = [ + 'wp_die_handler', 'wp_die_ajax_handler', 'wp_die_json_handler', 'wp_die_jsonp_handler', + 'wp_die_xml_handler', 'wp_die_xmlrpc_handler', + ]; + + /** "header", "body" or "footer" for a layout post type, else null. */ + public static function areaOf(string $postType): ?string + { + $area = array_search($postType, self::LAYOUT_TYPES, true); + + return is_string($area) ? $area : null; + } + + /** @return list */ + public function missing(): array + { + return array_values(array_filter(self::FUNCTIONS, static fn (string $name): bool => ! function_exists($name))); + } + + /** The live Theme Builder post (publish, no library TB); $create makes it like Divi's first save. 0 = none. */ + public function liveId(bool $create = false): int + { + return function_exists('et_theme_builder_get_theme_builder_post_id') + ? (int) et_theme_builder_get_theme_builder_post_id(true, $create) + : 0; + } + + /** The Theme Builder draft (auto-draft) of an unsaved Theme Builder session; 0 = none (F15). */ + public function draftId(): int + { + return function_exists('et_theme_builder_get_theme_builder_post_id') + ? (int) et_theme_builder_get_theme_builder_post_id(false, false) + : 0; + } + + /** @return list Templates listed by Theme Builder post $themeBuilderId, in Divi's order (F15). */ + public function templateIds(int $themeBuilderId): array + { + if ($themeBuilderId <= 0 || ! function_exists('et_theme_builder_get_theme_builder_template_ids')) { + return []; + } + + return array_values(array_map( + 'intval', + (array) et_theme_builder_get_theme_builder_template_ids(true, $themeBuilderId) + )); + } + + /** Template $id as Divi reads it, null when it is no template. */ + public function template(int $id, bool $attached): ?Template + { + if (! function_exists('et_theme_builder_get_template')) { + return null; + } + + $divi = et_theme_builder_get_template($id); + if (! is_array($divi) || $divi === []) { + return null; + } + + $marked = (string) get_post_meta($id, self::UNUSED, true); + + return Template::fromDivi($divi, (string) get_post_status($id), $attached, $marked === '' ? null : $marked); + } + + /** @return list