File manager - Edit - /home/rangceb/diohome.com/wp-includes6790ed/pomo/includes.zip
Back
PK X�\�/+Q� � no-results.phpnu �[��� <?php global $et_no_results_heading_tag; if ( empty( $et_no_results_heading_tag ) ){ $et_no_results_heading_tag = 'h1'; } ?> <div class="entry"> <<?php echo $et_no_results_heading_tag; ?> class="not-found-title"><?php esc_html_e('No Results Found','Divi'); ?></<?php echo $et_no_results_heading_tag; ?>> <p><?php esc_html_e('The page you requested could not be found. Try refining your search, or use the navigation above to locate the post.','Divi'); ?></p> </div> PK X�\�H鱬 � theme-builder.phpnu �[��� <?php // Prevent file from being loaded directly if ( ! defined( 'ABSPATH' ) ) { die( '-1' ); } /** * Output the opening page container div. * * @since ?? * * @return void */ function et_divi_filter_theme_builder_template_before_page_wrappers() { get_template_part( 'theme-before-wrappers' ); } add_action( 'et_theme_builder_template_before_page_wrappers', 'et_divi_filter_theme_builder_template_before_page_wrappers' ); /** * Output the header if necessary. * * @since ?? * * @param integer $layout_id * @param bool $layout_enabled * * @return void */ function et_divi_filter_theme_builder_template_before_header( $layout_id, $layout_enabled ) { if ( 0 === $layout_id && $layout_enabled ) { get_template_part( 'theme-header' ); } } add_action( 'et_theme_builder_template_before_header', 'et_divi_filter_theme_builder_template_before_header', 10, 2 ); /** * Output the opening page container div. * * @since ?? * * @return void */ function et_divi_filter_theme_builder_template_after_header() { get_template_part( 'theme-after-header' ); } add_action( 'et_theme_builder_template_after_header', 'et_divi_filter_theme_builder_template_after_header' ); /** * Output the opening main content div. * * @since ?? * * @return void */ function et_divi_filter_theme_builder_template_before_body() { ?> <div id="main-content"> <?php } add_action( 'et_theme_builder_template_before_body', 'et_divi_filter_theme_builder_template_before_body' ); /** * Output the closing main content div. * * @since ?? * * @return void */ function et_divi_filter_theme_builder_template_after_body() { ?> </div> <?php } add_action( 'et_theme_builder_template_after_body', 'et_divi_filter_theme_builder_template_after_body' ); /** * Output the footer if necessary. * * @since ?? * * @param integer $layout_id * @param bool $layout_enabled * * @return void */ function et_divi_filter_theme_builder_template_after_footer( $layout_id, $layout_enabled ) { if ( 0 === $layout_id && $layout_enabled ) { get_template_part( 'theme-footer' ); } get_template_part( 'theme-after-footer' ); } add_action( 'et_theme_builder_template_after_footer', 'et_divi_filter_theme_builder_template_after_footer', 10, 2 ); /** * Output the closing page container div. * * @since ?? * * @return void */ function et_divi_filter_theme_builder_template_after_page_wrappers() { get_template_part( 'theme-after-wrappers' ); } add_action( 'et_theme_builder_template_after_page_wrappers', 'et_divi_filter_theme_builder_template_after_page_wrappers' ); /** * Disable TB hooks in order to be compatible with LearnDash's Focus mode. * * @since ?? */ function et_divi_action_theme_builder_compatibility_learndash_focus_mode() { remove_action( 'et_theme_builder_template_before_page_wrappers', 'et_divi_filter_theme_builder_template_before_page_wrappers' ); remove_action( 'et_theme_builder_template_before_header', 'et_divi_filter_theme_builder_template_before_header' ); remove_action( 'et_theme_builder_template_after_header', 'et_divi_filter_theme_builder_template_after_header' ); remove_action( 'et_theme_builder_template_before_body', 'et_divi_filter_theme_builder_template_before_body' ); remove_action( 'et_theme_builder_template_after_body', 'et_divi_filter_theme_builder_template_after_body' ); remove_action( 'et_theme_builder_template_after_footer', 'et_divi_filter_theme_builder_template_after_footer' ); remove_action( 'et_theme_builder_template_after_page_wrappers', 'et_divi_filter_theme_builder_template_after_page_wrappers' ); } add_action( 'et_theme_builder_compatibility_learndash_focus_mode', 'et_divi_action_theme_builder_compatibility_learndash_focus_mode' ); PK X�\�O� O� # builder/main-structure-elements.phpnu �[��� <?php /** * Fires before the builder's structure element classes are loaded. * * @package Divi * @subpackage Builder * @since 4.4.9 */ do_action( 'et_builder_structure_elements_load' ); /** * Section Element class * * @since 3.0.16 */ class ET_Builder_Section extends ET_Builder_Structure_Element { /** * Sets up the element's properties. */ public function init() { $this->name = esc_html__( 'Section', 'et_builder' ); $this->plural = esc_html__( 'Sections', 'et_builder' ); $this->slug = 'et_pb_section'; $this->vb_support = 'on'; $this->settings_modal_toggles = array( 'general' => array( 'toggles' => array( 'background' => array( 'title' => et_builder_i18n( 'Background' ), 'sub_toggles' => array( 'main' => '', 'column_1' => array( 'name' => esc_html__( 'Column 1', 'et_builder' ) ), 'column_2' => array( 'name' => esc_html__( 'Column 2', 'et_builder' ) ), 'column_3' => array( 'name' => esc_html__( 'Column 3', 'et_builder' ) ), ), 'priority' => 80, ), ), ), 'advanced' => array( 'toggles' => array( 'layout' => et_builder_i18n( 'Layout' ), 'width' => array( 'title' => et_builder_i18n( 'Sizing' ), 'priority' => 65, ), 'margin_padding' => array( 'title' => esc_html__( 'Spacing', 'et_builder' ), 'sub_toggles' => array( 'main' => '', 'column_1' => array( 'name' => esc_html__( 'Column 1', 'et_builder' ) ), 'column_2' => array( 'name' => esc_html__( 'Column 2', 'et_builder' ) ), 'column_3' => array( 'name' => esc_html__( 'Column 3', 'et_builder' ) ), ), 'priority' => 70, ), ), ), 'custom_css' => array( 'toggles' => array( 'classes' => array( 'title' => esc_html__( 'CSS ID & Classes', 'et_builder' ), 'sub_toggles' => array( 'main' => '', 'column_1' => array( 'name' => esc_html__( 'Column 1', 'et_builder' ) ), 'column_2' => array( 'name' => esc_html__( 'Column 2', 'et_builder' ) ), 'column_3' => array( 'name' => esc_html__( 'Column 3', 'et_builder' ) ), ), ), 'custom_css' => array( 'title' => et_builder_i18n( 'Custom CSS' ), 'sub_toggles' => array( 'main' => '', 'column_1' => array( 'name' => esc_html__( 'Column 1', 'et_builder' ) ), 'column_2' => array( 'name' => esc_html__( 'Column 2', 'et_builder' ) ), 'column_3' => array( 'name' => esc_html__( 'Column 3', 'et_builder' ) ), ), ), ), ), ); $this->advanced_fields = array( 'background' => array( 'use_background_color' => 'fields_only', 'use_background_image' => true, 'use_background_color_gradient' => true, 'use_background_video' => true, 'use_background_color_reset' => 'fields_only', 'use_background_pattern' => true, 'use_background_mask' => true, 'css' => array( 'important' => 'all', 'main' => 'div.et_pb_section%%order_class%%', ), 'options' => array( 'background_color' => array( 'default' => '', 'hover' => 'tabs', ), 'allow_player_pause' => array( 'default_on_front' => 'off', ), 'background_video_pause_outside_viewport' => array( 'default_on_front' => 'on', ), 'parallax' => array( 'default_on_front' => 'off', ), 'parallax_method' => array( 'default_on_front' => 'on', ), ), ), 'max_width' => array( 'css' => array( 'module_alignment' => '%%order_class%%', ), 'options' => array( 'module_alignment' => array( 'label' => esc_html__( 'Section Alignment', 'et_builder' ), ), ), 'extra' => array( 'inner' => array( 'css' => array( 'main' => array( '%%order_class%% > .et_pb_row', '%%order_class%%.et_section_specialty > .et_pb_row' ), ), 'options' => array( 'width' => array( 'label' => esc_html__( 'Inner Width', 'et_builder' ), 'depends_show_if' => 'on', 'specialty_only' => 'yes', ), 'max_width' => array( 'label' => esc_html__( 'Inner Max Width', 'et_builder' ), 'depends_show_if' => 'on', 'specialty_only' => 'yes', ), 'module_alignment' => array( 'label' => esc_html__( 'Row Alignment', 'et_builder' ), 'depends_show_if' => 'on', 'specialty_only' => 'yes', ), ), ), ), ), 'fonts' => false, 'text' => false, 'button' => false, 'z_index' => array( 'default' => 'auto', ), 'position_fields' => array( 'default' => 'relative', ), ); $this->help_videos = array( array( 'id' => '3kmJ_mMVB1w', 'name' => esc_html__( 'An introduction to Sections', 'et_builder' ), ), ); } /** * Get the settings fields data for this element. * * @return array|array[] */ public function get_fields() { $fields = array( 'inner_shadow' => array( 'label' => esc_html__( 'Show Inner Shadow', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'description' => esc_html__( 'Here you can select whether or not your section has an inner shadow. This can look great when you have colored backgrounds or background images.', 'et_builder' ), 'tab_slug' => 'advanced', 'toggle_slug' => 'layout', 'default_on_front' => 'off', ), 'make_equal' => array( 'label' => esc_html__( 'Equalize Column Heights', 'et_builder' ), 'description' => esc_html__( 'Equalizing column heights will force all columns to assume the height of the tallest column in the row. All columns will have the same height, keeping their appearance uniform.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'tab_slug' => 'advanced', 'toggle_slug' => 'width', 'specialty_only' => 'yes', ), 'use_custom_gutter' => array( 'label' => esc_html__( 'Use Custom Gutter Width', 'et_builder' ), 'description' => esc_html__( 'Enable this option to define custom gutter width for this section.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'affects' => array( 'gutter_width', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'width', 'specialty_only' => 'yes', ), 'gutter_width' => array( 'label' => esc_html__( 'Gutter Width', 'et_builder' ), 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 1, 'max' => 4, 'step' => 1, 'min_limit' => 1, 'max_limit' => 4, ), 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'width', 'specialty_only' => 'yes', 'validate_unit' => false, 'fixed_range' => true, 'default_on_front' => (string) et_get_option( 'gutter_width', '3' ), 'hover' => 'tabs', 'description' => esc_html__( 'Gutter width controls the space between each column in a row. Lowering the gutter width will cause modules to become closer together.', 'et_builder' ), ), 'columns_background' => array( 'type' => 'column_settings_background', 'option_category' => 'configuration', 'toggle_slug' => 'background', 'specialty_only' => 'yes', 'priority' => 99, ), 'columns_padding' => array( 'type' => 'column_settings_padding', 'option_category' => 'configuration', 'tab_slug' => 'advanced', 'toggle_slug' => 'margin_padding', 'specialty_only' => 'yes', 'priority' => 99, ), 'fullwidth' => array( 'type' => 'hidden', 'default_on_front' => 'off', ), 'specialty' => array( 'type' => 'skip', 'default_on_front' => 'off', 'affects' => array( 'inner_width', 'inner_max_width' ), ), 'columns_css' => array( 'type' => 'column_settings_css', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'custom_css', 'priority' => 20, ), 'columns_css_fields' => array( 'type' => 'column_settings_css_fields', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'classes', 'priority' => 20, ), 'custom_padding_last_edited' => array( 'type' => 'skip', 'tab_slug' => 'advanced', 'specialty_only' => 'yes', ), '__video_background' => array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Module_Helper_ResponsiveOptions', 'get_video_background' ), 'computed_depends_on' => array( 'background_video_mp4', 'background_video_webm', 'background_video_width', 'background_video_height', ), 'computed_minimum' => array( 'background_video_mp4', 'background_video_webm', ), ), 'prev_background_color' => array( 'type' => 'skip', ), 'next_background_color' => array( 'type' => 'skip', ), ); $column_fields = $this->get_column_fields( 3, array( 'parallax' => array( 'default_on_front' => 'off', ), 'parallax_method' => array( 'default_on_front' => 'on', ), 'background_color' => array(), 'bg_img' => array(), 'background_size' => array(), 'background_image_width' => array(), 'background_image_height' => array(), 'background_repeat' => array(), 'background_position' => array(), 'background_horizontal_offset' => array(), 'background_vertical_offset' => array(), 'background_blend' => array(), 'use_background_color_gradient' => array(), 'background_color_gradient_repeat' => array(), 'background_color_gradient_type' => array(), 'background_color_gradient_direction' => array(), 'background_color_gradient_direction_radial' => array(), 'background_color_gradient_stops' => array(), 'background_color_gradient_unit' => array(), 'background_color_gradient_start' => array(), 'background_color_gradient_end' => array(), 'background_color_gradient_start_position' => array(), 'background_color_gradient_end_position' => array(), 'background_color_gradient_overlays_image' => array(), 'background_pattern_style' => array(), 'background_enable_pattern_style' => array(), 'background_pattern_color' => array(), 'background_pattern_transform' => array(), 'background_pattern_size' => array(), 'background_pattern_width' => array(), 'background_pattern_height' => array(), 'background_pattern_repeat_origin' => array(), 'background_pattern_horizontal_offset' => array(), 'background_pattern_vertical_offset' => array(), 'background_pattern_repeat' => array(), 'background_pattern_blend_mode' => array(), 'background_mask_style' => array(), 'background_mask_color' => array(), 'background_mask_transform' => array(), 'background_mask_aspect_ratio' => array(), 'background_mask_size' => array(), 'background_mask_width' => array(), 'background_mask_height' => array(), 'background_mask_position' => array(), 'background_mask_horizontal_offset' => array(), 'background_mask_vertical_offset' => array(), 'background_mask_blend_mode' => array(), 'background_enable_mask_style' => array(), 'background_video_mp4' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_webm' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_width' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_height' => array( 'computed_affects' => array( '__video_background', ), ), 'allow_player_pause' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_pause_outside_viewport' => array( 'computed_affects' => array( '__video_background', ), ), '__video_background' => array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Column', 'get_column_video_background', ), 'computed_depends_on' => array( 'background_video_mp4', 'background_video_webm', 'background_video_width', 'background_video_height', ), 'computed_minimum' => array( 'background_video_mp4', 'background_video_webm', ), ), 'padding_top' => array( 'tab_slug' => 'advanced' ), 'padding_right' => array( 'tab_slug' => 'advanced' ), 'padding_bottom' => array( 'tab_slug' => 'advanced' ), 'padding_left' => array( 'tab_slug' => 'advanced' ), 'padding_top_bottom_link' => array( 'tab_slug' => 'advanced' ), 'padding_left_right_link' => array( 'tab_slug' => 'advanced' ), 'padding_%column_index%_tablet' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_phone' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_last_edited' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'module_id' => array( 'tab_slug' => 'custom_css' ), 'module_class' => array( 'tab_slug' => 'custom_css' ), 'custom_css_before' => array( 'tab_slug' => 'custom_css' ), 'custom_css_main' => array( 'tab_slug' => 'custom_css' ), 'custom_css_after' => array( 'tab_slug' => 'custom_css' ), ) ); return array_merge( $fields, $column_fields ); } /** * Get CSS fields transition of section. */ public function get_transition_fields_css_props() { $fields = parent::get_transition_fields_css_props(); // Section Dividers Height. foreach ( array( 'top', 'bottom' ) as $placement ) { // Inside sprintf, the double %% prints a literal '%' character. $selector = sprintf( '%%%%order_class%%%%.section_has_divider.et_pb_%1$s_divider .et_pb_%1$s_inside_divider', $placement ); $fields[ "{$placement}_divider_height" ] = array( 'height' => $selector, 'background-size' => $selector, ); } return $fields; } /** * Check if current background is transparent background or not. * * @param string $background_color Background color. * * @return boolean Transparent color status. * @since 3.24.1 */ public function is_transparent_background( $background_color = '' ) { $page_setting_section_background = et_builder_settings_get( 'et_pb_section_background_color', get_the_ID() ); $is_empty_background_color = '' === $background_color && '' === $page_setting_section_background; $is_layout_block = ET_GB_Block_Layout::is_layout_block() || ET_GB_Block_Layout::is_layout_block_preview(); return 'rgba(255,255,255,0)' === $background_color || ( $is_layout_block && $is_empty_background_color ) || ( et_is_builder_plugin_active() && $is_empty_background_color ); } /** * Check if current background should have initial background color. * * @since 3.24.1 * * @param string $mode Preview mode. * * @return boolean */ public function is_initial_background_color( $mode = 'desktop' ) { // Ensure $mode parameter not empty. $mode = '' === $mode ? 'desktop' : $mode; $device_suffix = 'desktop' !== $mode && 'hover' !== $mode ? "_{$mode}" : ''; $parallax = 'hover' === $mode ? et_pb_hover_options()->get_raw_value( 'parallax', $this->props ) : et_pb_responsive_options()->get_any_value( $this->props, "parallax{$device_suffix}", '', true ); $background_blend = 'hover' === $mode ? et_pb_hover_options()->get_raw_value( 'background_blend', $this->props ) : et_pb_responsive_options()->get_any_value( $this->props, "background_blend{$device_suffix}", '', true ); $use_gradient_value = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'use_background_color_gradient', $mode ); $background_image = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_image', $mode ); $bg_blend_default = self::$_->array_get( $this->fields_unprocessed, 'background_blend.default', '' ); $is_gradient_active = 'on' === $use_gradient_value; $is_image_active = '' !== $background_image && 'on' !== $parallax; $is_image_blend = '' !== $background_blend && $background_blend !== $bg_blend_default; return $is_gradient_active && $is_image_active && $is_image_blend; } /** * Generate parallax image background markup. * * @since 3.24.1 * @since 4.15.0 Added $props property. * * @param string $base_name Background base name. * @param array $props Props (optional). * * @return string */ public function get_parallax_image_background( $base_name = 'background', $props = array() ) { $props = empty( $props ) ? $this->props : $props; $attr_prefix = "{$base_name}_"; $custom_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $parallax_processed = array(); $parallax_background = ''; $hover_suffix = et_pb_hover_options()->get_suffix(); $sticky_suffix = et_pb_sticky_options()->get_suffix(); $preview_modes = array( $hover_suffix, $sticky_suffix, '_phone', '_tablet', '' ); // Featured Image as Background. $featured_image = ''; $featured_placement = ''; $featured_image_src = ''; if ( $this->featured_image_background ) { $featured_image = self::$_->array_get( $props, 'featured_image', '' ); $featured_placement = self::$_->array_get( $props, 'featured_placement', '' ); $featured_image_src_obj = wp_get_attachment_image_src( get_post_thumbnail_id( self::_get_main_post_id() ), 'full' ); $featured_image_src = isset( $featured_image_src_obj[0] ) ? $featured_image_src_obj[0] : ''; } // Parallax Gradient. $background_options = et_pb_background_options(); $is_gradient_on = false; foreach ( $preview_modes as $suffix ) { $is_hover = $hover_suffix === $suffix; $is_sticky = $sticky_suffix === $suffix; // A. Bail early if hover or responsive settings disabled on mobile/hover. if ( '' !== $suffix ) { // Ensure responsive settings is enabled on mobile. if ( ! $is_hover && ! $is_sticky && ! et_pb_responsive_options()->is_responsive_enabled( $props, $base_name ) ) { continue; } // Ensure hover settings is enabled. if ( $is_hover && ! et_pb_hover_options()->is_enabled( $base_name, $props ) ) { continue; } // Ensure sticky setting is enabled. if ( $is_sticky && ! et_pb_sticky_options()->is_enabled( $base_name, $props ) ) { continue; } } // Prepare preview mode. $mode = '' !== $suffix ? str_replace( '_', '', $suffix ) : 'desktop'; $mode = $is_hover ? 'hover' : $mode; $mode = $is_sticky ? 'sticky' : $mode; // B.1. Get inherited background value. $use_gradient_options = et_pb_responsive_options()->get_inheritance_background_value( $props, "use_{$base_name}_color_gradient", $mode, $base_name, $this->fields_unprocessed ); $background_image = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$attr_prefix}image", $mode, $base_name, $this->fields_unprocessed ); $parallax = et_pb_responsive_options()->get_any_value( $props, "parallax{$suffix}", '', true ); $parallax_method = et_pb_responsive_options()->get_any_value( $props, "parallax_method{$suffix}", '', true ); $gradient_overlays_image = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_color_gradient_overlays_image{$suffix}", '', true ); $background_gradient_blend = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_blend{$suffix}", 'normal', true ); // B.2. Set default value for parallax and parallax method on hover when they are empty. if ( $is_hover || $is_sticky ) { $parallax = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax", $props, $parallax ); $parallax_method = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax_method", $props, $parallax_method ); $gradient_overlays_image = et_pb_hover_options()->get_raw_value( "{$base_name}_color_gradient_overlays_image", $props, $gradient_overlays_image ); $background_gradient_blend = et_pb_hover_options()->get_raw_value( "{$base_name}_blend", $props, $background_gradient_blend ); $gradient_properties_desktop = $background_options->get_gradient_properties( $props, $base_name, '' ); $gradient_properties = $background_options->get_gradient_mode_properties( $mode, $props, $base_name, $gradient_properties_desktop ); $background_gradient_style = $background_options->get_gradient_style( $gradient_properties ); } else { $gradient_properties = $background_options->get_gradient_properties( $props, $base_name, $suffix ); $background_gradient_style = $background_options->get_gradient_style( $gradient_properties ); } if ( 'on' === $use_gradient_options && 'on' === $gradient_overlays_image && 'on' === $parallax ) { $is_gradient_on = '' !== $background_gradient_style; } // B.3. Override background image with featured image if needed. if ( 'on' === $featured_image && 'background' === $featured_placement && '' !== $featured_image_src ) { $background_image = $featured_image_src; } // C.1. Parallax BG Class to inform if other modes exist. $parallax_classname = array(); $parallax_gradient_classname = array(); if ( ( '_tablet' === $suffix || '' === $suffix ) && in_array( '_phone', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_phone_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_phone_exist'; } if ( '' === $suffix && in_array( '_tablet', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_tablet_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_tablet_exist'; } if ( in_array( $hover_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_hover_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_hover_exist'; } if ( in_array( $sticky_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_sticky_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_sticky_exist'; } // C.2. Set up parallax class and wrapper. if ( '' !== $background_image && 'on' === $parallax ) { $parallax_classname[] = 'et_parallax_bg'; $parallax_gradient_classname[] = 'et_parallax_gradient'; if ( 'off' === $parallax_method ) { $parallax_classname[] = 'et_pb_parallax_css'; $parallax_gradient_classname[] = 'et_pb_parallax_css'; if ( isset( $props['inner_shadow'] ) && 'off' !== $props['inner_shadow'] ) { $parallax_classname[] = 'et_pb_inner_shadow'; } } // Parallax BG Class with suffix. if ( '' !== $suffix ) { $parallax_classname[] = "et_parallax_bg{$suffix}"; $parallax_gradient_classname[] = "et_parallax_gradient{$suffix}"; } $background_gradient_image = sprintf( 'background-image: %1$s;', esc_attr( $background_gradient_style ) ); $background_gradient_blend = 'normal' !== $background_gradient_blend ? sprintf( 'mix-blend-mode: %1$s;', esc_attr( $background_gradient_blend ) ) : ''; $parallax_gradient = sprintf( '<span class="%1$s" style="%2$s%3$s" ></span>', esc_attr( implode( ' ', $parallax_gradient_classname ) ), // #1 et_core_esc_previously( $background_gradient_image ), // #2 et_core_esc_previously( $background_gradient_blend ) // #3 ); $parallax_background .= sprintf( '<span class="et_parallax_bg_wrap"><span class="%1$s" style="background-image: url(%2$s);" ></span>%3$s</span>', esc_attr( implode( ' ', $parallax_classname ) ), esc_url( $background_image ), $is_gradient_on ? et_core_esc_previously( $parallax_gradient ) : '' ); // Cleanup. $background_gradient_image = null; $background_gradient_blend = null; $parallax_gradient = null; // set `.et_parallax_bg_wrap` border-radius. et_set_parallax_bg_wrap_border_radius( $props, $this->slug, '%%order_class%%' ); } // C.3. Hover parallax class. if ( '' !== $background_image && $is_hover ) { $this->add_classname( 'et_pb_section_parallax_hover' ); } // C.4. Sticky parallax class. if ( '' !== $background_image && $is_sticky ) { $this->add_classname( 'et_pb_section_parallax_sticky' ); } array_push( $parallax_processed, $suffix ); } // Added classname for module wrapper. if ( '' !== $parallax_background ) { $this->add_classname( 'et_pb_section_parallax' ); } return $parallax_background; } /** * Generates the structure module's HTML output based on {@see ET_Builder_Section::$props}. * * @param array $atts List of unprocessed attributes. * @param mixed $content Content being processed. * @param string $function_name Slug of module that is used for rendering output. * * @return string The module's HTML output. */ public function render( $atts, $content, $function_name ) { $multi_view = et_pb_multi_view_options( $this ); $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $background_video_mp4 = $this->props['background_video_mp4']; $background_video_webm = $this->props['background_video_webm']; $inner_shadow = $this->props['inner_shadow']; $parallax = $this->props['parallax']; $parallax_method = $this->props['parallax_method']; $fullwidth = $this->props['fullwidth']; $specialty = $this->props['specialty']; $background_color_1 = isset( $this->props['background_color_1'] ) ? $this->props['background_color_1'] : ''; $background_color_2 = isset( $this->props['background_color_2'] ) ? $this->props['background_color_2'] : ''; $background_color_3 = isset( $this->props['background_color_3'] ) ? $this->props['background_color_3'] : ''; $bg_img_1 = isset( $this->props['bg_img_1'] ) ? $this->props['bg_img_1'] : ''; $bg_img_2 = isset( $this->props['bg_img_2'] ) ? $this->props['bg_img_2'] : ''; $bg_img_3 = isset( $this->props['bg_img_3'] ) ? $this->props['bg_img_3'] : ''; $background_size_1 = isset( $this->props['background_size_1'] ) ? $this->props['background_size_1'] : ''; $background_size_2 = isset( $this->props['background_size_2'] ) ? $this->props['background_size_2'] : ''; $background_size_3 = isset( $this->props['background_size_3'] ) ? $this->props['background_size_3'] : ''; $background_image_width_1 = isset( $this->props['background_image_width_1'] ) ? $this->props['background_image_width_1'] : ''; $background_image_width_2 = isset( $this->props['background_image_width_2'] ) ? $this->props['background_image_width_2'] : ''; $background_image_width_3 = isset( $this->props['background_image_width_3'] ) ? $this->props['background_image_width_3'] : ''; $background_image_height_1 = isset( $this->props['background_image_height_1'] ) ? $this->props['background_image_height_1'] : ''; $background_image_height_2 = isset( $this->props['background_image_height_2'] ) ? $this->props['background_image_height_2'] : ''; $background_image_height_3 = isset( $this->props['background_image_height_3'] ) ? $this->props['background_image_height_3'] : ''; $background_position_1 = isset( $this->props['background_position_1'] ) ? $this->props['background_position_1'] : ''; $background_position_2 = isset( $this->props['background_position_2'] ) ? $this->props['background_position_2'] : ''; $background_position_3 = isset( $this->props['background_position_3'] ) ? $this->props['background_position_3'] : ''; $background_horizontal_offset_1 = isset( $this->props['background_horizontal_offset_1'] ) ? $this->props['background_horizontal_offset_1'] : ''; $background_horizontal_offset_2 = isset( $this->props['background_horizontal_offset_2'] ) ? $this->props['background_horizontal_offset_2'] : ''; $background_horizontal_offset_3 = isset( $this->props['background_horizontal_offset_3'] ) ? $this->props['background_horizontal_offset_3'] : ''; $background_vertical_offset_1 = isset( $this->props['background_vertical_offset_1'] ) ? $this->props['background_vertical_offset_1'] : ''; $background_vertical_offset_2 = isset( $this->props['background_vertical_offset_2'] ) ? $this->props['background_vertical_offset_2'] : ''; $background_vertical_offset_3 = isset( $this->props['background_vertical_offset_3'] ) ? $this->props['background_vertical_offset_3'] : ''; $background_repeat_1 = isset( $this->props['background_repeat_1'] ) ? $this->props['background_repeat_1'] : ''; $background_repeat_2 = isset( $this->props['background_repeat_2'] ) ? $this->props['background_repeat_2'] : ''; $background_repeat_3 = isset( $this->props['background_repeat_3'] ) ? $this->props['background_repeat_3'] : ''; $background_blend_1 = isset( $this->props['background_blend_1'] ) ? $this->props['background_blend_1'] : ''; $background_blend_2 = isset( $this->props['background_blend_2'] ) ? $this->props['background_blend_2'] : ''; $background_blend_3 = isset( $this->props['background_blend_3'] ) ? $this->props['background_blend_3'] : ''; $parallax_1 = isset( $this->props['parallax_1'] ) ? $this->props['parallax_1'] : ''; $parallax_2 = isset( $this->props['parallax_2'] ) ? $this->props['parallax_2'] : ''; $parallax_3 = isset( $this->props['parallax_3'] ) ? $this->props['parallax_3'] : ''; $parallax_method_1 = isset( $this->props['parallax_method_1'] ) ? $this->props['parallax_method_1'] : ''; $parallax_method_2 = isset( $this->props['parallax_method_2'] ) ? $this->props['parallax_method_2'] : ''; $parallax_method_3 = isset( $this->props['parallax_method_3'] ) ? $this->props['parallax_method_3'] : ''; $padding_top_1 = isset( $this->props['padding_top_1'] ) ? $this->props['padding_top_1'] : ''; $padding_top_2 = isset( $this->props['padding_top_2'] ) ? $this->props['padding_top_2'] : ''; $padding_top_3 = isset( $this->props['padding_top_3'] ) ? $this->props['padding_top_3'] : ''; $padding_right_1 = isset( $this->props['padding_right_1'] ) ? $this->props['padding_right_1'] : ''; $padding_right_2 = isset( $this->props['padding_right_2'] ) ? $this->props['padding_right_2'] : ''; $padding_right_3 = isset( $this->props['padding_right_3'] ) ? $this->props['padding_right_3'] : ''; $padding_left_1 = isset( $this->props['padding_left_1'] ) ? $this->props['padding_left_1'] : ''; $padding_left_2 = isset( $this->props['padding_left_2'] ) ? $this->props['padding_left_2'] : ''; $padding_left_3 = isset( $this->props['padding_left_3'] ) ? $this->props['padding_left_3'] : ''; $padding_bottom_1 = isset( $this->props['padding_bottom_1'] ) ? $this->props['padding_bottom_1'] : ''; $padding_bottom_2 = isset( $this->props['padding_bottom_2'] ) ? $this->props['padding_bottom_2'] : ''; $padding_bottom_3 = isset( $this->props['padding_bottom_3'] ) ? $this->props['padding_bottom_3'] : ''; $padding_1_tablet = isset( $this->props['padding_1_tablet'] ) ? $this->props['padding_1_tablet'] : ''; $padding_2_tablet = isset( $this->props['padding_2_tablet'] ) ? $this->props['padding_2_tablet'] : ''; $padding_3_tablet = isset( $this->props['padding_3_tablet'] ) ? $this->props['padding_3_tablet'] : ''; $padding_1_phone = isset( $this->props['padding_1_phone'] ) ? $this->props['padding_1_phone'] : ''; $padding_2_phone = isset( $this->props['padding_2_phone'] ) ? $this->props['padding_2_phone'] : ''; $padding_3_phone = isset( $this->props['padding_3_phone'] ) ? $this->props['padding_3_phone'] : ''; $padding_1_last_edited = isset( $this->props['padding_1_last_edited'] ) ? $this->props['padding_1_last_edited'] : ''; $padding_2_last_edited = isset( $this->props['padding_2_last_edited'] ) ? $this->props['padding_2_last_edited'] : ''; $padding_3_last_edited = isset( $this->props['padding_3_last_edited'] ) ? $this->props['padding_3_last_edited'] : ''; $gutter_width = $this->props['gutter_width']; $gutter_width_hover = $this->get_hover_value( 'gutter_width' ); $make_equal = $this->props['make_equal']; $global_module = $this->props['global_module']; $use_custom_gutter = $this->props['use_custom_gutter']; $module_id_1 = isset( $this->props['module_id_1'] ) ? $this->props['module_id_1'] : ''; $module_id_2 = isset( $this->props['module_id_2'] ) ? $this->props['module_id_2'] : ''; $module_id_3 = isset( $this->props['module_id_3'] ) ? $this->props['module_id_3'] : ''; $module_class_1 = isset( $this->props['module_class_1'] ) ? $this->props['module_class_1'] : ''; $module_class_2 = isset( $this->props['module_class_2'] ) ? $this->props['module_class_2'] : ''; $module_class_3 = isset( $this->props['module_class_3'] ) ? $this->props['module_class_3'] : ''; $custom_css_before_1 = isset( $this->props['custom_css_before_1'] ) ? $this->props['custom_css_before_1'] : ''; $custom_css_before_2 = isset( $this->props['custom_css_before_2'] ) ? $this->props['custom_css_before_2'] : ''; $custom_css_before_3 = isset( $this->props['custom_css_before_3'] ) ? $this->props['custom_css_before_3'] : ''; $custom_css_main_1 = isset( $this->props['custom_css_main_1'] ) ? $this->props['custom_css_main_1'] : ''; $custom_css_main_2 = isset( $this->props['custom_css_main_2'] ) ? $this->props['custom_css_main_2'] : ''; $custom_css_main_3 = isset( $this->props['custom_css_main_3'] ) ? $this->props['custom_css_main_3'] : ''; $custom_css_after_1 = isset( $this->props['custom_css_after_1'] ) ? $this->props['custom_css_after_1'] : ''; $custom_css_after_2 = isset( $this->props['custom_css_after_2'] ) ? $this->props['custom_css_after_2'] : ''; $custom_css_after_3 = isset( $this->props['custom_css_after_3'] ) ? $this->props['custom_css_after_3'] : ''; $custom_css_before_1_hover = $this->get_hover_value( 'custom_css_before_1' ); $custom_css_before_2_hover = $this->get_hover_value( 'custom_css_before_2' ); $custom_css_before_3_hover = $this->get_hover_value( 'custom_css_before_3' ); $custom_css_main_1_hover = $this->get_hover_value( 'custom_css_main_1' ); $custom_css_main_2_hover = $this->get_hover_value( 'custom_css_main_2' ); $custom_css_main_3_hover = $this->get_hover_value( 'custom_css_main_3' ); $custom_css_after_1_hover = $this->get_hover_value( 'custom_css_after_1' ); $custom_css_after_2_hover = $this->get_hover_value( 'custom_css_after_2' ); $custom_css_after_3_hover = $this->get_hover_value( 'custom_css_after_3' ); $custom_css_before_1_sticky = $sticky->get_value( 'custom_css_before_1', $this->props, '' ); $custom_css_before_2_sticky = $sticky->get_value( 'custom_css_before_2', $this->props, '' ); $custom_css_before_3_sticky = $sticky->get_value( 'custom_css_before_3', $this->props, '' ); $custom_css_main_1_sticky = $sticky->get_value( 'custom_css_main_1', $this->props, '' ); $custom_css_main_2_sticky = $sticky->get_value( 'custom_css_main_2', $this->props, '' ); $custom_css_main_3_sticky = $sticky->get_value( 'custom_css_main_3', $this->props, '' ); $custom_css_after_1_sticky = $sticky->get_value( 'custom_css_after_1', $this->props, '' ); $custom_css_after_2_sticky = $sticky->get_value( 'custom_css_after_2', $this->props, '' ); $custom_css_after_3_sticky = $sticky->get_value( 'custom_css_after_3', $this->props, '' ); $use_background_color_gradient_1 = isset( $this->props['use_background_color_gradient_1'] ) ? $this->props['use_background_color_gradient_1'] : ''; $use_background_color_gradient_2 = isset( $this->props['use_background_color_gradient_2'] ) ? $this->props['use_background_color_gradient_2'] : ''; $use_background_color_gradient_3 = isset( $this->props['use_background_color_gradient_3'] ) ? $this->props['use_background_color_gradient_3'] : ''; $background_color_gradient_repeat_1 = isset( $this->props['background_color_gradient_repeat_1'] ) ? $this->props['background_color_gradient_repeat_1'] : ''; $background_color_gradient_repeat_2 = isset( $this->props['background_color_gradient_repeat_2'] ) ? $this->props['background_color_gradient_repeat_2'] : ''; $background_color_gradient_repeat_3 = isset( $this->props['background_color_gradient_repeat_3'] ) ? $this->props['background_color_gradient_repeat_3'] : ''; $background_color_gradient_type_1 = isset( $this->props['background_color_gradient_type_1'] ) ? $this->props['background_color_gradient_type_1'] : ''; $background_color_gradient_type_2 = isset( $this->props['background_color_gradient_type_2'] ) ? $this->props['background_color_gradient_type_2'] : ''; $background_color_gradient_type_3 = isset( $this->props['background_color_gradient_type_3'] ) ? $this->props['background_color_gradient_type_3'] : ''; $background_color_gradient_direction_1 = isset( $this->props['background_color_gradient_direction_1'] ) ? $this->props['background_color_gradient_direction_1'] : ''; $background_color_gradient_direction_2 = isset( $this->props['background_color_gradient_direction_2'] ) ? $this->props['background_color_gradient_direction_2'] : ''; $background_color_gradient_direction_3 = isset( $this->props['background_color_gradient_direction_3'] ) ? $this->props['background_color_gradient_direction_3'] : ''; $background_color_gradient_direction_radial_1 = isset( $this->props['background_color_gradient_direction_radial_1'] ) ? $this->props['background_color_gradient_direction_radial_1'] : ''; $background_color_gradient_direction_radial_2 = isset( $this->props['background_color_gradient_direction_radial_2'] ) ? $this->props['background_color_gradient_direction_radial_2'] : ''; $background_color_gradient_direction_radial_3 = isset( $this->props['background_color_gradient_direction_radial_3'] ) ? $this->props['background_color_gradient_direction_radial_3'] : ''; $background_color_gradient_stops_1 = isset( $this->props['background_color_gradient_stops_1'] ) ? $this->props['background_color_gradient_stops_1'] : ''; $background_color_gradient_stops_2 = isset( $this->props['background_color_gradient_stops_2'] ) ? $this->props['background_color_gradient_stops_2'] : ''; $background_color_gradient_stops_3 = isset( $this->props['background_color_gradient_stops_3'] ) ? $this->props['background_color_gradient_stops_3'] : ''; $background_color_gradient_unit_1 = isset( $this->props['background_color_gradient_unit_1'] ) ? $this->props['background_color_gradient_unit_1'] : ''; $background_color_gradient_unit_2 = isset( $this->props['background_color_gradient_unit_2'] ) ? $this->props['background_color_gradient_unit_2'] : ''; $background_color_gradient_unit_3 = isset( $this->props['background_color_gradient_unit_3'] ) ? $this->props['background_color_gradient_unit_3'] : ''; $background_color_gradient_overlays_image_1 = isset( $this->props['background_color_gradient_overlays_image_1'] ) ? $this->props['background_color_gradient_overlays_image_1'] : ''; $background_color_gradient_overlays_image_2 = isset( $this->props['background_color_gradient_overlays_image_2'] ) ? $this->props['background_color_gradient_overlays_image_2'] : ''; $background_color_gradient_overlays_image_3 = isset( $this->props['background_color_gradient_overlays_image_3'] ) ? $this->props['background_color_gradient_overlays_image_3'] : ''; $background_color_gradient_start_1 = isset( $this->props['background_color_gradient_start_1'] ) ? $this->props['background_color_gradient_start_1'] : ''; $background_color_gradient_start_2 = isset( $this->props['background_color_gradient_start_2'] ) ? $this->props['background_color_gradient_start_2'] : ''; $background_color_gradient_start_3 = isset( $this->props['background_color_gradient_start_3'] ) ? $this->props['background_color_gradient_start_3'] : ''; $background_color_gradient_end_1 = isset( $this->props['background_color_gradient_end_1'] ) ? $this->props['background_color_gradient_end_1'] : ''; $background_color_gradient_end_2 = isset( $this->props['background_color_gradient_end_2'] ) ? $this->props['background_color_gradient_end_2'] : ''; $background_color_gradient_end_3 = isset( $this->props['background_color_gradient_end_3'] ) ? $this->props['background_color_gradient_end_3'] : ''; $background_color_gradient_start_position_1 = isset( $this->props['background_color_gradient_start_position_1'] ) ? $this->props['background_color_gradient_start_position_1'] : ''; $background_color_gradient_start_position_2 = isset( $this->props['background_color_gradient_start_position_2'] ) ? $this->props['background_color_gradient_start_position_2'] : ''; $background_color_gradient_start_position_3 = isset( $this->props['background_color_gradient_start_position_3'] ) ? $this->props['background_color_gradient_start_position_3'] : ''; $background_color_gradient_end_position_1 = isset( $this->props['background_color_gradient_end_position_1'] ) ? $this->props['background_color_gradient_end_position_1'] : ''; $background_color_gradient_end_position_2 = isset( $this->props['background_color_gradient_end_position_2'] ) ? $this->props['background_color_gradient_end_position_2'] : ''; $background_color_gradient_end_position_3 = isset( $this->props['background_color_gradient_end_position_3'] ) ? $this->props['background_color_gradient_end_position_3'] : ''; $background_enable_pattern_style_1 = isset( $this->props['background_enable_pattern_style_1'] ) ? $this->props['background_enable_pattern_style_1'] : ''; $background_enable_pattern_style_2 = isset( $this->props['background_enable_pattern_style_2'] ) ? $this->props['background_enable_pattern_style_2'] : ''; $background_enable_pattern_style_3 = isset( $this->props['background_enable_pattern_style_3'] ) ? $this->props['background_enable_pattern_style_3'] : ''; $background_pattern_style_1 = isset( $this->props['background_pattern_style_1'] ) ? $this->props['background_pattern_style_1'] : ''; $background_pattern_style_2 = isset( $this->props['background_pattern_style_2'] ) ? $this->props['background_pattern_style_2'] : ''; $background_pattern_style_3 = isset( $this->props['background_pattern_style_3'] ) ? $this->props['background_pattern_style_3'] : ''; $background_pattern_color_1 = isset( $this->props['background_pattern_color_1'] ) ? $this->props['background_pattern_color_1'] : ''; $background_pattern_color_2 = isset( $this->props['background_pattern_color_2'] ) ? $this->props['background_pattern_color_2'] : ''; $background_pattern_color_3 = isset( $this->props['background_pattern_color_3'] ) ? $this->props['background_pattern_color_3'] : ''; $background_pattern_transform_1 = isset( $this->props['background_pattern_transform_1'] ) ? $this->props['background_pattern_transform_1'] : ''; $background_pattern_transform_2 = isset( $this->props['background_pattern_transform_2'] ) ? $this->props['background_pattern_transform_2'] : ''; $background_pattern_transform_3 = isset( $this->props['background_pattern_transform_3'] ) ? $this->props['background_pattern_transform_3'] : ''; $background_pattern_size_1 = isset( $this->props['background_pattern_size_1'] ) ? $this->props['background_pattern_size_1'] : ''; $background_pattern_size_2 = isset( $this->props['background_pattern_size_2'] ) ? $this->props['background_pattern_size_2'] : ''; $background_pattern_size_3 = isset( $this->props['background_pattern_size_3'] ) ? $this->props['background_pattern_size_3'] : ''; $background_pattern_width_1 = isset( $this->props['background_pattern_width_1'] ) ? $this->props['background_pattern_width_1'] : ''; $background_pattern_width_2 = isset( $this->props['background_pattern_width_2'] ) ? $this->props['background_pattern_width_2'] : ''; $background_pattern_width_3 = isset( $this->props['background_pattern_width_3'] ) ? $this->props['background_pattern_width_3'] : ''; $background_pattern_height_1 = isset( $this->props['background_pattern_height_1'] ) ? $this->props['background_pattern_height_1'] : ''; $background_pattern_height_2 = isset( $this->props['background_pattern_height_2'] ) ? $this->props['background_pattern_height_2'] : ''; $background_pattern_height_3 = isset( $this->props['background_pattern_height_3'] ) ? $this->props['background_pattern_height_3'] : ''; $background_pattern_repeat_origin_1 = isset( $this->props['background_pattern_repeat_origin_1'] ) ? $this->props['background_pattern_repeat_origin_1'] : ''; $background_pattern_repeat_origin_2 = isset( $this->props['background_pattern_repeat_origin_2'] ) ? $this->props['background_pattern_repeat_origin_2'] : ''; $background_pattern_repeat_origin_3 = isset( $this->props['background_pattern_repeat_origin_3'] ) ? $this->props['background_pattern_repeat_origin_3'] : ''; $background_pattern_horizontal_offset_1 = isset( $this->props['background_pattern_horizontal_offset_1'] ) ? $this->props['background_pattern_horizontal_offset_1'] : ''; $background_pattern_horizontal_offset_2 = isset( $this->props['background_pattern_horizontal_offset_2'] ) ? $this->props['background_pattern_horizontal_offset_2'] : ''; $background_pattern_horizontal_offset_3 = isset( $this->props['background_pattern_horizontal_offset_3'] ) ? $this->props['background_pattern_horizontal_offset_3'] : ''; $background_pattern_vertical_offset_1 = isset( $this->props['background_pattern_vertical_offset_1'] ) ? $this->props['background_pattern_vertical_offset_1'] : ''; $background_pattern_vertical_offset_2 = isset( $this->props['background_pattern_vertical_offset_2'] ) ? $this->props['background_pattern_vertical_offset_2'] : ''; $background_pattern_vertical_offset_3 = isset( $this->props['background_pattern_vertical_offset_3'] ) ? $this->props['background_pattern_vertical_offset_3'] : ''; $background_pattern_repeat_1 = isset( $this->props['background_pattern_repeat_1'] ) ? $this->props['background_pattern_repeat_1'] : ''; $background_pattern_repeat_2 = isset( $this->props['background_pattern_repeat_2'] ) ? $this->props['background_pattern_repeat_2'] : ''; $background_pattern_repeat_3 = isset( $this->props['background_pattern_repeat_3'] ) ? $this->props['background_pattern_repeat_3'] : ''; $background_pattern_blend_mode_1 = isset( $this->props['background_pattern_blend_mode_1'] ) ? $this->props['background_pattern_blend_mode_1'] : ''; $background_pattern_blend_mode_2 = isset( $this->props['background_pattern_blend_mode_2'] ) ? $this->props['background_pattern_blend_mode_2'] : ''; $background_pattern_blend_mode_3 = isset( $this->props['background_pattern_blend_mode_3'] ) ? $this->props['background_pattern_blend_mode_3'] : ''; $background_enable_mask_style_1 = isset( $this->props['background_enable_mask_style_1'] ) ? $this->props['background_enable_mask_style_1'] : ''; $background_enable_mask_style_2 = isset( $this->props['background_enable_mask_style_2'] ) ? $this->props['background_enable_mask_style_2'] : ''; $background_enable_mask_style_3 = isset( $this->props['background_enable_mask_style_3'] ) ? $this->props['background_enable_mask_style_3'] : ''; $background_mask_style_1 = isset( $this->props['background_mask_style_1'] ) ? $this->props['background_mask_style_1'] : ''; $background_mask_style_2 = isset( $this->props['background_mask_style_2'] ) ? $this->props['background_mask_style_2'] : ''; $background_mask_style_3 = isset( $this->props['background_mask_style_3'] ) ? $this->props['background_mask_style_3'] : ''; $background_mask_color_1 = isset( $this->props['background_mask_color_1'] ) ? $this->props['background_mask_color_1'] : ''; $background_mask_color_2 = isset( $this->props['background_mask_color_2'] ) ? $this->props['background_mask_color_2'] : ''; $background_mask_color_3 = isset( $this->props['background_mask_color_3'] ) ? $this->props['background_mask_color_3'] : ''; $background_mask_transform_1 = isset( $this->props['background_mask_transform_1'] ) ? $this->props['background_mask_transform_1'] : ''; $background_mask_transform_2 = isset( $this->props['background_mask_transform_2'] ) ? $this->props['background_mask_transform_2'] : ''; $background_mask_transform_3 = isset( $this->props['background_mask_transform_3'] ) ? $this->props['background_mask_transform_3'] : ''; $background_mask_aspect_ratio_1 = isset( $this->props['background_mask_aspect_ratio_1'] ) ? $this->props['background_mask_aspect_ratio_1'] : ''; $background_mask_aspect_ratio_2 = isset( $this->props['background_mask_aspect_ratio_2'] ) ? $this->props['background_mask_aspect_ratio_2'] : ''; $background_mask_aspect_ratio_3 = isset( $this->props['background_mask_aspect_ratio_3'] ) ? $this->props['background_mask_aspect_ratio_3'] : ''; $background_mask_size_1 = isset( $this->props['background_mask_size_1'] ) ? $this->props['background_mask_size_1'] : ''; $background_mask_size_2 = isset( $this->props['background_mask_size_2'] ) ? $this->props['background_mask_size_2'] : ''; $background_mask_size_3 = isset( $this->props['background_mask_size_3'] ) ? $this->props['background_mask_size_3'] : ''; $background_mask_width_1 = isset( $this->props['background_mask_width_1'] ) ? $this->props['background_mask_width_1'] : ''; $background_mask_width_2 = isset( $this->props['background_mask_width_2'] ) ? $this->props['background_mask_width_2'] : ''; $background_mask_width_3 = isset( $this->props['background_mask_width_3'] ) ? $this->props['background_mask_width_3'] : ''; $background_mask_height_1 = isset( $this->props['background_mask_height_1'] ) ? $this->props['background_mask_height_1'] : ''; $background_mask_height_2 = isset( $this->props['background_mask_height_2'] ) ? $this->props['background_mask_height_2'] : ''; $background_mask_height_3 = isset( $this->props['background_mask_height_3'] ) ? $this->props['background_mask_height_3'] : ''; $background_mask_position_1 = isset( $this->props['background_mask_position_1'] ) ? $this->props['background_mask_position_1'] : ''; $background_mask_position_2 = isset( $this->props['background_mask_position_2'] ) ? $this->props['background_mask_position_2'] : ''; $background_mask_position_3 = isset( $this->props['background_mask_position_3'] ) ? $this->props['background_mask_position_3'] : ''; $background_mask_horizontal_offset_1 = isset( $this->props['background_mask_horizontal_offset_1'] ) ? $this->props['background_mask_horizontal_offset_1'] : ''; $background_mask_horizontal_offset_2 = isset( $this->props['background_mask_horizontal_offset_2'] ) ? $this->props['background_mask_horizontal_offset_2'] : ''; $background_mask_horizontal_offset_3 = isset( $this->props['background_mask_horizontal_offset_3'] ) ? $this->props['background_mask_horizontal_offset_3'] : ''; $background_mask_vertical_offset_1 = isset( $this->props['background_mask_vertical_offset_1'] ) ? $this->props['background_mask_vertical_offset_1'] : ''; $background_mask_vertical_offset_2 = isset( $this->props['background_mask_vertical_offset_2'] ) ? $this->props['background_mask_vertical_offset_2'] : ''; $background_mask_vertical_offset_3 = isset( $this->props['background_mask_vertical_offset_3'] ) ? $this->props['background_mask_vertical_offset_3'] : ''; $background_mask_blend_mode_1 = isset( $this->props['background_mask_blend_mode_1'] ) ? $this->props['background_mask_blend_mode_1'] : ''; $background_mask_blend_mode_2 = isset( $this->props['background_mask_blend_mode_2'] ) ? $this->props['background_mask_blend_mode_2'] : ''; $background_mask_blend_mode_3 = isset( $this->props['background_mask_blend_mode_3'] ) ? $this->props['background_mask_blend_mode_3'] : ''; $background_video_mp4_1 = isset( $this->props['background_video_mp4_1'] ) ? $this->props['background_video_mp4_1'] : ''; $background_video_mp4_2 = isset( $this->props['background_video_mp4_2'] ) ? $this->props['background_video_mp4_2'] : ''; $background_video_mp4_3 = isset( $this->props['background_video_mp4_3'] ) ? $this->props['background_video_mp4_3'] : ''; $background_video_webm_1 = isset( $this->props['background_video_webm_1'] ) ? $this->props['background_video_webm_1'] : ''; $background_video_webm_2 = isset( $this->props['background_video_webm_2'] ) ? $this->props['background_video_webm_2'] : ''; $background_video_webm_3 = isset( $this->props['background_video_webm_3'] ) ? $this->props['background_video_webm_3'] : ''; $background_video_width_1 = isset( $this->props['background_video_width_1'] ) ? $this->props['background_video_width_1'] : ''; $background_video_width_2 = isset( $this->props['background_video_width_2'] ) ? $this->props['background_video_width_2'] : ''; $background_video_width_3 = isset( $this->props['background_video_width_3'] ) ? $this->props['background_video_width_3'] : ''; $background_video_height_1 = isset( $this->props['background_video_height_1'] ) ? $this->props['background_video_height_1'] : ''; $background_video_height_2 = isset( $this->props['background_video_height_2'] ) ? $this->props['background_video_height_2'] : ''; $background_video_height_3 = isset( $this->props['background_video_height_3'] ) ? $this->props['background_video_height_3'] : ''; $allow_player_pause_1 = isset( $this->props['allow_player_pause_1'] ) ? $this->props['allow_player_pause_1'] : ''; $allow_player_pause_2 = isset( $this->props['allow_player_pause_2'] ) ? $this->props['allow_player_pause_2'] : ''; $allow_player_pause_3 = isset( $this->props['allow_player_pause_3'] ) ? $this->props['allow_player_pause_3'] : ''; $background_video_pause_outside_viewport_1 = isset( $this->props['background_video_pause_outside_viewport_1'] ) ? $this->props['background_video_pause_outside_viewport_1'] : ''; $background_video_pause_outside_viewport_2 = isset( $this->props['background_video_pause_outside_viewport_2'] ) ? $this->props['background_video_pause_outside_viewport_2'] : ''; $background_video_pause_outside_viewport_3 = isset( $this->props['background_video_pause_outside_viewport_3'] ) ? $this->props['background_video_pause_outside_viewport_3'] : ''; $prev_background_color = $this->props['prev_background_color']; $next_background_color = $this->props['next_background_color']; $is_background_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, 'background' ); global $et_pb_rendering_specialty_section; // Check Background Image. $background_image = $this->props['background_image']; if ( '' === $background_image && $is_background_responsive ) { $background_image_tablet = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_image', 'tablet' ); $background_image_phone = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_image', 'phone' ); $background_image = '' !== $background_image_tablet ? $background_image_tablet : $background_image_phone; } // Background Color. $background_color = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_color', 'desktop' ); $background_color_tablet = ''; $background_color_phone = ''; $processed_background_color = $this->is_initial_background_color() ? 'inherit' : $background_color; $processed_background_color_tablet = ''; $processed_background_color_phone = ''; if ( $is_background_responsive ) { $background_color_tablet = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_color', 'tablet' ); $background_color_phone = et_pb_responsive_options()->get_inheritance_background_value( $this->props, 'background_color', 'phone' ); $processed_background_color_tablet = $this->is_initial_background_color( 'tablet' ) ? 'inherit' : $background_color_tablet; $processed_background_color_phone = $this->is_initial_background_color( 'phone' ) ? 'inherit' : $background_color_phone; } if ( '' !== $global_module ) { $global_content = et_pb_load_global_module( $global_module, '', $prev_background_color, $next_background_color ); if ( '' !== $global_content ) { return do_shortcode( et_pb_fix_shortcodes( wpautop( $global_content ) ) ); } } $gutter_class = ''; $gutter_hover_data = ''; if ( 'on' === $specialty ) { global $et_pb_all_column_settings, $et_pb_rendering_column_content, $et_pb_rendering_column_content_row; $et_pb_all_column_settings_backup = $et_pb_all_column_settings; $et_pb_all_column_settings = ! isset( $et_pb_all_column_settings ) ? array() : $et_pb_all_column_settings; if ( 'on' === $make_equal ) { $this->add_classname( 'et_pb_equal_columns' ); } if ( 'on' === $use_custom_gutter && '' !== $gutter_width ) { $gutter_width = '0' === $gutter_width ? '1' : $gutter_width; // set the gutter to 1 if 0 entered by user. $gutter_class .= ' et_pb_gutters' . $gutter_width; if ( et_builder_is_hover_enabled( 'gutter_width', $this->props ) && ! empty( $gutter_width_hover ) && $gutter_width !== $gutter_width_hover ) { $gutter_class .= ' et_pb_gutter_hover'; $gutter_hover_data = sprintf( ' data-original_gutter="%1$s" data-hover_gutter="%2$s"', esc_attr( $gutter_width ), esc_attr( $gutter_width_hover ) ); } } // Column hover backgrounds. $column_hover_backgrounds = array(); $column_sticky_backgrounds = array(); for ( $i = 0; $i <= 3; $i ++ ) { $column_hover_backgrounds = array_merge( $column_hover_backgrounds, array( "column_{$i}_color_hover" => $hover->get_value( "background_color_{$i}", $this->props, false ), "column_{$i}_color_hover_enabled" => $hover->is_enabled( "background_color_{$i}", $this->props ), ) ); $column_sticky_backgrounds = array_merge( $column_sticky_backgrounds, array( "column_{$i}_color_sticky" => $sticky->get_value( "background_color_{$i}", $this->props, false ), "column_{$i}_color_sticky_enabled" => $sticky->is_enabled( "background_color_{$i}", $this->props ), ) ); } $et_pb_column_backgrounds = array( array( 'color' => $background_color_1, 'color_hover' => $column_hover_backgrounds['column_1_color_hover'], 'color_hover_enabled' => $column_hover_backgrounds['column_1_color_hover_enabled'], 'color_sticky' => $column_sticky_backgrounds['column_1_color_sticky'], 'color_sticky_enabled' => $column_sticky_backgrounds['column_1_color_sticky_enabled'], 'image' => $bg_img_1, 'image_size' => $background_size_1, 'image_width' => $background_image_width_1, 'image_height' => $background_image_height_1, 'image_position' => $background_position_1, 'image_horizontal_offset' => $background_horizontal_offset_1, 'image_vertical_offset' => $background_vertical_offset_1, 'image_repeat' => $background_repeat_1, 'image_blend' => $background_blend_1, ), array( 'color' => $background_color_2, 'color_hover' => $column_hover_backgrounds['column_2_color_hover'], 'color_hover_enabled' => $column_hover_backgrounds['column_2_color_hover_enabled'], 'color_sticky' => $column_sticky_backgrounds['column_2_color_sticky'], 'color_sticky_enabled' => $column_sticky_backgrounds['column_2_color_sticky_enabled'], 'image' => $bg_img_2, 'image_size' => $background_size_2, 'image_width' => $background_image_width_2, 'image_height' => $background_image_height_2, 'image_position' => $background_position_2, 'image_horizontal_offset' => $background_horizontal_offset_2, 'image_vertical_offset' => $background_vertical_offset_2, 'image_repeat' => $background_repeat_2, 'image_blend' => $background_blend_2, ), array( 'color' => $background_color_3, 'color_hover' => $column_hover_backgrounds['column_3_color_hover'], 'color_hover_enabled' => $column_hover_backgrounds['column_3_color_hover_enabled'], 'color_sticky' => $column_sticky_backgrounds['column_3_color_sticky'], 'color_sticky_enabled' => $column_sticky_backgrounds['column_3_color_sticky_enabled'], 'image' => $bg_img_3, 'image_size' => $background_size_3, 'image_width' => $background_image_width_3, 'image_height' => $background_image_height_3, 'image_position' => $background_position_3, 'image_horizontal_offset' => $background_horizontal_offset_3, 'image_vertical_offset' => $background_vertical_offset_3, 'image_repeat' => $background_repeat_3, 'image_blend' => $background_blend_3, ), ); $et_pb_column_backgrounds_gradient = array( array( 'active' => $use_background_color_gradient_1, 'repeat' => $background_color_gradient_repeat_1, 'type' => $background_color_gradient_type_1, 'direction' => $background_color_gradient_direction_1, 'radial_direction' => $background_color_gradient_direction_radial_1, 'stops' => $background_color_gradient_stops_1, 'unit' => $background_color_gradient_unit_1, 'overlays_image' => $background_color_gradient_overlays_image_1, 'color_start' => $background_color_gradient_start_1, 'color_end' => $background_color_gradient_end_1, 'start_position' => $background_color_gradient_start_position_1, 'end_position' => $background_color_gradient_end_position_1, ), array( 'active' => $use_background_color_gradient_2, 'repeat' => $background_color_gradient_repeat_2, 'type' => $background_color_gradient_type_2, 'direction' => $background_color_gradient_direction_2, 'radial_direction' => $background_color_gradient_direction_radial_2, 'stops' => $background_color_gradient_stops_2, 'unit' => $background_color_gradient_unit_2, 'overlays_image' => $background_color_gradient_overlays_image_2, 'color_start' => $background_color_gradient_start_2, 'color_end' => $background_color_gradient_end_2, 'start_position' => $background_color_gradient_start_position_2, 'end_position' => $background_color_gradient_end_position_2, ), array( 'active' => $use_background_color_gradient_3, 'repeat' => $background_color_gradient_repeat_3, 'type' => $background_color_gradient_type_3, 'direction' => $background_color_gradient_direction_3, 'radial_direction' => $background_color_gradient_direction_radial_3, 'stops' => $background_color_gradient_stops_3, 'unit' => $background_color_gradient_unit_3, 'overlays_image' => $background_color_gradient_overlays_image_3, 'color_start' => $background_color_gradient_start_3, 'color_end' => $background_color_gradient_end_3, 'start_position' => $background_color_gradient_start_position_3, 'end_position' => $background_color_gradient_end_position_3, ), ); $et_pb_column_backgrounds_pattern = array( array( 'enable_pattern_style' => $background_enable_pattern_style_1, 'pattern_style' => $background_pattern_style_1, 'pattern_color' => $background_pattern_color_1, 'pattern_transform' => $background_pattern_transform_1, 'pattern_size' => $background_pattern_size_1, 'pattern_width' => $background_pattern_width_1, 'pattern_height' => $background_pattern_height_1, 'pattern_repeat' => $background_pattern_repeat_1, 'pattern_repeat_origin' => $background_pattern_repeat_origin_1, 'pattern_horizontal_offset' => $background_pattern_horizontal_offset_1, 'pattern_vertical_offset' => $background_pattern_vertical_offset_1, 'pattern_blend_mode' => $background_pattern_blend_mode_1, ), array( 'enable_pattern_style' => $background_enable_pattern_style_2, 'pattern_style' => $background_pattern_style_2, 'pattern_color' => $background_pattern_color_2, 'pattern_transform' => $background_pattern_transform_2, 'pattern_size' => $background_pattern_size_2, 'pattern_width' => $background_pattern_width_2, 'pattern_height' => $background_pattern_height_2, 'pattern_repeat' => $background_pattern_repeat_2, 'pattern_repeat_origin' => $background_pattern_repeat_origin_2, 'pattern_horizontal_offset' => $background_pattern_horizontal_offset_2, 'pattern_vertical_offset' => $background_pattern_vertical_offset_2, 'pattern_blend_mode' => $background_pattern_blend_mode_2, ), array( 'enable_pattern_style' => $background_enable_pattern_style_3, 'pattern_style' => $background_pattern_style_3, 'pattern_color' => $background_pattern_color_3, 'pattern_transform' => $background_pattern_transform_3, 'pattern_size' => $background_pattern_size_3, 'pattern_width' => $background_pattern_width_3, 'pattern_height' => $background_pattern_height_3, 'pattern_repeat' => $background_pattern_repeat_3, 'pattern_repeat_origin' => $background_pattern_repeat_origin_3, 'pattern_horizontal_offset' => $background_pattern_horizontal_offset_3, 'pattern_vertical_offset' => $background_pattern_vertical_offset_3, 'pattern_blend_mode' => $background_pattern_blend_mode_3, ), ); $et_pb_column_backgrounds_mask = array( array( 'enable_mask_style' => $background_enable_mask_style_1, 'mask_style' => $background_mask_style_1, 'mask_color' => $background_mask_color_1, 'mask_transform' => $background_mask_transform_1, 'mask_aspect_ratio' => $background_mask_aspect_ratio_1, 'mask_size' => $background_mask_size_1, 'mask_width' => $background_mask_width_1, 'mask_height' => $background_mask_height_1, 'mask_position' => $background_mask_position_1, 'mask_horizontal_offset' => $background_mask_horizontal_offset_1, 'mask_vertical_offset' => $background_mask_vertical_offset_1, 'mask_blend_mode' => $background_mask_blend_mode_1, ), array( 'enable_mask_style' => $background_enable_mask_style_2, 'mask_style' => $background_mask_style_2, 'mask_color' => $background_mask_color_2, 'mask_transform' => $background_mask_transform_2, 'mask_aspect_ratio' => $background_mask_aspect_ratio_2, 'mask_size' => $background_mask_size_2, 'mask_width' => $background_mask_width_2, 'mask_height' => $background_mask_height_2, 'mask_position' => $background_mask_position_2, 'mask_horizontal_offset' => $background_mask_horizontal_offset_2, 'mask_vertical_offset' => $background_mask_vertical_offset_2, 'mask_blend_mode' => $background_mask_blend_mode_2, ), array( 'enable_mask_style' => $background_enable_mask_style_3, 'mask_style' => $background_mask_style_3, 'mask_color' => $background_mask_color_3, 'mask_transform' => $background_mask_transform_3, 'mask_aspect_ratio' => $background_mask_aspect_ratio_3, 'mask_size' => $background_mask_size_3, 'mask_width' => $background_mask_width_3, 'mask_height' => $background_mask_height_3, 'mask_position' => $background_mask_position_3, 'mask_horizontal_offset' => $background_mask_horizontal_offset_3, 'mask_vertical_offset' => $background_mask_vertical_offset_3, 'mask_blend_mode' => $background_mask_blend_mode_3, ), ); $et_pb_column_backgrounds_video = array( array( 'background_video_mp4' => $background_video_mp4_1, 'background_video_webm' => $background_video_webm_1, 'background_video_width' => $background_video_width_1, 'background_video_height' => $background_video_height_1, 'background_video_allow_pause' => $allow_player_pause_1, 'background_video_pause_outside_viewport' => $background_video_pause_outside_viewport_1, ), array( 'background_video_mp4' => $background_video_mp4_2, 'background_video_webm' => $background_video_webm_2, 'background_video_width' => $background_video_width_2, 'background_video_height' => $background_video_height_2, 'background_video_allow_pause' => $allow_player_pause_2, 'background_video_pause_outside_viewport' => $background_video_pause_outside_viewport_2, ), array( 'background_video_mp4' => $background_video_mp4_3, 'background_video_webm' => $background_video_webm_3, 'background_video_width' => $background_video_width_3, 'background_video_height' => $background_video_height_3, 'background_video_allow_pause' => $allow_player_pause_3, 'background_video_pause_outside_viewport' => $background_video_pause_outside_viewport_3, ), ); // Column hover paddings. $column_hover_paddings = array(); $column_sticky_paddings = array(); for ( $i = 0; $i <= 3; $i++ ) { $column_hover_paddings = array_merge( $column_hover_paddings, array( "column_{$i}_padding_hover_enabled" => $hover->is_enabled( "padding_{$i}", $this->props ), "column_{$i}_padding_top" => $hover->get_compose_value( "padding_top_{$i}", "padding_{$i}", $this->props ), "column_{$i}_padding_right" => $hover->get_compose_value( "padding_right_{$i}", "padding_{$i}", $this->props ), "column_{$i}_padding_bottom" => $hover->get_compose_value( "padding_bottom_{$i}", "padding_{$i}", $this->props ), "column_{$i}_padding_left" => $hover->get_compose_value( "padding_left_{$i}", "padding_{$i}", $this->props ), ) ); $sticky_padding_values = explode( '|', $sticky->get_value( "padding_{$i}", $this->props, '|||' ) ); $column_sticky_paddings = array_merge( $column_sticky_paddings, array( "column_{$i}_padding_sticky_enabled" => $sticky->is_enabled( "padding_{$i}", $this->props ), "column_{$i}_padding_top" => $sticky_padding_values[0], "column_{$i}_padding_right" => $sticky_padding_values[1], "column_{$i}_padding_bottom" => $sticky_padding_values[2], "column_{$i}_padding_left" => $sticky_padding_values[3], ) ); } $et_pb_column_paddings = array( array( 'padding-top' => $padding_top_1, 'padding-right' => $padding_right_1, 'padding-bottom' => $padding_bottom_1, 'padding-left' => $padding_left_1, 'padding-hover-enabled' => $column_hover_paddings['column_1_padding_hover_enabled'], 'padding-top-hover' => $column_hover_paddings['column_1_padding_top'], 'padding-right-hover' => $column_hover_paddings['column_1_padding_right'], 'padding-bottom-hover' => $column_hover_paddings['column_1_padding_bottom'], 'padding-left-hover' => $column_hover_paddings['column_1_padding_left'], 'padding-sticky-enabled' => $column_sticky_paddings['column_1_padding_sticky_enabled'], 'padding-top-sticky' => $column_sticky_paddings['column_1_padding_top'], 'padding-right-sticky' => $column_sticky_paddings['column_1_padding_right'], 'padding-bottom-sticky' => $column_sticky_paddings['column_1_padding_bottom'], 'padding-left-sticky' => $column_sticky_paddings['column_1_padding_left'], ), array( 'padding-top' => $padding_top_2, 'padding-right' => $padding_right_2, 'padding-bottom' => $padding_bottom_2, 'padding-left' => $padding_left_2, 'padding-hover-enabled' => $column_hover_paddings['column_2_padding_hover_enabled'], 'padding-top-hover' => $column_hover_paddings['column_2_padding_top'], 'padding-right-hover' => $column_hover_paddings['column_2_padding_right'], 'padding-bottom-hover' => $column_hover_paddings['column_2_padding_bottom'], 'padding-left-hover' => $column_hover_paddings['column_2_padding_left'], 'padding-sticky-enabled' => $column_sticky_paddings['column_2_padding_sticky_enabled'], 'padding-top-sticky' => $column_sticky_paddings['column_2_padding_top'], 'padding-right-sticky' => $column_sticky_paddings['column_2_padding_right'], 'padding-bottom-sticky' => $column_sticky_paddings['column_2_padding_bottom'], 'padding-left-sticky' => $column_sticky_paddings['column_2_padding_left'], ), array( 'padding-top' => $padding_top_3, 'padding-right' => $padding_right_3, 'padding-bottom' => $padding_bottom_3, 'padding-left' => $padding_left_3, 'padding-hover-enabled' => $column_hover_paddings['column_3_padding_hover_enabled'], 'padding-top-hover' => $column_hover_paddings['column_3_padding_top'], 'padding-right-hover' => $column_hover_paddings['column_3_padding_right'], 'padding-bottom-hover' => $column_hover_paddings['column_3_padding_bottom'], 'padding-left-hover' => $column_hover_paddings['column_3_padding_left'], 'padding-sticky-enabled' => $column_sticky_paddings['column_3_padding_sticky_enabled'], 'padding-top-sticky' => $column_sticky_paddings['column_3_padding_top'], 'padding-right-sticky' => $column_sticky_paddings['column_3_padding_right'], 'padding-bottom-sticky' => $column_sticky_paddings['column_3_padding_bottom'], 'padding-left-sticky' => $column_sticky_paddings['column_3_padding_left'], ), ); $et_pb_column_paddings_mobile = array( array( 'tablet' => explode( '|', $padding_1_tablet ), 'phone' => explode( '|', $padding_1_phone ), 'last_edited' => $padding_1_last_edited, ), array( 'tablet' => explode( '|', $padding_2_tablet ), 'phone' => explode( '|', $padding_2_phone ), 'last_edited' => $padding_2_last_edited, ), array( 'tablet' => explode( '|', $padding_3_tablet ), 'phone' => explode( '|', $padding_3_phone ), 'last_edited' => $padding_3_last_edited, ), ); $et_pb_column_parallax = array( array( $parallax_1, $parallax_method_1 ), array( $parallax_2, $parallax_method_2 ), array( $parallax_3, $parallax_method_3 ), ); $et_pb_column_css = array( 'css_class' => array( $module_class_1, $module_class_2, $module_class_3 ), 'css_id' => array( $module_id_1, $module_id_2, $module_id_3 ), 'custom_css_before' => array( $custom_css_before_1, $custom_css_before_2, $custom_css_before_3 ), 'custom_css_main' => array( $custom_css_main_1, $custom_css_main_2, $custom_css_main_3 ), 'custom_css_after' => array( $custom_css_after_1, $custom_css_after_2, $custom_css_after_3 ), 'custom_css_before_hover' => array( $custom_css_before_1_hover, $custom_css_before_2_hover, $custom_css_before_3_hover ), 'custom_css_main_hover' => array( $custom_css_main_1_hover, $custom_css_main_2_hover, $custom_css_main_3_hover ), 'custom_css_after_hover' => array( $custom_css_after_1_hover, $custom_css_after_2_hover, $custom_css_after_3_hover ), 'custom_css_before_sticky' => array( $custom_css_before_1_sticky, $custom_css_before_2_sticky, $custom_css_before_3_sticky ), 'custom_css_main_sticky' => array( $custom_css_main_1_sticky, $custom_css_main_2_sticky, $custom_css_main_3_sticky ), 'custom_css_after_sticky' => array( $custom_css_after_1_sticky, $custom_css_after_2_sticky, $custom_css_after_3_sticky ), ); $internal_columns_settings_array = array( 'keep_column_padding_mobile' => 'on', 'et_pb_column_backgrounds' => $et_pb_column_backgrounds, 'et_pb_column_backgrounds_gradient' => $et_pb_column_backgrounds_gradient, 'et_pb_column_backgrounds_pattern' => $et_pb_column_backgrounds_pattern, 'et_pb_column_backgrounds_mask' => $et_pb_column_backgrounds_mask, 'et_pb_column_backgrounds_video' => $et_pb_column_backgrounds_video, 'et_pb_column_parallax' => $et_pb_column_parallax, 'et_pb_columns_counter' => 0, 'et_pb_column_paddings' => $et_pb_column_paddings, 'et_pb_column_paddings_mobile' => $et_pb_column_paddings_mobile, 'et_pb_column_css' => $et_pb_column_css, ); $current_row_position = $et_pb_rendering_column_content ? 'internal_row' : 'regular_row'; $et_pb_all_column_settings[ $current_row_position ] = $internal_columns_settings_array; $et_pb_rendering_specialty_section = true; if ( $et_pb_rendering_column_content ) { $et_pb_rendering_column_content_row = true; } } else { $et_pb_rendering_specialty_section = false; } $background_video = $this->video_background(); $parallax_image = $this->get_parallax_image_background(); // Background Color. $background_color_values = array( 'desktop' => 'rgba(255,255,255,0)' !== $processed_background_color ? esc_html( $processed_background_color ) : '', 'tablet' => 'rgba(255,255,255,0)' !== $processed_background_color_tablet ? esc_html( $processed_background_color_tablet ) : '', 'phone' => 'rgba(255,255,255,0)' !== $processed_background_color_phone ? esc_html( $processed_background_color_phone ) : '', ); et_pb_responsive_options()->generate_responsive_css( $background_color_values, '%%order_class%%.et_pb_section', 'background-color', $function_name, ' !important;', 'color' ); // Background hover styles. $hover_background_color = $hover->get_value( 'background_color', $this->props, $background_color ); $hover_background_selector = '%%order_class%%.et_pb_section:hover'; if ( $hover_background_color !== $background_color ) { $el_style = array( 'selector' => $hover_background_selector, 'declaration' => sprintf( 'background-color:%s !important;', esc_attr( $hover_background_color ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } // Background sticky styles. $sticky_background_color = $sticky->get_value( 'background_color', $this->props, $background_color ); if ( $sticky_background_color !== $background_color ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%.et_pb_section', $this->is_sticky_module ), 'declaration' => sprintf( 'background-color:%s !important;', esc_attr( $sticky_background_color ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); // Add hover style in sticky state. if ( $hover_background_color !== $background_color ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( $hover_background_selector, $this->is_sticky_module ), 'declaration' => sprintf( 'background-color:%s !important;', esc_attr( $hover_background_color ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } // Transparent is default for Builder Plugin, but not for theme. $is_transparent_background = $this->is_transparent_background( $background_color ); $is_transparent_background_tablet = $this->is_transparent_background( $background_color_tablet ); $is_transparent_background_phone = $this->is_transparent_background( $background_color_phone ); $is_background_color = ( '' !== $background_color && ! $is_transparent_background ) || ( '' !== $background_color_tablet && ! $is_transparent_background_tablet ) || ( '' !== $background_color_phone && ! $is_transparent_background_phone ); if ( ! empty( $background_video ) || $is_background_color || '' !== $background_image ) { $this->add_classname( 'et_pb_with_background' ); } // Background UI. if ( 'on' === $parallax ) { $this->add_classname( 'et_pb_section_parallax' ); } // CSS Filters. $this->add_classname( $this->generate_css_filters( $function_name ) ); if ( 'on' === $inner_shadow && ! ( '' !== $background_image && 'on' === $parallax && 'off' === $parallax_method ) ) { $this->add_classname( 'et_pb_inner_shadow' ); } if ( 'on' === $fullwidth ) { $this->add_classname( 'et_pb_fullwidth_section' ); } if ( 'on' === $specialty ) { $this->add_classname( 'et_section_specialty' ); } else { $this->add_classname( 'et_section_regular' ); } if ( $is_transparent_background || $is_transparent_background_tablet || $is_transparent_background_phone ) { $this->add_classname( 'et_section_transparent' ); } // Setup for SVG. $bottom = ''; $top = ''; $divider = ET_Builder_Module_Fields_Factory::get( 'Divider' ); // pass section number for background color usage. $divider->count = $this->render_count(); // Check if animation is enabled. $animation_enabled = $this->_features_manager->get( 'section_divider_enabled', function() { return $this->is_section_divider_enabled(); } ); if ( $animation_enabled ) { // Divider Placement. foreach ( array( 'bottom', 'top' ) as $placement ) { // Divider Responsive. foreach ( array( 'desktop', 'tablet', 'phone' ) as $device ) { // Ensure responsive settings for style is active on tablet and phone. $is_desktop = 'desktop' === $device; $is_responsive_style = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$placement}_divider_style" ); // Get all responsive values if it's exist and not empty. $values = array(); if ( ! $is_desktop ) { $values = et_pb_responsive_options()->get_any_responsive_values( $this->props, array( "{$placement}_divider_color" => '', "{$placement}_divider_height" => '', "{$placement}_divider_repeat" => '', "{$placement}_divider_flip" => '', "{$placement}_divider_arrangement" => '', ), false, $device ); } // Get Divider Style. $divider_style = $is_desktop || ! empty( $values ) ? et_pb_responsive_options()->get_any_value( $this->props, "{$placement}_divider_style" ) : ''; if ( ! $is_desktop && $is_responsive_style ) { $divider_style = et_pb_responsive_options()->get_any_value( $this->props, "{$placement}_divider_style", '', true, $device ); } // Check if style is not default. if ( '' !== $divider_style ) { // get an svg for using in ::before. $breakpoint = ! $is_desktop ? $device : ''; $divider->process_svg( $placement, $this->props, $breakpoint, $values ); // Get the placeholder for the bottom/top. if ( 'bottom' === $placement && '' === $bottom ) { $bottom = $divider->get_svg( 'bottom' ); } elseif ( 'top' === $placement && '' === $top ) { $top = $divider->get_svg( 'top' ); } // add a corresponding class. $this->add_classname( $divider->classes ); } } } } // Position Options. if ( $multi_view->has_value( 'positioning', 'absolute' ) ) { $this->add_classname( 'et_pb_section--absolute' ); } if ( $multi_view->has_value( 'positioning', 'fixed' ) ) { $this->add_classname( 'et_pb_section--fixed' ); } $muti_view_attributes = $multi_view->render_attrs( array( 'classes' => array( 'et_pb_section--absolute' => array( 'positioning' => 'absolute', ), 'et_pb_section--fixed' => array( 'positioning' => 'fixed', ), ), ) ); // Remove automatically added classnames. $this->remove_classname( 'et_pb_module' ); if ( self::contains( $content, array( 'et_pb_menu', 'et_pb_fullwidth_menu' ) ) ) { $this->add_classname( 'et_pb_section--with-menu' ); } // Save module classes into variable BEFORE processing the content with `do_shortcode()` // Otherwise order classes messed up with internal sections if exist. $module_classes = $this->module_classname( $function_name ); $output = sprintf( '<div%4$s class="%3$s"%8$s %11$s> %9$s %7$s %2$s %12$s %13$s %5$s %1$s %6$s %10$s </div>', do_shortcode( et_pb_fix_shortcodes( $content ) ), // 1 $background_video, // 2 $module_classes, // 3 $this->module_id(), // 4 ( 'on' === $specialty ? sprintf( '<div class="et_pb_row%1$s"%2$s>', $gutter_class, et_core_esc_previously( $gutter_hover_data ) ) : '' ), // 5 ( 'on' === $specialty ? '</div>' : '' ), // 6 $parallax_image, // 7 $this->get_module_data_attributes(), // 8 et_core_esc_previously( $top ), // 9 et_core_esc_previously( $bottom ), // 10, et_core_esc_previously( $muti_view_attributes ), // 11 et_core_esc_previously( $this->background_pattern() ), // 12 et_core_esc_previously( $this->background_mask() ) // 13 ); if ( 'on' === $specialty ) { // reset the global column settings to make sure they are not affected by internal content. $et_pb_all_column_settings = $et_pb_all_column_settings_backup; if ( $et_pb_rendering_column_content_row ) { $et_pb_rendering_column_content_row = false; } } return $output; } /** * Process box shadow CSS styles of section. * * @since 4.10.0 */ public function is_section_divider_enabled() { // Divider Responsive. foreach ( array( 'desktop', 'tablet', 'phone' ) as $device ) { // Get Divider Style. $divider_style_enabled = et_pb_responsive_options()->get_any_value( $this->props, 'top_divider_style', false ) || et_pb_responsive_options()->get_any_value( $this->props, 'bottom_divider_style', false ) || et_pb_responsive_options()->get_any_value( $this->props, 'top_divider_style', false, true, $device ) || et_pb_responsive_options()->get_any_value( $this->props, 'bottom_divider_style', false, true, $device ); return $divider_style_enabled; } return false; } /** * Process box shadow CSS styles of section. * * @param string $function_name Module slug. */ public function process_box_shadow( $function_name ) { /** * Instance of BoxShadow module field. @var ET_Builder_Module_Field_BoxShadow $box_shadow */ $box_shadow = ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); $style = $box_shadow->get_value( $this->props ); $hover_style = $box_shadow->get_value( $this->props, array( 'hover' => true ) ); if ( ! empty( $style ) && 'none' !== $style && false === strpos( $style, 'inset' ) ) { // Make section z-index higher if it has outer box shadow #4762. $el_style = array( 'selector' => '%%order_class%%', 'declaration' => 'z-index: 10;', ); self::set_style( $function_name, $el_style ); } if ( ! empty( $hover_style ) && 'none' !== $hover_style && false === strpos( $hover_style, 'inset' ) ) { // Make section z-index higher if it has outer box shadow #4762. $el_style = array( 'selector' => '%%order_class%%:hover', 'declaration' => 'z-index: 10;', ); self::set_style( $function_name, $el_style ); } parent::process_box_shadow( $function_name ); } /** * Box shadow back compatibility. * * @param string $function_name Module slug. * * @return string */ private function _keep_box_shadow_compatibility( $function_name ) { /** * Instance of box shadow field factory. @var ET_Builder_Module_Field_BoxShadow $box_shadow */ $box_shadow = ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); $utils = ET_Core_Data_Utils::instance(); $atts = $this->props; $style = $box_shadow->get_value( $atts ); if ( ! empty( $style ) && ! is_admin() && version_compare( $utils->array_get( $atts, '_builder_version', '3.0.93' ), '3.0.94', 'lt' ) && ! $box_shadow->is_inset( $box_shadow->get_value( $atts ) ) ) { $class = '.' . self::get_module_order_class( $function_name ); return sprintf( '<style type="text/css">%1$s</style>', sprintf( '%1$s { z-index: 11; %2$s }', esc_html( $class ), esc_html( $style ) ) ); } return ''; } } new ET_Builder_Section(); /** * Class used to process row element(module). * * @since 4.6.2 */ class ET_Builder_Row extends ET_Builder_Structure_Element { /** * Sets up the element's properties. */ public function init() { $this->name = esc_html__( 'Row', 'et_builder' ); $this->plural = esc_html__( 'Rows', 'et_builder' ); $this->slug = 'et_pb_row'; $this->vb_support = 'on'; $this->child_slug = 'et_pb_column'; $this->child_item_text = esc_html__( 'Column', 'et_builder' ); $this->advanced_fields = array( 'background' => array( 'use_background_color' => true, 'use_background_image' => true, 'use_background_color_gradient' => true, 'use_background_video' => true, 'use_background_pattern' => true, 'use_background_mask' => true, 'options' => array( 'background_color' => array( 'default' => '', 'hover' => 'tabs', ), 'allow_player_pause' => array( 'default_on_front' => 'off', ), 'parallax' => array( 'default_on_front' => 'off', ), 'parallax_method' => array( 'default_on_front' => 'on', ), ), ), 'max_width' => array( 'css' => array( 'module_alignment' => '%%order_class%%.et_pb_row', ), 'options' => array( 'width' => array( 'default' => '80%', ), 'max_width' => array( 'default' => '1080px', 'range_settings' => array( 'min' => 0, 'max' => 2560, 'step' => 1, ), ), 'module_alignment' => array( 'label' => esc_html__( 'Row Alignment', 'et_builder' ), 'mobile_options' => true, 'description' => esc_html__( 'Rows can be aligned to the left, right or center. By default, rows are centered within their parent section.', 'et_builder' ), 'default_sticky' => 'center', ), ), 'toggle_slug' => 'width', 'toggle_title' => esc_html__( 'Alignment', 'et_builder' ), 'toggle_priority' => 50, ), 'margin_padding' => array( 'css' => array( 'main' => '%%order_class%%.et_pb_row', 'important' => 'all', ), ), 'fonts' => false, 'text' => false, 'button' => false, 'position_fields' => array( 'default' => 'relative', ), 'z_index' => array( 'important' => true, ), ); $this->settings_modal_toggles = array( 'general' => array( 'toggles' => array( 'column_structure' => array( 'title' => esc_html__( 'Column Structure', 'et_builder' ), 'priority' => 1, 'always_open' => true, ), ), ), 'advanced' => array( 'toggles' => array( 'width' => array( 'title' => et_builder_i18n( 'Sizing' ), 'priority' => 65, ), ), ), ); $this->help_videos = array( array( 'id' => 'R9ds7bEaHE8', 'name' => esc_html__( 'An introduction to Rows', 'et_builder' ), ), ); } /** * Get the settings fields data for this element. * * @return array|array[] */ public function get_fields() { $fields = array( 'column_structure' => array( 'label' => esc_html__( 'Column Structure', 'et_builder' ), 'description' => esc_html__( 'Here you can choose the Column Structure for this Row.', 'et_builder' ), 'type' => 'column-structure', 'default' => '4_4', 'options' => array( '4_4' => et_pb_get_column_svg( '4_4' ), '1_2,1_2' => et_pb_get_column_svg( '1_2,1_2' ), '1_3,1_3,1_3' => et_pb_get_column_svg( '1_3,1_3,1_3' ), '1_4,1_4,1_4,1_4' => et_pb_get_column_svg( '1_4,1_4,1_4,1_4' ), '1_4,1_4,1_4,1_4' => et_pb_get_column_svg( '1_4,1_4,1_4,1_4' ), '1_5,1_5,1_5,1_5,1_5' => et_pb_get_column_svg( '1_5,1_5,1_5,1_5,1_5' ), '1_6,1_6,1_6,1_6,1_6,1_6' => et_pb_get_column_svg( '1_6,1_6,1_6,1_6,1_6,1_6' ), '2_5,3_5' => et_pb_get_column_svg( '2_5,3_5' ), '3_5,2_5' => et_pb_get_column_svg( '3_5,2_5' ), '1_3,2_3' => et_pb_get_column_svg( '1_3,2_3' ), '2_3,1_3' => et_pb_get_column_svg( '2_3,1_3' ), '1_4,3_4' => et_pb_get_column_svg( '1_4,3_4' ), '3_4,1_4' => et_pb_get_column_svg( '3_4,1_4' ), '1_4,1_2,1_4' => et_pb_get_column_svg( '1_4,1_2,1_4' ), '1_5,3_5,1_5' => et_pb_get_column_svg( '1_5,3_5,1_5' ), '1_4,1_4,1_2' => et_pb_get_column_svg( '1_4,1_4,1_2' ), '1_2,1_4,1_4' => et_pb_get_column_svg( '1_2,1_4,1_4' ), '1_5,1_5,3_5' => et_pb_get_column_svg( '1_5,1_5,3_5' ), '3_5,1_5,1_5' => et_pb_get_column_svg( '3_5,1_5,1_5' ), '1_6,1_6,1_6,1_2' => et_pb_get_column_svg( '1_6,1_6,1_6,1_2' ), '1_2,1_6,1_6,1_6' => et_pb_get_column_svg( '1_2,1_6,1_6,1_6' ), ), 'toggle_slug' => 'column_structure', ), 'use_custom_gutter' => array( 'label' => esc_html__( 'Use Custom Gutter Width', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'affects' => array( 'gutter_width', ), 'description' => esc_html__( 'Enable this option to define custom gutter width for this row.', 'et_builder' ), 'tab_slug' => 'advanced', 'toggle_slug' => 'width', ), 'gutter_width' => array( 'label' => esc_html__( 'Gutter Width', 'et_builder' ), 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 1, 'max' => 4, 'step' => 1, 'min_limit' => 1, 'max_limit' => 4, ), 'depends_show_if' => 'on', 'description' => esc_html__( 'Adjust the spacing between each column in this row.', 'et_builder' ), 'validate_unit' => false, 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'width', 'default_on_front' => (string) et_get_option( 'gutter_width', '3' ), 'hover' => 'tabs', ), 'padding_mobile' => array( 'label' => esc_html__( 'Keep Custom Padding on Mobile', 'et_builder' ), 'type' => 'skip', // Remaining attribute for backward compatibility. 'tab_slug' => 'advanced', 'toggle_slug' => 'margin_padding', 'default_on_front' => '', ), 'make_equal' => array( 'label' => esc_html__( 'Equalize Column Heights', 'et_builder' ), 'description' => esc_html__( 'Equalizing column heights will force all columns to assume the height of the tallest column in the row. All columns will have the same height, keeping their appearance uniform.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'tab_slug' => 'advanced', 'toggle_slug' => 'width', ), 'column_padding_mobile' => array( 'label' => esc_html__( 'Keep Column Padding on Mobile', 'et_builder' ), 'tab_slug' => 'advanced', 'type' => 'skip', // Remaining attribute for backward compatibility. 'default_on_front' => '', ), 'custom_padding_last_edited' => array( 'type' => 'skip', 'tab_slug' => 'advanced', ), '__video_background' => array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Module_Helper_ResponsiveOptions', 'get_video_background' ), 'computed_depends_on' => array( 'background_video_mp4', 'background_video_webm', 'background_video_width', 'background_video_height', ), 'computed_minimum' => array( 'background_video_mp4', 'background_video_webm', ), ), ); $column_fields = $this->get_column_fields( 6, array( 'background_color' => array(), 'bg_img' => array(), 'padding_top_bottom_link' => array(), 'padding_left_right_link' => array(), 'parallax' => array( 'default_on_front' => 'off', ), 'parallax_method' => array( 'default_on_front' => 'on', ), 'background_size' => array(), 'background_image_width' => array(), 'background_image_height' => array(), 'background_position' => array(), 'background_horizontal_offset' => array(), 'background_vertical_offset' => array(), 'background_repeat' => array(), 'background_blend' => array(), 'use_background_color_gradient' => array(), 'background_color_gradient_repeat' => array(), 'background_color_gradient_type' => array(), 'background_color_gradient_direction' => array(), 'background_color_gradient_direction_radial' => array(), 'background_color_gradient_stops' => array(), 'background_color_gradient_unit' => array(), 'background_color_gradient_overlays_image' => array(), 'background_color_gradient_start' => array(), 'background_color_gradient_end' => array(), 'background_color_gradient_start_position' => array(), 'background_color_gradient_end_position' => array(), 'background_color_gradient_overlays_image' => array(), 'background_enable_pattern_style' => array(), 'background_pattern_style' => array(), 'background_pattern_color' => array(), 'background_pattern_transform' => array(), 'background_pattern_size' => array(), 'background_pattern_width' => array(), 'background_pattern_height' => array(), 'background_pattern_repeat' => array(), 'background_pattern_repeat_origin' => array(), 'background_pattern_horizontal_offset' => array(), 'background_pattern_vertical_offset' => array(), 'background_pattern_blend_mode' => array(), 'background_enable_mask_style' => array(), 'background_mask_style' => array(), 'background_mask_color' => array(), 'background_mask_transform' => array(), 'background_mask_aspect_ratio' => array(), 'background_mask_size' => array(), 'background_mask_width' => array(), 'background_mask_height' => array(), 'background_mask_position' => array(), 'background_mask_horizontal_offset' => array(), 'background_mask_vertical_offset' => array(), 'background_mask_blend_mode' => array(), 'background_video_mp4' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_webm' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_width' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_height' => array( 'computed_affects' => array( '__video_background', ), ), 'allow_player_pause' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_pause_outside_viewport' => array( 'computed_affects' => array( '__video_background', ), ), '__video_background' => array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Column', 'get_column_video_background', ), 'computed_depends_on' => array( 'background_video_mp4', 'background_video_webm', 'background_video_width', 'background_video_height', ), 'computed_minimum' => array( 'background_video_mp4', 'background_video_webm', ), ), 'padding_top' => array( 'tab_slug' => 'advanced' ), 'padding_right' => array( 'tab_slug' => 'advanced' ), 'padding_bottom' => array( 'tab_slug' => 'advanced' ), 'padding_left' => array( 'tab_slug' => 'advanced' ), 'padding_top_bottom_link' => array( 'tab_slug' => 'advanced' ), 'padding_left_right_link' => array( 'tab_slug' => 'advanced' ), 'padding_%column_index%_tablet' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_phone' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_last_edited' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'module_id' => array( 'tab_slug' => 'custom_css' ), 'module_class' => array( 'tab_slug' => 'custom_css' ), 'custom_css_before' => array( 'tab_slug' => 'custom_css' ), 'custom_css_main' => array( 'tab_slug' => 'custom_css' ), 'custom_css_after' => array( 'tab_slug' => 'custom_css' ), ) ); return array_merge( $fields, $column_fields ); } /** * Get CSS fields transition of row. * * @since 3.23 Add form field options group and background image on the fields list. */ public function get_transition_fields_css_props() { $fields = parent::get_transition_fields_css_props(); for ( $i = 1; $i <= 6; $i ++ ) { $selector = "%%order_class%% > .et_pb_column:nth-child({$i})"; $fields[ "background_color_{$i}" ] = array( 'background-color' => $selector ); $fields[ "padding_{$i}" ] = array( 'padding' => $selector ); } return $fields; } /** * Generates the structure module's HTML output based on {@see ET_Builder_Row::$props}. * * @param array $atts List of unprocessed attributes. * @param mixed $content Content being processed. * @param string $function_name Slug of module that is used for rendering output. * * @return string The module's HTML output. */ public function render( $atts, $content, $function_name ) { $custom_padding = $this->props['custom_padding']; $custom_padding_tablet = $this->props['custom_padding_tablet']; $custom_padding_phone = $this->props['custom_padding_phone']; $custom_padding_last_edited = $this->props['custom_padding_last_edited']; $column_padding_mobile = $this->props['column_padding_mobile']; $make_equal = $this->props['make_equal']; $padding_mobile = $this->props['padding_mobile']; $gutter_width = $this->props['gutter_width']; $gutter_width_hover = $this->get_hover_value( 'gutter_width' ); $global_module = $this->props['global_module']; $use_custom_gutter = $this->props['use_custom_gutter']; $hover = et_pb_hover_options(); global $et_pb_all_column_settings, $et_pb_rendering_column_content, $et_pb_rendering_column_content_row; $et_pb_all_column_settings = ! isset( $et_pb_all_column_settings ) ? array() : $et_pb_all_column_settings; $et_pb_all_column_settings_backup = $et_pb_all_column_settings; $keep_column_padding_mobile = $column_padding_mobile; if ( '' !== $global_module ) { $global_content = et_pb_load_global_module( $global_module, $function_name ); if ( '' !== $global_content ) { return do_shortcode( et_pb_fix_shortcodes( wpautop( $global_content ) ) ); } } $custom_padding_responsive_active = et_pb_get_responsive_status( $custom_padding_last_edited ); $padding_mobile_values = $custom_padding_responsive_active ? array( 'tablet' => explode( '|', $custom_padding_tablet ), 'phone' => explode( '|', $custom_padding_phone ), ) : array( 'tablet' => false, 'phone' => false, ); $internal_columns_settings_array = array( 'keep_column_padding_mobile' => $keep_column_padding_mobile, 'et_pb_columns_counter' => 0, ); $current_row_position = $et_pb_rendering_column_content ? 'internal_row' : 'regular_row'; $et_pb_all_column_settings[ $current_row_position ] = $internal_columns_settings_array; if ( $et_pb_rendering_column_content ) { $et_pb_rendering_column_content_row = true; } if ( 'on' === $make_equal ) { $this->add_classname( 'et_pb_equal_columns' ); } $gutter_hover_data = ''; if ( 'on' === $use_custom_gutter && '' !== $gutter_width ) { $gutter_width = '0' === $gutter_width ? '1' : $gutter_width; // set the gutter width to 1 if 0 entered by user. $this->add_classname( 'et_pb_gutters' . $gutter_width ); if ( et_builder_is_hover_enabled( 'gutter_width', $this->props ) && ! empty( $gutter_width_hover ) && $gutter_width !== $gutter_width_hover ) { $this->add_classname( 'et_pb_gutter_hover' ); $gutter_hover_data = sprintf( ' data-original_gutter="%1$s" data-hover_gutter="%2$s"', esc_attr( $gutter_width ), esc_attr( $gutter_width_hover ) ); } } $padding_values = explode( '|', $custom_padding ); if ( ! empty( $padding_values ) ) { // old version of Rows support only top and bottom padding, so we need to handle it along with the full padding in the recent version. if ( 2 === count( $padding_values ) ) { $padding_settings = array( 'top' => isset( $padding_values[0] ) ? $padding_values[0] : '', 'bottom' => isset( $padding_values[1] ) ? $padding_values[1] : '', ); } else { $padding_settings = array( 'top' => isset( $padding_values[0] ) ? $padding_values[0] : '', 'right' => isset( $padding_values[1] ) ? $padding_values[1] : '', 'bottom' => isset( $padding_values[2] ) ? $padding_values[2] : '', 'left' => isset( $padding_values[3] ) ? $padding_values[3] : '', ); } foreach ( $padding_settings as $padding_side => $value ) { if ( '' !== $value ) { $element_style = array( 'selector' => '%%order_class%%.et_pb_row', 'declaration' => sprintf( 'padding-%1$s: %2$s;', esc_html( $padding_side ), esc_html( $value ) ), ); // Backward compatibility. Keep Padding on Mobile is deprecated in favour of responsive inputs mechanism for custom padding // To ensure that it is compatibility with previous version of Divi, this option is now only used as last resort if no // responsive padding value is found, and padding_mobile value is saved (which is set to off by default). if ( in_array( $padding_mobile, array( 'on', 'off' ), true ) && 'on' !== $padding_mobile && ! $custom_padding_responsive_active ) { $element_style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } ET_Builder_Element::set_style( $function_name, $element_style ); } } } if ( ! empty( $padding_mobile_values['tablet'] ) || ! empty( $padding_values['phone'] ) ) { $padding_mobile_values_processed = array(); foreach ( array( 'tablet', 'phone' ) as $device ) { if ( empty( $padding_mobile_values[ $device ] ) ) { continue; } $padding_mobile_values_processed[ $device ] = array( 'padding-top' => isset( $padding_mobile_values[ $device ][0] ) ? $padding_mobile_values[ $device ][0] : '', 'padding-right' => isset( $padding_mobile_values[ $device ][1] ) ? $padding_mobile_values[ $device ][1] : '', 'padding-bottom' => isset( $padding_mobile_values[ $device ][2] ) ? $padding_mobile_values[ $device ][2] : '', 'padding-left' => isset( $padding_mobile_values[ $device ][3] ) ? $padding_mobile_values[ $device ][3] : '', ); } if ( ! empty( $padding_mobile_values_processed ) ) { et_pb_responsive_options()->generate_responsive_css( $padding_mobile_values_processed, '%%order_class%%.et_pb_row', '', $function_name, ' !important; ' ); } } $parallax_image = $this->get_parallax_image_background(); $background_video = $this->video_background(); // CSS Filters. $this->add_classname( $this->generate_css_filters( $function_name ) ); // Remove automatically added classnames. $this->remove_classname( 'et_pb_module' ); if ( self::contains( $content, array( 'et_pb_menu', 'et_pb_fullwidth_menu' ) ) ) { $this->add_classname( 'et_pb_row--with-menu' ); } // Save module classes into variable BEFORE processing the content with `do_shortcode()` // Otherwise order classes messed up with internal rows if exist. $module_classes = $this->module_classname( $function_name ); // Inner content shortcode parsing has to be done after all classname addition/removal. $inner_content = do_shortcode( et_pb_fix_shortcodes( $content ) ); $content_dependent_classname = '' === trim( $inner_content ) ? ' et_pb_row_empty' : ''; // reset the global column settings to make sure they are not affected by internal content // This has to be done after inner content's shortcode being parsed. $et_pb_all_column_settings = $et_pb_all_column_settings_backup; // Reset row's column content flag. if ( $et_pb_rendering_column_content_row ) { $et_pb_rendering_column_content_row = false; } $output = sprintf( '<div%4$s class="%2$s%7$s"%8$s> %1$s %6$s %5$s %9$s %10$s </div>', $inner_content, $module_classes, esc_html( $function_name ), $this->module_id(), $background_video, $parallax_image, $content_dependent_classname, et_core_esc_previously( $gutter_hover_data ), et_core_esc_previously( $this->background_pattern() ), // 9 et_core_esc_previously( $this->background_mask() ) // 10 ); return $output; } } new ET_Builder_Row(); /** * Class used to process inner row element(module). * * @since 4.6.2 */ class ET_Builder_Row_Inner extends ET_Builder_Structure_Element { /** * Sets up the element's properties. */ public function init() { $this->name = esc_html__( 'Row', 'et_builder' ); $this->plural = esc_html__( 'Rows', 'et_builder' ); $this->slug = 'et_pb_row_inner'; $this->vb_support = 'on'; $this->child_slug = 'et_pb_column_inner'; $this->child_item_text = esc_html__( 'Column', 'et_builder' ); $this->advanced_fields = array( 'background' => array( 'use_background_color' => true, 'use_background_image' => true, 'use_background_color_gradient' => true, 'use_background_video' => true, 'use_background_pattern' => true, 'use_background_mask' => true, ), 'margin_padding' => array( 'css' => array( 'main' => '%%order_class%%.et_pb_row_inner', 'important' => 'all', ), ), 'max_width' => array( 'css' => array( 'main' => '%%order_class%%.et_pb_row_inner', ), 'options' => array( 'module_alignment' => array( 'label' => esc_html__( 'Row Alignment', 'et_builder' ), 'description' => esc_html__( 'Rows can be aligned to the left, right or center. By default, rows are centered within their parent section.', 'et_builder' ), 'default_sticky' => 'center', ), ), ), 'fonts' => false, 'text' => false, 'button' => false, 'position_fields' => array( 'default' => 'relative', ), ); $this->settings_modal_toggles = array( 'general' => array( 'toggles' => array( 'column_structure' => array( 'title' => esc_html__( 'Column Structure', 'et_builder' ), 'priority' => 1, 'always_open' => true, ), ), ), 'advanced' => array( 'toggles' => array( 'width' => array( 'title' => et_builder_i18n( 'Sizing' ), 'priority' => 65, ), ), ), ); $this->help_videos = array( array( 'id' => 'R9ds7bEaHE8', 'name' => esc_html__( 'An introduction to Rows', 'et_builder' ), ), ); } /** * Get the settings fields data for this element. * * @return array|array[] */ public function get_fields() { $fields = array( 'column_structure' => array( 'label' => esc_html__( 'Column Structure', 'et_builder' ), 'description' => esc_html__( 'Here you can choose the Column Structure for this Row.', 'et_builder' ), 'type' => 'column-structure', 'default' => '4_4', 'options' => array( '4_4' => et_pb_get_column_svg( '4_4' ), '1_2,1_2' => et_pb_get_column_svg( '1_2,1_2' ), '1_3,1_3,1_3' => et_pb_get_column_svg( '1_3,1_3,1_3' ), '1_4,1_4,1_4,1_4' => et_pb_get_column_svg( '1_4,1_4,1_4,1_4' ), ), 'toggle_slug' => 'column_structure', ), 'padding_mobile' => array( 'label' => esc_html__( 'Keep Custom Padding on Mobile', 'et_builder' ), 'type' => 'skip', // Remaining attribute for backward compatibility. 'tab_slug' => 'advanced', 'toggle_slug' => 'margin_padding', ), 'use_custom_gutter' => array( 'label' => esc_html__( 'Use Custom Gutter Width', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'affects' => array( 'gutter_width', ), 'description' => esc_html__( 'Enable this option to define custom gutter width for this row.', 'et_builder' ), 'tab_slug' => 'advanced', 'toggle_slug' => 'width', ), 'gutter_width' => array( 'label' => esc_html__( 'Gutter Width', 'et_builder' ), 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 1, 'max' => 4, 'step' => 1, 'min_limit' => 1, 'max_limit' => 4, ), 'depends_show_if' => 'on', 'description' => esc_html__( 'Adjust the spacing between each column in this row.', 'et_builder' ), 'validate_unit' => false, 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'width', 'default_on_front' => (string) et_get_option( 'gutter_width', '3' ), 'hover' => 'tabs', ), 'make_equal' => array( 'label' => esc_html__( 'Equalize Column Heights', 'et_builder' ), 'description' => esc_html__( 'Equalizing column heights will force all columns to assume the height of the tallest column in the row. All columns will have the same height, keeping their appearance uniform.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'layout', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'tab_slug' => 'advanced', 'toggle_slug' => 'width', ), 'column_padding_mobile' => array( 'label' => esc_html__( 'Keep Column Padding on Mobile', 'et_builder' ), 'tab_slug' => 'advanced', 'type' => 'skip', // Remaining attribute for backward compatibility. ), 'custom_padding_last_edited' => array( 'type' => 'skip', 'tab_slug' => 'advanced', ), ); $column_fields = $this->get_column_fields( 4, array( 'background_color' => array(), 'bg_img' => array(), 'padding_top_bottom_link' => array(), 'padding_left_right_link' => array(), 'parallax' => array( 'default_on_front' => 'off', ), 'parallax_method' => array( 'default_on_front' => 'on', ), 'background_size' => array(), 'background_image_width' => array(), 'background_image_height' => array(), 'background_position' => array(), 'background_horizontal_offset' => array(), 'background_vertical_offset' => array(), 'background_repeat' => array(), 'background_blend' => array(), 'use_background_color_gradient' => array(), 'background_color_gradient_repeat' => array(), 'background_color_gradient_type' => array(), 'background_color_gradient_direction' => array(), 'background_color_gradient_direction_radial' => array(), 'background_color_gradient_stops' => array(), 'background_color_gradient_unit' => array(), 'background_color_gradient_overlays_image' => array(), 'background_color_gradient_start' => array(), 'background_color_gradient_end' => array(), 'background_color_gradient_start_position' => array(), 'background_color_gradient_end_position' => array(), 'background_color_gradient_overlays_image' => array(), 'background_enable_pattern_style' => array(), 'background_pattern_style' => array(), 'background_pattern_color' => array(), 'background_pattern_transform' => array(), 'background_pattern_size' => array(), 'background_pattern_width' => array(), 'background_pattern_height' => array(), 'background_pattern_repeat' => array(), 'background_pattern_repeat_origin' => array(), 'background_pattern_horizontal_offset' => array(), 'background_pattern_vertical_offset' => array(), 'background_pattern_blend_mode' => array(), 'background_enable_mask_style' => array(), 'background_mask_style' => array(), 'background_mask_color' => array(), 'background_mask_transform' => array(), 'background_mask_aspect_ratio' => array(), 'background_mask_size' => array(), 'background_mask_width' => array(), 'background_mask_height' => array(), 'background_mask_position' => array(), 'background_mask_horizontal_offset' => array(), 'background_mask_vertical_offset' => array(), 'background_mask_blend_mode' => array(), 'background_video_mp4' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_webm' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_width' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_height' => array( 'computed_affects' => array( '__video_background', ), ), 'allow_player_pause' => array( 'computed_affects' => array( '__video_background', ), ), 'background_video_pause_outside_viewport' => array( 'computed_affects' => array( '__video_background', ), ), '__video_background' => array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Column', 'get_column_video_background', ), 'computed_depends_on' => array( 'background_video_mp4', 'background_video_webm', 'background_video_width', 'background_video_height', ), 'computed_minimum' => array( 'background_video_mp4', 'background_video_webm', ), ), 'padding_top' => array( 'tab_slug' => 'advanced' ), 'padding_right' => array( 'tab_slug' => 'advanced' ), 'padding_bottom' => array( 'tab_slug' => 'advanced' ), 'padding_left' => array( 'tab_slug' => 'advanced' ), 'padding_top_bottom_link' => array( 'tab_slug' => 'advanced' ), 'padding_left_right_link' => array( 'tab_slug' => 'advanced' ), 'padding_%column_index%_tablet' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_phone' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'padding_%column_index%_last_edited' => array( 'has_custom_index_location' => true, 'tab_slug' => 'advanced', ), 'module_id' => array( 'tab_slug' => 'custom_css' ), 'module_class' => array( 'tab_slug' => 'custom_css' ), 'custom_css_before' => array( 'tab_slug' => 'custom_css' ), 'custom_css_main' => array( 'tab_slug' => 'custom_css' ), 'custom_css_after' => array( 'tab_slug' => 'custom_css' ), ) ); return array_merge( $fields, $column_fields ); } /** * Get CSS fields transition of inner row. */ public function get_transition_fields_css_props() { $fields = parent::get_transition_fields_css_props(); for ( $i = 1; $i <= 6; $i ++ ) { $selector = "%%order_class%% > .et_pb_column:nth-child({$i})"; $fields[ "background_color_{$i}" ] = array( 'background-color' => $selector ); $fields[ "padding_{$i}" ] = array( 'padding' => $selector ); } return $fields; } /** * Generates the structure module's HTML output based on {@see ET_Builder_Row_Inner::$props}. * * @param array $atts List of unprocessed attributes. * @param mixed $content Content being processed. * @param string $function_name Slug of module that is used for rendering output. * * @return string The module's HTML output. */ public function render( $atts, $content, $function_name ) { $gutter_width = $this->props['gutter_width']; $gutter_width_hover = $this->get_hover_value( 'gutter_width' ); $make_equal = $this->props['make_equal']; $custom_padding = $this->props['custom_padding']; $padding_mobile = $this->props['padding_mobile']; $custom_padding_tablet = $this->props['custom_padding_tablet']; $custom_padding_phone = $this->props['custom_padding_phone']; $custom_padding_last_edited = $this->props['custom_padding_last_edited']; $column_padding_mobile = $this->props['column_padding_mobile']; $global_module = $this->props['global_module']; $use_custom_gutter = $this->props['use_custom_gutter']; $hover = et_pb_hover_options(); global $et_pb_all_column_settings_inner, $et_pb_rendering_column_content, $et_pb_rendering_column_content_row; $et_pb_all_column_settings_inner = ! isset( $et_pb_all_column_settings_inner ) ? array() : $et_pb_all_column_settings_inner; $et_pb_all_column_settings_backup = $et_pb_all_column_settings_inner; $keep_column_padding_mobile = $column_padding_mobile; if ( '' !== $global_module ) { $global_content = et_pb_load_global_module( $global_module, $function_name ); if ( '' !== $global_content ) { return do_shortcode( et_pb_fix_shortcodes( wpautop( $global_content ) ) ); } } $custom_padding_responsive_active = et_pb_get_responsive_status( $custom_padding_last_edited ); $padding_mobile_values = $custom_padding_responsive_active ? array( 'tablet' => explode( '|', $custom_padding_tablet ), 'phone' => explode( '|', $custom_padding_phone ), ) : array( 'tablet' => false, 'phone' => false, ); $et_pb_columns_inner_counter = 0; $padding_values = explode( '|', $custom_padding ); if ( ! empty( $padding_values ) ) { // old version of Rows support only top and bottom padding, so we need to handle it along with the full padding in the recent version. if ( 2 === count( $padding_values ) ) { $padding_settings = array( 'top' => isset( $padding_values[0] ) ? $padding_values[0] : '', 'bottom' => isset( $padding_values[1] ) ? $padding_values[1] : '', ); } else { $padding_settings = array( 'top' => isset( $padding_values[0] ) ? $padding_values[0] : '', 'right' => isset( $padding_values[1] ) ? $padding_values[1] : '', 'bottom' => isset( $padding_values[2] ) ? $padding_values[2] : '', 'left' => isset( $padding_values[3] ) ? $padding_values[3] : '', ); } foreach ( $padding_settings as $padding_side => $value ) { if ( '' !== $value ) { $element_style = array( 'selector' => '.et_pb_column %%order_class%%', 'declaration' => sprintf( 'padding-%1$s: %2$s;', esc_html( $padding_side ), esc_html( $value ) ), ); // Backward compatibility. Keep Padding on Mobile is deprecated in favour of responsive inputs mechanism for custom padding // To ensure that it is compatibility with previous version of Divi, this option is now only used as last resort if no // responsive padding value is found, and padding_mobile value is saved (which is set to off by default). if ( in_array( $padding_mobile, array( 'on', 'off' ), true ) && 'on' !== $padding_mobile && ! $custom_padding_responsive_active ) { $element_style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } ET_Builder_Element::set_style( $function_name, $element_style ); } } } if ( ! empty( $padding_mobile_values['tablet'] ) || ! empty( $padding_values['phone'] ) ) { $padding_mobile_values_processed = array(); foreach ( array( 'tablet', 'phone' ) as $device ) { if ( empty( $padding_mobile_values[ $device ] ) ) { continue; } $padding_mobile_values_processed[ $device ] = array( 'padding-top' => isset( $padding_mobile_values[ $device ][0] ) ? $padding_mobile_values[ $device ][0] : '', 'padding-right' => isset( $padding_mobile_values[ $device ][1] ) ? $padding_mobile_values[ $device ][1] : '', 'padding-bottom' => isset( $padding_mobile_values[ $device ][2] ) ? $padding_mobile_values[ $device ][2] : '', 'padding-left' => isset( $padding_mobile_values[ $device ][3] ) ? $padding_mobile_values[ $device ][3] : '', ); } if ( ! empty( $padding_mobile_values_processed ) ) { et_pb_responsive_options()->generate_responsive_css( $padding_mobile_values_processed, '.et_pb_column %%order_class%%', '', $function_name, ' !important; ' ); } } $internal_columns_settings_array = array( 'keep_column_padding_mobile' => $keep_column_padding_mobile, 'et_pb_columns_inner_counter' => $et_pb_columns_inner_counter, 'et_pb_columns_counter' => 0, ); $current_row_position = $et_pb_rendering_column_content ? 'internal_row' : 'regular_row'; $et_pb_all_column_settings_inner[ $current_row_position ] = $internal_columns_settings_array; if ( 'on' === $make_equal ) { $this->add_classname( 'et_pb_equal_columns' ); } $gutter_hover_data = ''; if ( 'on' === $use_custom_gutter && '' !== $gutter_width ) { $gutter_width = '0' === $gutter_width ? '1' : $gutter_width; // set the gutter to 1 if 0 entered by user. $this->add_classname( 'et_pb_gutters' . $gutter_width ); if ( et_builder_is_hover_enabled( 'gutter_width', $this->props ) && ! empty( $gutter_width_hover ) && $gutter_width !== $gutter_width_hover ) { $this->add_classname( 'et_pb_gutter_hover' ); $gutter_hover_data = sprintf( ' data-original_gutter="%1$s" data-hover_gutter="%2$s"', esc_attr( $gutter_width ), esc_attr( $gutter_width_hover ) ); } } $parallax_image = $this->get_parallax_image_background(); $background_video = $this->video_background(); // CSS Filters. $this->add_classname( $this->generate_css_filters( $function_name ) ); // Remove automatically added classnames. $this->remove_classname( 'et_pb_module' ); if ( self::contains( $content, array( 'et_pb_menu', 'et_pb_fullwidth_menu' ) ) ) { $this->add_classname( 'et_pb_row--with-menu' ); } // Save module classes into variable BEFORE processing the content with `do_shortcode()` // Otherwise order classes messed up with internal rows if exist. $module_classes = $this->module_classname( $function_name ); // Inner content shortcode parsing has to be done after all classname addition/removal. $inner_content = do_shortcode( et_pb_fix_shortcodes( $content ) ); $content_dependent_classname = '' === trim( $inner_content ) ? ' et_pb_row_empty' : ''; // reset the global column settings to make sure they are not affected by internal content. $et_pb_all_column_settings_inner = $et_pb_all_column_settings_backup; $output = sprintf( '<div%4$s class="%2$s%7$s"%8$s> %1$s %5$s %6$s %9$s %10$s </div>', $inner_content, $module_classes, esc_html( $function_name ), $this->module_id(), $parallax_image, $background_video, $content_dependent_classname, et_core_esc_previously( $gutter_hover_data ), et_core_esc_previously( $this->background_pattern() ), // 9 et_core_esc_previously( $this->background_mask() ) // 10 ); return $output; } } new ET_Builder_Row_Inner(); /** * Class used to process column element(module). * * @since 4.6.2 */ class ET_Builder_Column extends ET_Builder_Structure_Element { /** * Sets up the element's properties. */ public function init() { $this->name = esc_html__( 'Column', 'et_builder' ); $this->plural = esc_html__( 'Columns', 'et_builder' ); $this->slug = 'et_pb_column'; $this->additional_shortcode_slugs = array( 'et_pb_column_inner' ); $this->child_title_var = 'admin_label'; $this->advanced_setting_title_text = esc_html__( 'Column', 'et_builder' ); $this->vb_support = 'on'; $this->type = 'child'; $this->advanced_fields = array( 'max_width' => false, 'fonts' => false, 'text' => false, 'margin_padding' => array( 'use_margin' => false, ), 'z_index' => array( 'default' => '2', 'important' => true, ), 'position_fields' => array( 'default' => 'relative', ), ); $this->settings_modal_toggles = array( 'general' => array( 'toggles' => array( 'admin_label' => array( 'title' => et_builder_i18n( 'Admin Label' ), 'priority' => 99, ), ), ), ); $this->help_videos = array( array( 'id' => 'R9ds7bEaHE8', 'name' => esc_html__( 'An introduction to the Column module', 'et_builder' ), ), ); } /** * Get the settings fields data for this element. * * @return array|array[] */ public function get_fields() { $fields = array( 'type' => array( 'default_on_front' => '4_4', 'type' => 'skip', ), 'specialty_columns' => array( 'type' => 'skip', ), 'saved_specialty_column_type' => array( 'type' => 'skip', ), 'module_id' => array( 'label' => esc_html__( 'CSS ID', 'et_builder' ), 'description' => esc_html__( "Assign a unique CSS ID to the element which can be used to assign custom CSS styles from within your child theme or from within Divi's custom CSS inputs.", 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'classes', 'option_class' => 'et_pb_custom_css_regular', ), 'module_class' => array( 'label' => esc_html__( 'CSS Class', 'et_builder' ), 'description' => esc_html__( "Assign any number of CSS Classes to the element, separated by spaces, which can be used to assign custom CSS styles from within your child theme or from within Divi's custom CSS inputs.", 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'classes', 'option_class' => 'et_pb_custom_css_regular', ), 'admin_label' => array( 'label' => et_builder_i18n( 'Admin Label' ), 'type' => 'text', 'option_category' => 'configuration', 'description' => esc_html__( 'This will change the label of the module in the builder for easy identification.', 'et_builder' ), 'toggle_slug' => 'admin_label', ), ); return $fields; } /** * Generates the structure module's HTML output based on {@see ET_Builder_Column::$props}. * * @param array $atts List of unprocessed attributes. * @param mixed $content Content being processed. * @param string $function_name Slug of module that is used for rendering output. * * @return string The module's HTML output. */ public function render( $atts, $content, $function_name ) { $type = $this->props['type']; $specialty_columns = $this->props['specialty_columns']; $saved_specialty_column_type = $this->props['saved_specialty_column_type']; $custom_css_class = ''; global $et_pb_all_column_settings, $et_pb_all_column_settings_inner, $et_specialty_column_type, $et_pb_rendering_column_content, $et_pb_rendering_column_content_row, $et_pb_rendering_specialty_section, $et_pb_column_completion; $is_specialty_column = 'et_pb_column_inner' !== $function_name && $et_pb_rendering_specialty_section; $gobal_column_settings_holder = 'et_pb_column_inner' === $function_name ? $et_pb_all_column_settings_inner : $et_pb_all_column_settings; $current_row_position = $et_pb_rendering_column_content_row ? 'internal_row' : 'regular_row'; $array_index = self::$_->array_get( $gobal_column_settings_holder, "{$current_row_position}.et_pb_columns_counter", 0 ); $keep_column_padding_mobile = self::$_->array_get( $gobal_column_settings_holder, "{$current_row_position}.keep_column_padding_mobile", 'on' ); $sticky = et_pb_sticky_options(); $background_options = et_pb_background_options(); if ( $is_specialty_column ) { $et_specialty_column_type = $type; $backgrounds_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_backgrounds", array() ); $background_gradient = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_backgrounds_gradient.[{$array_index}]", '' ); $pattern_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_backgrounds_pattern.[{$array_index}]", array() ); $mask_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_backgrounds_mask.[{$array_index}]", array() ); $background_video = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_backgrounds_video.[{$array_index}]", '' ); $paddings_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_paddings", array() ); $paddings_mobile_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_paddings_mobile", array() ); $column_css_array = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_css", array() ); $column_parallax = self::$_->array_get( $et_pb_all_column_settings, "{$current_row_position}.et_pb_column_parallax", '' ); $background_color = isset( $backgrounds_array[ $array_index ]['color'] ) ? $backgrounds_array[ $array_index ]['color'] : ''; $background_img = isset( $backgrounds_array[ $array_index ]['image'] ) ? $backgrounds_array[ $array_index ]['image'] : ''; $background_size = isset( $backgrounds_array[ $array_index ]['image_size'] ) ? $backgrounds_array[ $array_index ]['image_size'] : ''; $background_image_width = isset( $backgrounds_array[ $array_index ]['image_width'] ) ? $backgrounds_array[ $array_index ]['image_width'] : ''; $background_image_height = isset( $backgrounds_array[ $array_index ]['image_height'] ) ? $backgrounds_array[ $array_index ]['image_height'] : ''; $background_position = isset( $backgrounds_array[ $array_index ]['image_position'] ) ? $backgrounds_array[ $array_index ]['image_position'] : ''; $background_horizontal_offset = isset( $backgrounds_array[ $array_index ]['image_horizontal_offset'] ) ? $backgrounds_array[ $array_index ]['image_horizontal_offset'] : ''; $background_vertical_offset = isset( $backgrounds_array[ $array_index ]['image_vertical_offset'] ) ? $backgrounds_array[ $array_index ]['image_vertical_offset'] : ''; $background_repeat = isset( $backgrounds_array[ $array_index ]['image_repeat'] ) ? $backgrounds_array[ $array_index ]['image_repeat'] : ''; $background_blend = isset( $backgrounds_array[ $array_index ]['image_blend'] ) ? $backgrounds_array[ $array_index ]['image_blend'] : ''; $background_gradient_overlays_image = isset( $background_gradient['overlays_image'] ) ? $background_gradient['overlays_image'] : ''; $background_color_hover = isset( $backgrounds_array[ $array_index ] ) ? self::$_->array_get( $backgrounds_array[ $array_index ], 'color_hover' ) : ''; $background_color_hover_enabled = isset( $backgrounds_array[ $array_index ] ) ? self::$_->array_get( $backgrounds_array[ $array_index ], 'color_hover_enabled' ) : ''; $background_color_sticky = isset( $backgrounds_array[ $array_index ] ) ? self::$_->array_get( $backgrounds_array[ $array_index ], 'color_sticky' ) : ''; $background_color_sticky_enabled = isset( $backgrounds_array[ $array_index ] ) ? self::$_->array_get( $backgrounds_array[ $array_index ], 'color_sticky_enabled' ) : ''; // Pattern. $enable_pattern_style_default = $background_options->get_attr_default( 'style', 'background_enable_pattern', $this->fields_unprocessed ); $pattern_style_name_default = $background_options->get_attr_default( 'style', 'background_pattern', $this->fields_unprocessed ); $pattern_color_default = $background_options->get_attr_default( 'color', 'background_pattern', $this->fields_unprocessed ); $pattern_transform_default = $background_options->get_attr_default( 'transform', 'background_pattern', $this->fields_unprocessed ); $pattern_size_default = $background_options->get_attr_default( 'size', 'background_pattern', $this->fields_unprocessed ); $pattern_width_default = $background_options->get_attr_default( 'width', 'background_pattern', $this->fields_unprocessed ); $pattern_height_default = $background_options->get_attr_default( 'height', 'background_pattern', $this->fields_unprocessed ); $pattern_repeat_default = $background_options->get_attr_default( 'repeat', 'background_pattern', $this->fields_unprocessed ); $pattern_repeat_origin_default = $background_options->get_attr_default( 'repeat_origin', 'background_pattern', $this->fields_unprocessed ); $pattern_horizontal_offset_default = $background_options->get_attr_default( 'horizontal_offset', 'background_pattern', $this->fields_unprocessed ); $pattern_vertical_offset_default = $background_options->get_attr_default( 'vertical_offset', 'background_pattern', $this->fields_unprocessed ); $pattern_blend_mode_default = $background_options->get_attr_default( 'blend_mode', 'background_pattern', $this->fields_unprocessed ); $enable_pattern_style = ! empty( $pattern_array['enable_pattern_style'] ) ? $pattern_array['enable_pattern_style'] : $enable_pattern_style_default; $pattern_style_name = ! empty( $pattern_array['pattern_style'] ) ? $pattern_array['pattern_style'] : $pattern_style_name_default; $pattern_color = ! empty( $pattern_array['pattern_color'] ) ? $pattern_array['pattern_color'] : $pattern_color_default; $pattern_transform = ! empty( $pattern_array['pattern_transform'] ) ? $pattern_array['pattern_transform'] : $pattern_transform_default; $pattern_size = ! empty( $pattern_array['pattern_size'] ) ? $pattern_array['pattern_size'] : $pattern_size_default; $pattern_width = ! empty( $pattern_array['pattern_width'] ) ? $pattern_array['pattern_width'] : $pattern_width_default; $pattern_height = ! empty( $pattern_array['pattern_height'] ) ? $pattern_array['pattern_height'] : $pattern_height_default; $pattern_repeat = ! empty( $pattern_array['pattern_repeat'] ) ? $pattern_array['pattern_repeat'] : $pattern_repeat_default; $pattern_repeat_origin = ! empty( $pattern_array['pattern_repeat_origin'] ) ? $pattern_array['pattern_repeat_origin'] : $pattern_repeat_origin_default; $pattern_horizontal_offset = ! empty( $pattern_array['pattern_horizontal_offset'] ) ? $pattern_array['pattern_horizontal_offset'] : $pattern_horizontal_offset_default; $pattern_vertical_offset = ! empty( $pattern_array['pattern_vertical_offset'] ) ? $pattern_array['pattern_vertical_offset'] : $pattern_vertical_offset_default; $pattern_blend_mode = ! empty( $pattern_array['pattern_blend_mode'] ) ? $pattern_array['pattern_blend_mode'] : $pattern_blend_mode_default; // Mask. $enable_mask_style_default = $background_options->get_attr_default( 'style', 'background_enable_mask', $this->fields_unprocessed ); $mask_style_name_default = $background_options->get_attr_default( 'style', 'background_mask', $this->fields_unprocessed ); $mask_color_default = $background_options->get_attr_default( 'color', 'background_mask', $this->fields_unprocessed ); $mask_transform_default = $background_options->get_attr_default( 'transform', 'background_mask', $this->fields_unprocessed ); $mask_ratio_default = $background_options->get_attr_default( 'aspect_ratio', 'background_mask', $this->fields_unprocessed ); $mask_size_default = $background_options->get_attr_default( 'size', 'background_mask', $this->fields_unprocessed ); $mask_width_default = $background_options->get_attr_default( 'width', 'background_mask', $this->fields_unprocessed ); $mask_height_default = $background_options->get_attr_default( 'height', 'background_mask', $this->fields_unprocessed ); $mask_position_default = $background_options->get_attr_default( 'position', 'background_mask', $this->fields_unprocessed ); $mask_horizontal_offset_default = $background_options->get_attr_default( 'horizontal_offset', 'background_mask', $this->fields_unprocessed ); $mask_vertical_offset_default = $background_options->get_attr_default( 'vertical_offset', 'background_mask', $this->fields_unprocessed ); $mask_blend_mode_default = $background_options->get_attr_default( 'blend_mode', 'background_mask', $this->fields_unprocessed ); $enable_mask_style = ! empty( $mask_array['enable_mask_style'] ) ? $mask_array['enable_mask_style'] : $enable_mask_style_default; $mask_style_name = ! empty( $mask_array['mask_style'] ) ? $mask_array['mask_style'] : $mask_style_name_default; $mask_color = ! empty( $mask_array['mask_color'] ) ? $mask_array['mask_color'] : $mask_color_default; $mask_transform = ! empty( $mask_array['mask_transform'] ) ? $mask_array['mask_transform'] : $mask_transform_default; $mask_aspect_ratio = ! empty( $mask_array['mask_aspect_ratio'] ) ? $mask_array['mask_aspect_ratio'] : $mask_ratio_default; $mask_size = ! empty( $mask_array['mask_size'] ) ? $mask_array['mask_size'] : $mask_size_default; $mask_width = ! empty( $mask_array['mask_width'] ) ? $mask_array['mask_width'] : $mask_width_default; $mask_height = ! empty( $mask_array['mask_height'] ) ? $mask_array['mask_height'] : $mask_height_default; $mask_position = ! empty( $mask_array['mask_position'] ) ? $mask_array['mask_position'] : $mask_position_default; $mask_horizontal_offset = ! empty( $mask_array['mask_horizontal_offset'] ) ? $mask_array['mask_horizontal_offset'] : $mask_horizontal_offset_default; $mask_vertical_offset = ! empty( $mask_array['mask_vertical_offset'] ) ? $mask_array['mask_vertical_offset'] : $mask_vertical_offset_default; $mask_blend_mode = ! empty( $mask_array['mask_blend_mode'] ) ? $mask_array['mask_blend_mode'] : $mask_blend_mode_default; // Others. $padding_values = isset( $paddings_array[ $array_index ] ) ? $paddings_array[ $array_index ] : array(); $padding_mobile_values = isset( $paddings_mobile_array[ $array_index ] ) ? $paddings_mobile_array[ $array_index ] : array(); $padding_last_edited = isset( $padding_mobile_values['last_edited'] ) ? $padding_mobile_values['last_edited'] : 'off|desktop'; $padding_responsive_active = et_pb_get_responsive_status( $padding_last_edited ); $is_parallax = isset( $column_parallax[ $array_index ][0] ) && 'on' === $column_parallax[ $array_index ][0] ? true : false; $parallax_method = isset( $column_parallax[ $array_index ][0] ) && 'on' === $column_parallax[ $array_index ][0] ? $column_parallax[ $array_index ][1] : ''; $custom_css_class = isset( $column_css_array['css_class'][ $array_index ] ) ? ' ' . $column_css_array['css_class'][ $array_index ] : ''; $custom_css_id = isset( $column_css_array['css_id'][ $array_index ] ) ? $column_css_array['css_id'][ $array_index ] : ''; $custom_css_before = isset( $column_css_array['custom_css_before'][ $array_index ] ) ? $column_css_array['custom_css_before'][ $array_index ] : ''; $custom_css_main = isset( $column_css_array['custom_css_main'][ $array_index ] ) ? $column_css_array['custom_css_main'][ $array_index ] : ''; $custom_css_after = isset( $column_css_array['custom_css_after'][ $array_index ] ) ? $column_css_array['custom_css_after'][ $array_index ] : ''; $custom_css_before_hover = self::$_->array_get( $column_css_array, "custom_css_before_hover.[$array_index]", '' ); $custom_css_main_hover = self::$_->array_get( $column_css_array, "custom_css_main_hover.[$array_index]", '' ); $custom_css_after_hover = self::$_->array_get( $column_css_array, "custom_css_after_hover.[$array_index]", '' ); $custom_css_before_sticky = self::$_->array_get( $column_css_array, "custom_css_before_sticky.[$array_index]", '' ); $custom_css_main_sticky = self::$_->array_get( $column_css_array, "custom_css_main_sticky.[$array_index]", '' ); $custom_css_after_sticky = self::$_->array_get( $column_css_array, "custom_css_after_sticky.[$array_index]", '' ); } else { $custom_css_id = self::$_->array_get( $this->props, 'module_id', '' ); $parallax_method = self::$_->array_get( $this->props, 'parallax_method', '' ); } // et_pb_columns_counter should be updated for all columns to calculate the last column correctly. if ( isset( $gobal_column_settings_holder[ $current_row_position ] ) ) { if ( 'et_pb_column_inner' === $function_name ) { $et_pb_all_column_settings_inner[ $current_row_position ]['et_pb_columns_counter']++; } else { $et_pb_all_column_settings[ $current_row_position ]['et_pb_columns_counter']++; } } // Get column type value in array. $column_type = explode( '_', $type ); // Just in case for some reason column shortcode has no `type` attribute and causes unexpected $column_type values. if ( isset( $column_type[0] ) && isset( $column_type[1] ) ) { // Get column progress. $column_progress = intval( $column_type[0] ) / intval( $column_type[1] ); if ( 0 === $array_index ) { $et_pb_column_completion = $column_progress; } else { $et_pb_column_completion = $et_pb_column_completion + $column_progress; } } // Last column is when sum of column type value equals to 1. Compare value as a string, comparing integers returns inconsistent results. $is_last_column = '1' === strval( $et_pb_column_completion ); // Still need to manually output this for Specialty columns. if ( $is_specialty_column ) { $background_images = array(); $is_gradient_on = false; if ( '' !== $background_gradient && 'on' === $background_gradient['active'] ) { $has_background_gradient = true; $default_gradient = apply_filters( 'et_pb_default_gradient', array( 'repeat' => ET_Global_Settings::get_value( 'all_background_gradient_repeat' ), 'type' => ET_Global_Settings::get_value( 'all_background_gradient_type' ), 'direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction' ), 'radial_direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction_radial' ), 'stops' => ET_Global_Settings::get_value( 'all_background_gradient_stops' ), 'unit' => ET_Global_Settings::get_value( 'all_background_gradient_unit' ), ) ); $background_gradient = wp_parse_args( array_filter( $background_gradient ), $default_gradient ); $gradient_stops = str_replace( '|', ', ', $background_gradient['stops'] ); switch ( $background_gradient['type'] ) { case 'conic': $gradient_type = 'conic'; $gradient_direction = "from {$background_gradient['direction']} at {$background_gradient['radial_direction']}"; break; case 'elliptical': $gradient_type = 'radial'; $gradient_direction = "ellipse at {$background_gradient['radial_direction']}"; break; case 'radial': case 'circular': $gradient_type = 'radial'; $gradient_direction = "circle at {$background_gradient['radial_direction']}"; break; case 'linear': default: $gradient_type = 'linear'; $gradient_direction = $background_gradient['direction']; } // Apply gradient repeat (if set). if ( 'on' === $background_gradient['repeat'] ) { $gradient_type = 'repeating-' . $gradient_type; } $background_images[] = "{$gradient_type}-gradient( {$gradient_direction}, {$gradient_stops} )"; } if ( '' !== $background_img && 'on' !== $parallax_method ) { $has_background_image = true; $background_images[] = sprintf( 'url(%s)', esc_attr( $background_img ) ); if ( '' !== $background_size ) { $image_size_style = et_pb_background_options()->get_background_size_css( $background_size, $background_image_width, $background_image_height ); if ( isset( $image_size_style['size'] ) && $image_size_style['size'] ) { $el_style = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( 'background-size:%s;', esc_attr( $image_size_style['size'] ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } // Check if image size has 'stretch' value or not. $is_image_size_stretch = 'stretch' === $background_size; // Image Repeat. if ( '' !== $background_repeat && ! $is_image_size_stretch ) { $el_style = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( 'background-repeat:%s;', esc_attr( $background_repeat ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } // Check if image repeat has 'space' value or not. $is_image_repeat_space = 'space' === $background_repeat; // Image Position. if ( ! $is_image_size_stretch && ! $is_image_repeat_space && ( '' !== $background_position || $background_horizontal_offset || $background_vertical_offset ) ) { $image_position_style = et_pb_background_options()->get_background_position_css( $background_position, $background_horizontal_offset, $background_vertical_offset ); $el_style_position = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( 'background-position:%1$s; ', esc_attr( $image_position_style['position'] ) ), ); ET_Builder_Element::set_style( $function_name, $el_style_position ); } if ( '' !== $background_blend ) { $el_style = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( 'background-blend-mode:%s;', esc_attr( $background_blend ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } if ( ! empty( $background_images ) ) { if ( 'on' !== $background_gradient_overlays_image ) { // The browsers stack the images in the opposite order to what you'd expect. $background_images = array_reverse( $background_images ); } // Check if Parallax and Gradient Overlays are on. if ( 'on' === $background_gradient['active'] && 'on' === $background_gradient_overlays_image && $is_parallax ) { // Set background image to initial when Parallax and Gradient Overlays are on. $image_style = 'initial'; $is_gradient_on = true; } else { // Set background image styles only it's different compared to the larger device. $image_style = implode( ', ', $background_images ); } $background_images_declaration = sprintf( 'background-image: %1$s;', esc_html( $image_style ) ); $el_style = array( 'selector' => '%%order_class%%', 'declaration' => esc_attr( $background_images_declaration ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $background_color && 'rgba(0,0,0,0)' !== $background_color && ! isset( $has_background_gradient, $has_background_image ) ) { $el_style = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( 'background-color:%s;', esc_attr( $background_color ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); if ( $background_color_hover && $background_color_hover_enabled ) { $el_style = array( 'selector' => '%%order_class%%:hover', 'declaration' => sprintf( 'background-color:%s;', esc_attr( $background_color ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } // Sticky. if ( $background_color_sticky && $background_color_sticky_enabled ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%', $this->is_sticky_module ), 'declaration' => sprintf( 'background-color:%s;', esc_attr( $background_color_sticky ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); // Add hover styles in sticky state. if ( $background_color_hover && $background_color_hover_enabled ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:hover', $this->is_sticky_module ), 'declaration' => sprintf( 'background-color:%s;', esc_attr( $background_color_hover ) ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } } elseif ( isset( $has_background_gradient, $has_background_image ) ) { // Force background-color: initial. $el_style = array( 'selector' => '%%order_class%%', 'declaration' => 'background-color: initial;', ); ET_Builder_Element::set_style( $function_name, $el_style ); } // Pattern Style. $pattern_selector = $this->add_suffix_to_selectors( ' > .et_pb_background_pattern', '%%order_class%%' ); $is_pattern_active = 'off' !== $enable_pattern_style && '' !== $pattern_style_name; if ( $is_pattern_active ) { $style_pattern = ''; // Pattern Transform. $pattern_is_horizontal = $background_options->get_transform_state( $pattern_transform, 'horizontal' ); $pattern_is_vertical = $background_options->get_transform_state( $pattern_transform, 'vertical' ); $pattern_is_rotated = $background_options->get_transform_state( $pattern_transform, 'rotate' ); $pattern_is_inverted = $background_options->get_transform_state( $pattern_transform, 'invert' ); if ( '' !== $pattern_transform && ( $pattern_is_horizontal || $pattern_is_vertical ) ) { $style_pattern .= sprintf( 'transform: %s;', esc_attr( esc_html( $background_options->get_transform_css( $pattern_is_horizontal, $pattern_is_vertical ) ) ) ); }; // Pattern Image. $pattern_style_svg = et_pb_background_pattern_options()->get_svg( $pattern_style_name, $pattern_color, 'default', $pattern_is_rotated, $pattern_is_inverted ); $style_pattern .= sprintf( 'background-image: %s;', esc_html( $pattern_style_svg ) ); // Pattern Size. if ( '' !== $pattern_size && $pattern_size !== $pattern_size_default ) { $pattern_size_style = $background_options->get_background_size_css( $pattern_size, $pattern_width, $pattern_height ); if ( isset( $pattern_size_style['size'] ) && $pattern_size_style['size'] ) { $style_pattern .= sprintf( 'background-size: %1$s; ', esc_html( $pattern_size_style['size'] ) ); } } // Check if pattern size has 'stretch' value or not. $is_pattern_size_stretch = 'stretch' === $pattern_size; // Pattern Repeat. if ( '' !== $pattern_repeat && $pattern_repeat !== $pattern_repeat_default && ! $is_pattern_size_stretch ) { $style_pattern .= sprintf( 'background-repeat: %1$s; ', esc_html( $pattern_repeat ) ); } // Check if pattern repeat has 'space' value or not. $is_pattern_repeat_space = 'space' === $pattern_repeat; $has_pattern_repeat_value = '' !== $pattern_repeat_origin && $pattern_repeat_origin !== $pattern_repeat_origin_default; // Pattern Repeat Origin. if ( ! $is_pattern_size_stretch && ! $is_pattern_repeat_space && ( $has_pattern_repeat_value || $pattern_horizontal_offset || $pattern_vertical_offset ) ) { $pattern_position_style = $background_options->get_background_position_css( $pattern_repeat_origin, $pattern_horizontal_offset, $pattern_vertical_offset ); $style_pattern .= sprintf( 'background-position: %1$s; ', esc_html( $pattern_position_style['position'] ) ); } // Pattern Blend Mode. if ( '' !== $pattern_blend_mode && $pattern_blend_mode !== $pattern_blend_mode_default ) { $style_pattern .= sprintf( 'mix-blend-mode: %1$s; ', esc_html( $pattern_blend_mode ) ); } // Output Pattern Style. if ( '' !== $style_pattern ) { $el_pattern_style = array( 'selector' => $pattern_selector, 'declaration' => rtrim( $style_pattern ), ); ET_Builder_Element::set_style( $function_name, $el_pattern_style ); } } // Mask Style. $mask_selector = $this->add_suffix_to_selectors( ' > .et_pb_background_mask', '%%order_class%%' ); $is_mask_active = 'off' !== $enable_mask_style && '' !== $mask_style_name; if ( $is_mask_active ) { $style_mask = ''; // Mask Transform. $mask_is_horizontal = $background_options->get_transform_state( $mask_transform, 'horizontal' ); $mask_is_vertical = $background_options->get_transform_state( $mask_transform, 'vertical' ); $mask_is_rotated = $background_options->get_transform_state( $mask_transform, 'rotate' ); $mask_is_inverted = $background_options->get_transform_state( $mask_transform, 'invert' ); if ( '' !== $mask_transform && ( $mask_is_horizontal || $mask_is_vertical ) ) { $style_mask .= sprintf( 'transform: %s;', esc_attr( esc_html( $background_options->get_transform_css( $mask_is_horizontal, $mask_is_vertical ) ) ) ); }; // Mask Size. if ( '' !== $mask_size && $mask_size !== $mask_size_default ) { $mask_size_style = $background_options->get_background_size_css( $mask_size, $mask_width, $mask_height, $mask_size_default, 'mask' ); if ( isset( $mask_size_style['size'] ) && $mask_size_style['size'] ) { $style_mask .= sprintf( 'background-size: %1$s; ', esc_html( $mask_size_style['size'] ) ); } } // Mask Image. $mask_style_svg = et_pb_background_mask_options()->get_svg( $mask_style_name, $mask_color, $mask_aspect_ratio, $mask_is_rotated, $mask_is_inverted, $mask_size ); $style_mask .= sprintf( 'background-image: %s;', esc_html( $mask_style_svg ) ); // Check if mask size has 'stretch' value or not. $is_mask_size_stretch = 'stretch' === $mask_size; $has_mask_position_value = '' !== $mask_position && $mask_position !== $mask_position_default; // Mask Position. if ( ! $is_mask_size_stretch && ( $has_mask_position_value || $mask_horizontal_offset || $mask_vertical_offset ) ) { $mask_position_style = $background_options->get_background_position_css( $mask_position, $mask_horizontal_offset, $mask_vertical_offset ); $style_mask .= sprintf( 'background-position: %1$s; ', esc_html( $mask_position_style['position'] ) ); } // Mask Blend Mode. if ( '' !== $mask_blend_mode && $mask_blend_mode !== $mask_blend_mode_default ) { $style_mask .= sprintf( 'mix-blend-mode: %1$s; ', esc_html( $mask_blend_mode ) ); } // Output Mask Style. if ( '' !== $style_mask ) { $el_mask_style = array( 'selector' => $mask_selector, 'declaration' => rtrim( $style_mask ), ); ET_Builder_Element::set_style( $function_name, $el_mask_style ); } } if ( ! empty( $padding_values ) ) { $padding_hover_enabled = self::$_->array_get( $padding_values, 'padding-hover-enabled', false ); $padding_sticky_enabled = self::$_->array_get( $padding_values, 'padding-sticky-enabled', false ); $sticky_padding_selector = $sticky->add_sticky_to_selectors( '%%order_class%%', $this->is_sticky_module ); unset( $padding_values['padding-hover-enabled'], $padding_values['padding-sticky-enabled'] ); foreach ( $padding_values as $position => $value ) { if ( in_array( $position, array( 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ), true ) && ! empty( $value ) ) { $element_style = array( 'selector' => '%%order_class%%', 'declaration' => sprintf( '%1$s:%2$s;', esc_html( $position ), esc_html( et_builder_process_range_value( $value ) ) ), ); // Backward compatibility. Keep Padding on Mobile is deprecated in favour of responsive inputs mechanism for custom padding // To ensure that it is compatibility with previous version of Divi, this option is now only used as last resort if no // responsive padding value is found, and padding_mobile value is saved (which is set to off by default). if ( in_array( $keep_column_padding_mobile, array( 'on', 'off' ), true ) && 'on' !== $keep_column_padding_mobile && ! $padding_responsive_active ) { $element_style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } ET_Builder_Element::set_style( $function_name, $element_style ); } // Add padding hover styles. if ( $padding_hover_enabled && null !== self::$_->array_get( $padding_values, "{$position}-hover" ) && '' !== self::$_->array_get( $padding_values, "{$position}-hover" ) ) { $hover_value = $padding_values[ "{$position}-hover" ]; $element_style = array( 'selector' => '%%order_class%%:hover', 'declaration' => sprintf( '%1$s:%2$s;', esc_html( $position ), esc_html( et_builder_process_range_value( $hover_value ) ) ), ); ET_Builder_Element::set_style( $function_name, $element_style ); } // Sticky. $sticky_padding_value = self::$_->array_get( $padding_values, "{$position}-sticky", '' ); if ( $padding_sticky_enabled && ! empty( $sticky_padding_value ) ) { $element_style = array( 'selector' => $sticky_padding_selector, 'declaration' => sprintf( '%1$s:%2$s;', esc_html( $position ), esc_html( et_builder_process_range_value( $sticky_padding_value ) ) ), ); ET_Builder_Element::set_style( $function_name, $element_style ); } } } if ( $padding_responsive_active && ( ! empty( $padding_mobile_values['tablet'] ) || ! empty( $padding_values['phone'] ) ) ) { $padding_mobile_values_processed = array(); foreach ( array( 'tablet', 'phone' ) as $device ) { if ( empty( $padding_mobile_values[ $device ] ) ) { continue; } $padding_mobile_values_processed[ $device ] = array( 'padding-top' => isset( $padding_mobile_values[ $device ][0] ) ? $padding_mobile_values[ $device ][0] : '', 'padding-right' => isset( $padding_mobile_values[ $device ][1] ) ? $padding_mobile_values[ $device ][1] : '', 'padding-bottom' => isset( $padding_mobile_values[ $device ][2] ) ? $padding_mobile_values[ $device ][2] : '', 'padding-left' => isset( $padding_mobile_values[ $device ][3] ) ? $padding_mobile_values[ $device ][3] : '', ); } if ( ! empty( $padding_mobile_values_processed ) ) { $padding_mobile_selector = 'et_pb_column_inner' !== $function_name ? '.et_pb_row > .et_pb_column%%order_class%%' : '.et_pb_row_inner > .et_pb_column%%order_class%%'; et_pb_responsive_options()->generate_responsive_css( $padding_mobile_values_processed, $padding_mobile_selector, '', $function_name ); } } if ( '' !== $custom_css_before ) { $el_style = array( 'selector' => '%%order_class%%:before', 'declaration' => trim( $custom_css_before ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $custom_css_main ) { $el_style = array( 'selector' => '%%order_class%%', 'declaration' => trim( $custom_css_main ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $custom_css_after ) { $el_style = array( 'selector' => '%%order_class%%:after', 'declaration' => trim( $custom_css_after ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $custom_css_before_hover ) { $el_style = array( 'selector' => '%%order_class%%:hover:before', 'declaration' => trim( $custom_css_before_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $custom_css_main_hover ) { $el_style = array( 'selector' => '%%order_class%%:hover', 'declaration' => trim( $custom_css_main_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } if ( '' !== $custom_css_after_hover ) { $el_style = array( 'selector' => '%%order_class%%:hover:after', 'declaration' => trim( $custom_css_after_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } // Sticky. if ( ! empty( $custom_css_before_sticky ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:before', $this->is_sticky_module ), 'declaration' => trim( $custom_css_before_sticky ), ); ET_Builder_Element::set_style( $function_name, $el_style ); // Add hover styles in sticky state, in case some hover styles are overridden with sticky styles. if ( ! empty( $custom_css_before_hover ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:hover:before', $this->is_sticky_module ), 'declaration' => trim( $custom_css_before_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } if ( ! empty( $custom_css_main_sticky ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%', $this->is_sticky_module ), 'declaration' => trim( $custom_css_main_sticky ), ); ET_Builder_Element::set_style( $function_name, $el_style ); // Add hover styles in sticky state, in case some hover styles are overridden with sticky styles. if ( ! empty( $custom_css_main_hover ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:hover', $this->is_sticky_module ), 'declaration' => trim( $custom_css_main_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } if ( ! empty( $custom_css_after_sticky ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:after', $this->is_sticky_module ), 'declaration' => trim( $custom_css_after_sticky ), ); ET_Builder_Element::set_style( $function_name, $el_style ); // Add hover styles in sticky state, in case some hover styles are overridden with sticky styles. if ( ! empty( $custom_css_after_hover ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( '%%order_class%%:hover:after', $this->is_sticky_module ), 'declaration' => trim( $custom_css_after_hover ), ); ET_Builder_Element::set_style( $function_name, $el_style ); } } } if ( 'et_pb_column_inner' === $function_name ) { if ( '1_1' === $type ) { $type = '4_4'; } $et_specialty_column_type = '' !== $saved_specialty_column_type ? $saved_specialty_column_type : $et_specialty_column_type; switch ( $et_specialty_column_type ) { case '1_2': if ( '1_2' === $type ) { $type = '1_4'; } if ( '1_3' === $type ) { $type = '1_6'; } break; case '2_3': if ( '1_3' === $type ) { $type = '2_9'; } if ( '1_2' === $type ) { $type = '1_3'; } if ( '1_4' === $type ) { $type = '1_6'; } break; case '3_4': if ( '1_2' === $type ) { $type = '3_8'; } elseif ( '1_3' === $type ) { $type = '1_4'; } break; } } $video_background = ''; $parallax_image = ''; // Column background video. if ( $is_specialty_column ) { $video_background = trim( $this->video_background( $background_video ) ); if ( '' !== $background_img && '' !== $parallax_method ) { $background_gradient_style = $is_gradient_on ? sprintf( 'background-image: %1$s;', esc_html( $background_images[0] ) ) : ''; $background_gradient_blend = '' !== $background_blend && 'normal' !== $background_blend ? sprintf( 'mix-blend-mode: %1$s;', esc_html( $background_blend ) ) : ''; $parallax_gradient = sprintf( '<div class="et_parallax_gradient%1$s" style="%2$s%3$s" ></div>', ( 'off' === $parallax_method ? ' et_pb_parallax_css' : '' ), $background_gradient_style, $background_gradient_blend ); $parallax_image = sprintf( '<div class="et_parallax_bg_wrap"> <div class="et_parallax_bg%2$s" style="background-image: url(%1$s);"></div> %3$s </div>', esc_attr( $background_img ), ( 'off' === $parallax_method ? ' et_pb_parallax_css' : '' ), $is_gradient_on ? et_core_esc_previously( $parallax_gradient ) : '' ); } if ( '' !== $parallax_method ) { $this->add_classname( 'et_pb_section_parallax' ); } } else { $video_background = trim( $this->video_background() ); $parallax_image = $this->get_parallax_image_background(); } // Remove automatically added classname. $this->remove_classname( 'et_pb_module' ); $this->add_classname( 'et_pb_column_' . $type, 1 ); if ( '' !== $custom_css_class ) { $this->add_classname( $custom_css_class ); } if ( $is_specialty_column && '' !== $specialty_columns ) { $this->add_classname( 'et_pb_specialty_column' ); } // CSS Filters. $this->add_classname( $this->generate_css_filters( $function_name ) ); if ( '' !== $video_background ) { $this->add_classname( array( 'et_pb_section_video', 'et_pb_preload', ) ); } if ( $is_last_column ) { $this->add_classname( 'et-last-child' ); } if ( self::contains( $content, array( 'et_pb_menu', 'et_pb_fullwidth_menu' ) ) ) { $this->add_classname( 'et_pb_column--with-menu' ); } // Module classname in column has to be contained in variable BEFORE content is being parsed // as shortcode because column and column inner use the same ET_Builder_Column's render // classname doesn't work in nested situation because each called module doesn't have its own class init. $module_classname = $this->module_classname( $function_name ); // Inner content shortcode parsing has to be done after all classname addition/removal. $inner_content = do_shortcode( et_pb_fix_shortcodes( $content ) ); // Inner content dependant class in column shouldn't use add_classname/remove_classname method. $content_dependent_classname = '' === trim( $inner_content ) ? ' et_pb_column_empty' : ''; if ( $is_specialty_column ) { $pattern_args = array( 'background_enable_pattern_style' => $enable_pattern_style, 'background_pattern_style' => $pattern_style_name, ); $mask_args = array( 'background_enable_mask_style' => $enable_mask_style, 'background_mask_style' => $mask_style_name, 'background_mask_size' => $mask_size, ); $output = sprintf( '<div class="%1$s%6$s"%4$s> %3$s %5$s %7$s %8$s %2$s </div>', $module_classname, $inner_content, $parallax_image, '' !== $custom_css_id ? sprintf( ' id="%1$s"', esc_attr( $custom_css_id ) ) : '', // 5 $video_background, $content_dependent_classname, et_core_esc_previously( $this->background_pattern( $pattern_args ) ), // 7 et_core_esc_previously( $this->background_mask( $mask_args ) ) // 8 ); } else { $output = sprintf( '<div class="%1$s%6$s"%4$s> %3$s %5$s %7$s %8$s %2$s </div>', $module_classname, $inner_content, $parallax_image, '' !== $custom_css_id ? sprintf( ' id="%1$s"', esc_attr( $custom_css_id ) ) : '', // 5 $video_background, $content_dependent_classname, et_core_esc_previously( $this->background_pattern() ), // 7 et_core_esc_previously( $this->background_mask() ) // 8 ); } return $output; } } new ET_Builder_Column(); /** * Fires after the builder's structure element classes are loaded. * * @since 4.4.9 */ do_action( 'et_builder_structure_elements_loaded' ); PK X�\�jt� builder/conditions.phpnu �[��� <?php /** * Utility functions for checking conditions. * * To be included in this file a function must: * * * Return a bool value * * Have a name that asks a yes or no question (where the first word after * the et_ prefix is a word like: is, can, has, should, was, had, must, or will) * * @package Divi * @subpackage Builder * @since 4.0.7 */ // phpcs:disable Squiz.PHP.CommentedOutCode -- We may add `et_builder_()` in future. /* Function Template if ( ! function_exists( '' ) ): function et_builder_() { } endif; */ // phpcs:enable // Note: Functions in this file are sorted alphabetically. if ( ! function_exists( 'et_builder_is_frontend' ) ) : /** * Determine whether current request is frontend. * This excludes the visual builder. * * @return bool */ function et_builder_is_frontend() { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Only used to disable some FE optmizations. $is_builder = isset( $_GET['et_fb'] ) || isset( $_GET['et_bfb'] ); $is_block_layout_preview = isset( $_GET['et_block_layout_preview'] ); // phpcs:enable return $is_builder || is_admin() || wp_doing_ajax() || wp_doing_cron() || $is_block_layout_preview ? false : true; } endif; if ( ! function_exists( 'et_builder_is_frontend_or_builder' ) ) : /** * Determine whether current request is frontend. * This includes the visual builder. * * @since 4.10.0 * * @return bool */ function et_builder_is_frontend_or_builder() { static $et_builder_is_frontend_or_builder = null; if ( null === $et_builder_is_frontend_or_builder ) { if ( ! is_admin() && ! wp_doing_ajax() && ! wp_doing_cron() ) { $et_builder_is_frontend_or_builder = true; } } return $et_builder_is_frontend_or_builder; } endif; if ( ! function_exists( 'et_builder_is_loading_data' ) ) : /** * Determine whether builder is loading full data or not. * * @param string $type Is it a bb or vb. * * @return bool */ function et_builder_is_loading_data( $type = 'vb' ) { // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any stats, hence CSRF ok. if ( 'bb' === $type ) { return 'et_pb_get_backbone_templates' === et_()->array_get( $_POST, 'action' ); } $data_actions = array( 'et_fb_retrieve_builder_data', 'et_fb_update_builder_assets', 'et_pb_process_computed_property', ); return isset( $_POST['action'] ) && in_array( $_POST['action'], $data_actions, true ); // phpcs:enable } endif; if ( ! function_exists( 'et_builder_should_load_all_data' ) ) : /** * Determine whether to load full builder data. * * @return bool */ function et_builder_should_load_all_data() { $needs_cached_definitions = et_core_is_fb_enabled() && ! et_fb_dynamic_asset_exists( 'definitions' ); return $needs_cached_definitions || ( ET_Builder_Element::is_saving_cache() || et_builder_is_loading_data() ); } endif; if ( ! function_exists( 'et_builder_should_load_all_module_data' ) ) : /** * Determine whether to load all module data. * * @return bool */ function et_builder_should_load_all_module_data() { if ( ! et_builder_is_frontend() ) { // Always load everything when not a frontend request. return true; } $needs_cached_definitions = et_core_is_fb_enabled(); $et_dynamic_module_framework = et_builder_dynamic_module_framework(); $result = $needs_cached_definitions || ( ET_Builder_Element::is_saving_cache() || et_builder_is_loading_data() ) || 'on' !== $et_dynamic_module_framework; /** * Whether to load all module data, * including all module classes, on a given page load. * * @since 4.10.0 * * @param bool $result Whether to load all module data. */ return apply_filters( 'et_builder_should_load_all_module_data', $result ); } endif; if ( ! function_exists( 'et_builder_dynamic_module_framework' ) ) : /** * Determine whether module framework is on. * * @return string */ function et_builder_dynamic_module_framework() { global $shortname; if ( et_is_builder_plugin_active() ) { $options = get_option( 'et_pb_builder_options', array() ); $et_dynamic_module_framework = isset( $options['performance_main_dynamic_module_framework'] ) ? $options['performance_main_dynamic_module_framework'] : 'on'; } else { $et_dynamic_module_framework = et_get_option( $shortname . '_dynamic_module_framework', 'on' ); } return $et_dynamic_module_framework; } endif; if ( ! function_exists( 'et_builder_is_mod_pagespeed_enabled' ) ) : /** * Determine whether Mod PageSpeed is enabled. * * @return bool */ function et_builder_is_mod_pagespeed_enabled() { static $enabled; if ( isset( $enabled ) ) { // Use the cached value. return $enabled; } $key = 'et_check_mod_pagespeed'; $version = get_transient( $key ); if ( false === $version ) { // Mod PageSpeed is an output filter, hence it can't be detected from within the request. // To figure out whether it is active or not: // 1. Use `wp_remote_get` to make another request. // 2. Retrieve PageSpeed version from response headers (if set). // 3. Save the value in a transient for 24h. // The `et_check_mod_pagespeed` url parameter is also added to the request so // we can exit early (content is irrelevant, only headers matter). $args = [ $key => 'on', ]; // phpcs:disable WordPress.Security.NonceVerification -- Only checking arg is set. if ( isset( $_REQUEST['PageSpeed'] ) ) { // This isn't really needed but it's harmless and makes testing a lot easier. $args['PageSpeed'] = sanitize_text_field( $_REQUEST['PageSpeed'] ); } // phpcs:enable $request = wp_remote_get( add_query_arg( $args, get_home_url() ) ); // Apache header. $version = wp_remote_retrieve_header( $request, 'x-mod-pagespeed' ); if ( empty( $version ) ) { // Nginx header. $version = wp_remote_retrieve_header( $request, 'x-page-speed' ); } set_transient( $key, $version, DAY_IN_SECONDS ); } // Cache the value. $enabled = ! empty( $version ); return $enabled; } endif; PK X�\���� �� $ builder/class-et-global-settings.phpnu �[��� <?php /** * Global modules settings. * * @package Divi * @subpackage Builder */ /** * Global settings class. * * @todo Rename this class to `ET_Builder_Module_Settings` so that its name clearly indicates its purpose. */ class ET_Global_Settings { /** * List of default settings. * * @var array */ private static $_settings = array(); /** * Whether reinit default setting values. * * @var bool */ private static $_reinit_values = false; /** * Initialize the global settings. */ public static function init() { // The class can only be initialized once. if ( ! empty( self::$_settings ) && ! self::$_reinit_values ) { return; } // Reset _reinit_values property. It should only used once for every reinit() method call. if ( self::$_reinit_values ) { self::$_reinit_values = false; } self::set_values(); } /** * Allow global settings value to be reinitialized. Initially added a to make global * settings modifieable during unit/integration testing which uses PHPUnit & wp-browser */ public static function reinit() { self::$_reinit_values = true; } /** * Set default global setting value */ private static function set_values() { $hover = et_pb_hover_options(); $font_defaults_h1 = array( 'size' => '30px', 'letter_spacing' => '0px', 'line_height' => '1em', ); $font_defaults_h2 = array( 'size' => '26px', 'letter_spacing' => '0px', 'line_height' => '1em', ); $font_defaults = array( 'size' => '14', 'color' => '#666666', 'letter_spacing' => '0px', 'line_height' => '1.7em', ); $background_gradient_defaults = array( 'stops' => '#2b87da 0%|#29c4a9 100%', 'type' => 'linear', 'direction' => '180deg', 'direction_radial' => 'center', 'repeat' => 'off', 'unit' => '%', 'overlays_image' => 'off', 'start' => '#2b87da', 'end' => '#29c4a9', 'start_position' => '0%', 'end_position' => '100%', ); $background_image_defaults = array( 'size' => 'cover', 'position' => 'center', 'repeat' => 'no-repeat', 'blend' => 'normal', ); $background_blend_mode_defaults = array( 'background_blend_mode' => $background_image_defaults['blend'], ); $filter_defaults = array( 'filter_hue_rotate' => '0deg', 'filter_saturate' => '100%', 'filter_brightness' => '100%', 'filter_contrast' => '100%', 'filter_invert' => '0%', 'filter_sepia' => '0%', 'filter_opacity' => '100%', 'filter_blur' => '0px', ); $defaults = array( // Global: Buttons. 'all_buttons_font_size' => '20', 'all_buttons_border_width' => '2', 'all_buttons_border_radius' => '3', 'all_buttons_spacing' => '0', 'all_buttons_font_style' => '', $hover->get_hover_field( 'all_buttons_border_radius' ) => '3', $hover->get_hover_field( 'all_buttons_spacing' ) => '0', // Global: Background Gradients. 'all_background_gradient_repeat' => $background_gradient_defaults['repeat'], 'all_background_gradient_type' => $background_gradient_defaults['type'], 'all_background_gradient_direction' => $background_gradient_defaults['direction'], 'all_background_gradient_direction_radial' => $background_gradient_defaults['direction_radial'], 'all_background_gradient_stops' => $background_gradient_defaults['stops'], 'all_background_gradient_unit' => $background_gradient_defaults['unit'], 'all_background_gradient_overlays_image' => $background_gradient_defaults['overlays_image'], // Deprecated. 'all_background_gradient_start' => $background_gradient_defaults['start'], // Deprecated. 'all_background_gradient_end' => $background_gradient_defaults['end'], // Deprecated. 'all_background_gradient_start_position' => $background_gradient_defaults['start_position'], // Deprecated. 'all_background_gradient_end_position' => $background_gradient_defaults['end_position'], // Global: Filters. 'all_filter_hue_rotate' => $filter_defaults['filter_hue_rotate'], 'all_filter_saturate' => $filter_defaults['filter_saturate'], 'all_filter_brightness' => $filter_defaults['filter_brightness'], 'all_filter_contrast' => $filter_defaults['filter_contrast'], 'all_filter_invert' => $filter_defaults['filter_invert'], 'all_filter_sepia' => $filter_defaults['filter_sepia'], 'all_filter_opacity' => $filter_defaults['filter_opacity'], 'all_filter_blur' => $filter_defaults['filter_blur'], // Global: Mix Blend Mode. 'all_mix_blend_mode' => 'normal', // Module: Accordion. 'et_pb_accordion-toggle_font_size' => '16', 'et_pb_accordion-toggle_font_style' => '', 'et_pb_accordion-inactive_toggle_font_style' => '', 'et_pb_accordion-toggle_icon_size' => '16', 'et_pb_accordion-custom_padding' => '20', 'et_pb_accordion-toggle_line_height' => '1em', 'et_pb_accordion-toggle_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_accordion-body_font_size' => $font_defaults['size'], 'et_pb_accordion-body_line_height' => $font_defaults['line_height'], 'et_pb_accordion-body_letter_spacing' => $font_defaults['letter_spacing'], // Module: Audio. 'et_pb_audio-title_font_size' => '26', 'et_pb_audio-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_audio-title_line_height' => $font_defaults['line_height'], 'et_pb_audio-title_font_style' => '', 'et_pb_audio-caption_font_size' => $font_defaults['size'], 'et_pb_audio-caption_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_audio-caption_line_height' => $font_defaults['line_height'], 'et_pb_audio-caption_font_style' => '', 'et_pb_audio-title_text_color' => '#666666', 'et_pb_audio-background_size' => $background_image_defaults['size'], 'et_pb_audio-background_position' => $background_image_defaults['position'], 'et_pb_audio-background_repeat' => $background_image_defaults['repeat'], 'et_pb_audio-background_blend' => $background_image_defaults['blend'], // Module: Blog. 'et_pb_blog-header_font_size' => '18', 'et_pb_blog-header_font_style' => '', 'et_pb_blog-meta_font_size' => '14', 'et_pb_blog-meta_font_style' => '', 'et_pb_blog-meta_line_height' => $font_defaults['line_height'], 'et_pb_blog-meta_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blog-header_color' => '#333333', 'et_pb_blog-header_line_height' => '1em', 'et_pb_blog-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blog-body_font_size' => $font_defaults['size'], 'et_pb_blog-body_line_height' => $font_defaults['line_height'], 'et_pb_blog-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blog-pagination_font_size' => $font_defaults['size'], 'et_pb_blog-pagination_line_height' => $font_defaults['line_height'], 'et_pb_blog-pagination_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blog_masonry-header_font_size' => '26', 'et_pb_blog_masonry-header_font_style' => '', 'et_pb_blog_masonry-meta_font_size' => '14', 'et_pb_blog_masonry-meta_font_style' => '', 'et_pb_blog-read_more_font_size' => '14px', 'et_pb_blog-read_more_line_height' => $font_defaults['line_height'], // Module: Blurb. 'et_pb_blurb-header_font_size' => '18', 'et_pb_blurb-header_color' => '#333333', 'et_pb_blurb-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blurb-header_line_height' => '1em', 'et_pb_blurb-body_font_size' => $font_defaults['size'], 'et_pb_blurb-body_color' => '#666666', 'et_pb_blurb-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_blurb-body_line_height' => $font_defaults['line_height'], 'et_pb_blurb-text_orientation' => 'left', 'et_pb_blurb-background_size' => $background_image_defaults['size'], 'et_pb_blurb-background_position' => $background_image_defaults['position'], 'et_pb_blurb-background_repeat' => $background_image_defaults['repeat'], 'et_pb_blurb-background_blend' => $background_image_defaults['blend'], // Module: Circle Counter. 'et_pb_circle_counter-title_font_size' => '16', 'et_pb_circle_counter-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_circle_counter-title_line_height' => '1em', 'et_pb_circle_counter-title_font_style' => '', 'et_pb_circle_counter-number_font_size' => '46', 'et_pb_circle_counter-number_font_style' => '', 'et_pb_circle_counter-title_color' => '#333333', 'et_pb_circle_counter-number_line_height' => '225px', 'et_pb_circle_counter-number_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_circle_counter-circle_color_alpha' => '0.1', // Module: Comments. 'et_pb_comments-header_font_size' => $font_defaults_h1['size'], 'et_pb_comments-header_line_height' => $font_defaults_h1['line_height'], // Module: Contact Form. 'et_pb_contact_form-title_font_size' => '26', 'et_pb_contact_form-title_font_style' => '', 'et_pb_contact_form-form_field_font_size' => '14', 'et_pb_contact_form-form_field_font_style' => '', 'et_pb_contact_form-captcha_font_size' => '14', 'et_pb_contact_form-captcha_font_style' => '', 'et_pb_contact_form-padding' => '16', 'et_pb_contact_form-title_color' => '#333333', 'et_pb_contact_form-title_line_height' => '1em', 'et_pb_contact_form-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_contact_form-form_field_color' => '#999999', 'et_pb_contact_form-form_field_line_height' => $font_defaults['line_height'], 'et_pb_contact_form-form_field_letter_spacing' => $font_defaults['letter_spacing'], // Module: Countdown Timer. 'et_pb_countdown_timer-header_font_size' => '22', 'et_pb_countdown_timer-header_font_style' => '', 'et_pb_countdown_timer-header_color' => '#333333', 'et_pb_countdown_timer-header_line_height' => '1em', 'et_pb_countdown_timer-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_countdown_timer-numbers_font_size' => '54px', 'et_pb_countdown_timer-numbers_line_height' => '54px', 'et_pb_countdown_timer-numbers_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_countdown_timer-separator_font_size' => '54px', 'et_pb_countdown_timer-separator_line_height' => '54px', 'et_pb_countdown_timer-separator_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_countdown_timer-label_line_height' => '25px', 'et_pb_countdown_timer-label_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_countdown_timer-label_font_size' => $font_defaults['size'], 'et_pb_countdown_timer-background_size' => $background_image_defaults['size'], 'et_pb_countdown_timer-background_position' => $background_image_defaults['position'], 'et_pb_countdown_timer-background_repeat' => $background_image_defaults['repeat'], 'et_pb_countdown_timer-background_blend' => $background_image_defaults['blend'], // Module: Bar Counters Item. 'et_pb_counter-background_size' => $background_image_defaults['size'], 'et_pb_counter-background_position' => $background_image_defaults['position'], 'et_pb_counter-background_repeat' => $background_image_defaults['repeat'], 'et_pb_counter-background_blend' => $background_image_defaults['blend'], // Module: Bar Counters. 'et_pb_counters-title_font_size' => '12', 'et_pb_counters-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_counters-title_line_height' => $font_defaults['line_height'], 'et_pb_counters-title_font_style' => '', 'et_pb_counters-percent_font_size' => '12', 'et_pb_counters-percent_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_counters-percent_line_height' => $font_defaults['line_height'], 'et_pb_counters-percent_font_style' => '', 'et_pb_counters-border_radius' => '0', 'et_pb_counters-padding' => '0', 'et_pb_counters-title_color' => '#999999', 'et_pb_counters-percent_color' => '#ffffff', 'et_pb_counters-background_size' => $background_image_defaults['size'], 'et_pb_counters-background_position' => $background_image_defaults['position'], 'et_pb_counters-background_repeat' => $background_image_defaults['repeat'], 'et_pb_counters-background_blend' => $background_image_defaults['blend'], // Module: CTA. 'et_pb_cta-header_font_size' => '26', 'et_pb_cta-header_font_style' => '', 'et_pb_cta-custom_padding' => '40', 'et_pb_cta-header_text_color' => '#333333', 'et_pb_cta-header_line_height' => '1em', 'et_pb_cta-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_cta-body_font_size' => $font_defaults['size'], 'et_pb_cta-body_line_height' => $font_defaults['line_height'], 'et_pb_cta-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_cta-text_orientation' => 'center', 'et_pb_cta-background_size' => $background_image_defaults['size'], 'et_pb_cta-background_position' => $background_image_defaults['position'], 'et_pb_cta-background_repeat' => $background_image_defaults['repeat'], 'et_pb_cta-background_blend' => $background_image_defaults['blend'], // Module: Divider. 'et_pb_divider-divider_style' => 'solid', 'et_pb_divider-divider_weight' => '1', 'et_pb_divider-height' => '1', 'et_pb_divider-divider_position' => 'top', // Module: Filterable Portfolio. 'et_pb_filterable_portfolio-hover_overlay_color' => 'rgba(255,255,255,0.9)', 'et_pb_filterable_portfolio-title_font_size' => '18', 'et_pb_filterable_portfolio-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_filterable_portfolio-title_line_height' => $font_defaults['line_height'], 'et_pb_filterable_portfolio-title_font_style' => '', 'et_pb_filterable_portfolio-title_color' => '#333333', 'et_pb_filterable_portfolio-caption_font_size' => '14', 'et_pb_filterable_portfolio-caption_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_filterable_portfolio-caption_line_height' => $font_defaults['line_height'], 'et_pb_filterable_portfolio-caption_font_style' => '', 'et_pb_filterable_portfolio-filter_font_size' => '14', 'et_pb_filterable_portfolio-filter_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_filterable_portfolio-filter_line_height' => $font_defaults['line_height'], 'et_pb_filterable_portfolio-filter_font_style' => '', 'et_pb_filterable_portfolio-pagination_font_size' => '14', 'et_pb_filterable_portfolio-pagination_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_filterable_portfolio-pagination_line_height' => $font_defaults['line_height'], 'et_pb_filterable_portfolio-pagination_font_style' => '', 'et_pb_filterable_portfolio-background_size' => $background_image_defaults['size'], 'et_pb_filterable_portfolio-background_position' => $background_image_defaults['position'], 'et_pb_filterable_portfolio-background_repeat' => $background_image_defaults['repeat'], 'et_pb_filterable_portfolio-background_blend' => $background_image_defaults['blend'], 'et_pb_filterable_portfolio-zoom_icon_color' => '', // Module: Fullwidth Header. 'et_pb_fullwidth_header-scroll_down_icon_size' => '50px', 'et_pb_fullwidth_header-subhead_font_size' => '18px', 'et_pb_fullwidth_header-button_one_font_size' => '20px', 'et_pb_fullwidth_header-button_one_border_radius' => '3px', 'et_pb_fullwidth_header-button_two_font_size' => '20px', 'et_pb_fullwidth_header-button_two_border_radius' => '3px', 'et_pb_fullwidth_header-background_size' => $background_image_defaults['size'], 'et_pb_fullwidth_header-background_position' => $background_image_defaults['position'], 'et_pb_fullwidth_header-background_repeat' => $background_image_defaults['repeat'], 'et_pb_fullwidth_header-background_blend' => $background_image_defaults['blend'], // Module: Fullwidth Menu. 'et_pb_fullwidth_menu-background_size' => $background_image_defaults['size'], 'et_pb_fullwidth_menu-background_position' => $background_image_defaults['position'], 'et_pb_fullwidth_menu-background_repeat' => $background_image_defaults['repeat'], 'et_pb_fullwidth_menu-background_blend' => $background_image_defaults['blend'], // Module: Fullwidth Portfolio. 'et_pb_fullwidth_portfolio-background_size' => $background_image_defaults['size'], 'et_pb_fullwidth_portfolio-background_position' => $background_image_defaults['position'], 'et_pb_fullwidth_portfolio-background_repeat' => $background_image_defaults['repeat'], 'et_pb_fullwidth_portfolio-background_blend' => $background_image_defaults['blend'], 'et_pb_fullwidth_portfolio-zoom_icon_color' => '', // Module: Fullwidth Post Title. 'et_pb_fullwidth_post_title-title_font_size' => '26px', 'et_pb_fullwidth_post_title-title_line_height' => '1em', 'et_pb_fullwidth_post_title-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_fullwidth_post_title-meta_font_size' => $font_defaults['size'], 'et_pb_fullwidth_post_title-meta_line_height' => '1em', 'et_pb_fullwidth_post_title-meta_letter_spacing' => $font_defaults['letter_spacing'], // Module: Fullwidth Slider. 'et_pb_fullwidth_slider-header_font_size' => '46', 'et_pb_fullwidth_slider-header_font_style' => '', 'et_pb_fullwidth_slider-body_font_size' => '16', 'et_pb_fullwidth_slider-body_font_style' => '', 'et_pb_fullwidth_slider-body_line_height' => $font_defaults['line_height'], 'et_pb_fullwidth_slider-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_fullwidth_slider-padding' => '16', 'et_pb_fullwidth_slider-header_color' => '#ffffff', 'et_pb_fullwidth_slider-header_line_height' => '1em', 'et_pb_fullwidth_slider-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_fullwidth_slider-body_color' => '#ffffff', 'et_pb_fullwidth_slider-background_size' => $background_image_defaults['size'], 'et_pb_fullwidth_slider-background_position' => $background_image_defaults['position'], 'et_pb_fullwidth_slider-background_repeat' => $background_image_defaults['repeat'], 'et_pb_fullwidth_slider-background_blend' => $background_image_defaults['blend'], // Module: Gallery. 'et_pb_gallery-hover_overlay_color' => 'rgba(255,255,255,0.9)', 'et_pb_gallery-title_font_size' => '16', 'et_pb_gallery-title_color' => '#333333', 'et_pb_gallery-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_gallery-title_line_height' => '1em', 'et_pb_gallery-title_font_style' => '', 'et_pb_gallery-caption_font_size' => '14', 'et_pb_gallery-caption_font_style' => '', 'et_pb_gallery-caption_color' => '#f3f3f3', 'et_pb_gallery-caption_line_height' => '18px', 'et_pb_gallery-caption_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_gallery-pagination_font_size' => '16px', 'et_pb_gallery-pagination_line_height' => '1em', 'et_pb_gallery-zoom_icon_color' => '', // Module: Image. 'et_pb_image-animation' => 'left', // Module: Login. 'et_pb_login-header_font_size' => '26', 'et_pb_login-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_login-header_line_height' => $font_defaults['line_height'], 'et_pb_login-body_font_size' => $font_defaults['size'], 'et_pb_login-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_login-body_line_height' => $font_defaults['line_height'], 'et_pb_login-header_font_style' => '', 'et_pb_login-custom_padding' => '40', 'et_pb_login-focus_border_color' => '#ffffff', 'et_pb_login-background_size' => $background_image_defaults['size'], 'et_pb_login-background_position' => $background_image_defaults['position'], 'et_pb_login-background_repeat' => $background_image_defaults['repeat'], 'et_pb_login-background_blend' => $background_image_defaults['blend'], // Module: Menu. 'et_pb_menu-background_size' => $background_image_defaults['size'], 'et_pb_menu-background_position' => $background_image_defaults['position'], 'et_pb_menu-background_repeat' => $background_image_defaults['repeat'], 'et_pb_menu-background_blend' => $background_image_defaults['blend'], // Module: Number Counter. 'et_pb_number_counter-title_font_size' => '16', 'et_pb_number_counter-title_line_height' => '1em', 'et_pb_number_counter-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_number_counter-title_font_style' => '', 'et_pb_number_counter-number_font_size' => '72', 'et_pb_number_counter-number_line_height' => '72px', 'et_pb_number_counter-number_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_number_counter-number_font_style' => '', 'et_pb_number_counter-title_color' => '#333333', 'et_pb_number_counter-background_size' => $background_image_defaults['size'], 'et_pb_number_counter-background_position' => $background_image_defaults['position'], 'et_pb_number_counter-background_repeat' => $background_image_defaults['repeat'], 'et_pb_number_counter-background_blend' => $background_image_defaults['blend'], // Module: Portfolio. 'et_pb_portfolio-hover_overlay_color' => 'rgba(255,255,255,0.9)', 'et_pb_portfolio-title_font_size' => '18', 'et_pb_portfolio-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_portfolio-title_line_height' => $font_defaults['line_height'], 'et_pb_portfolio-title_font_style' => '', 'et_pb_portfolio-title_color' => '#333333', 'et_pb_portfolio-caption_font_size' => '14', 'et_pb_portfolio-caption_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_portfolio-caption_line_height' => $font_defaults['line_height'], 'et_pb_portfolio-caption_font_style' => '', 'et_pb_portfolio-pagination_font_size' => '14', 'et_pb_portfolio-pagination_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_portfolio-pagination_line_height' => $font_defaults['line_height'], 'et_pb_portfolio-pagination_font_style' => '', 'et_pb_portfolio-background_size' => $background_image_defaults['size'], 'et_pb_portfolio-background_position' => $background_image_defaults['position'], 'et_pb_portfolio-background_repeat' => $background_image_defaults['repeat'], 'et_pb_portfolio-background_blend' => $background_image_defaults['blend'], 'et_pb_portfolio-zoom_icon_color' => '', // Module: Post Title. 'et_pb_post_title-title_font_size' => '26px', 'et_pb_post_title-title_line_height' => '1em', 'et_pb_post_title-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_post_title-meta_font_size' => $font_defaults['size'], 'et_pb_post_title-meta_line_height' => '1em', 'et_pb_post_title-meta_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_post_title-parallax' => 'off', 'et_pb_post_title-background_size' => $background_image_defaults['size'], 'et_pb_post_title-background_position' => $background_image_defaults['position'], 'et_pb_post_title-background_repeat' => $background_image_defaults['repeat'], 'et_pb_post_title-background_blend' => $background_image_defaults['blend'], // Module: Post Slider. 'et_pb_post_slider-background_size' => $background_image_defaults['size'], 'et_pb_post_slider-background_position' => $background_image_defaults['position'], 'et_pb_post_slider-background_repeat' => $background_image_defaults['repeat'], 'et_pb_post_slider-background_blend' => $background_image_defaults['blend'], // Module: Pricing Tables Item (Pricing Table). 'et_pb_pricing_table-header_font_size' => '22px', 'et_pb_pricing_table-header_color' => '#ffffff', 'et_pb_pricing_table-header_line_height' => '1em', 'et_pb_pricing_table-subheader_font_size' => '16px', 'et_pb_pricing_table-subheader_color' => '#ffffff', 'et_pb_pricing_table-price_font_size' => '80px', 'et_pb_pricing_table-price_color' => '#2EA3F2', 'et_pb_pricing_table-price_line_height' => '82px', 'et_pb_pricing_table-body_line_height' => '24px', 'et_pb_pricing_table-background_size' => $background_image_defaults['size'], 'et_pb_pricing_table-background_position' => $background_image_defaults['position'], 'et_pb_pricing_table-background_repeat' => $background_image_defaults['repeat'], 'et_pb_pricing_table-background_blend' => $background_image_defaults['blend'], 'et_pb_pricing_table-excluded_letter_spacing' => '0px', 'et_pb_pricing_table-excluded_line_height' => '1.7em', // Module: Pricing Tables. 'et_pb_pricing_tables-header_font_size' => '22', 'et_pb_pricing_tables-header_font_style' => '', 'et_pb_pricing_tables-subheader_font_size' => '16', 'et_pb_pricing_tables-subheader_font_style' => '', 'et_pb_pricing_tables-price_font_size' => '80', 'et_pb_pricing_tables-price_font_style' => '', 'et_pb_pricing_tables-header_color' => '#ffffff', 'et_pb_pricing_tables-header_line_height' => '1em', 'et_pb_pricing_tables-subheader_color' => '#ffffff', 'et_pb_pricing_tables-currency_frequency_font_size' => '16px', 'et_pb_pricing_tables-currency_frequency_letter_spacing' => '0px', 'et_pb_pricing_tables-currency_frequency_line_height' => '1.7em', 'et_pb_pricing_tables-price_letter_spacing' => '0px', 'et_pb_pricing_tables-price_color' => '#2EA3F2', 'et_pb_pricing_tables-price_line_height' => '82px', 'et_pb_pricing_tables-body_line_height' => '24px', 'et_pb_pricing_tables-background_size' => $background_image_defaults['size'], 'et_pb_pricing_tables-background_position' => $background_image_defaults['position'], 'et_pb_pricing_tables-background_repeat' => $background_image_defaults['repeat'], 'et_pb_pricing_tables-background_blend' => $background_image_defaults['blend'], 'et_pb_pricing_tables-excluded_letter_spacing' => '0px', 'et_pb_pricing_tables-excluded_line_height' => '1.7em', // Module: Shop. 'et_pb_shop-title_font_size' => '16', 'et_pb_shop-title_font_style' => '', 'et_pb_shop-sale_badge_font_size' => '16', 'et_pb_shop-sale_badge_font_style' => '', 'et_pb_shop-price_font_size' => '14', 'et_pb_shop-price_font_style' => '', 'et_pb_shop-sale_price_font_size' => '14', 'et_pb_shop-sale_price_font_style' => '', 'et_pb_shop-title_color' => '#333333', 'et_pb_shop-title_line_height' => '1em', 'et_pb_shop-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_shop-price_line_height' => '26px', 'et_pb_shop-price_letter_spacing' => $font_defaults['letter_spacing'], // Module: Sidebar. 'et_pb_sidebar-header_font_size' => '18', 'et_pb_sidebar-header_font_style' => '', 'et_pb_sidebar-header_color' => '#333333', 'et_pb_sidebar-header_line_height' => '1em', 'et_pb_sidebar-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_sidebar-remove_border' => 'off', 'et_pb_sidebar-body_font_size' => $font_defaults['size'], 'et_pb_sidebar-body_line_height' => $font_defaults['line_height'], 'et_pb_sidebar-body_letter_spacing' => $font_defaults['letter_spacing'], // Module: Signup. 'et_pb_signup-header_font_size' => '26', 'et_pb_signup-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_signup-header_line_height' => $font_defaults['line_height'], 'et_pb_signup-body_font_size' => $font_defaults['size'], 'et_pb_signup-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_signup-body_line_height' => $font_defaults['line_height'], 'et_pb_signup-result_message_font_size' => $font_defaults_h2['size'], 'et_pb_signup-result_message_line_height' => $font_defaults_h2['line_height'], 'et_pb_signup-header_font_style' => '', 'et_pb_signup-padding' => '20', 'et_pb_signup-focus_border_color' => '#ffffff', 'et_pb_signup-background_size' => $background_image_defaults['size'], 'et_pb_signup-background_position' => $background_image_defaults['position'], 'et_pb_signup-background_repeat' => $background_image_defaults['repeat'], 'et_pb_signup-background_blend' => $background_image_defaults['blend'], 'et_pb_signup-form_field_font_size' => '14', 'et_pb_signup-form_field_line_height' => $font_defaults['line_height'], 'et_pb_signup_form-form_field_letter_spacing' => $font_defaults['letter_spacing'], // Module: Slider Item (Slide). 'et_pb_slide-header_font_size' => '26px', 'et_pb_slide-header_color' => '#ffffff', 'et_pb_slide-header_line_height' => '1em', 'et_pb_slide-body_font_size' => '16px', 'et_pb_slide-body_color' => '#ffffff', 'et_pb_slide-background_size' => $background_image_defaults['size'], 'et_pb_slide-background_position' => $background_image_defaults['position'], 'et_pb_slide-background_repeat' => $background_image_defaults['repeat'], 'et_pb_slide-background_blend' => $background_image_defaults['blend'], // Module: Slider. 'et_pb_slider-header_font_size' => '46', 'et_pb_slider-header_line_height' => '1em', 'et_pb_slider-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_slider-header_font_style' => '', 'et_pb_slider-body_font_size' => '16', 'et_pb_slider-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_slider-body_line_height' => $font_defaults['line_height'], 'et_pb_slider-body_font_style' => '', 'et_pb_slider-padding' => '16', 'et_pb_slider-header_color' => '#ffffff', 'et_pb_slider-body_color' => '#ffffff', 'et_pb_slider-background_size' => $background_image_defaults['size'], 'et_pb_slider-background_position' => $background_image_defaults['position'], 'et_pb_slider-background_repeat' => $background_image_defaults['repeat'], 'et_pb_slider-background_blend' => $background_image_defaults['blend'], // Module: Social Media Follow. 'et_pb_social_media_follow-icon_size' => '14', 'et_pb_social_media_follow-button_font_style' => '', // Module: Tabs. 'et_pb_tabs-tab_font_size' => $font_defaults['size'], 'et_pb_tabs-tab_line_height' => $font_defaults['line_height'], 'et_pb_tabs-tab_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_tabs-title_font_size' => $font_defaults['size'], 'et_pb_tabs-body_font_size' => $font_defaults['size'], 'et_pb_tabs-body_line_height' => $font_defaults['line_height'], 'et_pb_tabs-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_tabs-title_font_style' => '', 'et_pb_tabs-padding' => '30', 'et_pb_tabs-background_size' => $background_image_defaults['size'], 'et_pb_tabs-background_position' => $background_image_defaults['position'], 'et_pb_tabs-background_repeat' => $background_image_defaults['repeat'], 'et_pb_tabs-background_blend' => $background_image_defaults['blend'], // Module: Tabs Item (Tab). 'et_pb_tab-background_size' => $background_image_defaults['size'], 'et_pb_tab-background_position' => $background_image_defaults['position'], 'et_pb_tab-background_repeat' => $background_image_defaults['repeat'], 'et_pb_tab-background_blend' => $background_image_defaults['blend'], // Module: Team Member (Person). 'et_pb_team_member-header_font_size' => '18', 'et_pb_team_member-header_font_style' => '', 'et_pb_team_member-subheader_font_size' => '14', 'et_pb_team_member-subheader_font_style' => '', 'et_pb_team_member-social_network_icon_size' => '16', 'et_pb_team_member-header_color' => '#333333', 'et_pb_team_member-header_line_height' => '1em', 'et_pb_team_member-header_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_team_member-body_font_size' => $font_defaults['size'], 'et_pb_team_member-body_line_height' => $font_defaults['line_height'], 'et_pb_team_member-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_team_member-background_size' => $background_image_defaults['size'], 'et_pb_team_member-background_position' => $background_image_defaults['position'], 'et_pb_team_member-background_repeat' => $background_image_defaults['repeat'], 'et_pb_team_member-background_blend' => $background_image_defaults['blend'], // Module: Testimonial. 'et_pb_testimonial-portrait_border_radius' => '90', 'et_pb_testimonial-portrait_width' => '90', 'et_pb_testimonial-portrait_height' => '90', 'et_pb_testimonial-author_name_font_style' => 'bold', 'et_pb_testimonial-author_details_font_style' => 'bold', 'et_pb_testimonial-border_color' => '#ffffff', 'et_pb_testimonial-border_width' => '1px', 'et_pb_testimonial-body_font_size' => $font_defaults['size'], 'et_pb_testimonial-body_line_height' => '1.5em', 'et_pb_testimonial-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_testimonial-background_size' => $background_image_defaults['size'], 'et_pb_testimonial-background_position' => $background_image_defaults['position'], 'et_pb_testimonial-background_repeat' => $background_image_defaults['repeat'], 'et_pb_testimonial-background_blend' => $background_image_defaults['blend'], 'et_pb_testimonial-quote_icon_background_color' => '#f5f5f5', // Module: Text. 'et_pb_text-header_font_size' => $font_defaults_h1['size'], 'et_pb_text-header_letter_spacing' => $font_defaults_h1['letter_spacing'], 'et_pb_text-header_line_height' => $font_defaults_h1['line_height'], 'et_pb_text-text_font_size' => $font_defaults['size'], 'et_pb_text-text_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_text-text_line_height' => $font_defaults['line_height'], 'et_pb_text-border_color' => '#ffffff', 'et_pb_text-border_width' => '1px', 'et_pb_text-background_size' => $background_image_defaults['size'], 'et_pb_text-background_position' => $background_image_defaults['position'], 'et_pb_text-background_repeat' => $background_image_defaults['repeat'], 'et_pb_text-background_blend' => $background_image_defaults['blend'], // Module: Toggle. 'et_pb_toggle-title_font_size' => '16', 'et_pb_toggle-title_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_toggle-title_font_style' => '', 'et_pb_toggle-inactive_title_font_style' => '', 'et_pb_toggle-toggle_icon_size' => '16', 'et_pb_toggle-title_color' => '#333333', 'et_pb_toggle-title_line_height' => '1em', 'et_pb_toggle-custom_padding' => '20', 'et_pb_toggle-body_font_size' => $font_defaults['size'], 'et_pb_toggle-body_line_height' => $font_defaults['line_height'], 'et_pb_toggle-body_letter_spacing' => $font_defaults['letter_spacing'], 'et_pb_toggle-background_size' => $background_image_defaults['size'], 'et_pb_toggle-background_position' => $background_image_defaults['position'], 'et_pb_toggle-background_repeat' => $background_image_defaults['repeat'], 'et_pb_toggle-background_blend' => $background_image_defaults['blend'], // Module: Woo Title. 'et_pb_wc_title-header_font_size' => $font_defaults_h1['size'], 'et_pb_wc_title-header_line_height' => '1em', 'et_pb_wc_stock-in_stock_text_color' => '#77a464', // Global: Field Input. 'all_field_font_size' => '16', 'all_field_border_width' => '0', 'all_field_border_radius' => '3', 'all_field_spacing' => '0', 'all_field_font_style' => '', $hover->get_hover_field( 'all_field_border_radius' ) => '3', $hover->get_hover_field( 'all_field_spacing' ) => '0', ); if ( et_builder_has_limitation( 'forced_icon_color_default' ) ) { $defaults['et_pb_gallery-zoom_icon_color'] = et_get_option( 'accent_color', '#2ea3f2' ); $defaults['et_pb_portfolio-zoom_icon_color'] = et_get_option( 'accent_color', '#2ea3f2' ); $defaults['et_pb_fullwidth-portfolio-zoom_icon_color'] = et_get_option( 'accent_color', '#2ea3f2' ); $defaults['et_pb_filterable_portfolio-zoom_icon_color'] = et_get_option( 'accent_color', '#2ea3f2' ); } $module_presets_manager = ET_Builder_Global_Presets_Settings::instance(); if ( ! et_is_builder_plugin_active() && ! ET_Builder_Global_Presets_Settings::is_customizer_migrated() ) { $module_presets_manager->migrate_customizer_settings( $defaults ); } $custom_defaults_unmigrated = et_get_option( ET_Builder_Global_Presets_Settings::CUSTOM_DEFAULTS_UNMIGRATED_OPTION, false ); // reformat defaults array and add actual values to it. foreach ( $defaults as $setting_name => $default_value ) { $defaults[ $setting_name ] = array( 'default' => $default_value, ); if ( ! et_is_builder_plugin_active() ) { $actual_value = (string) et_get_option( $setting_name, '', '', true ); $add_as_actual = false; // Pass Theme Customizer non module specific settings. $setting_array = explode( '-', $setting_name ); $module_name = $setting_array[0]; if ( empty( $setting_array[1] ) || ! empty( $custom_defaults_unmigrated->$module_name ) && in_array( $setting_array[1], ET_Builder_Global_Presets_Settings::$phase_two_settings, true ) ) { $add_as_actual = true; } if ( $add_as_actual && '' !== $actual_value ) { $defaults[ $setting_name ]['actual'] = $actual_value; } } } self::$_settings = apply_filters( 'et_set_default_values', $defaults ); } /** * Get default global setting value * * @param string $name Setting name. * @param string $get_value Defines the value it should get: actual or default. * * @return mixed Global setting value or FALSE */ public static function get_value( $name, $get_value = 'actual' ) { $settings = self::$_settings; if ( ! isset( $settings[ $name ] ) ) { return false; } if ( isset( $settings[ $name ][ $get_value ] ) ) { $result = $settings[ $name ][ $get_value ]; } elseif ( 'actual' === $get_value && isset( $settings[ $name ]['default'] ) ) { $result = $settings[ $name ]['default']; } else { $result = false; } return $result; } /** * Translate 'on'/'off' into true/false * Pagebuilder use pseudo checkbox with 'on'/'off' value while customizer use true/false * which cause ET_Global_Settings' default value incompatibilities. * * @param string $name Setting name. * @param string $get_value Defines the value it should get: actual or default. * @param string $source pagebuilder or customizer. * * @return bool|string */ public static function get_checkbox_value( $name, $get_value = 'actual', $source = 'pagebuilder' ) { // Get value. $value = self::get_value( $name, $get_value ); // customizer to pagebuilder || pagebuilder to customizer. if ( 'customizer' === $source ) { if ( false === $value ) { return 'off'; } else { return 'on'; } } else { if ( 'off' === $value || false === $value ) { return false; } else { return true; } } } } /** * Initialize the global settings. */ function et_builder_init_global_settings() { ET_Global_Settings::init(); } PK X�\ �F F / builder/class-et-builder-plugin-compat-base.phpnu �[��� <?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Base class for plugin compatibility file * * @since 0.7 (builder version) */ class ET_Builder_Plugin_Compat_Base { public $plugin_id; /** * Get plugin dir path based on plugin_id * * @return sting */ function get_plugin_dir_path() { return WP_PLUGIN_DIR . '/' . $this->plugin_id; } /** * Get plugin data based on initialized plugin_id * * @return array */ function get_plugin_data() { return get_plugin_data( $this->get_plugin_dir_path(), false ); } /** * Get plugin version based on initialized plugin_id * * @return string */ function get_plugin_version() { $plugin_data = $this->get_plugin_data(); if ( ! isset( $plugin_data['Version'] ) ) { return false; } return $plugin_data['Version']; } } PK X�\��� � � builder/template-preview.phpnu �[��� <?php // Prevent file from being loaded directly if ( ! defined( 'ABSPATH' ) ) { die( '-1' ); } et_core_security_check( 'edit_posts', 'et_pb_preview_nonce', '', '_GET' ); $container_style = isset( $_POST['is_fb_preview'] ) || isset( $_GET['item_id'] ) ? 'max-width: none; padding: 0;' : ''; $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; if ( ! current_user_can( 'edit_post', $post_id ) ) { $post_id = 0; } $post = get_post( $post_id ); ?> <!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo( 'charset' ); ?>" /> <?php /** * Fires in the head, before {@see wp_head()} is called. This action can be used to * insert elements into the beginning of the head before any styles are scripts. * * @since 1.0 */ do_action( 'et_head_meta' ); $template_directory_uri = get_template_directory_uri(); ?> <link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" /> <script type="text/javascript"> document.documentElement.className = 'js'; </script> <?php wp_head(); ?> <style type="text/css"> html {margin-top: 0 !important;} .entry-content {margin-top: -1.5em !important;} </style> </head> <body <?php body_class(); ?>> <div id="page-container"> <div id="main-content"> <div class="container" style="<?php echo esc_attr( $container_style ); ?>"> <div id="<?php echo esc_attr( apply_filters( 'et_pb_preview_wrap_id', 'content' ) ); ?>"> <div class="<?php echo esc_attr( apply_filters( 'et_pb_preview_wrap_class', 'entry-content post-content entry content' ) ); ?>"> <?php if ( isset( $_GET['item_id'] ) ) { $item_id = (int) $_GET['item_id']; $rendered_template = et_theme_builder_render_library_template_preview( $item_id ); echo et_core_intentionally_unescaped( $rendered_template, 'html' ); } elseif ( isset( $_POST['shortcode'] ) ) { if ( $post ) { // Setup postdata so post-dependent data like dynamic content // can be resolved. setup_postdata( $post ); } // process content for builder plugin if ( et_is_builder_plugin_active() ) { $content = do_shortcode( wp_unslash( $_POST['shortcode'] ) ); $content = str_replace( ']]>', ']]>', $content ); $content = et_builder_get_builder_content_opening_wrapper() . et_builder_get_layout_opening_wrapper() . $content . et_builder_get_layout_closing_wrapper() . et_builder_get_builder_content_closing_wrapper(); } else { $content = apply_filters( 'the_content', wp_unslash( $_POST['shortcode'] ) ); $content = str_replace( ']]>', ']]>', $content ); } if ( $post ) { wp_reset_postdata(); } echo et_core_intentionally_unescaped( $content, 'html' ); } else { printf( '<p class="et-pb-preview-loading"><span>%1$s</span></p>', esc_html__( 'Loading preview...', 'et_builder' ) ); } ?> </div> <!-- .entry-content.post-content.entry --> </div> <!-- #content --> <?php echo et_builder_disabled_link_modal(); ?> </div><!-- .container --> </div><!-- #main-content --> </div> <!-- #page-container --> <?php wp_footer(); ?> </body> </html> PK X�\V�5X� X� builder/framework.phpnu �[��� <?php if ( ! defined( 'ET_BUILDER_CSS_WRAPPER_PREFIX' ) ) { define( 'ET_BUILDER_CSS_WRAPPER_PREFIX', '.et-db' ); } if ( ! defined( 'ET_BUILDER_CSS_CONTAINER_PREFIX' ) ) { define( 'ET_BUILDER_CSS_CONTAINER_PREFIX', '#et-boc' ); } if ( ! defined( 'ET_BUILDER_CSS_LAYOUT_PREFIX' ) ) { define( 'ET_BUILDER_CSS_LAYOUT_PREFIX', ET_BUILDER_CSS_CONTAINER_PREFIX . ' .et-l' ); } if ( ! defined( 'ET_BUILDER_CSS_PREFIX' ) ) { define( 'ET_BUILDER_CSS_PREFIX', ET_BUILDER_CSS_WRAPPER_PREFIX . ' ' . ET_BUILDER_CSS_LAYOUT_PREFIX ); } if ( ! defined( 'ET_BUILDER_PLACEHOLDER_LANDSCAPE_IMAGE_DATA' ) ) { define( 'ET_BUILDER_PLACEHOLDER_LANDSCAPE_IMAGE_DATA', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4MCIgaGVpZ2h0PSI1NDAiIHZpZXdCb3g9IjAgMCAxMDgwIDU0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPHBhdGggZmlsbD0iI0VCRUJFQiIgZD0iTTAgMGgxMDgwdjU0MEgweiIvPgogICAgICAgIDxwYXRoIGQ9Ik00NDUuNjQ5IDU0MGgtOTguOTk1TDE0NC42NDkgMzM3Ljk5NSAwIDQ4Mi42NDR2LTk4Ljk5NWwxMTYuMzY1LTExNi4zNjVjMTUuNjItMTUuNjIgNDAuOTQ3LTE1LjYyIDU2LjU2OCAwTDQ0NS42NSA1NDB6IiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgICAgICA8Y2lyY2xlIGZpbGwtb3BhY2l0eT0iLjA1IiBmaWxsPSIjMDAwIiBjeD0iMzMxIiBjeT0iMTQ4IiByPSI3MCIvPgogICAgICAgIDxwYXRoIGQ9Ik0xMDgwIDM3OXYxMTMuMTM3TDcyOC4xNjIgMTQwLjMgMzI4LjQ2MiA1NDBIMjE1LjMyNEw2OTkuODc4IDU1LjQ0NmMxNS42Mi0xNS42MiA0MC45NDgtMTUuNjIgNTYuNTY4IDBMMTA4MCAzNzl6IiBmaWxsLW9wYWNpdHk9Ii4yIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KICAgIDwvZz4KPC9zdmc+Cg==' ); } if ( ! defined( 'ET_BUILDER_PLACEHOLDER_PORTRAIT_IMAGE_DATA' ) ) { define( 'ET_BUILDER_PLACEHOLDER_PORTRAIT_IMAGE_DATA', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgdmlld0JveD0iMCAwIDUwMCA1MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxwYXRoIGZpbGw9IiNFQkVCRUIiIGQ9Ik0wIDBoNTAwdjUwMEgweiIvPgogICAgICAgIDxyZWN0IGZpbGwtb3BhY2l0eT0iLjEiIGZpbGw9IiMwMDAiIHg9IjY4IiB5PSIzMDUiIHdpZHRoPSIzNjQiIGhlaWdodD0iNTY4IiByeD0iMTgyIi8+CiAgICAgICAgPGNpcmNsZSBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsPSIjMDAwIiBjeD0iMjQ5IiBjeT0iMTcyIiByPSIxMDAiLz4KICAgIDwvZz4KPC9zdmc+Cg==' ); } if ( ! defined( 'ET_BUILDER_PLACEHOLDER_PORTRAIT_VARIATION_IMAGE_DATA' ) ) { define( 'ET_BUILDER_PLACEHOLDER_PORTRAIT_VARIATION_IMAGE_DATA', 'data:image/svg+xml;base64,PHN2ZyBpZD0ibXlTdmdFbGVtZW50IiB3aWR0aD0iNTQwIiBoZWlnaHQ9IjU0MCIgdmlld0JveD0iMCAwIDU0MCA1NDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8Zz4KCQk8cGF0aCBkPSJNMCwwaDU0MHY1NDBIMFYweiIgZmlsbD0iI0VCRUJFQiIvPgoJCTxwYXRoIGQ9Ik00NDUuNiw1NDBoLTk5bC0yMDItMjAyTDAsNDgyLjZ2LTk5bDExNi40LTExNi40YzE1LjYtMTUuNiw0MC45LTE1LjYsNTYuNiwwTDQ0NS42LDU0MEw0NDUuNiw1NDB6IiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KCQk8Y2lyY2xlIGN4PSIzMzEiIGN5PSIxNDgiIHI9IjcwIiBmaWxsLW9wYWNpdHk9Ii4wNSIgZmlsbD0iIzAwMCIvPgoJCTxwb2x5Z29uIHBvaW50cz0iNTQwLDIxNS4yIDIxNS4yLDU0MCAzMjguMyw1NDAgNTQwLDMyOC4zIiBmaWxsLW9wYWNpdHk9Ii4yIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz4KCTwvZz4KPC9zdmc+' ); } // phpcs:ignore WordPress.Security.NonceVerification -- Only checking arg is set. if ( isset( $_REQUEST['et_check_mod_pagespeed'] ) ) { // This is an internal request used to check response headers, hence we exit early. // Must still output some html or else Mod Pagepeed won't add any header. echo '<html><head/></html>'; die(); } // Detect Codeception and load additional code required by tests. if ( class_exists( 'Codeception\TestCase\WPTestCase' ) ) { foreach ( glob( ET_BUILDER_DIR . 'tests/codeception/wpunit/*.php' ) as $test_file ) { require_once $test_file; } } require_once ET_BUILDER_DIR . 'autoload.php'; /** * Retrieve a commonly used translation. * * @since 4.4.9 * * @param string $key Translation key. * @param boolean $reset Reset cache. * * @return string */ function et_builder_i18n( $key, $reset = false ) { static $cache; if ( true === $reset ) { $cache = array(); } if ( ! isset( $cache[ $key ] ) ) { $cache[ $key ] = ET_Builder_I18n::get( $key ); } return $cache[ $key ]; } /** * Resets commonly used translations cache. * * @since 4.4.9 * * @return void */ function et_builder_i18n_reset_cache() { et_builder_i18n( '', true ); } /** * Escape translation with optional value and caches the result. * * @since 4.4.9 * * @param string $text * @param string $value * * @return string. */ function et_esc_html_once( $text, $value = '' ) { static $cache = array(); if ( isset( $cache[ $text ][ $value ] ) ) { return $cache[ $text ][ $value ]; } $escaped = esc_html( '' === $value ? $text : sprintf( $text, $value ) ); $cache[ $text ][ $value ] = $escaped; return $escaped; } /** * Checks to see if Critical CSS is enabled. * * @since 4.10.0 * * @return string. */ function et_builder_is_critical_enabled() { global $shortname; $value = false; if ( et_is_builder_plugin_active() ) { $options = get_option( 'et_pb_builder_options', array() ); $value = isset( $options['performance_main_critical_css'] ) ? $options['performance_main_critical_css'] : 'on'; } else { $value = et_get_option( $shortname . '_critical_css', 'on' ); } return apply_filters( 'et_pb_critical_css_enabled', 'on' === $value ); } require_once ET_BUILDER_DIR . 'compat/early.php'; require_once ET_BUILDER_DIR . 'compat/scripts.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/blocks/Layout.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/blocks/PostExcerpt.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/utils/Conversion.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/utils/Editor.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/EditorTypography.php'; require_once ET_BUILDER_DIR . 'core.php'; require_once ET_BUILDER_DIR . 'conditions.php'; require_once ET_BUILDER_DIR . 'post/PostStack.php'; if ( ! ( is_admin() || wp_doing_ajax() || is_customize_preview() || is_et_pb_preview() ) ) { require_once ET_BUILDER_DIR . 'feature/DoNotCachePage.php'; } require_once ET_BUILDER_DIR . 'feature/ClassicEditor.php'; require_once ET_BUILDER_DIR . 'feature/AjaxCache.php'; require_once ET_BUILDER_DIR . 'feature/post-content.php'; require_once ET_BUILDER_DIR . 'feature/content-retriever/ContentRetriever.php'; if ( et_builder_is_critical_enabled() ) { require_once ET_BUILDER_DIR . 'feature/CriticalCSS.php'; } require_once ET_BUILDER_DIR . 'feature/dynamic-assets/dynamic-assets.php'; require_once ET_BUILDER_DIR . 'feature/dynamic-assets/class-dynamic-assets.php'; require_once ET_BUILDER_DIR . 'feature/dynamic-content.php'; require_once ET_BUILDER_DIR . 'feature/search-posts.php'; require_once ET_BUILDER_DIR . 'feature/ErrorReport.php'; require_once ET_BUILDER_DIR . 'api/DiviExtensions.php'; require_once ET_BUILDER_DIR . 'api/rest/BlockLayout.php'; require_once ET_BUILDER_DIR . 'frontend-builder/theme-builder/theme-builder.php'; require_once ET_BUILDER_DIR . 'feature/global-presets/Settings.php'; require_once ET_BUILDER_DIR . 'feature/global-presets/History.php'; require_once ET_BUILDER_DIR . 'feature/window.php'; require_once ET_BUILDER_DIR . 'feature/et-server-frame.php'; require_once ET_BUILDER_DIR . 'feature/ajax-data/AjaxData.php'; require_once ET_BUILDER_DIR . 'feature/display-conditions/DisplayConditions.php'; require_once ET_BUILDER_DIR . 'feature/icon-manager/ExtendedFontIcons.php'; require_once ET_BUILDER_DIR . 'feature/background-masks/Functions.php'; require_once ET_BUILDER_DIR . 'feature/background-masks/PatternFields.php'; require_once ET_BUILDER_DIR . 'feature/background-masks/MaskFields.php'; require_once ET_BUILDER_DIR . 'feature/gutenberg/BlockTemplates.php'; require_once ET_BUILDER_DIR . 'feature/local-library.php'; require_once ET_BUILDER_DIR . 'feature/ai-button.php'; // Conditional Includes. if ( et_is_woocommerce_plugin_active() ) { require_once ET_BUILDER_DIR . 'feature/woocommerce-modules.php'; } global $shortname; if ( apply_filters( 'et_builder_enable_jquery_body', true ) && ! ( is_admin() || wp_doing_ajax() || et_is_builder_plugin_active() || is_customize_preview() || is_et_pb_preview() ) && 'on' === et_get_option( $shortname . '_enable_jquery_body', 'on' ) ) { require_once ET_BUILDER_DIR . 'feature/JQueryBody.php'; } if ( wp_doing_ajax() && ! is_customize_preview() ) { define( 'WPE_HEARTBEAT_INTERVAL', et_builder_heartbeat_interval() ); // Default ajax request exceptions $builder_load_requests = array( 'action' => array( 'et_pb_get_backbone_template', 'et_pb_get_backbone_templates', 'et_pb_process_computed_property', 'et_fb_ajax_render_shortcode', 'et_fb_ajax_save', 'et_fb_get_shortcode_from_fb_object', 'et_fb_get_html_from_shortcode', 'et_fb_ajax_drop_autosave', 'et_fb_get_saved_layouts', 'et_fb_save_layout', 'et_fb_get_cloud_item_content', 'et_fb_update_layout', 'et_pb_execute_content_shortcodes', 'et_pb_ab_builder_data', 'et_pb_create_ab_tables', 'et_pb_update_stats_table', 'et_pb_ab_clear_cache', 'et_pb_ab_clear_stats', 'et_fb_prepare_shortcode', 'et_fb_process_imported_content', 'et_fb_get_saved_templates', 'et_fb_retrieve_builder_data', 'et_fb_update_builder_assets', 'et_pb_process_custom_font', 'et_builder_email_add_account', // email opt-in module. 'et_builder_email_remove_account', // email opt-in module. 'et_builder_email_get_lists', // email opt-in module. 'et_builder_save_settings', // builder plugin dashboard (global builder settings). 'save_epanel', // ePanel (global builder settings). 'save_epanel_temp', // ePanel (temp global builder settings). 'et_builder_library_get_layout', 'et_builder_library_update_terms', 'et_builder_toggle_cloud_status', 'et_builder_library_save_temp_layout', 'et_builder_library_remove_temp_layout', 'et_builder_library_clear_temp_presets', 'et_builder_library_update_item', 'et_builder_library_convert_item', 'et_theme_builder_library_update_item', 'et_theme_builder_library_save_temp_layout', 'et_theme_builder_library_remove_temp_layout', 'et_theme_builder_library_get_preset_items', 'et_theme_builder_library_get_set_items', 'et_builder_library_upload_thumbnail', 'et_builder_library_get_cloud_token', 'et_builder_library_get_layouts_data', 'et_theme_builder_library_get_items_data', 'et_theme_builder_library_update_terms', 'et_theme_builder_library_get_item', 'et_fb_fetch_attachments', 'et_pb_get_saved_templates', 'et_builder_resolve_post_content', 'et_builder_activate_bfb_auto_draft', 'et_builder_toggle_bfb', 'et_fb_error_report', 'et_core_portability_import', 'et_core_version_rollback', 'update-theme', 'et_safe_mode_update', 'et_core_portability_export', 'et_core_portability_import_default_presets', 'et_builder_migrate_module_customizer_phase_two', 'et_builder_save_global_presets_history', 'et_builder_retrieve_global_presets_history', 'et_theme_builder_api_export_theme_builder_step', 'et_theme_builder_api_import_theme_builder_step', 'et_pb_submit_subscribe_form', 'et_builder_get_woocommerce_tabs', 'et_builder_global_colors_save', 'et_builder_default_colors_update', 'et_builder_ajax_save_domain_token', 'et_fb_fetch_before_after_components', 'et_code_snippets_library_get_items', 'et_builder_global_colors_get', 'et_update_customizer_fonts', 'et_ai_shortcode_string_to_object', ), ); // AJAX requests that use PHP modules cache for performance reasons. $builder_use_cache_actions = array( 'heartbeat', 'et_builder_retrieve_global_presets_history', 'et_fb_get_saved_templates', 'et_fb_ajax_save', 'et_fb_ajax_drop_autosave', ); // Added built-in third party plugins support // Easy Digital Downloads if ( class_exists( 'Easy_Digital_Downloads' ) ) { $builder_load_requests['action'][] = 'edd_load_gateway'; } // WooCommerce - it uses its own ajax endpoint instead of admin-ajax.php if ( class_exists( 'WooCommerce' ) ) { $builder_load_requests['wc-ajax'] = array( 'update_order_review', ); } // WPML. if ( class_exists( 'SitePress' ) ) { $builder_load_requests['action'][] = 'et_builder_wpml_translate_layout'; } // Merging third party exceptions; built-in exceptions should not be removable $builder_custom_load_requests = apply_filters( 'et_builder_load_requests', array() ); if ( ! empty( $builder_custom_load_requests ) ) { foreach ( $builder_custom_load_requests as $builder_custom_query_string => $builder_custom_possible_values ) { if ( ! isset( $builder_load_requests[ $builder_custom_query_string ] ) ) { $builder_load_requests[ $builder_custom_query_string ] = $builder_custom_possible_values; } else { $builder_load_requests[ $builder_custom_query_string ] = array_merge( $builder_custom_possible_values, $builder_load_requests[ $builder_custom_query_string ] ); } } } // Legacy compatibility for action only request exception filter $builder_load_actions = apply_filters( 'et_builder_load_actions', array() ); if ( ! empty( $builder_load_actions ) ) { $builder_load_requests['action'] = array_merge( $builder_load_actions, $builder_load_requests['action'] ); } // Determine whether current AJAX request should load builder or not $load_builder_on_ajax = false; // If current request's query string exists on list of possible values, load builder // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification foreach ( $builder_load_requests as $query_string => $possible_values ) { if ( ! is_array( $possible_values ) ) { continue; } if ( isset( $_REQUEST[ $query_string ] ) && in_array( $_REQUEST[ $query_string ], $possible_values ) ) { $load_builder_on_ajax = true; break; } } define( 'ET_BUILDER_LOAD_ON_AJAX', $load_builder_on_ajax ); $action = et_()->array_get( $_POST, 'action', false ); $force_builder_load = isset( $_POST['et_load_builder_modules'] ) && '1' === $_POST['et_load_builder_modules']; $force_memory_limit = 'et_fb_retrieve_builder_data' === $action; if ( 'heartbeat' === $action ) { // if this is the heartbeat, and if its not packing our heartbeat data, then return if ( ! isset( $_REQUEST['data'] ) || ! isset( $_REQUEST['data']['et'] ) ) { return; } } elseif ( ! $force_builder_load && ! $load_builder_on_ajax ) { return; } if ( $force_memory_limit || et_should_memory_limit_increase() ) { et_increase_memory_limit(); } if ( $action && in_array( $action, $builder_use_cache_actions ) ) { add_filter( 'et_builder_ajax_use_cache', '__return_true' ); } // phpcs:enable } /** * Get product script handle for the main script bundle. * * @since 4.10.0 */ function et_get_combined_script_handle() { global $shortname; if ( 'divi' === $shortname ) { $combined_script_handle = 'divi-custom-script'; } elseif ( 'extra' === $shortname ) { $combined_script_handle = 'extra-scripts'; } else { $combined_script_handle = 'divi-builder-custom-script'; } return $combined_script_handle; } /** * Localizes the main front end bundle and adds variables * for et-builder-modules-global-functions-script. * * @since 4.10.0 */ function et_builder_load_global_functions_script() { wp_localize_script( et_get_combined_script_handle(), 'et_builder_utils_params', array( 'condition' => array( 'diviTheme' => function_exists( 'et_divi_fonts_url' ), 'extraTheme' => function_exists( 'et_extra_fonts_url' ), ), 'scrollLocations' => et_builder_get_window_scroll_locations(), 'builderScrollLocations' => et_builder_get_onload_scroll_locations(), 'onloadScrollLocation' => et_builder_get_onload_scroll_location(), 'builderType' => et_builder_get_current_builder_type(), ) ); } add_action( 'wp_enqueue_scripts', 'et_builder_load_global_functions_script', 11 ); function et_builder_load_modules_styles() { $current_page_id = apply_filters( 'et_is_ab_testing_active_post_id', get_the_ID() ); $is_fb_enabled = et_core_is_fb_enabled(); $ab_tests = function_exists( 'et_builder_ab_get_current_tests' ) ? et_builder_ab_get_current_tests() : array(); $is_ab_testing = ! empty( $ab_tests ); $google_maps_api_url_args = array( 'v' => 3, 'key' => et_pb_get_google_api_key(), ); if ( $is_fb_enabled && ! et_builder_tb_enabled() && ! et_builder_bfb_enabled() ) { $google_maps_api_url_args['callback'] = 'ETBuilderInitGoogleMaps'; } $google_maps_api_url = add_query_arg( $google_maps_api_url_args, is_ssl() ? 'https://maps.googleapis.com/maps/api/js' : 'http://maps.googleapis.com/maps/api/js' ); wp_register_script( 'salvattore', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/salvattore.js', array(), ET_BUILDER_VERSION, true ); wp_register_script( 'google-maps-api', esc_url_raw( $google_maps_api_url ), array(), ET_BUILDER_VERSION, true ); $frontend_scripts_data = array( 'builderCssContainerPrefix' => ET_BUILDER_CSS_CONTAINER_PREFIX, 'builderCssLayoutPrefix' => ET_BUILDER_CSS_LAYOUT_PREFIX, ); wp_localize_script( et_get_combined_script_handle(), 'et_frontend_scripts', $frontend_scripts_data ); // Load modules wrapper on CPT. // Use get_the_ID() explicitly so we decide based on the first post of an archive page. if ( et_builder_post_is_of_custom_post_type( get_the_ID() ) ) { wp_enqueue_script( 'et-builder-cpt-modules-wrapper', ET_BUILDER_URI . '/scripts/cpt-modules-wrapper.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); $modules_wrapper_data = array( 'builderCssContainerPrefix' => ET_BUILDER_CSS_CONTAINER_PREFIX, 'builderCssLayoutPrefix' => ET_BUILDER_CSS_LAYOUT_PREFIX, ); wp_localize_script( 'et-builder-cpt-modules-wrapper', 'et_modules_wrapper', $modules_wrapper_data ); } if ( et_builder_has_limitation( 'register_fittext_script' ) ) { wp_register_script( 'fittext', ET_BUILDER_URI . '/scripts/ext/jquery.fittext.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); } // Load visible.min.js only if AB testing active on current page OR VB (because post settings is synced between VB and BB) if ( $is_ab_testing || $is_fb_enabled ) { wp_enqueue_script( 'et-jquery-visible-viewport', ET_BUILDER_URI . '/scripts/ext/jquery.visible.min.js', array( 'jquery', et_get_combined_script_handle() ), ET_BUILDER_VERSION, true ); } $pb_custom_data = array( 'ajaxurl' => is_ssl() ? admin_url( 'admin-ajax.php' ) : admin_url( 'admin-ajax.php', 'http' ), 'images_uri' => get_template_directory_uri() . '/images', 'builder_images_uri' => ET_BUILDER_URI . '/images', 'et_frontend_nonce' => wp_create_nonce( 'et_frontend_nonce' ), 'subscription_failed' => esc_html__( 'Please, check the fields below to make sure you entered the correct information.', 'et_builder' ), 'et_ab_log_nonce' => wp_create_nonce( 'et_ab_testing_log_nonce' ), 'fill_message' => esc_html__( 'Please, fill in the following fields:', 'et_builder' ), 'contact_error_message' => esc_html__( 'Please, fix the following errors:', 'et_builder' ), 'invalid' => esc_html__( 'Invalid email', 'et_builder' ), 'captcha' => esc_html__( 'Captcha', 'et_builder' ), 'prev' => esc_html__( 'Prev', 'et_builder' ), 'previous' => esc_html__( 'Previous', 'et_builder' ), 'next' => esc_html__( 'Next', 'et_builder' ), 'wrong_captcha' => esc_html__( 'You entered the wrong number in captcha.', 'et_builder' ), 'wrong_checkbox' => esc_html__( 'Checkbox', 'et_builder' ), 'ignore_waypoints' => et_is_ignore_waypoints() ? 'yes' : 'no', 'is_divi_theme_used' => function_exists( 'et_divi_fonts_url' ), 'widget_search_selector' => apply_filters( 'et_pb_widget_search_selector', '.widget_search' ), 'ab_tests' => $ab_tests, 'is_ab_testing_active' => $is_ab_testing, 'page_id' => $current_page_id, 'unique_test_id' => get_post_meta( $current_page_id, '_et_pb_ab_testing_id', true ), 'ab_bounce_rate' => '' !== get_post_meta( $current_page_id, '_et_pb_ab_bounce_rate_limit', true ) ? get_post_meta( $current_page_id, '_et_pb_ab_bounce_rate_limit', true ) : 5, 'is_cache_plugin_active' => false === et_pb_detect_cache_plugins() ? 'no' : 'yes', 'is_shortcode_tracking' => get_post_meta( $current_page_id, '_et_pb_enable_shortcode_tracking', true ), 'tinymce_uri' => defined( 'ET_FB_ASSETS_URI' ) ? ET_FB_ASSETS_URI . '/vendors' : '', 'accent_color' => et_builder_accent_color(), /** * Filters Waypoints options for client side rendering. * * @since 4.15.0 * * @param array $options { * Filtered Waypoints options. Only support `context` at this moment because * there is no test case for other properties. * * @type string[] $context List of container selectors for the Waypoint. The * element will iterate and looking for the closest * parent element matches the given selectors. */ 'waypoints_options' => apply_filters( 'et_builder_waypoints_options', array() ), ); wp_localize_script( et_get_combined_script_handle(), 'et_pb_custom', $pb_custom_data ); /** * Only load this during builder preview screen session */ if ( is_et_pb_preview() ) { // Set fixed protocol for preview URL to prevent cross origin issue $preview_scheme = is_ssl() ? 'https' : 'http'; // Get home url, then parse it $preview_origin_component = parse_url( home_url( '', $preview_scheme ) ); // Rebuild origin URL, strip sub-directory address if there's any (postMessage e.origin doesn't pass sub-directory address) $preview_origin = ''; // Perform check, prevent unnecessary error if ( isset( $preview_origin_component['scheme'] ) && isset( $preview_origin_component['host'] ) ) { $preview_origin = "{$preview_origin_component['scheme']}://{$preview_origin_component['host']}"; // Append port number if different port number is being used if ( isset( $preview_origin_component['port'] ) ) { $preview_origin = "{$preview_origin}:{$preview_origin_component['port']}"; } } // Enqueue theme's style.css if it hasn't been enqueued (possibly being hardcoded by theme) if ( ! et_builder_has_theme_style_enqueued() && et_builder_has_limitation( 'force_enqueue_theme_style' ) ) { wp_enqueue_style( 'et-builder-theme-style-css', get_stylesheet_uri(), array() ); } wp_enqueue_style( 'et-builder-preview-style', ET_BUILDER_URI . '/styles/preview.css', array(), ET_BUILDER_VERSION ); wp_enqueue_script( 'et-builder-preview-script', ET_BUILDER_URI . '/frontend-builder/build/frontend-builder-preview.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); $preview_params_data = array( 'preview_origin' => esc_url( $preview_origin ), 'alert_origin_not_matched' => sprintf( esc_html__( 'Unauthorized access. Preview cannot be accessed outside %1$s.', 'et_builder' ), esc_url( home_url( '', $preview_scheme ) ) ), ); wp_localize_script( 'et-builder-preview-script', 'et_preview_params', $preview_params_data ); } } add_action( 'wp_enqueue_scripts', 'et_builder_load_modules_styles', 11 ); function et_builder_get_modules_js_data() { // Data shouldn't be loaded in Builder, so always pass an empty array there. $animation_data = et_core_is_fb_enabled() ? array() : et_builder_handle_animation_data(); $animation_data_json = json_encode( $animation_data ); $link_options_data = et_core_is_fb_enabled() ? array() : et_builder_handle_link_options_data(); $link_options_data_json = json_encode( $link_options_data ); if ( empty( $animation_data ) && empty( $link_options_data ) ) { return; } ?> <script type="text/javascript"> <?php if ( $animation_data ) : ?> var et_animation_data = <?php echo et_core_esc_previously( $animation_data_json ); ?>; <?php endif; if ( $link_options_data ) : ?> var et_link_options_data = <?php echo et_core_esc_previously( $link_options_data_json ); ?>; <?php endif; ?> </script> <?php } add_action( 'wp_footer', 'et_builder_get_modules_js_data' ); // Force Backbone templates cache to be cleared on language change to make sure the settings modal is translated // defaults for arguments are provided because their number is different for both the actions function et_pb_force_clear_template_cache( $meta_id = false, $object_id = false, $meta_key = false, $_meta_value = false ) { $current_action = current_action(); if ( ( 'updated_user_meta' === $current_action && 'locale' === $meta_key ) || 'update_option_WPLANG' === $current_action ) { et_update_option( 'et_pb_clear_templates_cache', true ); } } add_action( 'update_option_WPLANG', 'et_pb_force_clear_template_cache' ); add_action( 'updated_user_meta', 'et_pb_force_clear_template_cache', 10, 4 ); function et_builder_handle_animation_data( $element_data = false ) { static $data = array(); static $data_classes = array(); if ( ! $element_data ) { return $data; } // This should not be possible but let's be safe if ( empty( $element_data['class'] ) ) { return; } // Prevent duplication animation data entries created by global modules if ( in_array( $element_data['class'], $data_classes ) ) { return; } $data[] = et_core_esc_previously( $element_data ); $data_classes[] = et_core_esc_previously( $element_data['class'] ); } function et_builder_handle_link_options_data( $element_data = false ) { static $data = array(); static $data_classes = array(); if ( ! $element_data ) { return $data; } // Safe checks bellow if ( empty( $element_data['class'] ) ) { return; } // Prevent duplication link options data entries created by global modules if ( in_array( $element_data['class'], $data_classes ) ) { return; } $data[] = $element_data; $data_classes[] = $element_data['class']; } /** * Get list of combined scripts and their possible alternative names. * * @return array */ function et_builder_get_minified_scripts() { $static_scripts = array( 'waypoints', 'jquery-waypoints', ); return apply_filters( 'et_builder_get_minified_scripts', $static_scripts ); } /** * Get list of concatenated & minified styles (sans style.css) * * @return array */ function et_builder_get_minified_styles() { $minified_styles = array( 'et-animations', ); return apply_filters( 'et_builder_get_minified_styles', $minified_styles ); } /** * Re-enqueue listed concatenated & minified scripts (and their possible alternative name) used empty string * to keep its dependency in order but avoiding WordPress to print the script to avoid the same file printed twice * Case in point: salvattore that is being called via builder module's render() method * * @return void */ function et_builder_dequeue_minified_scripts() { if ( ! is_admin() ) { /** * Builder script handle name * * @since 3.?? * * @param string */ $builder_script_handle = et_get_combined_script_handle(); foreach ( et_builder_get_minified_scripts() as $script ) { // Get script's localized data before the script is dequeued. $script_data = wp_scripts()->get_data( $script, 'data' ); // If to-be dequeued script has localized data, get builder script's data and concatenated both to ensure compatibility. // Concatenating is needed because script's localize data is saved as string (encoded array concatenated into variable name). if ( $script_data && '' !== trim( $script_data ) ) { // If builder script handle localized data returns false/empty, $script_data still need to be added. $concatenated_scripts_data = implode( ' ', array_filter( array( wp_scripts()->get_data( $builder_script_handle, 'data' ), $script_data, ) ) ); // Add concatenated localized data to builder script handle. wp_scripts()->add_data( $builder_script_handle, 'data', $concatenated_scripts_data ); } // If dequeued script has inline script, get it then re-add it to builder script handle using appropriate position. $inline_script_positions = array( 'before', 'after' ); foreach ( $inline_script_positions as $inline_script_position ) { $inline_script = wp_scripts()->get_data( $script, $inline_script_position ); // Inline script is saved as array. add_inline_script() method will handle it appending process. if ( is_array( $inline_script ) && ! empty( $inline_script ) ) { wp_scripts()->add_inline_script( $builder_script_handle, implode( ' ', $inline_script ), $inline_script_position ); } } wp_dequeue_script( $script ); wp_deregister_script( $script ); wp_register_script( $script, '', array(), ET_BUILDER_VERSION, true ); } } } add_action( 'wp_print_scripts', 'et_builder_dequeue_minified_scripts', 99999999 ); // <head> add_action( 'wp_print_footer_scripts', 'et_builder_dequeue_minified_scripts', 9 ); // <footer> function et_builder_dequeue_minifieds_styles() { if ( ! is_admin() ) { // Get builder minified + combined style handle. $builder_optimized_style_name = apply_filters( 'et_builder_optimized_style_handle', '' ); foreach ( et_builder_get_minified_styles() as $style ) { // If dequeued style has inline style, get it then re-add it to minified + combiled style handle. // Inline style only has 'after' position. $inline_style = wp_styles()->get_data( $style, 'after' ); // Inline style is saved as array. add_inline_style() method will handle it appending process. if ( is_array( $inline_style ) && ! empty( $inline_style ) ) { wp_styles()->add_inline_style( $builder_optimized_style_name, implode( ' ', $inline_style ), 'after' ); } wp_dequeue_style( $style ); wp_deregister_style( $style ); wp_register_style( $style, '', array(), ET_BUILDER_VERSION ); } } } add_action( 'wp_print_styles', 'et_builder_dequeue_minifieds_styles', 99999999 ); // <head> /** * Determine whether current theme supports Waypoints or not * * @return bool */ function et_is_ignore_waypoints() { // WPBakery Visual Composer plugin conflicts with waypoints if ( class_exists( 'Vc_Manager' ) ) { return true; } // always return false if not in divi plugin if ( ! et_is_builder_plugin_active() ) { return false; } $theme_data = wp_get_theme(); if ( empty( $theme_data ) ) { return false; } // list of themes without Waypoints support $no_waypoints_themes = array( 'Avada', ); $no_waypoints_themes = apply_filters( 'et_pb_no_waypoints_themes', $no_waypoints_themes ); // return true if current theme doesn't support Waypoints if ( in_array( $theme_data->Name, $no_waypoints_themes, true ) ) { return true; } return false; } /** * Determine whether current page has enqueued theme's style.css or not * This is mainly used on preview screen to decide to enqueue theme's style nor not * * @return bool */ function et_builder_has_theme_style_enqueued() { global $wp_styles; if ( ! empty( $wp_styles->queue ) ) { $theme_style_uri = get_stylesheet_uri(); foreach ( $wp_styles->queue as $handle ) { if ( isset( $wp_styles->registered[ $handle ]->src ) && $theme_style_uri === $wp_styles->registered[ $handle ]->src ) { return true; } } } return false; } /** * Added specific body classes for builder related situation * This enables theme to adjust its case independently * * @return array */ function et_builder_body_classes( $classes ) { if ( is_et_pb_preview() ) { $classes[] = 'et-pb-preview'; } $post_id = et_core_page_resource_get_the_ID(); $post_type = get_post_type( $post_id ); // Add layout classes when on library page if ( et_core_is_fb_enabled() && 'et_pb_layout' === $post_type ) { $layout_type = et_fb_get_layout_type( $post_id ); $layout_scope = et_fb_get_layout_term_slug( $post_id, 'scope' ); $classes[] = "et_pb_library_page-{$layout_type}"; $classes[] = "et_pb_library_page-{$layout_scope}"; } return $classes; } add_filter( 'body_class', 'et_builder_body_classes' ); if ( ! function_exists( 'et_builder_add_main_elements' ) ) : /** * Add Main Elements. * * @since 4.10.0 * @access public * @return void */ function et_builder_add_main_elements() { if ( ET_BUILDER_CACHE_MODULES ) { ET_Builder_Element::init_cache(); } // init this AFTER def cache has been init above. et_builder_init_shortcode_manager(); require_once ET_BUILDER_DIR . 'main-structure-elements.php'; require_once ET_BUILDER_DIR . 'class-et-builder-module-use-detection.php'; /** * Fires after the builder's structural element classes are loaded. * * @since 4.10.0 */ do_action( 'et_builder_ready' ); } endif; if ( ! function_exists( 'et_builder_init_shortcode_manager' ) ) : /** * Initialize shortcode manager. * * @since 4.10.0 * @access public * @return void */ function et_builder_init_shortcode_manager() { $manager = new ET_Builder_Module_Shortcode_Manager(); $manager->init(); do_action( 'et_builder_shortcode_manager_init' ); } endif; if ( ! function_exists( 'et_builder_load_framework' ) ) : function et_builder_load_framework() { require_once ET_BUILDER_DIR . 'functions.php'; require_once ET_BUILDER_DIR . 'compat/woocommerce.php'; require_once ET_BUILDER_DIR . 'class-et-global-settings.php'; require_once ET_BUILDER_DIR . 'feature/BlockEditorIntegration.php'; if ( is_admin() ) { global $pagenow, $et_current_memory_limit; if ( ! empty( $pagenow ) && in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) { $et_current_memory_limit = et_core_get_memory_limit(); } } /** * Filters builder modules loading hook. Load builder files on front-end and on specific admin pages only by default. * * @since 3.1 * * @param string Hook name. */ $action_hook = apply_filters( 'et_builder_modules_load_hook', is_admin() ? 'wp_loaded' : 'wp' ); if ( et_builder_should_load_framework() ) { require_once ET_BUILDER_DIR . 'class-et-builder-value.php'; require_once ET_BUILDER_DIR . 'class-et-builder-element.php'; require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-base.php'; require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-loader.php'; require_once ET_BUILDER_DIR . 'class-et-builder-theme-compat-base.php'; require_once ET_BUILDER_DIR . 'class-et-builder-theme-compat-handler.php'; require_once ET_BUILDER_DIR . 'ab-testing.php'; require_once ET_BUILDER_DIR . 'class-et-builder-settings.php'; require_once ET_BUILDER_DIR . 'class-et-builder-post-feature-base.php'; require_once ET_BUILDER_DIR . 'class-et-builder-global-feature-base.php'; require_once ET_BUILDER_DIR . 'class-et-builder-module-features.php'; require_once ET_BUILDER_DIR . 'class-et-builder-google-fonts-feature.php'; require_once ET_BUILDER_DIR . 'class-et-builder-dynamic-assets-feature.php'; require_once ET_BUILDER_DIR . 'class-et-builder-module-shortcode-manager.php'; $builder_settings_loaded = true; do_action( 'et_builder_framework_loaded' ); add_action( $action_hook, 'et_builder_init_global_settings', apply_filters( 'et_pb_load_global_settings_priority', 9 ) ); add_action( $action_hook, 'et_builder_add_main_elements', apply_filters( 'et_pb_load_main_elements_priority', 10 ) ); } elseif ( is_admin() ) { require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-base.php'; require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-loader.php'; require_once ET_BUILDER_DIR . 'class-et-builder-settings.php'; $builder_settings_loaded = true; } if ( isset( $builder_settings_loaded ) ) { add_action( 'init', 'et_builder_settings_init', 100 ); } add_action( $action_hook, 'et_builder_load_frontend_builder' ); if ( isset( $_GET['et_bfb'] ) && is_user_logged_in() ) { add_filter( 'wpe_heartbeat_allowed_pages', 'et_bfb_wpe_heartbeat_allowed_pages' ); } } endif; /** * Checking whether current page is BFB page based on its query string only; Suitable for basic * early check BEFORE $wp_query global is generated in case builder need to alter query * configuration. This is needed because BFB layout is basically loaded in front-end * * @since 3.19.9 * * @return bool */ function et_bfb_maybe_bfb_url() { $has_bfb_query_string = ! empty( $_GET['et_fb'] ) && ! empty( $_GET['et_bfb'] ); $has_vb_permission = et_pb_is_allowed( 'use_visual_builder' ); // This check assumes that $wp_query isn't ready (to be used before query is parsed) so any // query based check such as is_single(), etc don't exist yet. Thus BFB URL might valid if: // 1. not admin page // 2. user has logged in // 3. has `et_fb` & `et_bfb` query string // 4. has visual builder permission return ! is_admin() && is_user_logged_in() && $has_bfb_query_string && $has_vb_permission; } /** * Get verified query string value for et_bfb_make_post_type_queryable() * * @since 3.19.9 * * @param string $param_name * * @return string|number|bool */ function et_bfb_get_make_queryable_param( $param_name ) { $param = isset( $_GET[ "et_{$param_name}" ] ) ? $_GET[ "et_{$param_name}" ] : false; $param_nonce = isset( $_GET[ "et_{$param_name}_nonce" ] ) ? $_GET[ "et_{$param_name}_nonce" ] : false; $verified_param = $param && $param_nonce && wp_verify_nonce( $param_nonce, "et_{$param_name}_{$param}" ); return $verified_param ? $param : false; } /** * Set builder's registered post type's publicly_queryable property to true (if needed) so publicly * hidden post type can render BFB page on backend edit screen * * @see WP->parse_request() on how request is parsed * * @since 3.19.9 * * @return void */ function et_bfb_make_post_type_queryable() { // Valid query isn't available at this point so builder will guess whether current request is // BFB based on available value; Stop if this might not be BFB url if ( ! et_bfb_maybe_bfb_url() ) { return; } $get_post_id = absint( et_bfb_get_make_queryable_param( 'post_id' ) ); $get_post_type = sanitize_text_field( et_bfb_get_make_queryable_param( 'post_type' ) ); // Stop if no valid post id / post type for make queryable found on query string if ( ! $get_post_id || ! $get_post_type ) { return; } $post_type_object = get_post_type_object( $get_post_type ); // Stop if requested post type doesn't exist if ( is_null( $post_type_object ) ) { return; } $unqueryable_post_type = et_builder_get_third_party_unqueryable_post_types(); $is_post_type_unqueryable = in_array( $get_post_type, $unqueryable_post_type ); // CPT's version of edit_post is always available on cap->edit_post regardless CPT's meta_map_cap // or capability_type setting are set or not. If meta_map_cap is set to true, WordPress // automatically translates it into edit_post. Otherwise, CPT version of edit_post is sent as // it is and it is plugin / post type registrant's responsibility to add the capability to role // and map it into primitive capabilities on map_meta_cap() $capability = isset( $post_type_object->cap->edit_post ) ? $post_type_object->cap->edit_post : 'edit_post'; $can_edit_this_post = current_user_can( $capability, $get_post_id ); // Flip publicly_queryable of current request so BFB layout page can be rendered. // Note: post_type existence have been verified on is_null( $post_type_object ) check above if ( $is_post_type_unqueryable && $can_edit_this_post ) { global $wp_post_types; $wp_post_types[ $get_post_type ]->publicly_queryable = true; } } add_action( 'init', 'et_bfb_make_post_type_queryable' ); /** * Modify rewrite rule's redirect of current BFB request if its post type's `publicly_queryable` * is set to false and its `query_var` is NOT set to `false`. When this situation happens, current * BFB page cannot be rendered because rewrite rule's redirect value doesn't have `post_type` * param which makes page query gets incorrect page value * * @since 3.19.9 * * @return void */ function et_bfb_make_cpt_rewrite_rule_queryable( $value ) { // Get verified make queryable post_type param from query string $unqueryable_post_type = et_bfb_get_make_queryable_param( 'post_type' ); // Make sure that value is array, current request might be BFB, and verified post_type from // query string exist. Note: need to use early return otherwise the rest need multiple stack // if/else condition if ( ! is_array( $value ) || ! et_bfb_maybe_bfb_url() || ! $unqueryable_post_type ) { return $value; } $rewrite_regex = $unqueryable_post_type . '/([^/]+)(?:/([0-9]+))?/?$'; $rewrite_redirect = isset( $value[ $rewrite_regex ] ) ? $value[ $rewrite_regex ] : false; $has_post_type_substr = $rewrite_redirect && strpos( $rewrite_redirect, '?post_type=' ) !== false; $post_type_object = get_post_type_object( $unqueryable_post_type ); // If current page's post type object `query_var` isn't falsey and no `post_type=` substring is // found on current page's post type rewrite rule redirect value, modify the rewrite rule // redirect value so it can picks up current post type when query is parsed if ( $post_type_object->query_var && ! $has_post_type_substr ) { $value[ $rewrite_regex ] = 'index.php?post_type=' . $unqueryable_post_type . '&name=$matches[1]&page=$matches[2]'; } return $value; } add_filter( 'option_rewrite_rules', 'et_bfb_make_cpt_rewrite_rule_queryable' ); if ( ! function_exists( 'et_bfb_wpe_heartbeat_allowed_pages' ) ) : function et_bfb_wpe_heartbeat_allowed_pages( $pages ) { global $pagenow; $pages[] = $pagenow; return $pages; } endif; function et_builder_load_frontend_builder() { global $et_current_memory_limit; $et_current_memory_limit = et_core_get_memory_limit(); // Set memory limit when there is a limit set, and that is less than 256M. if ( $et_current_memory_limit && $et_current_memory_limit < 256 ) { @ini_set( 'memory_limit', '256M' ); } require_once ET_BUILDER_DIR . 'frontend-builder/init.php'; } if ( ! function_exists( 'et_pb_get_google_api_key' ) ) : function et_pb_get_google_api_key() { $google_api_option = get_option( 'et_google_api_settings' ); $google_api_key = isset( $google_api_option['api_key'] ) ? $google_api_option['api_key'] : ''; return $google_api_key; } endif; if ( ! function_exists( 'et_pb_enqueue_google_maps_script' ) ) : function et_pb_enqueue_google_maps_script() { $google_api_option = get_option( 'et_google_api_settings' ); $google_maps_script_enqueue = ! $google_api_option || ! isset( $google_api_option['enqueue_google_maps_script'] ) || ( isset( $google_api_option['enqueue_google_maps_script'] ) && 'on' === $google_api_option['enqueue_google_maps_script'] ) ? true : false; return apply_filters( 'et_pb_enqueue_google_maps_script', $google_maps_script_enqueue ); } endif; /** * Add pseudo-action via the_content to hook filter/action at the end of main content * * @param string content string * @return string content string */ function et_pb_content_main_query( $content ) { global $post, $et_pb_comments_print; // Perform filter on main query + if builder is used only if ( is_main_query() && et_pb_is_pagebuilder_used( get_the_ID() ) ) { add_filter( 'comment_class', 'et_pb_add_non_builder_comment_class', 10, 5 ); // Actual front-end only adjustment. has_shortcode() can't use passed $content since // Its shortcode has been parsed if ( false === $et_pb_comments_print && ! et_fb_is_enabled() && has_shortcode( $post->post_content, 'et_pb_comments' ) ) { add_filter( 'get_comments_number', '__return_zero' ); add_filter( 'comments_open', '__return_false' ); add_filter( 'comments_array', '__return_empty_array' ); } } return $content; } add_filter( 'the_content', 'et_pb_content_main_query', 1500 ); add_filter( 'et_builder_render_layout', 'et_pb_content_main_query', 1500 ); /** * Added special class name for comment items that are placed outside builder * * See {@see 'comment_class'}. * * @param array $classes classname * @param string $comment comma separated list of additional classes * @param int $comment_ID comment ID * @param WP_Comment $comment comment object * @param int|WP_Post $post_id post ID or WP_Post object * * @return array modified classname */ function et_pb_add_non_builder_comment_class( $classes, $class, $comment_ID, $comment, $post_id ) { $classes[] = 'et-pb-non-builder-comment'; return $classes; } /** * Enqueue Open Sans for the builder UI. * * @since 4.0 * * @return void */ function et_builder_enqueue_open_sans() { if ( wp_style_is( 'et-core-main-fonts', 'enqueued' ) ) { return; } $protocol = is_ssl() ? 'https' : 'http'; $query_args = array( 'family' => 'Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800', 'subset' => 'latin,latin-ext', ); wp_enqueue_style( 'et-fb-fonts', esc_url_raw( add_query_arg( $query_args, "{$protocol}://fonts.googleapis.com/css" ) ), array(), null ); } et_builder_load_framework(); // Register assets that need to be fired at head function et_builder_enqueue_assets_head() { // Setup WP media. // Around 5.2-alpha, `wp_enqueue_media` started using a function defined in a file // which is only included in admin. Unfortunately there's no safe/reliable way to conditionally // load this other than checking the WP version. if ( version_compare( $GLOBALS['wp_version'], '5.2-alpha-44947', '>=' ) ) { require_once ABSPATH . 'wp-admin/includes/post.php'; } wp_enqueue_media(); // Setup Builder Media Library wp_enqueue_script( 'et_pb_media_library', ET_BUILDER_URI . '/scripts/ext/media-library.js', array( 'media-editor' ), ET_BUILDER_PRODUCT_VERSION, true ); } // TODO, make this fire late enough, so that the_content has fired and ET_Builder_Element::get_computed_vars() is ready // currently its being called in temporary_app_boot() in view.php function et_builder_enqueue_assets_main() { $ver = ET_BUILDER_VERSION; $root = ET_BUILDER_URI; $assets = ET_BUILDER_URI . '/frontend-builder/assets'; wp_register_script( 'wp-color-picker-alpha', ET_BUILDER_URI . '/scripts/ext/wp-color-picker-alpha.min.js', array( 'jquery', 'wp-color-picker' ) ); wp_localize_script( 'wp-color-picker-alpha', 'et_pb_color_picker_strings', apply_filters( 'et_pb_color_picker_strings_builder', array( 'legacy_pick' => esc_html__( 'Select', 'et_builder' ), 'legacy_current' => esc_html__( 'Current Color', 'et_builder' ), ) ) ); wp_enqueue_script( 'wp-color-picker-alpha' ); wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_style( 'et-core-admin', ET_CORE_URL . 'admin/css/core.css', array(), ET_CORE_VERSION ); wp_enqueue_style( 'et-core-portability', ET_CORE_URL . 'admin/css/portability.css', array(), ET_CORE_VERSION ); wp_register_style( 'et_pb_admin_date_css', "{$root}/styles/jquery-ui-1.12.1.custom.css", array(), $ver ); wp_register_style( 'et-fb-top-window', "{$assets}/css/fb-top-window.css", array(), $ver ); $conditional_deps = array(); if ( ! et_builder_bfb_enabled() && ! et_builder_tb_enabled() ) { $conditional_deps[] = 'et-fb-top-window'; } // Enqueue the appropriate bundle CSS (hot/start/build) $deps = array_merge( array( 'et_pb_admin_date_css', 'wp-mediaelement', 'wp-color-picker', 'et-core-admin', ), $conditional_deps ); et_fb_enqueue_bundle( 'et-frontend-builder', 'bundle.css', $deps ); // Load Divi Builder style.css file with hardcore CSS resets and Full Open Sans font if the Divi Builder plugin is active if ( et_is_builder_plugin_active() ) { // `bundle.css` was removed from `divi-builder-style.css` and is now enqueued separately for the DBP as well. wp_enqueue_style( 'et-builder-divi-builder-styles', "{$assets}/css/divi-builder-style.css", array_merge( array( 'et-core-admin', 'wp-color-picker' ), $conditional_deps ), $ver ); } wp_enqueue_script( 'mce-view' ); if ( ! et_core_use_google_fonts() || et_is_builder_plugin_active() ) { et_builder_enqueue_open_sans(); } wp_enqueue_style( 'et-frontend-builder-failure-modal', "{$assets}/css/failure_modal.css", array(), $ver ); wp_enqueue_style( 'et-frontend-builder-notification-modal', "{$root}/styles/notification_popup_styles.css", array(), $ver ); } PK X�\3� � � % builder/class-et-builder-settings.phpnu �[��� <?php /** * Builder settings API. * * @package Divi. * @subpackage Builder. * * @since 4.6.2 */ require_once 'module/field/Factory.php'; /** * Class for handling interacting with builder settings. */ class ET_Builder_Settings { /** * Builder setting fields. * * @see ET_Builder_Settings::_get_builder_settings_fields() * * @var array */ protected static $_BUILDER_SETTINGS_FIELDS; /** * Builder setting values. * * @see ET_Builder_Settings::_get_builder_settings_values() * * @var array */ protected static $_BUILDER_SETTINGS_VALUES; /** * Page setting fields. * * @see ET_Builder_Settings::_get_page_settings_fields() * * @var array */ protected static $_PAGE_SETTINGS_FIELDS; /** * Map of page settings fields to post metas. * e.x `'_et_pb_static_css_file' => 'et_pb_static_css_file'` * * @var array */ protected static $_PAGE_SETTINGS_FIELDS_META_KEY_MAP = array(); /** * List of page setting fields slug whose values are default. * * @var array[] */ protected static $_PAGE_SETTINGS_IS_DEFAULT = array(); /** * Page setting fields value array. * * @var array */ protected static $_PAGE_SETTINGS_VALUES; /** * `ET_Builder_Settings` instance. * * @var ET_Builder_Settings */ protected static $_instance; /** * ET_Builder_Settings constructor. */ public function __construct() { if ( null !== self::$_instance ) { wp_die( esc_html( get_class( $this ) . 'is a singleton class. You cannot create a another instance.' ) ); } $this->_initialize(); $this->_register_callbacks(); } /** * Get ab testing fields. * * @return array */ protected static function _get_ab_testing_fields() { return array( 'et_pb_enable_ab_testing' => array( 'type' => 'yes_no_button', 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'id' => 'et_pb_enable_ab_testing', 'label' => esc_html__( 'Enable Split Testing', 'et_builder' ), 'autoload' => false, 'default' => 'off', 'class' => 'et-pb-visible', 'affects' => array( 'et_pb_ab_bounce_rate_limit', 'et_pb_ab_stats_refresh_interval', 'et_pb_enable_shortcode_tracking', ), 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', ), 'et_pb_ab_bounce_rate_limit' => array( 'type' => 'range', 'id' => 'et_pb_ab_bounce_rate_limit', 'label' => esc_html__( 'Bounce Rate Limit', 'et_builder' ), 'default' => 5, 'range_settings' => array( 'step' => 1, 'min' => 3, 'max' => 60, ), 'depends_show_if' => 'on', 'mobile_options' => false, 'unitless' => true, 'depends_on' => array( 'et_pb_enable_ab_testing', ), 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', ), 'et_pb_ab_stats_refresh_interval' => array( 'type' => 'select', 'id' => 'et_pb_ab_stats_refresh_interval', 'label' => esc_html__( 'Stats refresh interval', 'et_builder' ), 'autoload' => false, 'depends_show_if' => 'on', 'default' => 'hourly', 'options' => array( 'hourly' => esc_html__( 'Hourly', 'et_builder' ), 'daily' => esc_html__( 'Daily', 'et_builder' ), ), 'depends_on' => array( 'et_pb_enable_ab_testing', ), 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', ), 'et_pb_enable_shortcode_tracking' => array( 'type' => 'yes_no_button', 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'id' => 'et_pb_enable_shortcode_tracking', 'label' => esc_html__( 'Shortcode Tracking', 'et_builder' ), 'depends_show_if' => 'on', 'affects' => array( 'et_pb_ab_current_shortcode', ), 'depends_on' => array( 'et_pb_enable_ab_testing', ), 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', ), 'et_pb_ab_current_shortcode' => array( 'type' => 'textarea', 'id' => 'et_pb_ab_current_shortcode', 'label' => esc_html__( 'Shortcode for Tracking:', 'et_builder' ), 'autoload' => false, 'readonly' => 'readonly', 'depends_show_if' => 'on', 'depends_on' => array( 'et_pb_enable_shortcode_tracking', ), 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', ), 'et_pb_ab_subjects' => array( 'id' => 'et_pb_ab_subjects', 'type' => 'hidden', 'tab_slug' => 'content', 'toggle_slug' => 'ab_testing', 'autoload' => false, ), ); } /** * Get builder settings fields. * * @return array */ protected static function _get_builder_settings_fields() { $builder_settings_fields = array( 'et_pb_static_css_file' => self::_get_static_css_generation_field( 'builder' ), 'et_pb_css_in_footer' => array( 'type' => 'yes_no_button', 'id' => 'et_pb_css_in_footer', 'index' => -1, 'label' => esc_html__( 'Output Styles Inline', 'et_builder' ), 'description' => esc_html__( 'With previous versions of the builder, css styles for the modules\' design settings were output inline in the footer. Enable this option to restore that behavior.', 'et_builder' ), 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'default' => 'off', 'validation_type' => 'simple_text', 'tab_slug' => 'advanced', 'toggle_slug' => 'performance', ), 'et_pb_product_tour_global' => array( 'type' => 'yes_no_button', 'id' => 'et_pb_product_tour_global', 'index' => -1, 'label' => esc_html__( 'Product Tour', 'et_builder' ), 'description' => esc_html__( 'If enabled Product Tour will be started automatically when Visual Builder launched for the first time', 'et_builder' ), 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'default' => 'on', 'validation_type' => 'simple_text', 'tab_slug' => 'advanced', 'toggle_slug' => 'product_tour', ), 'et_enable_bfb' => array( 'type' => 'yes_no_button', 'id' => 'et_enable_bfb', 'index' => -1, 'label' => esc_html__( 'Enable The Latest Divi Builder Experience', 'et_builder' ), 'description' => esc_html__( 'Disabling this option will load the legacy Divi Builder interface when editing a post using the classic WordPress post editor. The legacy builder lacks many features and interface improvements, but it can still be used if you are experiencing trouble with the new interface.', 'et_builder' ), 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'default' => 'off', 'validation_type' => 'simple_text', 'tab_slug' => 'advanced', 'toggle_slug' => 'enable_bfb', 'main_setting_name' => 'et_bfb_settings', 'sub_setting_name' => 'enable_bfb', 'is_global' => true, ), 'et_enable_classic_editor' => array( 'type' => 'yes_no_button', 'id' => 'et_enable_classic_editor', 'index' => -1, 'label' => esc_html__( 'Enable Classic Editor', 'et_builder' ), 'description' => esc_html__( 'Use Classic Editor instead of Gutenberg / Block Editor', 'et_builder' ), 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'default' => 'off', 'validation_type' => 'simple_text', 'tab_slug' => 'advanced', 'toggle_slug' => 'enable_classic_editor', ), 'et_pb_post_type_integration' => array( 'type' => 'checkbox_list', 'usefor' => 'custom', 'id' => 'et_pb_post_type_integration', 'index' => -1, 'label' => esc_html__( 'Enable Divi Builder On Post Types', 'et_builder' ), 'description' => esc_html__( 'By default, the Divi Builder is only accessible on standard post types. This option lets you enable the builder on any custom post type currently registered on your website, however the builder may not be compatible with all custom post types.', 'et_builder' ), 'options' => 'ET_Builder_Settings::get_registered_post_type_options', 'default' => self::_get_post_type_options_defaults(), 'validation_type' => 'on_off_array', 'et_save_values' => true, 'tab_slug' => 'post_type_integration', 'toggle_slug' => 'performance', ), ); // Remove "Enable Classic Editor" options for versions of WordPress // that don't have the Gutenberg editor. if ( version_compare( $GLOBALS['wp_version'], '5.0-beta', '<' ) ) { unset( $builder_settings_fields['et_enable_classic_editor'] ); } return $builder_settings_fields; } /** * Get builder settings in ePanel format. * * @return array */ protected static function _get_builder_settings_in_epanel_format() { $tabs = self::get_tabs( 'builder' ); $fields = self::get_fields( 'builder' ); $result = array(); $result[] = array( 'name' => 'wrap-builder', 'type' => 'contenttab-wrapstart', ); $result[] = array( 'type' => 'subnavtab-start' ); $tab_content = array(); $index = 0; foreach ( $tabs as $tab_slug => $tab_name ) { $index++; $tab_content_started = false; foreach ( $fields as $field_name => $field_info ) { if ( $field_info['tab_slug'] !== $tab_slug ) { continue; } if ( ! $tab_content_started ) { $result[] = array( 'name' => "builder-{$index}", 'type' => 'subnav-tab', 'desc' => $tab_name, ); $tab_content[] = array( 'name' => "builder-{$index}", 'type' => 'subcontent-start', ); $tab_content_started = true; } $field_type = $field_info['type']; if ( 'yes_no_button' === $field_type ) { $field_type = 'checkbox2'; } $tab_content[] = array_merge( $field_info, array( 'name' => $field_info['label'], 'id' => $field_name, 'type' => $field_type, 'std' => $field_info['default'], 'desc' => $field_info['description'], 'is_builder_field' => true, ) ); } if ( $tab_content_started ) { $tab_content[] = array( 'name' => "builder-{$index}", 'type' => 'subcontent-end', ); } } $result[] = array( 'type' => 'subnavtab-end' ); $result = array_merge( $result, $tab_content ); $result[] = array( 'name' => 'wrap-builder', 'type' => 'contenttab-wrapend', ); return $result; } /** * Get builder settings values. * * @return array */ protected static function _get_builder_settings_values() { return array( 'et_pb_static_css_file' => et_get_option( 'et_pb_static_css_file', 'on' ), 'et_pb_css_in_footer' => et_get_option( 'et_pb_css_in_footer', 'off' ), ); } /** * Get page settings fields. * * @param string $post_type Page post type. * * @return array */ protected static function _get_page_settings_fields( $post_type = 'post' ) { $fields = array(); $overflow = ET_Builder_Module_Fields_Factory::get( 'Overflow' ); if ( et_pb_is_allowed( 'ab_testing' ) ) { $fields = self::_get_ab_testing_fields(); } $overflow_fields = $overflow->get_fields( array( 'prefix' => 'et_pb_', 'tab_slug' => 'advanced', 'toggle_slug' => 'visibility', ) ); foreach ( $overflow_fields as $field => &$definition ) { $definition['id'] = $field; } $fields = array_merge( $fields, $overflow_fields ); $fields = array_merge( $fields, array( 'et_pb_custom_css' => array( 'type' => 'codemirror', 'id' => 'et_pb_custom_css', 'mode' => 'css', 'inline' => false, 'label' => et_builder_i18n( 'Custom CSS' ), 'tab_slug' => 'advanced', 'toggle_slug' => 'custom_css', ), 'et_pb_page_gutter_width' => array( 'type' => 'range', 'id' => 'et_pb_page_gutter_width', 'meta_key' => '_et_pb_gutter_width', 'label' => esc_html__( 'Gutter Width', 'et_builder' ), 'range_settings' => array( 'step' => 1, 'min' => 1, 'max' => 4, 'min_limit' => 1, 'max_limit' => 4, ), 'default' => (string) et_get_option( 'gutter_width', '3' ), 'mobile_options' => false, 'validate_unit' => false, 'tab_slug' => 'design', 'toggle_slug' => 'spacing', ), 'et_pb_light_text_color' => array( 'type' => 'color-alpha', 'id' => 'et_pb_light_text_color', 'label' => esc_html__( 'Light Text Color', 'et_builder' ), 'default' => '#ffffff', 'tab_slug' => 'design', 'toggle_slug' => 'text', ), 'et_pb_dark_text_color' => array( 'type' => 'color-alpha', 'id' => 'et_pb_dark_text_color', 'label' => esc_html__( 'Dark Text Color', 'et_builder' ), 'default' => '#666666', 'tab_slug' => 'design', 'toggle_slug' => 'text', ), 'et_pb_post_settings_title' => array( 'type' => 'text', 'id' => 'et_pb_post_settings_title', 'show_in_bb' => false, 'post_field' => 'post_title', 'label' => et_builder_i18n( 'Title' ), 'default' => '', 'tab_slug' => 'content', 'toggle_slug' => 'main_content', ), 'et_pb_post_settings_excerpt' => array( 'type' => 'textarea', 'id' => 'et_pb_post_settings_excerpt', 'show_in_bb' => false, 'post_field' => 'post_excerpt', 'label' => 'product' === $post_type ? esc_html__( 'Short Description', 'et_builder' ) : esc_html__( 'Excerpt', 'et_builder' ), 'default' => '', 'tab_slug' => 'content', 'toggle_slug' => 'main_content', ), 'et_pb_post_settings_image' => array( 'type' => 'upload', 'id' => 'et_pb_post_settings_image', 'show_in_bb' => false, 'meta_key' => '_thumbnail_id', // This meta must not be updated during save_post or it will overwrite // the value set in the WP edit page.... 'save_post' => false, 'label' => 'product' === $post_type ? esc_html__( 'Product Image', 'et_builder' ) : esc_html__( 'Featured Image', 'et_builder' ), 'embed' => false, 'attachment_id' => true, 'upload_button_text' => esc_attr__( 'Select', 'et_builder' ), 'choose_text' => esc_attr__( 'Set featured image', 'et_builder' ), 'update_text' => esc_attr__( 'Set As Image', 'et_builder' ), 'tab_slug' => 'content', 'toggle_slug' => 'main_content', ), 'et_pb_post_settings_categories' => array( 'id' => 'et_pb_post_settings_categories', 'show_in_bb' => false, 'label' => esc_html__( 'Categories', 'et_builder' ), 'type' => 'categories', 'option_category' => 'basic_option', 'post_type' => 'post', 'taxonomy_name' => 'category', 'renderer_options' => array( 'use_terms' => false, ), 'tab_slug' => 'content', 'toggle_slug' => 'main_content', 'depends_on_post_type' => array( 'post' ), ), 'et_pb_post_settings_tags' => array( 'id' => 'et_pb_post_settings_tags', 'show_in_bb' => false, 'label' => esc_html__( 'Tags', 'et_builder' ), 'type' => 'categories', 'option_category' => 'basic_option', 'post_type' => 'post', 'taxonomy_name' => 'post_tag', 'renderer_options' => array( 'use_terms' => false, ), 'tab_slug' => 'content', 'toggle_slug' => 'main_content', 'depends_on_post_type' => array( 'post' ), ), 'et_pb_post_settings_project_categories' => array( 'id' => 'et_pb_post_settings_project_categories', 'show_in_bb' => false, 'label' => esc_html__( 'Categories', 'et_builder' ), 'type' => 'categories', 'option_category' => 'basic_option', 'post_type' => 'project', 'taxonomy_name' => 'project_category', 'renderer_options' => array( 'use_terms' => false, ), 'tab_slug' => 'content', 'toggle_slug' => 'main_content', 'depends_on_post_type' => array( 'project' ), ), 'et_pb_post_settings_project_tags' => array( 'id' => 'et_pb_post_settings_project_tags', 'show_in_bb' => false, 'label' => esc_html__( 'Tags', 'et_builder' ), 'type' => 'categories', 'option_category' => 'basic_option', 'post_type' => 'project', 'taxonomy_name' => 'project_tag', 'renderer_options' => array( 'use_terms' => false, ), 'tab_slug' => 'content', 'toggle_slug' => 'main_content', 'depends_on_post_type' => array( 'project' ), ), 'et_pb_content_area_background_color' => array( 'type' => 'color-alpha', 'id' => 'et_pb_content_area_background_color', 'label' => esc_html__( 'Content Area Background Color', 'et_builder' ), 'default' => 'rgba(255,255,255,0)', 'tab_slug' => 'content', 'toggle_slug' => 'background', 'depends_on_post_type' => array( 'page' ), ), 'et_pb_section_background_color' => array( 'type' => 'color-alpha', 'id' => 'et_pb_section_background_color', 'label' => esc_html__( 'Section Background Color', 'et_builder' ), 'default' => '#ffffff', 'tab_slug' => 'content', 'toggle_slug' => 'background', ), 'et_pb_page_z_index' => array( 'type' => 'range', 'id' => 'et_pb_page_z_index', 'range_settings' => array( 'min' => -1000, 'max' => 1000, 'step' => 1, ), 'unitless' => true, 'label' => esc_html__( 'Z Index', 'et_builder' ), 'default' => '', 'tab_slug' => 'advanced', 'toggle_slug' => 'position', ), 'et_pb_static_css_file' => self::_get_static_css_generation_field( 'page' ), 'global_colors_info' => array( 'id' => 'global_colors_info', 'type' => 'hidden', 'tab_slug' => 'content', ), ) ); return $fields; } /** * Get page setting fields' meta_key map. Most page settings' field meta key is identical to * its field['id'] but some fields use different meta_key. Map might need in some situations. * * @since 3.20 * * @param bool $meta_key_to_id Reverse mapping if set to false. * * @return array */ public static function get_page_setting_meta_key_map( $meta_key_to_id = true ) { static $map = array(); // Less likely to change, populate it once will be sufficient. if ( empty( $map ) ) { foreach ( self::_get_page_settings_fields() as $field_id => $field ) { if ( isset( $field['meta_key'] ) ) { // The map can be reversed if needed. if ( $meta_key_to_id ) { $map[ $field['meta_key'] ] = $field_id; } else { $map[ $field_id ] = $field['meta_key']; } } } } return $map; } /** * Get page settings values. * * @param int $post_id Post id. * * @return mixed|void */ protected static function _get_page_settings_values( $post_id ) { $post_id = $post_id ? $post_id : get_the_ID(); if ( ! empty( self::$_PAGE_SETTINGS_VALUES[ $post_id ] ) ) { return self::$_PAGE_SETTINGS_VALUES[ $post_id ]; } $overflow = et_pb_overflow(); $OVERFLOW_DEFAULT = ET_Builder_Module_Helper_Overflow::OVERFLOW_DEFAULT; $is_default = array(); // Page settings fields. $fields = array(); // phpcs:disable ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- Needed for consistency with other variable names. if ( ! empty( self::$_PAGE_SETTINGS_FIELDS ) ) { $fields = self::$_PAGE_SETTINGS_FIELDS; } // phpcs:enable // Defaults. $default_bounce_rate_limit = 5; // Get values. $ab_bounce_rate_limit = get_post_meta( $post_id, '_et_pb_ab_bounce_rate_limit', true ); $et_pb_ab_bounce_rate_limit = '' !== $ab_bounce_rate_limit ? $ab_bounce_rate_limit : $default_bounce_rate_limit; $is_default[] = $et_pb_ab_bounce_rate_limit === $default_bounce_rate_limit ? 'et_pb_ab_bounce_rate_limit' : ''; $color_palette = implode( '|', et_pb_get_default_color_palette() ); $default = array( '#000000', '#FFFFFF', '#E02B20', '#E09900', '#EDF000', '#7CDA24', '#0C71C3', '#8300E9' ); $et_pb_saved_color_palette = '' !== $color_palette ? $color_palette : $default; $et_pb_global_color_palette = et_builder_get_all_global_colors( true ); $is_default[] = $et_pb_saved_color_palette === $default ? 'et_pb_color_palette' : ''; $gutter_width = get_post_meta( $post_id, '_et_pb_gutter_width', true ); $default = et_()->array_get( $fields, array( 'et_pb_page_gutter_width', 'default' ) ); $et_pb_page_gutter_width = '' !== $gutter_width ? $gutter_width : $default; $is_default[] = $et_pb_page_gutter_width === $default ? 'et_pb_page_gutter_width' : ''; $light_text_color = get_post_meta( $post_id, '_et_pb_light_text_color', true ); $default = et_()->array_get( $fields, array( 'et_pb_light_text_color', 'default' ) ); $et_pb_light_text_color = '' !== $light_text_color ? $light_text_color : $default; $is_default[] = strtolower( $et_pb_light_text_color ) === $default ? 'et_pb_light_text_color' : ''; $dark_text_color = get_post_meta( $post_id, '_et_pb_dark_text_color', true ); $default = et_()->array_get( $fields, array( 'et_pb_dark_text_color', 'default' ) ); $et_pb_dark_text_color = '' !== $dark_text_color ? $dark_text_color : $default; $is_default[] = strtolower( $et_pb_dark_text_color ) === $default ? 'et_pb_dark_text_color' : ''; $content_area_background_color = get_post_meta( $post_id, '_et_pb_content_area_background_color', true ); $content_area_background_color = et_builder_is_global_color( $content_area_background_color ) ? et_builder_get_global_color( $content_area_background_color ) : $content_area_background_color; $default = et_()->array_get( $fields, array( 'et_pb_content_area_background_color', 'default' ) ); $et_pb_content_area_background_color = '' !== $content_area_background_color ? $content_area_background_color : $default; $is_default[] = strtolower( $et_pb_content_area_background_color ) === $default ? 'et_pb_content_area_background_color' : ''; $section_background_color = get_post_meta( $post_id, '_et_pb_section_background_color', true ); $section_background_color = et_builder_is_global_color( $section_background_color ) ? et_builder_get_global_color( $section_background_color ) : $section_background_color; $default = et_()->array_get( $fields, array( 'et_pb_section_background_color', 'default' ) ); $et_pb_section_background_color = '' !== $section_background_color ? $section_background_color : $default; $is_default[] = strtolower( $et_pb_section_background_color ) === $default ? 'et_pb_section_background_color' : ''; $overflow_x = (string) get_post_meta( $post_id, $overflow->get_field_x( '_et_pb_' ), true ); $is_default[] = empty( $overflow_x ) || $overflow_x === $OVERFLOW_DEFAULT ? $overflow->get_field_x( 'et_pb_' ) : ''; $overflow_y = (string) get_post_meta( $post_id, $overflow->get_field_y( '_et_pb_' ), true ); $is_default[] = empty( $overflow_y ) || $overflow_y === $OVERFLOW_DEFAULT ? $overflow->get_field_y( 'et_pb_' ) : ''; $static_css_file = get_post_meta( $post_id, '_et_pb_static_css_file', true ); $default = et_()->array_get( $fields, array( 'et_pb_static_css_file', 'default' ) ); $et_pb_static_css_file = '' !== $static_css_file ? $static_css_file : $default; $is_default[] = $et_pb_static_css_file === $default ? 'et_pb_static_css_file' : ''; $page_global_colors_info = get_post_meta( $post_id, '_global_colors_info', true ); self::$_PAGE_SETTINGS_IS_DEFAULT[ $post_id ] = $is_default; $post = get_post( $post_id ); $values = array( 'et_pb_enable_ab_testing' => et_is_ab_testing_active( $post_id ) ? 'on' : 'off', 'et_pb_ab_bounce_rate_limit' => $et_pb_ab_bounce_rate_limit, 'et_pb_ab_stats_refresh_interval' => et_pb_ab_get_refresh_interval( $post_id ), 'et_pb_ab_subjects' => et_pb_ab_get_subjects( $post_id ), 'et_pb_enable_shortcode_tracking' => get_post_meta( $post_id, '_et_pb_enable_shortcode_tracking', true ), 'et_pb_ab_current_shortcode' => '[et_pb_split_track id="' . $post_id . '" /]', 'et_pb_custom_css' => get_post_meta( $post_id, '_et_pb_custom_css', true ), 'et_pb_color_palette' => $et_pb_saved_color_palette, 'et_pb_gc_palette' => maybe_unserialize( $et_pb_global_color_palette ), 'et_pb_page_gutter_width' => $et_pb_page_gutter_width, 'et_pb_light_text_color' => strtolower( $et_pb_light_text_color ), 'et_pb_dark_text_color' => strtolower( $et_pb_dark_text_color ), 'et_pb_content_area_background_color' => strtolower( $et_pb_content_area_background_color ), 'et_pb_section_background_color' => strtolower( $et_pb_section_background_color ), 'et_pb_static_css_file' => $et_pb_static_css_file, 'et_pb_post_settings_title' => $post ? $post->post_title : '', 'et_pb_post_settings_excerpt' => $post ? $post->post_excerpt : '', 'et_pb_post_settings_image' => get_post_thumbnail_id( $post_id ), 'et_pb_post_settings_categories' => self::_get_object_terms( $post_id, 'category' ), 'et_pb_post_settings_tags' => self::_get_object_terms( $post_id, 'post_tag' ), 'et_pb_post_settings_project_categories' => self::_get_object_terms( $post_id, 'project_category' ), 'et_pb_post_settings_project_tags' => self::_get_object_terms( $post_id, 'project_tag' ), et_pb_overflow()->get_field_x( 'et_pb_' ) => $overflow_x, et_pb_overflow()->get_field_y( 'et_pb_' ) => $overflow_y, 'et_pb_page_z_index' => get_post_meta( $post_id, '_et_pb_page_z_index', true ), 'global_colors_info' => $page_global_colors_info ? $page_global_colors_info : '{}', ); /** * Filters Divi Builder page settings values. * * @since 3.0.45 * * @param mixed[] $builder_settings { * Builder Settings Values * * @type string $setting_name Setting value. * ... * } * @param string|int $post_id */ self::$_PAGE_SETTINGS_VALUES[ $post_id ] = apply_filters( 'et_builder_page_settings_values', $values, $post_id ); $values = self::$_PAGE_SETTINGS_VALUES[ $post_id ]; /** * Filters the Divi Builder's page settings values. * * @deprecated {@see 'et_builder_page_settings_values'} * * @since 2.7.0 * @since 3.0.45 Deprecation. */ return apply_filters( 'et_pb_get_builder_settings_values', $values, $post_id ); } /** * Get Static CSS File Generation setting field. * * @param string $scope Field scope. * * @return array */ protected static function _get_static_css_generation_field( $scope ) { $description = array( 'page' => esc_html__( "When this option is enabled, the builder's inline CSS styles for this page will be cached and served as a static file. Enabling this option can help improve performance.", 'et_builder' ), 'builder' => esc_html__( "When this option is enabled, the builder's inline CSS styles for all pages will be cached and served as static files. Enabling this option can help improve performance.", 'et_builder' ), ); return array( 'type' => 'yes_no_button', 'id' => 'et_pb_static_css_file', 'index' => -1, 'label' => esc_html__( 'Static CSS File Generation', 'et_builder' ), 'description' => $description[ $scope ], 'options' => array( 'on' => __( 'On', 'et_builder' ), 'off' => __( 'Off', 'et_builder' ), ), 'default' => 'on', 'validation_type' => 'simple_text', 'after' => array( 'type' => 'button', 'link' => '#', 'class' => 'et_builder_clear_static_css', 'title' => esc_html_x( 'Clear', 'clear static css files', 'et_builder' ), 'authorize' => false, 'is_after_element' => true, ), 'tab_slug' => 'advanced', 'toggle_slug' => 'performance', ); } /** * Get an array of default value of all post types where Divi Builder is enabled on. * * @return array */ protected static function _get_post_type_options_defaults() { $post_types = et_builder_get_enabled_builder_post_types(); $post_type_options = array(); foreach ( $post_types as $post_type ) { $post_type_options[ $post_type ] = 'on'; } return $post_type_options; } /** * Returns all taxonomy terms for a given post. * * @param int $post_id Post ID. * @param string $taxonomy Taxonomy name. * * @return string */ protected static function _get_object_terms( $post_id, $taxonomy ) { $terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) ); return is_array( $terms ) ? implode( ',', $terms ) : ''; } /** * Get registered post type options. * * @return array */ public static function get_registered_post_type_options() { return et_get_registered_post_type_options( 'ET_Builder_Settings::sort_post_types' ); } /** * Sort post types callback. * * @param WP_Post $a The first post type to compare. * @param WP_Post $b Tge second post type to compare. * * @return int|lt|mixed */ public static function sort_post_types( $a, $b ) { // ASCII has a total of 127 characters, so 500 as the interval // should be a sufficiently high number. $rank_priority = array( 'page' => 1500, 'post' => 1000, 'project' => 500, ); $a_rank = isset( $rank_priority[ $a->name ] ) ? $rank_priority[ $a->name ] : 0; $b_rank = isset( $rank_priority[ $b->name ] ) ? $rank_priority[ $b->name ] : 0; return strcasecmp( $a->label, $b->label ) - $a_rank + $b_rank; } /** * Initialize the builder settings. */ protected function _initialize() { /** * Filters Divi Builder settings field definitions. * * @since 3.0.45 */ self::$_BUILDER_SETTINGS_FIELDS = apply_filters( 'et_builder_settings_definitions', self::_get_builder_settings_fields() ); /** * Filters Divi Builder settings values. * * @since 3.0.45 * * @param mixed[] $builder_settings { * Builder Settings Values * * @type string $setting_name Setting value. * ... * } */ self::$_BUILDER_SETTINGS_VALUES = apply_filters( 'et_builder_settings_values', self::_get_builder_settings_values() ); if ( function_exists( 'is_product' ) && is_product() ) { self::$_PAGE_SETTINGS_FIELDS = self::_get_page_settings_fields( 'product' ); } else { self::$_PAGE_SETTINGS_FIELDS = self::_get_page_settings_fields(); } /** * Filters Divi Builder page settings field definitions. * * @since 3.29 Customize Page Settings Fields for Product CPT. * @since 3.0.45 */ self::$_PAGE_SETTINGS_FIELDS = apply_filters( 'et_builder_page_settings_definitions', self::$_PAGE_SETTINGS_FIELDS ); /** * Filters Divi Builder page settings field definitions. * * @deprecated {@see 'et_builder_page_settings_definitions'} * * @since 2.7.0 * @since 3.0.45 Deprecation. */ self::$_PAGE_SETTINGS_FIELDS = apply_filters( 'et_pb_get_builder_settings_configurations', self::$_PAGE_SETTINGS_FIELDS ); self::$_PAGE_SETTINGS_VALUES = array(); } /** * Remove static resources for a post when `et_pb_css_in_footer` or `et_pb_static_css_file` field update. * * @param string $setting Setting field slug. * @param string $setting_value Setting field value. */ protected static function _maybe_clear_cached_static_css_files( $setting, $setting_value ) { if ( in_array( $setting, array( 'et_pb_css_in_footer', 'et_pb_static_css_file' ), true ) ) { ET_Core_PageResource::remove_static_resources( 'all', 'all' ); } } /** * Register callbacks. */ protected function _register_callbacks() { $class = get_class( $this ); if ( ! is_admin() ) { // Setup post meta callback registration on preview page. Priority has to be less than 10 // so get_post_meta used on self::_get_page_settings_values() are affected. add_action( 'wp', array( $this, '_register_preview_post_metadata' ), 5 ); return; } add_action( 'et_builder_settings_update_option', array( $class, 'update_option_cb' ), 10, 3 ); // setup plugin style options, rather than epanel. if ( et_is_builder_plugin_active() ) { add_filter( 'et_builder_plugin_dashboard_sections', array( $class, 'add_plugin_dashboard_sections' ) ); add_filter( 'et_builder_plugin_dashboard_fields_data', array( $class, 'add_plugin_dashboard_fields_data' ) ); add_action( 'et_pb_builder_after_save_options', array( $class, 'plugin_dashboard_option_saved_cb' ), 10, 4 ); add_action( 'et_pb_builder_option_value', array( $class, 'plugin_dashboard_option_value_cb' ), 10, 2 ); } else { add_filter( 'et_epanel_tab_names', array( $class, 'add_epanel_tab' ) ); add_filter( 'et_epanel_layout_data', array( $class, 'add_epanel_tab_content' ) ); add_action( 'et_epanel_update_option', array( $class, 'update_option_cb' ), 10, 3 ); } } /** * Adds a tab for the builder to ePanel's tabs array. * {@see 'et_epanel_tab_names'} * * @param string[] $tabs ePanel's tabs array. * * @return string[] $tabs */ public static function add_epanel_tab( $tabs ) { $builder_tab = esc_html_x( 'Builder', 'Divi Builder', 'et_builder' ); $keys = array_keys( $tabs ); $values = array_values( $tabs ); array_splice( $keys, 2, 0, 'builder' ); array_splice( $values, 2, 0, $builder_tab ); return array_combine( $keys, $values ); } /** * Adds builder settings fields data to the builder plugin's options dashboard. * {@see 'et_builder_plugin_dashboard_fields_data'} * * @param array[] $dashboard_data Plugin options array. * * @return array[] $dashboard_data */ public static function add_plugin_dashboard_fields_data( $dashboard_data ) { $tabs = self::get_tabs( 'builder' ); $fields = self::get_fields( 'builder' ); $toggles = self::get_toggles(); foreach ( $tabs as $tab_slug => $tab_name ) { $section = $tab_slug . '_main_options'; if ( ! isset( $dashboard_data[ $section ] ) ) { $dashboard_data[ $section ] = array(); } $dashboard_data[ $section ][] = array( 'type' => 'main_title', 'title' => '', ); foreach ( $toggles as $toggle_slug => $toggle ) { $section_started = false; foreach ( $fields as $field_slug => $field_info ) { if ( $tab_slug !== $field_info['tab_slug'] || $toggle_slug !== $field_info['toggle_slug'] ) { continue; } if ( 'et_pb_css_in_footer' === $field_info['id'] ) { continue; } if ( ! $section_started ) { $dashboard_data[ $section ][] = array( 'type' => 'section_start', 'title' => $toggles[ $toggle_slug ], ); $section_started = true; } $field_info['hint_text'] = $field_info['description']; $field_info['name'] = $field_info['id']; $field_info['title'] = $field_info['label']; $dashboard_data[ $section ][] = $field_info; if ( isset( $field_info['after'] ) ) { $dashboard_data[ $section ][] = $field_info['after']; } } if ( $section_started ) { $dashboard_data[ $section ][] = array( 'type' => 'section_end' ); } } } return $dashboard_data; } /** * Adds tabs for builder settings to the builder plugin's options dashboard. * {@see 'et_builder_plugin_dashboard_sections'} * * @param array[] $sections Sections configuration. * * @return array[] $sections */ public static function add_plugin_dashboard_sections( $sections ) { $tabs = self::get_tabs( 'builder' ); foreach ( $tabs as $tab_slug => $tab_name ) { $sections[ $tab_slug ] = array( 'title' => et_core_esc_previously( $tab_name ), 'contents' => array( 'main' => esc_html__( 'Main', 'et_builder' ), ), ); } return $sections; } /** * Adds builder settings to ePanel. {@see 'et_epanel_layout_data'} * * @param array $layout_data Layout setting fields configuration. * * @return array $data */ public static function add_epanel_tab_content( $layout_data ) { $result = array(); $done = false; foreach ( $layout_data as $data ) { $result[] = $data; if ( $done || ! isset( $data['name'], $data['type'] ) ) { continue; } if ( 'wrap-navigation' === $data['name'] && 'contenttab-wrapend' === $data['type'] ) { $builder_options = self::_get_builder_settings_in_epanel_format(); $result = array_merge( $result, $builder_options ); $done = true; } } return $result; } /** * Update setting option callback. * * @param string $setting Setting field slug. * @param string $setting_value Setting field value. * @param string $post_id Post id or global. */ public static function update_option_cb( $setting, $setting_value, $post_id = 'global' ) { if ( did_action( 'wp_ajax_et_fb_ajax_save' ) ) { return; } self::_maybe_clear_cached_static_css_files( $setting, $setting_value ); } /** * Returns builder settings fields data for the provided settings scope. * * @param string $scope Get settings fields for scope (page|builder|all). Default 'page'. * * @return array[] See {@link ET_Builder_Element::get_fields()} for structure. */ public static function get_fields( $scope = 'page' ) { $fields = array(); if ( 'builder' === $scope ) { $fields = self::$_BUILDER_SETTINGS_FIELDS; } elseif ( 'page' === $scope ) { $fields = self::$_PAGE_SETTINGS_FIELDS; } return $fields; } /** * Get class instance. * * @return ET_Builder_Settings */ public static function get_instance() { if ( null === self::$_instance ) { self::$_instance = new ET_Builder_Settings(); } return self::$_instance; } /** * Returns the localized tab names for the builder settings. * * @param string $scope Accepts 'page', 'builder'. * * @return string[] { * Localized Tab Names. * * @type string $tab_slug Tab name * ... * } */ public static function get_tabs( $scope = 'page' ) { $result = array(); $advanced = esc_html_x( 'Advanced', 'Design Settings', 'et_builder' ); $post_type_integration = esc_html_x( 'Post Type Integration', 'Builder Settings', 'et_builder' ); if ( 'page' === $scope ) { $result = array( 'content' => esc_html_x( 'Content', 'Content Settings', 'et_builder' ), 'design' => esc_html_x( 'Design', 'Design Settings', 'et_builder' ), 'advanced' => $advanced, ); } elseif ( 'builder' === $scope ) { $result = array( 'post_type_integration' => $post_type_integration, 'advanced' => $advanced, ); } /** * Filters the builder's settings tabs. * * @since 3.0.45 * * @param string[] $tabs { * Localized Tab Names. * * @type string $tab_slug Tab name * ... * } * * @param string $scope Accepts 'page', 'builder'. */ return apply_filters( 'et_builder_settings_tabs', $result, $scope ); } /** * Returns the localized title of the builder page settings modal. * * @return Array */ public static function get_title() { global $post; $post_type = isset( $post->post_type ) ? $post->post_type : 'page'; $post_type_obj = get_post_type_object( $post_type ); $settings = esc_html_x( '%s Settings', 'Page, Post, Product, etc.', 'et_builder' ); /** * Filters the title of the builder's page settings modal. * * @since 3.0.45 * * @param string $title */ $default_title = apply_filters( 'et_builder_page_settings_modal_title', sprintf( $settings, $post_type_obj->labels->singular_name ) ); $titles = array( 'post_content' => $default_title, ); // Add titles for Theme Builder Layouts, so they can be used by Visual Theme Builder Editor. $theme_builder_layouts = et_theme_builder_get_template_layouts(); // Unset main template from Theme Builder layouts to avoid PHP Notices. if ( isset( $theme_builder_layouts['et_template'] ) ) { unset( $theme_builder_layouts['et_template'] ); } foreach ( $theme_builder_layouts as $key => $theme_builder_layout ) { $template_obj = get_post_type_object( $key ); $title = apply_filters( 'et_builder_page_settings_modal_title', sprintf( $settings, $template_obj->labels->singular_name ) ); $titles = array_merge( $titles, array( $key => $title ) ); } return $titles; } /** * Returns the localized toggle/group names for the builder page settings modal. * * @return string[] { * Localized Toggle Names * * @type string $toggle_slug Toggle name * } */ public static function get_toggles() { $utils = ET_Core_Data_Utils::instance(); // Get current post type singular name and use it as toggle title. // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. $post_type = wp_doing_ajax() ? $utils->array_get( $_POST, 'et_post_type' ) : get_post_type( et_core_page_resource_get_the_ID() ); $post_type_obj = get_post_type_object( $post_type ); $toggles = array( 'main_content' => ! empty( $post_type_obj ) ? $post_type_obj->labels->singular_name : '', 'background' => et_builder_i18n( 'Background' ), 'color_palette' => esc_html__( 'Color Palette', 'et_builder' ), 'custom_css' => et_builder_i18n( 'Custom CSS' ), 'enable_bfb' => esc_html__( 'Enable The Latest Divi Builder Experience', 'et_builder' ), 'enable_classic_editor' => esc_html__( 'Enable Classic Editor', 'et_builder' ), 'performance' => esc_html__( 'Performance', 'et_builder' ), 'product_tour' => esc_html__( 'Product Tour', 'et_builder' ), 'spacing' => esc_html__( 'Spacing', 'et_builder' ), 'ab_testing' => esc_html__( 'Split Testing', 'et_builder' ), 'text' => et_builder_i18n( 'Text' ), 'visibility' => et_builder_i18n( 'Visibility' ), 'position' => et_builder_i18n( 'Position' ), ); /** * Filters the builder page settings modal's option group toggles. * * @since 3.0.45 * * @param string[] $toggles { * Localized Toggle Names * * @type string $toggle_slug Toggle name * ... * } */ return apply_filters( 'et_builder_page_settings_modal_toggles', $toggles ); } /** * Returns the values of builder settings for each Theme Builder Area combined. * * @param string $scope Get values for scope (page|builder|all). Default 'page'. * @param string|int $post_id Optional. If not provided, {@link get_the_ID()} will be used. * @param bool $exclude_defaults Optional. Whether to exclude default value. * * @return array { * Settings Values * * @type mixed $setting_key The value for the setting. * ... * } */ public static function get_settings_values( $scope = 'page', $post_id = null, $exclude_defaults = false ) { $results = array(); $post_id = $post_id ? $post_id : get_the_ID(); $results['post_content'] = self::get_values( $scope, $post_id, $exclude_defaults ); // In Theme Builder we can return results without TB Layouts. if ( et_builder_tb_enabled() ) { return $results; } $theme_builder_layouts = et_theme_builder_get_template_layouts(); // Unset main template from Theme Builder layouts to avoid PHP Notices. if ( isset( $theme_builder_layouts['et_template'] ) ) { unset( $theme_builder_layouts['et_template'] ); } // Check if any active Theme Builder Area is used and add settings. foreach ( $theme_builder_layouts as $key => $theme_builder_layout ) { if ( is_array( $theme_builder_layout ) && 0 !== $theme_builder_layout['id'] && $theme_builder_layout['enabled'] && $theme_builder_layout['override'] ) { $page_settings_values = self::get_values( $scope, $theme_builder_layout['id'], $exclude_defaults ); $results[ $key ] = $page_settings_values; } } return $results; } /** * Returns the values of builder settings for the provided settings scope. * * @param string $scope Get values for scope (page|builder|all). Default 'page'. * @param string|int $post_id Optional. If not provided, {@link get_the_ID()} will be used. * @param bool $exclude_defaults Optional. Whether to exclude default value. * * @return mixed[] { * Settings Values * * @type mixed $setting_key The value for the setting. * ... * } */ public static function get_values( $scope = 'page', $post_id = null, $exclude_defaults = false ) { $post_id = $post_id ? $post_id : get_the_ID(); $result = array(); if ( 'builder' === $scope ) { $result = self::$_BUILDER_SETTINGS_VALUES; } elseif ( 'page' === $scope ) { $result = self::_get_page_settings_values( $post_id ); } elseif ( 'all' === $scope ) { $result = array( 'page' => self::_get_page_settings_values( $post_id ), 'builder' => self::$_BUILDER_SETTINGS_VALUES, ); } if ( $exclude_defaults ) { if ( 'all' !== $scope ) { $result = array( $result ); } foreach ( $result as $key => $settings ) { $result[ $key ] = array_diff_key( $result[ $key ], array_flip( self::$_PAGE_SETTINGS_IS_DEFAULT[ $post_id ] ) ); } if ( 'all' !== $scope ) { $result = $result[0]; } } return $result; } /** * Callback to automatically clear static resources when option is disabled in divi builder plugin dashboard. * * @param array $processed_options Setting options array. * @param string $option_name Updated option name. * @param array $field_info Field info. * @param array $output Options array. */ public static function plugin_dashboard_option_saved_cb( $processed_options, $option_name, $field_info, $output ) { if ( ! isset( $field_info['id'] ) ) { return; } $setting = $field_info['id']; $setting_value = $processed_options[ $option_name ]; if ( ! isset( self::$_BUILDER_SETTINGS_FIELDS[ $setting ] ) ) { return; } et_update_option( $setting, $setting_value ); self::_maybe_clear_cached_static_css_files( $setting, $setting_value ); } /** * Callback to get dashboard option value from {@see ET_Builder_Settings::$_BUILDER_SETTINGS_VALUES}. * * @param string $option_value Option value. * @param array $option Option array. * * @return mixed */ public static function plugin_dashboard_option_value_cb( $option_value, $option ) { if ( ! isset( $option['id'] ) ) { return $option_value; } $setting = $option['id']; if ( ! isset( self::$_BUILDER_SETTINGS_VALUES[ $setting ] ) ) { return $option_value; } return self::$_BUILDER_SETTINGS_VALUES[ $setting ]; } /** * Register filter callback for modifying page settings post meta value based on current * autosave data if current page is valid builder preview page. * * @since 3.20 * * @return void */ public static function _register_preview_post_metadata() { if ( ! is_user_logged_in() || ! is_preview() || ! et_pb_is_pagebuilder_used() ) { return; } // Populate page settings fields meta_key map. Most page setting field id is identical (sans // `_` prefix) to its meta_key name but some field has completely different meta_key name. foreach ( self::$_PAGE_SETTINGS_FIELDS as $field_id => $field ) { $meta_key = isset( $field['meta_key'] ) ? $field['meta_key'] : '_' . $field_id; self::$_PAGE_SETTINGS_FIELDS_META_KEY_MAP[ $meta_key ] = $field_id; } // Register filter for modifying page setting's post_meta value. add_filter( 'get_post_metadata', array( 'ET_Builder_Settings', 'modify_preview_post_metadata' ), 10, 4 ); } /** * Get page settings' post meta value in preview page. This method should only be called on * preview page only * * @since 3.20 * * @return array */ public static function get_preview_post_metadata() { static $preview_post_metadata = null; // Value retrieval should only be done once. if ( is_null( $preview_post_metadata ) ) { // Get autosave data of current post of current user. $current_user_id = get_current_user_id(); $preview_post_metadata = get_post_meta( get_the_ID(), "_et_builder_settings_autosave_{$current_user_id}", true ); // Returned value should be array. if ( ! is_array( $preview_post_metadata ) ) { $preview_post_metadata = array(); } } return $preview_post_metadata; } /** * Modify page settings' post meta value in preview page. This should only be hooked after * checking whether current page is valid preview page or not * * @see get_metadata() * * @since 3.20 * * @param null|array|string $value Post meta value. * @param int $object_id Post id. * @param string $meta_key Meta key. * @param bool $single Meta value, or an array of values. * * @return null|array|string */ public static function modify_preview_post_metadata( $value, $object_id, $meta_key, $single ) { $current_user_id = get_current_user_id(); // Bail if $meta_key value is equal to meta_key value used to save current page autosave data. if ( "_et_builder_settings_autosave_{$current_user_id}" === $meta_key ) { return $value; } // Bail if $meta_key is not page settings field's meta key. if ( ! isset( self::$_PAGE_SETTINGS_FIELDS_META_KEY_MAP[ $meta_key ] ) ) { return $value; } // Bail if current $meta_key value doesn't exist on preview page autosave data. $preview_post_meta_key = self::$_PAGE_SETTINGS_FIELDS_META_KEY_MAP[ $meta_key ]; $preview_post_metadata = self::get_preview_post_metadata(); if ( ! isset( $preview_post_metadata[ $preview_post_meta_key ] ) ) { return $value; } return $preview_post_metadata[ $preview_post_meta_key ]; } } if ( ! function_exists( 'et_builder_settings_init' ) ) : /** * Initializes the builder settings class if needed. * {@see 'current_screen'} * * @param WP_Screen $screen Optional. Default `null`. */ function et_builder_settings_init( $screen = null ) { $init_settings = et_builder_should_load_framework() || wp_doing_ajax(); if ( ! $init_settings && is_a( $screen, 'WP_Screen' ) ) { $init_settings = 1 === preg_match( '/et_\w+_options/', $screen->base ); } if ( $init_settings ) { ET_Builder_Settings::get_instance(); } } add_action( 'current_screen', 'et_builder_settings_init' ); endif; if ( ! function_exists( 'et_builder_settings_get' ) ) : /** * Get a builder setting value. Default and global setting values are considered when applicable. * * @param string $setting Page setting name. * @param string|int $post_id Optional. The post id. * * @return mixed */ function et_builder_settings_get( $setting, $post_id = '' ) { $builder_fields = ET_Builder_Settings::get_fields( 'builder' ); $builder_values = ET_Builder_Settings::get_values( 'builder' ); $page_fields = ET_Builder_Settings::get_fields(); $page_values = ET_Builder_Settings::get_values( 'page', $post_id ); $has_page = isset( $page_fields[ $setting ] ); $has_global = isset( $builder_fields[ $setting ] ); $global_value = ''; $value = $global_value; $global_is_default = false; if ( ! $has_page && ! $has_global ) { return $value; } if ( $has_global ) { $global_value = $builder_values[ $setting ]; $global_has_default = isset( $builder_fields[ $setting ]['default'] ); $global_is_default = $global_has_default && $global_value === $builder_fields[ $setting ]['default']; $value = $global_value; } if ( $has_page ) { $page_value = $page_values[ $setting ]; $page_has_default = isset( $page_fields[ $setting ]['default'] ); $page_is_default = $page_has_default && $page_value === $page_fields[ $setting ]['default']; $value = $page_value; } if ( ! $has_page || ( $page_is_default && ! $global_is_default ) ) { $value = $global_value; } elseif ( ! $has_global || ! $page_is_default ) { $value = $page_value; } return $value; } endif; if ( ! function_exists( 'et_builder_setting_is_off' ) ) : /** * Whether or not a builder setting is off. Default and global setting values are * considered when applicable. * * @param string $setting Page setting name. * @param string|int $post_id Optional. The post id. * * @return bool */ function et_builder_setting_is_off( $setting, $post_id = '' ) { return 'off' === et_builder_settings_get( $setting, $post_id ); } endif; if ( ! function_exists( 'et_builder_setting_is_on' ) ) : /** * Whether or not a builder setting is on. Default and global setting values are * considered when applicable. * * @param string $setting Page setting name. * @param string|int $post_id Optional. The post id. * * @return bool */ function et_builder_setting_is_on( $setting, $post_id = '' ) { return 'on' === et_builder_settings_get( $setting, $post_id ); } endif; PK X�\6�{q q 1 builder/class-et-builder-plugin-compat-loader.phpnu �[��� <?php /** * Load plugin compatibility file if supported plugins are activated. * * @since 0.7 (builder version) * * @package Divi */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class ET_Builder_Plugin_Compat_Loader. */ class ET_Builder_Plugin_Compat_Loader { /** * Unique instance of class. * * @var ET_Builder_Plugin_Compat_Loader */ public static $instance; /** * Constructor. */ private function __construct() { $this->init_hooks(); } /** * Gets the instance of the class. */ public static function init() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Hook methods to WordPress action and filter. * * @return void */ private function init_hooks() { // Load plugin.php for frontend usage. if ( ! function_exists( 'is_plugin_active' ) || ! function_exists( 'get_plugins' ) ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; } // Loop plugin list and load active plugin compatibility file. foreach ( array_keys( get_plugins() ) as $plugin ) { // Load plugin compat file if plugin is active. if ( is_plugin_active( $plugin ) ) { $plugin_compat_name = dirname( $plugin ); $plugin_compat_url = apply_filters( "et_builder_plugin_compat_path_{$plugin_compat_name}", ET_BUILDER_DIR . "plugin-compat/{$plugin_compat_name}.php", $plugin_compat_name ); // Load plugin compat file (if compat file found). if ( file_exists( $plugin_compat_url ) ) { require_once $plugin_compat_url; } } } } } ET_Builder_Plugin_Compat_Loader::init(); PK X�\@ųm m builder/_et_builder_version.phpnu �[��� <?php // Note, this will be updated automatically during grunt release task $ET_BUILDER_VERSION = '3.17.3'; PK X�\��u��S �S 5 builder/class-et-builder-module-shortcode-manager.phpnu �[��� <?php /** * Shortcode Manager Class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Handles module shortcodes. * * @since 4.10.0 */ class ET_Builder_Module_Shortcode_Manager { /** * Modules container. * * @access public * @var array */ public static $modules_map = []; /** * WooCommerce modules container. * * @access public * @var array */ public static $woo_modules_map = []; /** * Structural Modules container. * * @access public * @var array */ public static $structural_modules_map = []; /** * Initialize shortcode manager class. * * @since 4.10.0 * @access public * @return void */ public function init() { $this->register_modules(); $this->register_fullwidth_modules(); $this->register_structural_modules(); $this->register_woo_modules(); $this->register_shortcode(); } /** * Get modules map. * * @since 4.14.5 * * @param string $type Modules map type. * * @return array Modules map. */ public static function get_modules_map( $type = false ) { if ( 'woo_modules' === $type ) { return self::$woo_modules_map; } if ( 'structural_modules' === $type ) { return self::$structural_modules_map; } return self::$modules_map; } /** * Start registering shortcodes. * * @since 4.10.0 * @access public * @return void */ public function register_shortcode() { // is_saving_cache or other scenarios where we need to load everything. if ( et_builder_should_load_all_module_data() ) { $this->register_all_shortcodes(); } else { $this->register_lazy_shortcodes(); } } /** * Register normal modules. * * Modules dependent to each other will have * to have a dependency parameter on them. * Eg : et_pb_accordion_item needs et_pb_toggle so we * have to pass add that on the `deps` key. * * @since 4.10.0 * @access public * @return void */ public function register_modules() { $modules = [ 'et_pb_accordion' => [ 'classname' => 'ET_Builder_Module_Accordion', ], 'et_pb_accordion_item' => [ 'classname' => 'ET_Builder_Module_Accordion_Item', 'deps' => array( 'et_pb_toggle' ), ], 'et_pb_audio' => [ 'classname' => 'ET_Builder_Module_Audio', ], 'et_pb_counters' => [ 'classname' => 'ET_Builder_Module_Bar_Counters', ], 'et_pb_counter' => [ 'classname' => 'ET_Builder_Module_Bar_Counters_Item', ], 'et_pb_blog' => [ 'classname' => 'ET_Builder_Module_Blog', ], 'et_pb_blurb' => [ 'classname' => 'ET_Builder_Module_Blurb', ], 'et_pb_button' => [ 'classname' => 'ET_Builder_Module_Button', ], 'et_pb_circle_counter' => [ 'classname' => 'ET_Builder_Module_Circle_Counter', ], 'et_pb_code' => [ 'classname' => 'ET_Builder_Module_Code', ], 'et_pb_comments' => [ 'classname' => 'ET_Builder_Module_Comments', ], 'et_pb_contact_form' => [ 'classname' => 'ET_Builder_Module_Contact_Form', ], 'et_pb_contact_field' => [ 'classname' => 'ET_Builder_Module_Contact_Form_Item', ], 'et_pb_countdown_timer' => [ 'classname' => 'ET_Builder_Module_Countdown_Timer', ], 'et_pb_cta' => [ 'classname' => 'ET_Builder_Module_Cta', ], 'et_pb_divider' => [ 'classname' => 'ET_Builder_Module_Divider', ], 'et_pb_filterable_portfolio' => [ 'classname' => 'ET_Builder_Module_Filterable_Portfolio', ], 'et_pb_gallery' => [ 'classname' => 'ET_Builder_Module_Gallery', ], 'et_pb_image' => [ 'classname' => 'ET_Builder_Module_Image', ], 'et_pb_login' => [ 'classname' => 'ET_Builder_Module_Login', ], 'et_pb_map' => [ 'classname' => 'ET_Builder_Module_Map', ], 'et_pb_map_pin' => [ 'classname' => 'ET_Builder_Module_Map_Item', ], 'et_pb_menu' => [ 'classname' => 'ET_Builder_Module_Menu', ], 'et_pb_number_counter' => [ 'classname' => 'ET_Builder_Module_Number_Counter', ], 'et_pb_portfolio' => [ 'classname' => 'ET_Builder_Module_Portfolio', ], 'et_pb_post_content' => [ 'classname' => 'ET_Builder_Module_PostContent', ], 'et_pb_post_slider' => [ 'classname' => 'ET_Builder_Module_Post_Slider', ], 'et_pb_post_title' => [ 'classname' => 'ET_Builder_Module_Post_Title', ], 'et_pb_post_nav' => [ 'classname' => 'ET_Builder_Module_Posts_Navigation', ], 'et_pb_pricing_tables' => [ 'classname' => 'ET_Builder_Module_Pricing_Tables', ], 'et_pb_pricing_table' => [ 'classname' => 'ET_Builder_Module_Pricing_Tables_Item', ], 'et_pb_search' => [ 'classname' => 'ET_Builder_Module_Search', ], 'et_pb_sidebar' => [ 'classname' => 'ET_Builder_Module_Sidebar', ], 'et_pb_signup' => [ 'classname' => 'ET_Builder_Module_Signup', ], 'et_pb_signup_custom_field' => [ 'classname' => 'ET_Builder_Module_Signup_Item', 'preload_deps' => array( 'et_pb_contact_field' ), ], 'et_pb_slider' => [ 'classname' => 'ET_Builder_Module_Slider', ], 'et_pb_slide' => [ 'classname' => 'ET_Builder_Module_Slider_Item', ], 'et_pb_social_media_follow' => [ 'classname' => 'ET_Builder_Module_Social_Media_Follow', ], 'et_pb_social_media_follow_network' => [ 'classname' => 'ET_Builder_Module_Social_Media_Follow_Item', ], 'et_pb_tabs' => [ 'classname' => 'ET_Builder_Module_Tabs', ], 'et_pb_tab' => [ 'classname' => 'ET_Builder_Module_Tabs_Item', ], 'et_pb_team_member' => [ 'classname' => 'ET_Builder_Module_Team_Member', ], 'et_pb_testimonial' => [ 'classname' => 'ET_Builder_Module_Testimonial', ], 'et_pb_text' => [ 'classname' => 'ET_Builder_Module_Text', ], 'et_pb_toggle' => [ 'classname' => 'ET_Builder_Module_Toggle', ], 'et_pb_video' => [ 'classname' => 'ET_Builder_Module_Video', ], 'et_pb_video_slider' => [ 'classname' => 'ET_Builder_Module_Video_Slider', ], 'et_pb_video_slider_item' => [ 'classname' => 'ET_Builder_Module_Video_Slider_Item', ], 'et_pb_icon' => [ 'classname' => 'ET_Builder_Module_Icon', ], 'et_pb_heading' => [ 'classname' => 'ET_Builder_Module_Heading', ], ]; /** * Filters built-in Divi Builder module class names. * * 3rd-party plugins can use this filter to override Divi Builder modules. * * NOTE: Overriding built-in modules is not ideal and should only be used as a temporary solution. * The recommended approach for achieving this is using the official API: * https://www.elegantthemes.com/documentation/developers/divi-module/how-to-create-a-divi-builder-module/ * * @since 4.11.0 * * @param array $additional_modules Additional modules. */ $additional_modules = apply_filters( 'et_module_classes', [] ); self::$modules_map = array_merge( self::$modules_map, $modules, $additional_modules ); } /** * Register fullwidth modules. * * @since 4.10.0 * @access public * @return void */ public function register_fullwidth_modules() { $modules = [ 'et_pb_fullwidth_code' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Code', ], 'et_pb_fullwidth_header' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Header', ], 'et_pb_fullwidth_image' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Image', ], 'et_pb_fullwidth_map' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Map', ], 'et_pb_fullwidth_menu' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Menu', ], 'et_pb_fullwidth_portfolio' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Portfolio', ], 'et_pb_fullwidth_post_content' => [ 'classname' => 'ET_Builder_Module_Fullwidth_PostContent', ], 'et_pb_fullwidth_post_slider' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Post_Slider', ], 'et_pb_fullwidth_post_title' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Post_Title', ], 'et_pb_fullwidth_slider' => [ 'classname' => 'ET_Builder_Module_Fullwidth_Slider', ], ]; /** * Filters built-in Divi Builder module class names. * * 3rd-party plugins can use this filter to override Divi Builder modules. * * NOTE: Overriding built-in modules is not ideal and should only be used as a temporary solution. * The recommended approach for achieving this is using the official API: * https://www.elegantthemes.com/documentation/developers/divi-module/how-to-create-a-divi-builder-module/ * * @since 4.11.0 * * @param array $additional_modules Additional modules. */ $additional_modules = apply_filters( 'et_fullwidth_module_classes', [] ); self::$modules_map = array_merge( self::$modules_map, $modules, $additional_modules ); } /** * Register structural modules. * * @since 4.10.0 * @access public * @return void */ public function register_structural_modules() { $modules = [ 'et_pb_section' => [ 'classname' => 'ET_Builder_Section', ], 'et_pb_row' => [ 'classname' => 'ET_Builder_Row', ], 'et_pb_row_inner' => [ 'classname' => 'ET_Builder_Row_Inner', ], 'et_pb_column' => [ 'classname' => 'ET_Builder_Column', ], ]; /** * Filters built-in Divi Builder module class names. * * 3rd-party plugins can use this filter to override Divi Builder modules. * * NOTE: Overriding built-in modules is not ideal and should only be used as a temporary solution. * The recommended approach for achieving this is using the official API: * https://www.elegantthemes.com/documentation/developers/divi-module/how-to-create-a-divi-builder-module/ * * @since 4.11.0 * * @param array $additional_modules Additional modules. */ $additional_modules = apply_filters( 'et_structural_module_classes', [] ); self::$structural_modules_map = array_merge( $modules, $additional_modules ); } /** * Register woocommerce modules. * * @since 4.10.0 * @access public * @return void */ public function register_woo_modules() { $woo_modules = [ 'et_pb_wc_add_to_cart' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Add_To_Cart', ], 'et_pb_wc_additional_info' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Additional_Info', ], 'et_pb_wc_breadcrumb' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Breadcrumb', ], 'et_pb_wc_cart_notice' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Cart_Notice', ], 'et_pb_wc_description' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Description', ], 'et_pb_wc_gallery' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Gallery', ], 'et_pb_wc_images' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Images', ], 'et_pb_wc_meta' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Meta', ], 'et_pb_wc_price' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Price', ], 'et_pb_wc_rating' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Rating', ], 'et_pb_wc_related_products' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Related_Products', ], 'et_pb_wc_reviews' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Reviews', ], 'et_pb_wc_stock' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Stock', ], 'et_pb_wc_tabs' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Tabs', ], 'et_pb_wc_title' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Title', ], 'et_pb_wc_upsells' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Upsells', ], 'et_pb_wc_cart_products' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Cart_Products', ], 'et_pb_wc_cross_sells' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Cross_Sells', ], 'et_pb_wc_cart_totals' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Cart_Totals', ], 'et_pb_wc_checkout_billing' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Checkout_Billing', ], 'et_pb_wc_checkout_shipping' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Checkout_Shipping', ], 'et_pb_wc_checkout_order_details' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Checkout_Order_Details', ], 'et_pb_wc_checkout_payment_info' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Checkout_Payment_Info', ], 'et_pb_wc_checkout_additional_info' => [ 'classname' => 'ET_Builder_Module_Woocommerce_Checkout_Additional_Info', ], 'et_pb_shop' => [ 'classname' => 'ET_Builder_Module_Shop', ], ]; /** * Filters built-in Divi Builder module class names. * * 3rd-party plugins can use this filter to override Divi Builder modules. * * NOTE: Overriding built-in modules is not ideal and should only be used as a temporary solution. * The recommended approach for achieving this is using the official API: * https://www.elegantthemes.com/documentation/developers/divi-module/how-to-create-a-divi-builder-module/ * * @since 4.11.0 * * @param array $additional_modules Additional modules. */ $additional_modules = apply_filters( 'et_woo_module_classes', [] ); self::$woo_modules_map = $woo_modules; self::$modules_map = array_merge( self::$modules_map, $woo_modules, $additional_modules ); } /** * Register shortcode. * * @since 4.10.0 * @access public * @return void */ public function register_all_shortcodes() { $et_builder_module_files = glob( ET_BUILDER_DIR . 'module/*.php' ); $et_builder_module_types = glob( ET_BUILDER_DIR . 'module/type/*.php' ); if ( ! $et_builder_module_files ) { return; } /** * Fires before the builder's module classes are loaded. * * @since 3.0.77 */ do_action( 'et_builder_modules_load' ); foreach ( $et_builder_module_types as $module_type ) { require_once $module_type; } foreach ( $et_builder_module_files as $module_file ) { // skip this all caps version, if it exists. // See https://github.com/elegantthemes/Divi/issues/24780. if ( 'CTA.php' === basename( $module_file ) ) { continue; } require_once $module_file; } if ( et_is_woocommerce_plugin_active() ) { $et_builder_woocommerce_module_files = glob( ET_BUILDER_DIR . 'module/woocommerce/*.php' ); foreach ( $et_builder_woocommerce_module_files as $module_type ) { require_once $module_type; } } /** * Fires after the builder's module classes are loaded. * * NOTE: this hook only fires on : * - Visual Builder pages * - Front end cache prime initial request * * IT DOES NOT fire on ALL front end requests * * @since 3.0.77 * @deprecated ?? Introduced shortcode manager. * Use {@see et_builder_module_loading}/{@see et_builder_module_loaded}/{@see et_builder_ready} instead. */ do_action( 'et_builder_modules_loaded' ); } /** * Lazy load shortcodes. * * @since 4.10.0 * @access public * @return void */ public function register_lazy_shortcodes() { // A fake handler has to be registered for every shortcode, otherways // code will exit early and the pre_do_shortcode_tag hook won't be executed. foreach ( self::$modules_map as $shortcode_slug => $module_data ) { add_shortcode( $shortcode_slug, '__return_empty_string' ); } // Load modules as needed. add_filter( 'pre_do_shortcode_tag', [ $this, 'load_modules' ], 99, 2 ); // Ensure all our module slugs are always considered, even when not loaded (yet). add_filter( 'et_builder_get_module_slugs_by_post_type', [ $this, 'add_module_slugs' ] ); add_filter( 'et_builder_get_woocommerce_modules', [ $this, 'add_woo_slugs' ] ); // Ensure all our structural module slugs are always considered, even when not loaded (yet). add_filter( 'et_builder_get_structural_module_slugs', [ $this, 'add_structural_module_slugs' ] ); /** * Fires after the builder's module classes are loaded. * * This hook is fired here for legacy reasons only. * Do not depend on this hook in the future. * * @since 3.0.77 * @deprecated ?? Introduced shortcode manager. * Use {@see et_builder_module_loading}/{@see et_builder_module_loaded}/{@see et_builder_ready} instead. */ do_action( 'et_builder_modules_loaded' ); /** * Fires after the builder's module shortcodes are lazy registered. * * @since 4.10.0 */ do_action( 'et_builder_module_lazy_shortcodes_registered' ); } /** * Add slugs for all our woo modules. * * @since 4.10.0 * @access public * @param array $loaded Loaded woo modules slugs. * @return array */ public function add_woo_slugs( $loaded ) { static $module_slugs; // Only compute this once. if ( empty( $module_slugs ) ) { $module_slugs = array_keys( self::$woo_modules_map ); } return array_unique( array_merge( $loaded, $module_slugs ) ); } /** * Add slugs for all our modules. * * @since 4.10.0 * @access public * @param array $loaded Loaded modules slugs. * @return array */ public function add_module_slugs( $loaded ) { static $module_slugs; // Only compute this once. if ( empty( $module_slugs ) ) { $module_slugs = array_keys( self::$modules_map ); } return array_unique( array_merge( $loaded, $module_slugs ) ); } /** * Add slugs for all our structural modules. * * @since 4.10.0 * @access public * @param array $loaded Loaded modules slugs. * @return array */ public function add_structural_module_slugs( $loaded ) { static $structural_module_slugs; // Only compute this once. if ( empty( $structural_module_slugs ) ) { $structural_module_slugs = array_keys( self::$structural_modules_map ); } return array_unique( array_merge( $loaded, $structural_module_slugs ) ); } /** * Load modules. * * @since 4.10.0 * @access public * @param mixed $override Whether to override do_shortcode return value or not. * @param string $tag Shortcode tag. * @return mixed */ public function load_modules( $override, $tag ) { $this->maybe_load_module_from_slug( $tag ); return $override; } /** * Instantiate module from a shortcode slug. * * @since 4.10.0 * @access public * @param string $tag Shortcode tag. * @return void */ public function maybe_load_module_from_slug( $tag ) { if ( empty( self::$modules_map[ $tag ] ) ) { // None of our business. return; } $module =& self::$modules_map[ $tag ]; if ( empty( $module['instance'] ) ) { /** * Fires before module class is instantiated. * * @param string $tag Shortcode tag for module. * @param array $module Module loading configuration details. * * @since 4.10.0 */ do_action( 'et_builder_module_loading', $tag, $module ); /** * Fires before module class is instantiated. * * The dynamic portion of the hook, `$tag`, refers to the shortcode tag. * * @param array $module Module loading configuration details. * * @since 4.10.0 */ do_action( "et_builder_module_loading_{$tag}", $module ); // Load dependency before the class if needed. if ( ! empty( $module['preload_deps'] ) ) { foreach ( $module['preload_deps'] as $slug ) { $this->maybe_load_module_from_slug( $slug ); } } if ( class_exists( $module['classname'] ) ) { $module['instance'] = new $module['classname'](); } if ( ! empty( $module['deps'] ) ) { foreach ( $module['deps'] as $slug ) { $this->maybe_load_module_from_slug( $slug ); } } /** * Fires after module class is instantiated. * * @param string $tag Shortcode tag for module. * @param array $module Module loading configuration details. * * @since 4.10.0 */ do_action( 'et_builder_module_loaded', $tag, $module ); /** * Fires after module class is instantiated. * * The dynamic portion of the hook, `$tag`, refers to the shortcode tag. * * @param array $module Module loading configuration details. * * @since 4.10.0 */ do_action( "et_builder_module_loaded_{$tag}", $module ); } } } PK X�\���e� e� $ builder/class-et-builder-element.phpnu �[��� <?php /** * Base class for all builder elements. * * @package Divi * @subpackage Builder * @since 1.0 */ if ( ! defined( 'ET_BUILDER_OPTIMIZE_TEMPLATES' ) ) { define( 'ET_BUILDER_OPTIMIZE_TEMPLATES', true ); } define( 'ET_BUILDER_AJAX_TEMPLATES_AMOUNT', apply_filters( 'et_pb_templates_loading_amount', ET_BUILDER_OPTIMIZE_TEMPLATES ? 20 : 10 ) ); add_action( 'init', array( 'ET_Builder_Element', 'set_media_queries' ), 11 ); /** * Base class for all builder elements. * * @since 1.0 */ class ET_Builder_Element { /** * Module name. * * @var string */ public $name; /** * Legacy template name (Extra). * * @var string */ public $template_name; /** * Module plural name. * * @var string */ public $plural; /** * Module slug. * * @var string */ public $slug; /** * Module type e.g child. * * @var string */ public $type; /** * Child module slug. * * @var string */ public $child_slug; /** * Set true if module use raw content e.g Code module. * * @var bool */ public $use_raw_content = false; /** * Modules fields. * * @var array */ public $fields = array(); /** * Modules advanced fields. * * @var mixed */ public $advanced_fields; /** * Whether module has advanced fields. * * @var bool */ public $has_advanced_fields; /** * ET_Builder_Module_Field_TextShadow utility class. * * @var ET_Builder_Module_Field_TextShadow */ public $text_shadow; /** * ET_Builder_Module_Field_MarginPadding utility class. * * Utility class to handle margin and padding fields. * * @var ET_Builder_Module_Field_MarginPadding */ public $margin_padding; /** * Holds module's additional fields. * * @var array */ public $_additional_fields_options; /** * Holds new item(module) text. * * @var string */ public $child_item_text; /** * Holds Title text. * * @var string */ public $advanced_setting_title_text; /** * Holds Settings text. * * @var string */ public $settings_text; /** * Holds defaults. * * @var array */ public $defaults; /** * Legacy fields defaults. * * @var array */ public $fields_defaults; /** * Additional shortcode slugs. * * @var string|array */ public $additional_shortcode_slugs; /** * Boolean to indicate whether the module is a fullwidth module. * * @var bool */ public $fullwidth; /** * Holds Module's global settings slug. * * @var string */ public $global_settings_slug; /** * Cached translations. * * @since 4.4.9 * * @var array[] */ protected static $i18n; /** * See {@see deprecations.php} * * @var array[] */ protected static $_deprecations; /** * Unprocessed attributes. * * @since 3.17.2 * * @var mixed[] */ protected $attrs_unprocessed = array(); /** * Unprocessed content. * * @since 3.17.2 * * @var string */ protected $content_unprocessed = ''; /** * Settings used to render the module's output. * * @since 3.1 Renamed from `$shortcode_atts` to `$props`. * @since 1.0 * * @var array */ public $props = array(); /** * What appears inside the element. For structure elements, this will contain children * elements. For parent modules, this will include child modules. * * @since 3.1 Renamed from `$shortcode_content` to `$content`. * @since 1.0 * * @var string */ public $content; /** * Configuration for module's wrapper and inner wrapper * * @since 3.1 * * @var array */ public $wrapper_settings = array(); /** * Unique field definitions that are used in each modules. * * @var array */ public $fields_unprocessed = array(); /** * Main css selector of element. * * @var string */ public $main_css_element; /** * Custom css fields of module. * * @var array|mixed */ public $custom_css_fields = array(); /** * Child item label var. e.x `admin_title` field var. * * @var string */ public $child_title_var; /** * Child item label fallback var. * * @var string */ public $child_title_fallback_var; /** * Divi Builder enabled Post Types. * * @var array|mixed|void */ public $post_types = array(); /** * Main modules tabs. e.g `Content`, `Design` and `Advanced` * * @var array|mixed|void */ public $main_tabs = array(); /** * BB :: Main modules tabs. e.g `general`, `advanced` and `custom_css` * * @var array */ public $used_tabs = array(); /** * Whether module support custom css options e.g `Advanced > Custom CSS` toggle. * * @var bool */ public $custom_css_tab; /** * Whether module support visual builder. e.g `on` or `off`. * * @var string */ public $vb_support = 'off'; /** * Options list to not replace %22 with double quotes while rendering. * * @var array */ public $dbl_quote_exception_options = array( 'et_pb_font_icon', 'et_pb_button_one_icon', 'et_pb_button_two_icon', 'et_pb_button_icon', 'et_pb_content' ); /** * Module's settings modal custom tabs. * * @var array|array[] */ public $settings_modal_tabs = array(); /** * Module's settings modal toggles. e.x `background`, `custom css`. * * @var array|mixed */ public $settings_modal_toggles = array(); /** * Whether module support post featured image background. * * @var bool */ public $featured_image_background = false; /** * All CSS classes name the module has. * * @var array */ public $classname = array(); /** * Module's help video configuration array. * * @var array */ public $help_videos = array(); /** * Whether `ET_Builder_Module_Settings_Migration` class initialized. * * @var bool */ public static $settings_migrations_initialized = false; /** * An array of modules where `module_classname()` used. * * @var array */ public static $uses_module_classname = array(); /** * Holds module's shortcode. * * @var string|string[]|null */ public $_original_content; /** * Unique field definitions across all modules. * * @var array */ protected static $_fields_unprocessed = array(); /** * Default props of each modules. * * @var array */ protected static $_default_props = array(); /** * Slugs of modules for which an option template has been rebuilt. * * @var array */ protected static $_has_rebuilt_option_template = array(); /** * Modules cache. * * @var bool|array */ private static $_cache = false; /** * Keys map of unique BB templates. * * @var array */ private static $_unique_bb_keys_map = array(); /** * List of unique BB templates. * * @var array */ private static $_unique_bb_keys_values = array(); /** * List of tabs/newlines characters. * * @var array */ private static $_unique_bb_strip = array( "\t", "\r", "\n" ); /** * Scroll effects fields of all modules. * * @var array */ public static $_scroll_effects_fields = array( 'desktop' => array(), 'tablet' => array(), 'phone' => array(), ); /** * Sticky element configuration * * @since 4.6.0 * * @var array */ public static $sticky_elements = array(); /** * Number of times {@see self::render()} has been executed. * * @var int */ private $_render_count; /** * Number of times {@see self::render()} has been executed for the shop module. * * @var int */ private static $_shop_render_count = 0; /** * Slug of a module whose render count should also be bumped when this module's is bumped. * * @var string */ protected $_bumps_render_count; /** * Priority number applied to some CSS rules. * * @var int */ protected $_style_priority; /** * Nnly needed for BB + hover. * * @var bool */ protected $is_background = false; /** * Whether module is official divi module. * * @var bool */ private $_is_official_module; /** * Whether module is woocommerce module. * * @var bool */ private $_is_woocommerce_module; /** * Uses for ligatures disabling at elements with letter-spacing CSS property. * * @var array */ private $letter_spacing_fix_selectors = array(); /** * Holds module styles for the current request. * * @var array */ private static $styles = array(); /** * Holds module free form styles for the current request. * * @var string */ private static $_free_form_styles = ''; /** * Holds internal module styles for the current module. * e.x In the Blog post module, {@see $internal_modules_styles} will hold style of all posts. * * @var array */ private static $internal_modules_styles = array(); /** * Whether to save styles to the {@see $internal_modules_styles}. * * @var bool */ private static $prepare_internal_styles = false; /** * Internal modules counter. * * @var int */ private static $internal_modules_counter = 10000; /** * Media queries key value pairs. {@see get_media_quries()} * * @var array */ private static $media_queries = array(); /** * List of all modules instance that extends this class. * * @var array */ private static $modules = array(); /** * List of all parent modules instance that extends this class. * e.x Accordion, BarCounters. * * @var array */ private static $parent_modules = array(); /** * List of all child modules instance that extends this class. * e.x AccordionItem, BarCountersItems. * * @var array */ private static $child_modules = array(); /** * List of all woocommerce modules instance that extends this class. * * @var array */ private static $woocommerce_modules = array(); /** * Hold current module index while loading backbone templates in batch. * * @var int */ private static $current_module_index = 0; /** * List of all structure modules objects. * e.x Section, Row, Row Inner, Columns. * * @var array */ private static $structure_modules = array(); /** * List of all structure modules slugs. * * @var array */ private static $structure_module_slugs = array(); /** * List of all modules slugs by post type. * * @var array */ private static $_module_slugs_by_post_type = array(); /** * Module Icons displayed in Add Module modals. * * @var array */ private static $module_icons = array(); /** * List of all modules help videos. * * @var array */ private static $module_help_videos = array(); /** * Parent module's motion/scroll effects options settings. * * @var array */ private static $parent_motion_effects = array(); /** * A stack of the current active theme builder layout post type. * * @var string[] */ protected static $theme_builder_layout = array(); /** * A stack of the current active WP Editor template post type such as: * - wp_template * - wp_template_part * * @var array[] */ public static $wp_editor_template = array(); /** * Compile list of modules that has rich editor option. * * @var array */ protected static $has_content_modules = array(); /** * Whether loading backbone templates. * * @var bool */ private static $loading_backbone_templates = false; /** * Instance of `ET_Core_Data_Utils`. * * @var ET_Core_Data_Utils */ protected static $_ = null; /** * `ET_Core_PageResource` class instance. * * @var ET_Core_PageResource */ public static $advanced_styles_manager = null; /** * `ET_Core_PageResource` class instance. * * @var ET_Core_PageResource */ public static $deferred_styles_manager = null; /** * Whether to force inline styles. * * @var bool */ public static $forced_inline_styles = false; /** * `ET_Core_Data_Utils` instance. * * @var ET_Core_Data_Utils */ public static $data_utils = null; /** * `ET_Builder_Module_Helper_OptionTemplate` instance. * * @var ET_Builder_Module_Helper_OptionTemplate */ public static $option_template = null; /** * Composite field dependencies settings. * e.g `show_if`, `show_if_not` * * @var array */ public static $field_dependencies = array(); /** * Whether element indexes can be reset or not. * * @var bool */ public static $can_reset_element_indexes = true; const DEFAULT_PRIORITY = 10; const HIDE_ON_MOBILE = 'et-hide-mobile'; /** * Credits of all custom modules. * * @var array */ protected $module_credits; /** * Store various indices for modules etc. * * @since 4.0 * * @var array $indices { * Module Indices By Post Type * * @type array $post_type { * Module Indices * * @type int|int[] $index_type Current index value(s) { * Index Values By Module Slug * * @type int $slug Index value * } * } * } */ protected static $_indices = array(); const INDEX_SECTION = 'section'; const INDEX_ROW = 'row'; const INDEX_ROW_INNER = 'row_inner'; const INDEX_COLUMN = 'column'; const INDEX_COLUMN_INNER = 'column_inner'; const INDEX_MODULE = 'module'; const INDEX_MODULE_ITEM = 'module_item'; const INDEX_MODULE_ORDER = 'module_order'; const INDEX_INNER_MODULE_ORDER = 'inner_module_order'; /** * Instance of `ET_Builder_Global_Presets_Settings`. * * @var ET_Builder_Global_Presets_Settings */ protected static $global_presets_manager = null; /** * Flag whether the module is rendering. * * @var boolean */ protected $is_rendering = false; /** * Flag if current module is sticky or not * * @var boolean */ protected $is_sticky_module = false; /** * List of props keys that need to inherit the value * * Intended to be used in ET_Builder_Module_Helper_MultiViewOptions helper * * @since 4.0.2 * * @var array */ public $mv_inherited_props = array(); /** * Background related values generated by process_advanced_background_options() * Disabled by default; activated by setting $save_processed_background property to true * Only gradient related value is saved right now; more can be added later if needed * * @since 4.15.0 No longer in use. * @since 4.3.3 * * @var array */ protected $processed_background = array(); /** * Set true to save processed background so it can be modified & reapplied on another element. * * @since 4.15.0 No longer in use. * * @var bool */ protected $save_processed_background = false; /** * Holds active position origin/location for all devices. * * @var array */ protected $position_locations = array(); /** * Module settings that are exposed so layout block previewer can correctly render it * despites tweaks performed to make block preview correctly aligned * * @var array */ protected static $layout_block_assistive_settings = array(); /** * Holds feature manager object. * * @var object */ protected $_features_manager; /** * All module slugs. * * @since 4.10.0 * * @var array */ public static $all_module_slugs = array(); /** * Whether current module uses unique ID or not. * * The unique ID will be added once the module is added via Divi Builder. * * @since 4.13.1 * * @var boolean */ protected $_use_unique_id = false; /** * Whether WordPress lazy load is disabled or not. * * @since 4.21.1 * * @var boolean */ public static $is_wp_lazy_load_disabled = false; /** * ET_Builder_Element constructor. */ public function __construct() { self::$current_module_index++; if ( ! self::$_deprecations ) { self::$_deprecations = require_once ET_BUILDER_DIR . 'deprecations.php'; self::$_deprecations = self::$_deprecations['classes']['\ET_Builder_Module_Blurb']; } if ( ! self::$settings_migrations_initialized ) { self::$settings_migrations_initialized = true; require_once 'module/settings/Migration.php'; ET_Builder_Module_Settings_Migration::init(); add_filter( 'the_content', array( get_class( $this ), 'reset_element_indexes' ), 9999 ); add_filter( 'et_builder_render_layout', array( get_class( $this ), 'reset_element_indexes' ), 9999 ); } if ( self::$loading_backbone_templates || et_admin_backbone_templates_being_loaded() ) { if ( ! self::$loading_backbone_templates ) { self::$loading_backbone_templates = true; } // phpcs:disable WordPress.Security.NonceVerification -- The `$_POST` values has not been saved in db, and is therefore not susceptible to CSRF. $start_from = isset( $_POST['et_templates_start_from'] ) ? (int) sanitize_text_field( $_POST['et_templates_start_from'] ) : 0; $post_type = isset( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : ''; // phpcs:enable if ( 'layout' === $post_type ) { // need - 2 to include the et_pb_section and et_pb_row modules. $start_from = self::get_modules_count( 'page' ) - 2; } $current_module_index = self::$current_module_index - 1; if ( ! ( $current_module_index >= $start_from && $current_module_index < ( ET_BUILDER_AJAX_TEMPLATES_AMOUNT + $start_from ) ) ) { return; } } if ( null === self::$advanced_styles_manager && ! is_admin() && ! et_fb_is_enabled() ) { $result = self::setup_advanced_styles_manager(); self::$advanced_styles_manager = $result['manager']; if ( isset( $result['deferred'] ) ) { self::$deferred_styles_manager = $result['deferred']; } if ( $result['add_hooks'] ) { // Schedule callback to run in the footer so we can pass the module design styles to the page resource. add_action( 'wp_footer', array( 'ET_Builder_Element', 'set_advanced_styles' ), 19 ); // Add filter for the resource data so we can prevent theme customizer css from being // included with the builder css inline on first-load (since its in the head already). add_filter( 'et_core_page_resource_get_data', array( 'ET_Builder_Element', 'filter_page_resource_data' ), 10, 3 ); } add_action( 'wp_footer', array( 'ET_Builder_Element', 'maybe_force_inline_styles' ), 19 ); } if ( null === self::$data_utils ) { self::$data_utils = ET_Core_Data_Utils::instance(); self::$_ = self::$data_utils; // Disable wptexturize for code modules because it break things. // Doing that here so it only happen once. add_filter( 'no_texturize_shortcodes', array( $this, 'disable_wptexturize' ) ); } if ( null === self::$global_presets_manager ) { self::$global_presets_manager = ET_Builder_Global_Presets_Settings::instance(); } if ( null === self::$option_template ) { self::$option_template = et_pb_option_template(); } $this->init(); $this->settings_modal_tabs = $this->get_settings_modal_tabs(); $this->settings_modal_toggles = $this->get_settings_modal_toggles(); $this->custom_css_fields = $this->get_custom_css_fields_config(); $this->_set_advanced_fields_config(); $current_class = get_class( $this ); $this->_is_official_module = self::_is_official_module( $current_class ); $this->_is_woocommerce_module = self::_is_woocommerce_module( $current_class ); $this->make_options_filterable(); if ( et_fb_is_builder_ajax() ) { // Ensure `et_fb_is_enabled` returns true while setting fields to avoid // 3rd party modules using the function to generate different // definitions when they are updated via the AJAX call. add_filter( 'et_fb_is_enabled', '__return_true' ); $this->set_fields(); remove_filter( 'et_fb_is_enabled', '__return_true' ); } else { $this->set_fields(); } $this->set_factory_objects(); $this->_additional_fields_options = array(); $slug = $this->slug; // Use module cache compression only when we sure we can also decompress. $use_compression = function_exists( 'gzinflate' ) && function_exists( 'gzdeflate' ); // Disable compression when debugging. if ( function_exists( 'et_builder_definition_sort' ) ) { $use_compression = false; } // Need to instantiate this early so that it can add its hooks. $this->_features_manager = ET_Builder_Module_Features::instance(); if ( ! empty( self::$_cache[ $slug ] ) ) { // We got sum cache, let's use it. $cache = self::$_cache[ $slug ]; $fields_unprocessed = array(); $fields_map = $cache['fields_unprocessed']; // Since arrays in PHP 5.x require more memory, we have to rely (again) // on COW (copy on write) to limit RAM usage.... if ( is_array( $fields_map ) ) { // Old cache storage format (array). foreach ( $fields_map as $field => $key ) { $fields_unprocessed[ $field ] = self::$_fields_unprocessed[ $key ]; } } else { // New cache storage format (string) key1,field1\n ... keyN,fieldN // Decompress data when possible. if ( $use_compression ) { $fields_map = gzinflate( $fields_map ); } $fields_map = explode( "\n", $fields_map ); foreach ( $fields_map as $data ) { list ( $key, $field ) = explode( ':', $data ); $fields_unprocessed[ $field ] = self::$_fields_unprocessed[ $key ]; } } $this->has_advanced_fields = ! empty( $cache['advanced_fields'] ); $this->advanced_fields = $cache['advanced_fields']; $this->fields_unprocessed = $fields_unprocessed; $this->settings_modal_toggles = $cache['settings_modal_toggles']; $this->custom_css_fields = $cache['custom_css_fields']; // Claim some RAM not needed anymore. unset( self::$_cache[ $slug ] ); } else { // Compute expensive stuff. $this->_add_additional_fields(); $this->_add_custom_css_fields(); $this->_maybe_add_global_defaults(); $this->_finalize_all_fields(); // Consider caching only official modules. if ( $this->_is_official_module && false !== self::$_cache ) { // We got no cache, let's create it. $fields_unprocessed = ''; // Since arrays in PHP 5.x require more memory, we can't store // fields_unprocessed as is but have to replace values with hashes // when saving the cache and reverse the process when loading it. foreach ( $this->fields_unprocessed as $field => $definition ) { $key = md5( serialize( $definition ) ); $fields_unprocessed .= "$key:$field\n"; } // Trim last newline. $fields_unprocessed = trim( $fields_unprocessed ); // Compress data when possible. if ( $use_compression ) { $fields_unprocessed = gzdeflate( $fields_unprocessed ); } self::$_cache[ $slug ] = array( 'advanced_fields' => $this->advanced_fields, 'fields_unprocessed' => $fields_unprocessed, 'settings_modal_toggles' => $this->settings_modal_toggles, 'custom_css_fields' => $this->custom_css_fields, ); } } if ( ! isset( $this->main_css_element ) ) { $this->main_css_element = '%%order_class%%'; } $this->_render_count = 0; $this->type = isset( $this->type ) ? $this->type : ''; $this->_style_priority = (int) self::DEFAULT_PRIORITY; if ( isset( $this->type ) && 'child' === $this->type ) { $this->_style_priority = $this->_style_priority + 1; } else { // add default toggles. $default_general_toggles = array( 'admin_label' => array( 'title' => et_builder_i18n( 'Admin Label' ), 'priority' => 99, ), ); $this->_add_settings_modal_toggles( 'general', $default_general_toggles ); } $this->_add_settings_modal_toggles( 'custom_css', array( 'visibility' => array( 'title' => et_builder_i18n( 'Visibility' ), 'priority' => 99, ), ) ); $i18n =& self::$i18n; if ( ! isset( $i18n['conditions'] ) ) { $i18n['conditions'] = array( 'label' => esc_html__( 'Conditions', 'et_builder' ), ); } $this->_add_settings_modal_toggles( 'custom_css', array( 'conditions' => array( 'title' => $i18n['conditions']['label'], 'priority' => 98, ), ) ); if ( ! isset( $i18n['toggles'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['toggles'] = array( 'scroll' => esc_html__( 'Scroll Effects', 'et_builder' ), ); // phpcs:enable } if ( ! isset( $i18n['help'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment -- Tests won't need translator comment. $i18n['help'] = array( 'design_advanced_module_settings' => esc_html__( 'Design Settings and Advanced Module Settings', 'et_builder' ), 'save_load_from_library' => esc_html__( 'Saving and loading from the library', 'et_builder' ), ); // phpcs:enable } $this->_add_settings_modal_toggles( 'custom_css', array( 'scroll_effects' => array( 'title' => $i18n['toggles']['scroll'], 'priority' => 200, ), ) ); $this->main_tabs = $this->get_main_tabs(); $this->custom_css_tab = isset( $this->custom_css_tab ) ? $this->custom_css_tab : true; self::$modules[ $this->slug ] = $this; $post_types = ! empty( $this->post_types ) ? $this->post_types : et_builder_get_builder_post_types(); // all modules should be assigned for et_pb_layout post type to work in the library. if ( ! in_array( 'et_pb_layout', $post_types, true ) ) { $post_types[] = 'et_pb_layout'; } $this->post_types = apply_filters( 'et_builder_module_post_types', $post_types, $this->slug, $this->post_types ); foreach ( $this->post_types as $post_type ) { if ( ! in_array( $post_type, $this->post_types, true ) ) { $this->register_post_type( $post_type ); } if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) { self::$_module_slugs_by_post_type[ $post_type ] = array(); } if ( ! in_array( $this->slug, self::$_module_slugs_by_post_type[ $post_type ], true ) ) { self::$_module_slugs_by_post_type[ $post_type ][] = $this->slug; } if ( isset( $this->additional_shortcode ) && ! in_array( $this->additional_shortcode, self::$_module_slugs_by_post_type[ $post_type ], true ) ) { self::$_module_slugs_by_post_type[ $post_type ][] = $this->additional_shortcode; } if ( isset( $this->additional_shortcode_slugs ) ) { foreach ( $this->additional_shortcode_slugs as $additional_shortcode_slug ) { if ( ! in_array( $additional_shortcode_slug, self::$_module_slugs_by_post_type[ $post_type ], true ) ) { self::$_module_slugs_by_post_type[ $post_type ][] = $additional_shortcode_slug; } } } if ( 'child' === $this->type ) { self::$child_modules[ $post_type ][ $this->slug ] = $this; if ( isset( $this->additional_shortcode_slugs ) ) { foreach ( $this->additional_shortcode_slugs as $additional_slug ) { self::$child_modules[ $post_type ][ $additional_slug ] = $this; } } } else { self::$parent_modules[ $post_type ][ $this->slug ] = $this; } } // WooCommerce modules data only deterimine whether a module is WooCommerce modules or not // it doesn't need to be aware whether it is available in certain CPT or not. if ( $this->_is_woocommerce_module ) { self::$woocommerce_modules[] = $this->slug; } if ( ! isset( $this->no_render ) ) { $shortcode_slugs = array( $this->slug ); if ( ! empty( $this->additional_shortcode_slugs ) ) { $shortcode_slugs = array_merge( $shortcode_slugs, $this->additional_shortcode_slugs ); } foreach ( $shortcode_slugs as $shortcode_slug ) { if ( $this->_is_woocommerce_module ) { if ( et_is_woocommerce_plugin_active() ) { add_shortcode( $shortcode_slug, array( $this, '_render' ) ); } else { add_shortcode( $shortcode_slug, '__return_empty_string' ); } } else { add_shortcode( $shortcode_slug, array( $this, '_render' ) ); } } if ( isset( $this->additional_shortcode ) ) { add_shortcode( $this->additional_shortcode, array( $this, 'additional_render' ) ); } } if ( isset( $this->icon ) ) { self::$_->array_set( self::$module_icons, "{$this->slug}.icon", $this->icon ); } if ( isset( $this->icon_path ) ) { self::$_->array_set( self::$module_icons, "{$this->slug}.icon_path", $this->icon_path ); } // Push module's help videos to all help videos array if there's any. if ( ! empty( $this->help_videos ) ) { // Automatically add design tab and library tutorial. DRY. if ( 'et_pb_column' !== $this->slug ) { // Adding next tabs (design & tab) helps. $next_tabs_help = array( 'id' => '1iqjhnHVA9Y', 'name' => $i18n['help']['design_advanced_module_settings'], ); // Adjust row name. if ( in_array( $this->slug, array( 'et_pb_row', 'et_pb_row_inner' ), true ) ) { $next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Row Settings', 'et_builder' ); } // Adjust section name. if ( 'et_pb_section' === $this->slug ) { $next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Section Settings', 'et_builder' ); } $this->help_videos[] = $next_tabs_help; // Adding Divi Library helps. $this->help_videos[] = array( 'id' => 'boNZZ0MYU0E', 'name' => $i18n['help']['save_load_from_library'], ); } self::$module_help_videos[ $this->slug ] = $this->help_videos; } // Push module slug if this module has content option. These modules' content option need // to be autop-ed during saving process to avoid unstyled body content in Divi Builder Plugin due // to content not having <p> tag because it doesn't wrapped by newline during saving process. if ( ! $this->use_raw_content && ! $this->child_slug && 'tiny_mce' === self::$_->array_get( $this->get_fields(), 'content.type' ) ) { self::$has_content_modules[] = $this->slug; } } /** * Return all module slugs. * * @return array * * @since 4.10.0 */ public static function get_all_module_slugs() { // cache this only after et_builder_ready has run. if ( ! did_action( 'et_builder_ready' ) || empty( self::$all_module_slugs ) ) { self::$all_module_slugs = array_merge( self::get_structure_module_slugs(), self::get_module_slugs_by_post_type() ); } return self::$all_module_slugs; } /** * Make private/protected methods readable. * * @param string $name Method to call. * @param array $args Arguments to pass when calling. * @return mixed|bool Return value of the callback, false otherwise. */ public function __call( $name, $args ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['methods'] ); $value = null; $old_method_exists = method_exists( $this, $name ); if ( $old_method_exists && ! $is_deprecated ) { // Inaccessible method (protected or private) that isn't deprecated. et_debug( "{$message} Attempted to call {$class}::{$name}() from out of scope.", 4, false ); return $value; } $message .= " {$class}::{$name}()"; if ( ! $is_deprecated ) { $message .= " doesn't exist."; } else { $message .= ' is deprecated.'; $new_method = self::$_deprecations['methods'][ $name ]; if ( ! is_string( $new_method ) ) { // Default value for a method that has no replacement. $value = $new_method; } elseif ( method_exists( $this, $new_method ) && ! $old_method_exists ) { $message .= " Use {$class}::{$new_method}() instead."; // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found $value = call_user_func_array( array( $this, $new_method ), $args ); } elseif ( $old_method_exists && function_exists( $new_method ) ) { // New method is a function. $message .= " Use {$new_method}() instead."; // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found $value = call_user_func_array( $new_method, $args ); } elseif ( $old_method_exists ) { // Ensure that our current caller is not the same as the method we're about to call. // as that would cause an infinite recursion situation. It happens when a child class // method which has been deprecated calls itself on the parent class (using parent::) // This also happens when we use $this->__call() to call a deprecated method from its // replacement method so that a deprecation notice will be output. $trace = debug_backtrace(); $callers = array( self::$_->array_get( $trace, '1.function' ), self::$_->array_get( $trace, '2.function' ), ); if ( ! in_array( $name, $callers, true ) ) { $message .= " Use {$class}::{$new_method}() instead."; // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found $value = call_user_func_array( array( $this, $name ), $args ); } } } et_debug( $message, 4, false ); return $value; } /** * Makes private properties readable. * * @param string $name Property name. * * @return mixed|string|null */ public function &__get( $name ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] ); $value = null; if ( property_exists( $this, $name ) && ! $is_deprecated ) { // Inaccessible property (protected or private) that isn't deprecated. et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false ); return $value; } $message .= " {$class}::\${$name}"; if ( ! $is_deprecated ) { $message .= " doesn't exist."; $should_set_value = true; } else { $message .= ' is deprecated.'; $new_prop = self::$_deprecations['properties'][ $name ]; if ( $new_prop && is_string( $new_prop ) && property_exists( $this, $new_prop ) ) { $message .= " Use {$class}::\${$new_prop} instead."; $value = &$this->$new_prop; } elseif ( ! is_string( $new_prop ) || ! $new_prop ) { // Default value. $value = $new_prop; $should_set_value = true; } } if ( isset( $should_set_value ) ) { // Create the property so we can return a reference to it which allows it to be // used like this: $this->name[] = 'something'. $this->$name = $value; $value = &$this->$name; } et_debug( $message, 4, false ); return $value; } /** * Make private properties checkable. * * @param string $name Property to check if set. * @return bool Whether the property is set. */ public function __isset( $name ) { $prop_name = array_key_exists( $name, self::$_deprecations['properties'] ) ? self::$_deprecations['properties'][ $name ] : $name; if ( ! $prop_name || ! is_string( $prop_name ) ) { return false; } return property_exists( $this, $prop_name ); } /** * Set a property's value. * * @param string $name Property key. * @param mixed $value Property value. */ public function __set( $name, $value ) { $class = get_class( $this ); $message = "You're Doing It Wrong!"; $is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] ); $property_exists = property_exists( $this, $name ); $has_replacement = $property_exists && is_string( self::$_deprecations['properties'][ $name ] ) && self::$_deprecations['properties'][ $name ]; if ( $property_exists && ! $is_deprecated ) { // Inaccessible property (protected or private) that isn't deprecated. et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false ); return; } if ( ( ! $property_exists && ! $is_deprecated ) || ! $has_replacement ) { // Always allow setting values for properties that are undeclared. $this->$name = $value; } if ( ! $is_deprecated ) { return; } $message = " {$class}::\${$name} is deprecated."; $replacement = self::$_deprecations['properties'][ $name ]; if ( $replacement && is_string( $replacement ) ) { $message .= " Use {$class}::\${$replacement} instead."; $this->$replacement = $value; // Unset deprecated property so next time it's updated we process it again. unset( $this->$name ); } et_debug( $message, 4, false ); } /** * Determine whether class is Divi official module or not. * * @param string $class_name Module class name. * * @return bool */ private static function _is_official_module( $class_name ) { try { $reflection = new ReflectionClass( $class_name ); $is_official = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH ); } catch ( Exception $err ) { $is_official = false; } return $is_official; } /** * Determine whether class is WooCommerce module or not. * * @param string $class_name Module class name. * * @return bool */ private static function _is_woocommerce_module( $class_name ) { try { $reflection = new ReflectionClass( $class_name ); $is_woocommerce = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH . '/module/woocommerce' ); } catch ( Exception $err ) { $is_woocommerce = false; } return $is_woocommerce; } /** * Set configuration for module's advanced fields. */ protected function _set_advanced_fields_config() { $this->advanced_fields = $this->get_advanced_fields_config(); // 3rd-Party Backwards Compatability if ( isset( $this->advanced_fields['custom_margin_padding'] ) ) { $this->advanced_fields['margin_padding'] = $this->advanced_fields['custom_margin_padding']; unset( $this->advanced_fields['custom_margin_padding'] ); } } /** * Get whether third party post interference should be respected. * Current use case is for plugins like Toolset that render a * loop within a layout which renders another layout for * each post - in this case we must NOT override the * current post so the loop works as expected. * * @since 4.0.6 * * @return boolean */ protected static function _should_respect_post_interference() { $post = ET_Post_Stack::get(); return null !== $post && get_the_ID() !== $post->ID; } /** * Retrieve the main query post id. * Accounts for third party interference with the current post. * * @since 4.0.6 * * @return integer|boolean */ protected static function _get_main_post_id() { if ( self::_should_respect_post_interference() ) { return get_the_ID(); } return ET_Post_Stack::get_main_post_id(); } /** * Retrieve Post ID from 1 of 4 sources depending on which exists: * - $_POST['current_page']['id'] * - $_POST['et_post_id'] * - $_GET['post'] * - get_the_ID() * * @since 3.17.2 * * @return int|bool */ public static function get_current_post_id() { // Getting correct post id in computed_callback request. // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( wp_doing_ajax() && self::$_->array_get( $_POST, 'current_page.id' ) ) { return absint( self::$_->array_get( $_POST, 'current_page.id' ) ); } if ( wp_doing_ajax() && isset( $_POST['et_post_id'] ) ) { return absint( $_POST['et_post_id'] ); } if ( isset( $_POST['post'] ) ) { return absint( $_POST['post'] ); } return self::_get_main_post_id(); // phpcs:enable } /** * Retrieve Post ID from 1 of 3 sources depending on which exists: * - get_the_ID() * - $_GET['post'] * - $_POST['et_post_id'] * * @since 4.0 * * @return int|bool */ public static function get_current_post_id_reverse() { // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. $post_id = self::_get_main_post_id(); // try to get post id from get_post_ID(). if ( false !== $post_id ) { return $post_id; } if ( wp_doing_ajax() ) { // get the post ID if loading data for VB. return isset( $_POST['et_post_id'] ) ? absint( $_POST['et_post_id'] ) : false; } // fallback to $_GET['post'] to cover the BB data loading. return isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; // phpcs:enable } /** * Get the current ID depending on the current request. * * @since 4.0 * * @return int|bool */ public function get_the_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName -- This function name is consistent with WP core `get_the_ID()` function return self::get_current_post_id_reverse(); } /** * Setup the advanced styles manager * * @param int $post_id Post id. * * @return array * @since 4.0 Made public. * * {@internal * Before the styles manager was implemented, the advanced styles were output inline in the footer. * That resulted in them being the last styles parsed by the browser, thus giving them higher * priority than other styles on the page. With the styles manager, the advanced styles are * enqueued at the very end of the <head>. This is for backwards compatibility (to maintain * the same priority for the styles as before).}} */ public static function setup_advanced_styles_manager( $post_id = 0 ) { if ( 0 === $post_id && et_core_page_resource_is_singular() ) { // It doesn't matter if post id is 0 because we're going to force inline styles. $post_id = et_core_page_resource_get_the_ID(); } /** This filter is documented in frontend-builder/theme-builder/frontend.php */ $is_critical_enabled = apply_filters( 'et_builder_critical_css_enabled', false ); $deferred = false; $is_preview = is_preview() || is_et_pb_preview(); $forced_in_footer = $post_id && et_builder_setting_is_on( 'et_pb_css_in_footer', $post_id ); $forced_inline = ! $post_id || $is_preview || $forced_in_footer || et_builder_setting_is_off( 'et_pb_static_css_file', $post_id ) || et_core_is_safe_mode_active() || ET_GB_Block_Layout::is_layout_block_preview(); $unified_styles = ! $forced_inline && ! $forced_in_footer; $resource_owner = $unified_styles ? 'core' : 'builder'; $resource_slug = $unified_styles ? 'unified' : 'module-design'; $resource_slug .= $unified_styles && et_builder_post_is_of_custom_post_type( $post_id ) ? '-cpt' : ''; // Temporarily keep resource slug before TB slug processing. $temp_resource_slug = $resource_slug; $resource_slug = et_theme_builder_decorate_page_resource_slug( $post_id, $resource_slug ); // TB should be prioritized over WP Editor. If resource slug is not changed, it is // not for TB. Ensure current module is one of WP Editor template before checking. if ( $temp_resource_slug === $resource_slug && self::is_wp_editor_template() ) { $resource_slug = et_builder_wp_editor_decorate_page_resource_slug( $post_id, $resource_slug ); } // If the post is password protected and a password has not been provided yet, // no content (including any custom style) will be printed. // When static css file option is enabled this will result in missing styles. if ( ! $forced_inline && post_password_required( $post_id ? $post_id : null ) ) { $forced_inline = true; } if ( $is_preview ) { // Don't let previews cause existing saved static css files to be modified. $resource_slug .= '-preview'; } $manager = et_core_page_resource_get( $resource_owner, $resource_slug, $post_id, 40 ); $has_file = $manager->has_file(); $manager_data = [ 'manager' => $manager, 'add_hooks' => true, ]; if ( $is_critical_enabled ) { $deferred = et_core_page_resource_get( $resource_owner, $resource_slug . '-deferred', $post_id, 40 ); $has_file = $has_file && $deferred->has_file(); $manager_data['deferred'] = $deferred; } if ( ! $forced_inline && ! $forced_in_footer && $has_file ) { // This post currently has a fully configured styles manager. $manager_data['add_hooks'] = false; /** * Filters the Style Managers used to output Critical/Deferred Builder CSS. * * @since 4.10.0 * * @param array $manager_data Style Managers. */ $manager_data = apply_filters( 'et_builder_module_style_manager', $manager_data ); return $manager_data; } $manager->forced_inline = $forced_inline; $manager->write_file_location = 'footer'; if ( $deferred ) { $deferred->forced_inline = $forced_inline; $deferred->write_file_location = 'footer'; } if ( $forced_in_footer || $forced_inline ) { // Restore legacy behavior--output inline styles in the footer. $manager->set_output_location( 'footer' ); if ( $deferred ) { $deferred->set_output_location( 'footer' ); } } /** This filter is documented in class-et-builder-element.php */ $manager_data = apply_filters( 'et_builder_module_style_manager', $manager_data ); return $manager_data; } /** * Passes the module design styles for the current page to the advanced styles manager. * {@see 'wp_footer' (19) Must run before the style manager's footer callback} */ public static function set_advanced_styles() { $styles = ''; $custom = ''; if ( et_core_is_builder_used_on_current_request() ) { $custom = et_pb_get_page_custom_css(); } // Pass styles to page resource which will handle their output. /** This filter is documented in frontend-builder/theme-builder/frontend.php */ $is_critical_enabled = apply_filters( 'et_builder_critical_css_enabled', false ); $critical = $is_critical_enabled ? self::get_style( false, 0, true ) . self::get_style( true, 0, true ) : []; $styles = self::get_style() . self::get_style( true ); if ( empty( $critical ) ) { // No critical styles defined, just enqueue everything as usual. $styles = $custom . $styles; if ( ! empty( $styles ) ) { if ( isset( self::$deferred_styles_manager ) ) { self::$deferred_styles_manager->set_data( $styles, 40 ); } else { self::$advanced_styles_manager->set_data( $styles, 40 ); } } } else { // Add page css to the critical section. $critical = $custom . $critical; self::$advanced_styles_manager->set_data( $critical, 40 ); if ( ! empty( $styles ) ) { // Defer everything else. self::$deferred_styles_manager->set_data( $styles, 40 ); } } } /** * Set {@see ET_Builder_Element::$advanced_styles_manager} to force inline styles. */ public static function maybe_force_inline_styles() { if ( et_core_is_fb_enabled() || self::$advanced_styles_manager->forced_inline || ! self::$forced_inline_styles ) { return; } self::$advanced_styles_manager->forced_inline = true; self::$advanced_styles_manager->write_file_location = 'footer'; self::$advanced_styles_manager->set_output_location( 'footer' ); if ( isset( self::$deferred_styles_manager ) ) { self::$deferred_styles_manager->forced_inline = true; self::$deferred_styles_manager->write_file_location = 'footer'; self::$deferred_styles_manager->set_output_location( 'footer' ); } } /** * Filters the unified page resource data. The data is an array of arrays of strings keyed by * priority. The builder's styles are set with a priority of 40. Here we want to make sure * only the builder's styles are output in the footer on first-page load so we aren't * duplicating the customizer and custom css styles which are already in the <head>. * {@see 'et_core_page_resource_get_data'} * * @param array[] $data { * Arrays of strings keyed by priority. * * @type string[] $priority Resource data. * ... * }. * @param string $context Where the data will be used. Accepts 'inline', 'file'. * @param ET_Core_PageResource $resource The resource instance. * @return array */ public static function filter_page_resource_data( $data, $context, $resource ) { global $wp_current_filter; if ( 'inline' !== $context || ! in_array( 'wp_footer', $wp_current_filter, true ) ) { return $data; } if ( false === strpos( $resource->slug, 'unified' ) ) { return $data; } if ( 'footer' !== $resource->location ) { // This is the first load of a page that doesn't currently have a unified static css file. // The theme customizer and custom css have already been inlined in the <head> using the // unified resource's ID. It's invalid HTML to have duplicated IDs on the page so we'll // fix that here since it only applies to this page load anyway. $resource->slug = $resource->slug . '-2'; } return isset( $data[40] ) ? array( 40 => $data[40] ) : array(); } /** * Get the slugs for all current builder modules. * * @since 3.0.85 * * @param string $post_type Get module slugs for this post type. If falsy, all slugs are returned. * * @return array */ public static function get_module_slugs_by_post_type( $post_type = 'post' ) { if ( $post_type ) { if ( isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) { $slugs = self::$_module_slugs_by_post_type[ $post_type ]; } else { // We get all modules when post type is not enabled so that posts that have // had their post type support disabled still load all necessary modules. $slugs = array_keys( self::get_modules() ); } /** * Filters the module slugs and post type. * * @since 4.10.0 * * @param array $slugs Module Slugs. * @param string $post_type Current post type. */ return apply_filters( 'et_builder_get_module_slugs_by_post_type', $slugs, $post_type ); } return self::$_module_slugs_by_post_type; } /** * Get whether the module has Visual Builder support or not * * @since 3.1 * * @return bool */ public function has_vb_support() { return 'off' !== $this->vb_support; } /** * Create Factory objects * * @since 3.23 Add margin padding fields object. * * @return void */ public function set_factory_objects() { // Load features fields. $this->text_shadow = ET_Builder_Module_Fields_Factory::get( 'TextShadow' ); $this->margin_padding = ET_Builder_Module_Fields_Factory::get( 'MarginPadding' ); } /** * Populates {@see $fields_unprocessed}. * * @param array $fields Fields. */ protected function _set_fields_unprocessed( $fields ) { $unprocessed = &self::$_fields_unprocessed; foreach ( $fields as $field => $definition ) { if ( true === $definition ) { continue; } // Have to use md5 now because needed by modules cache. $key = md5( serialize( $definition ) ); if ( ! isset( $unprocessed[ $key ] ) ) { $unprocessed[ $key ] = $definition; } $this->fields_unprocessed[ $field ] = $unprocessed[ $key ]; } } /** * Populates {@see self::$fields_unprocessed}. */ public function set_fields() { $fields_unprocessed = $this->get_complete_fields(); // Add _builder_version field to all modules. $fields_unprocessed['_builder_version'] = array( 'type' => 'skip' ); // Add _dynamic_attributes field to all modules. $fields_unprocessed['_dynamic_attributes'] = array( 'type' => 'skip' ); // Add support for the style presets. $fields_unprocessed['_module_preset'] = array( 'type' => 'skip' ); // Add support for unique ID. Initially added for Contact Form module. if ( $this->_use_unique_id ) { $fields_unprocessed['_unique_id'] = array( 'type' => 'skip' ); } if ( function_exists( 'et_builder_definition_sort' ) ) { et_builder_definition_sort( $fields_unprocessed ); } if ( $this->_is_official_module ) { $this->_set_fields_unprocessed( $fields_unprocessed ); return; } // 3rd-Party module backwards compatability starts here foreach ( $fields_unprocessed as $field => $info ) { if ( isset( $info['depends_to'] ) ) { $fields_unprocessed[ $field ]['depends_on'] = $info['depends_to']; } if ( isset( $info['depends_default'] ) && $info['depends_default'] && ! isset( $info['depends_show_if'] ) ) { $fields_unprocessed[ $field ]['depends_show_if'] = 'on'; $message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'depends_default'. Use 'show_if' instead."; et_debug( $message ); } // Process renderer of string type only. if ( isset( $info['renderer'] ) && is_string( $info['renderer'] ) ) { $original_renderer = $info['renderer']; $updated_field_type = $info['renderer']; // convert renderer into type. switch ( $info['renderer'] ) { case 'et_builder_include_categories_option': case 'et_builder_include_categories_shop_option': $updated_field_type = 'categories'; break; case 'et_builder_get_widget_areas': $updated_field_type = 'select_sidebar'; break; case 'et_pb_get_font_icon_list': case 'et_pb_get_font_down_icon_list': $updated_field_type = 'select_icon'; break; case 'et_builder_get_gallery_settings': $updated_field_type = 'upload_gallery'; break; case 'et_builder_generate_center_map_setting': $updated_field_type = 'center_map'; break; } $fields_unprocessed[ $field ]['type'] = $updated_field_type; if ( 'et_pb_get_font_down_icon_list' === $info['renderer'] ) { $fields_unprocessed[ $field ]['renderer_options'] = array( 'icons_list' => 'icon_down' ); } // Output developer warning if renderer was converted to type. if ( $original_renderer !== $updated_field_type ) { $message = "You're Doing It Wrong! Module setting definition for {$field} has a deprecated value: "; $message .= "'{$original_renderer}' for parameter 'renderer'. Use '{$updated_field_type}' instead."; et_debug( $message ); } } // Normalize `affects` field names if needed. if ( isset( $info['affects'] ) ) { $affects_original = $fields_unprocessed[ $field ]['affects']; $fields_unprocessed[ $field ]['affects'] = array(); // BB supports comma separated list of affected fields, convert it to array of fields if this is the case. // Some plugins use combination of various lists, handle all of them. foreach ( $affects_original as $affect_item ) { if ( strpos( $affect_item, ',' ) !== false ) { $fields_unprocessed[ $field ]['affects'] = array_merge( $fields_unprocessed[ $field ]['affects'], explode( ',', str_replace( ' ', '', $affect_item ) ) ); } else { $fields_unprocessed[ $field ]['affects'][] = $affect_item; } } array_walk( $fields_unprocessed[ $field ]['affects'], array( $this, 'normalize_affect_fields' ) ); } if ( 'content_new' === $field ) { $fields_unprocessed['content'] = $fields_unprocessed['content_new']; unset( $fields_unprocessed['content_new'] ); $message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'content_new'. Use 'content' instead."; et_debug( $message ); } // convert old color pickers to the new ones supporting alpha channel. if ( 'color' === self::$_->array_get( $info, 'type' ) ) { $info['type'] = 'color-alpha'; $fields_unprocessed[ $field ] = $info; $message = "You're Doing It Wrong! You're using wrong type for the '" . $field . "'. It should be 'color-alpha' instead of 'color'."; et_debug( $message, 4, false ); } // convert input type to text. if ( 'input' === self::$_->array_get( $info, 'type' ) ) { $info['type'] = 'text'; $fields_unprocessed[ $field ] = $info; $message = "You're Doing It Wrong! Setting definition for {$field} has a deprecated value: 'input' for parameter: 'type'. Use 'text' instead."; et_debug( $message ); } // Normalize default values. if ( isset( $info['default'] ) ) { $fields_unprocessed[ $field ]['default'] = $this->_normalize_field_default( $field, $info['default'], $fields_unprocessed[ $field ]['type'] ); } } // Set default values in field definitions based on the legacy defaults "rules". if ( isset( $this->fields_defaults ) ) { foreach ( $this->fields_defaults as $field => $value ) { if ( ! isset( $fields_unprocessed[ $field ] ) ) { continue; } $condition = is_array( $value ) ? self::$_->array_get( $value, '1' ) : false; $set_default_on_front = 'only_default_setting' !== $condition; $default = $this->_normalize_field_default( $field, $value, $fields_unprocessed[ $field ]['type'] ); // Always set default value if exists. Only default_on_front should be conditional. $fields_unprocessed[ $field ]['default'] = $default; if ( ! $set_default_on_front ) { continue; } $has_default = isset( $fields_unprocessed[ $field ]['default'] ); if ( ! $has_default || $fields_unprocessed[ $field ]['default'] !== $default ) { $fields_unprocessed[ $field ]['default_on_front'] = $default; } } } // Legacy Defaults Rule #4 (AKA: longest-running undetected bug in the codebase): // Fields listed in allowlisted_fields that aren't in fields_defaults lose their definitions. if ( isset( $this->allowlisted_fields ) ) { $disable_allowlisted_fields = isset( $this->force_unallowlisted_fields ) && $this->force_unallowlisted_fields; if ( ! $disable_allowlisted_fields && ! is_admin() && ! et_fb_is_enabled() ) { foreach ( $this->allowlisted_fields as $field ) { if ( isset( $this->fields_defaults ) && array_key_exists( $field, $this->fields_defaults ) ) { continue; } $fields_unprocessed[ $field ] = array(); } } } $this->_set_fields_unprocessed( $fields_unprocessed ); } /** * Normalize default value depends on field type. * * @param string $field Field. * @param mixed $default_value Default value. * @param string $type Field type. * * @return mixed|string */ protected function _normalize_field_default( $field, $default_value, $type = '' ) { $normalized_value = is_array( $default_value ) ? $default_value[0] : $default_value; // normalize default value depends on field type. switch ( $type ) { case 'yes_no_button': if ( is_numeric( $normalized_value ) ) { $normalized_value = (bool) $normalized_value ? 'on' : 'off'; $message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be either 'on' or 'off'."; et_debug( $message, 4, false ); } break; case 'color-alpha': if ( is_numeric( $normalized_value ) ) { $normalized_value = ''; $message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be string value."; et_debug( $message, 4, false ); } // Make sure provided HEX code is a valid color code. if ( strpos( $normalized_value, '#' ) === 0 && ! in_array( strlen( $normalized_value ), array( 4, 7 ), true ) ) { $normalized_value = ''; $message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be valid hex color code."; et_debug( $message, 4, false ); } break; } return $normalized_value; } /** * Normalize `affects` fields name if needed. * Some 3rd party modules use `#et_pb_<field_name>` format which is wrong and doesn't work in VB, but works in BB. * Convert it to correct format and output notice for developer * * @param string $field_name Field name. * * @return void */ public function normalize_affect_fields( &$field_name ) { if ( strpos( $field_name, '#et_pb_' ) !== false ) { // Truncate field name from the string wherever it's placed. $new_field_name = substr( $field_name, strpos( $field_name, '#et_pb_' ) + 7 ); $message = "You're Doing It Wrong! You're using wrong name for 'affects' attribute. It should be '" . $new_field_name . "' instead of '" . $field_name . "'"; $field_name = $new_field_name; et_debug( $message, 4, false ); } // content_new renamed to content, so rename it in affected fields list as well. if ( 'content_new' === $field_name ) { $field_name = 'content'; } } /** * Finalizes the configuration of {@see self::$fields_unprocessed}. * Includes filter and fields processing for Visual Builder * * @return void */ protected function _finalize_all_fields() { $fields_unprocessed = $this->fields_unprocessed; $fields_before_filter = $fields_unprocessed; /** * Filters module fields. * * @since 3.1 * * @param array $fields_unprocessed See {@see self::$fields_unprocessed}. */ $fields_unprocessed = apply_filters( "et_pb_all_fields_unprocessed_{$this->slug}", $fields_unprocessed ); $is_saving_modules_cache = et_core_is_saving_builder_modules_cache(); $need_dynamic_assets = et_core_is_fb_enabled() && ! et_fb_dynamic_asset_exists( 'definitions' ); // Check if this is an AJAX request since this is how VB and BB loads the initial module data et_core_is_fb_enabled() always returns `false` here // Make exception for requests that are regenerating modules cache and // VB page which has no dynamic definitions asset so it can cache the definitions correctly. if ( ! wp_doing_ajax() && ! $is_saving_modules_cache && ! $need_dynamic_assets ) { $this->_set_fields_unprocessed( $fields_unprocessed ); return; } foreach ( array_keys( $fields_unprocessed ) as $field_name ) { $field_info = $fields_unprocessed[ $field_name ]; $affected_fields = self::$_->array_get( $field_info, 'affects', array() ); foreach ( $affected_fields as $affected_field ) { if ( ! isset( $fields_unprocessed[ $affected_field ] ) ) { continue; } if ( ! isset( $fields_unprocessed[ $affected_field ]['depends_on'] ) ) { $fields_unprocessed[ $affected_field ]['depends_on'] = array(); } // Avoid value duplication. if ( ! in_array( $field_name, $fields_unprocessed[ $affected_field ]['depends_on'], true ) ) { $fields_unprocessed[ $affected_field ]['depends_on'][] = $field_name; } // Set `depends_show_if = on` if no condition defined for the affected field for backward compatibility with old plugins. if ( ! isset( $fields_unprocessed[ $affected_field ]['depends_show_if'] ) && ! isset( $fields_unprocessed[ $affected_field ]['depends_show_if_not'] ) ) { // Deprecation notice has already been logged for this. $fields_unprocessed[ $affected_field ]['depends_show_if'] = 'on'; } } // Unset renderer to avoid errors in VB because of errors in 3rd party plugins // BB compat. Still need this data, so leave it for BB. if ( ( self::is_loading_vb_data() || et_fb_is_enabled() ) && isset( $fields_unprocessed[ $field_name ]['renderer'] ) ) { unset( $fields_unprocessed[ $field_name ]['renderer'] ); } if ( isset( $fields_unprocessed[ $field_name ]['use_plugin_main'] ) ) { $fields_unprocessed[ $field_name ]['use_limited_main'] = $fields_unprocessed[ $field_name ]['use_plugin_main']; unset( $fields_unprocessed[ $field_name ]['use_plugin_main'] ); $message = "You're Doing It Wrong! Setting definition for {$field_name} includes deprecated parameter: 'use_plugin_main'. Use 'use_limited_main' instead."; et_debug( $message ); } if ( isset( $fields_unprocessed[ $field_name ]['plugin_main'] ) ) { $fields_unprocessed[ $field_name ]['limited_main'] = $fields_unprocessed[ $field_name ]['plugin_main']; unset( $fields_unprocessed[ $field_name ]['plugin_main'] ); $message = "You're Doing It Wrong! Setting definition for {$field_name} includes deprecated parameter: 'plugin_main'. Use 'limited_main' instead."; et_debug( $message ); } } // determine custom fields added via filter and add specific flag to identify them in VB. $keys_before_filter = array_keys( $fields_before_filter ); $keys_after_filter = array_keys( $fields_unprocessed ); $added_fields = array_diff( $keys_after_filter, $keys_before_filter ); if ( ! empty( $added_fields ) ) { foreach ( $added_fields as $key ) { $fields_unprocessed[ $key ]['vb_support'] = false; } } $this->_set_fields_unprocessed( $fields_unprocessed ); } /** * Register builder enabled post types. * * @param string $post_type Post type. */ private function register_post_type( $post_type ) { $this->post_types[] = $post_type; self::$parent_modules[ $post_type ] = array(); self::$child_modules[ $post_type ] = array(); } /** * Double quote are saved as "%22" in shortcode attributes. * Decode them back into " * * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * @param bool $et_fb_processing_shortcode_object FB processing shortcode flag. * * @return void */ private function _decode_double_quotes( $enabled_dynamic_attributes, $et_fb_processing_shortcode_object ) { if ( ! isset( $this->props ) ) { return; } // need to encode HTML entities in Admin Area( for BB ) if Visual Editor disabled for the user. $need_html_entities_decode = is_admin() && ! user_can_richedit(); $shortcode_attributes = array(); $font_icon_options = et_pb_get_font_icon_field_names(); $font_icon_options_as_keys = array_flip( $font_icon_options ); $url_options = array( 'url', 'button_link', 'button_url' ); $url_options_as_keys = array_flip( $url_options ); foreach ( $this->props as $attribute_key => $attribute_value ) { if ( $et_fb_processing_shortcode_object && ! empty( $enabled_dynamic_attributes ) && in_array( $attribute_key, $enabled_dynamic_attributes, true ) ) { // Do not decode dynamic content values when preparing them for VB. $shortcode_attributes[ $attribute_key ] = $attribute_value; continue; } // decode HTML entities and remove trailing and leading quote if needed. $processed_attr_value = $need_html_entities_decode ? trim( htmlspecialchars_decode( $attribute_value, ENT_QUOTES ), '"' ) : $attribute_value; $field_type = empty( $this->fields_unprocessed[ $attribute_key ]['type'] ) ? '' : $this->fields_unprocessed[ $attribute_key ]['type']; // the icon shortcodes are fine. if ( isset( $font_icon_options_as_keys[ $attribute_key ] ) || 'select_icon' === $field_type ) { $shortcode_attributes[ $attribute_key ] = $processed_attr_value; // icon attributes must not be str_replaced. // Add responsive and hover types attributes on the font icon options list. Just // assign empty string value because we just need the key. $font_icon_options_as_keys[ "{$attribute_key}_tablet" ] = ''; $font_icon_options_as_keys[ "{$attribute_key}_phone" ] = ''; $font_icon_options_as_keys[ "{$attribute_key}__hover" ] = ''; continue; } // Set empty TinyMCE content '<br /><br />' as empty string. if ( 'tiny_mce' === $field_type && 'ltbrgtbr' === preg_replace( '/[^a-z]/', '', $processed_attr_value ) ) { $processed_attr_value = ''; } // URLs are weird since they can allow non-ascii characters so we escape those separately. if ( isset( $url_options_as_keys[ $attribute_key ] ) ) { $url = $processed_attr_value; $url_parsed = wp_parse_url( $url ); if ( isset( $url_parsed['query'] ) ) { $replace = str_replace( array( '%91', '%93' ), array( '[', ']' ), $url_parsed['query'] ); $url = str_replace( $url_parsed['query'], $replace, $url ); } $shortcode_attributes[ $attribute_key ] = esc_url_raw( $url ); } else { $processed_attr_value = str_replace( array( '%22', '%92', '%91', '%93', '%5c' ), array( '"', '\\', '[', ']', '\\' ), $processed_attr_value ); $shortcode_attributes[ $attribute_key ] = $processed_attr_value; } } $this->props = $shortcode_attributes; } /** * Provide a way for sub-class to access $this->_render_count without a chance to alter its value * * @return int */ protected function render_count() { return $this->_render_count; } /** * Bumps the render count for this module instance and the module instance whose slug is * set as {@see self::$_bumps_render_count} (if any). * * @since 3.10 */ protected function _bump_render_count() { $this->_render_count++; if ( $this->_bumps_render_count ) { $module = self::get_module( $this->_bumps_render_count, $this->get_post_type() ); $module->_render_count++; } } /** * Check whether ab testing enabled for current module and calculate whether it should be displayed currently or not * * @param array $shortcode_atts Shortcode attributes. * * @return bool */ private function _is_display_module( $shortcode_atts ) { $ab_subject_id = isset( $shortcode_atts['ab_subject_id'] ) && '' !== $shortcode_atts['ab_subject_id'] ? $shortcode_atts['ab_subject_id'] : false; // return true if testing is disabled or current module has no subject id. if ( ! $ab_subject_id ) { return true; } return $this->_check_ab_test_subject( $ab_subject_id ); } /** * Check whether the current module should be displayed or not. * * @param bool|integer $ab_subject_id subject id. * * @return bool */ private function _check_ab_test_subject( $ab_subject_id = false ) { global $et_pb_ab_subject; if ( ! $ab_subject_id ) { return true; } return et_()->array_get( $et_pb_ab_subject, self::get_layout_id(), '' ) === $ab_subject_id; } /** * Get an index. * * @since 4.0 * @since 4.14.8 Add WP Editor template check. * * @param string $key The path in the array. * * @return mixed */ protected static function _get_index( $key ) { $theme_builder_group = self::get_theme_builder_layout_type(); // TB should be prioritized over WP Editor. if ( 'default' === $theme_builder_group ) { $theme_builder_group = self::get_wp_editor_template_type( true ); } $key = array_merge( array( $theme_builder_group ), (array) $key ); return et_()->array_get( self::$_indices, $key, -1 ); } /** * Set an index. * * @since 4.0 * @since 4.14.8 Add WP Editor template check. * * @param string $key The path in the array. * @param mixed $index The value to set. * * @return void */ protected static function _set_index( $key, $index ) { $theme_builder_group = self::get_theme_builder_layout_type(); // TB should be prioritized over WP Editor. if ( 'default' === $theme_builder_group ) { $theme_builder_group = self::get_wp_editor_template_type( true ); } $key = array_merge( array( $theme_builder_group ), (array) $key ); et_()->array_set( self::$_indices, $key, $index ); } /** * Resets indexes used when generating element addresses. * * @param string $content Element content. * @param bool $force Whether forcefully reset indexes even when not the main query or * {@see self::$can_reset_element_indexes} is false. * * @return string */ public static function reset_element_indexes( $content = '', $force = false ) { if ( ! $force && ( ! self::$can_reset_element_indexes || ! is_main_query() ) ) { return $content; } $slugs = self::get_parent_slugs_regex(); if ( $content && ! preg_match( "/{$slugs}/", $content ) ) { // At least one builder element should be present. return $content; } global $wp_current_filter; if ( in_array( 'the_content', $wp_current_filter, true ) ) { $call_counts = array_count_values( $wp_current_filter ); if ( $call_counts['the_content'] > 1 ) { // This is a nested call. We only want to reset indexes after the top-most call. return $content; } } self::_set_index( self::INDEX_SECTION, -1 ); self::_set_index( self::INDEX_ROW, -1 ); self::_set_index( self::INDEX_ROW_INNER, -1 ); self::_set_index( self::INDEX_COLUMN, -1 ); self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); if ( $force ) { // Reset module order classes. self::_set_index( self::INDEX_MODULE_ORDER, array() ); self::_set_index( self::INDEX_INNER_MODULE_ORDER, array() ); } return $content; } /** * Generates the element's address. Every builder element on the page is assigned an address * based on it's index and those of it's parents using the following format: * * `$section.$row.$column.$module[.$module_item]` * * For example, if a module is the forth module in the first column in the third row in the * second section on the page, it's address would be: `1.2.0.3` (indexes are zero-based). * * @since 3.1 Renamed from `_get_current_shortcode_address()` to `generate_element_address()` * @since 3.0.60 * * @param string $render_slug render slug. * * @return string */ public function generate_element_address( $render_slug = '' ) { // Flag child module. $this->type isn't accurate in this context since some modules reuse other // modules' render() method for rendering their output (ie. accordion item). // Even though Column and Column Inner are child elements of Row they shouldn't be processed as child items. $is_child_module = in_array( $render_slug, self::get_child_slugs( $this->get_post_type() ), true ) && false === strpos( $render_slug, '_column_inner' ) && false === strpos( $render_slug, '_column' ); if ( false !== strpos( $render_slug, '_section' ) ) { self::_set_index( self::INDEX_SECTION, self::_get_index( self::INDEX_SECTION ) + 1 ); // Reset every module index inside section. self::_set_index( self::INDEX_ROW, -1 ); self::_set_index( self::INDEX_ROW_INNER, -1 ); self::_set_index( self::INDEX_COLUMN, -1 ); self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_row_inner' ) ) { self::_set_index( self::INDEX_ROW_INNER, self::_get_index( self::INDEX_ROW_INNER ) + 1 ); // Reset every module index inside row inner. self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_row' ) ) { self::_set_index( self::INDEX_ROW, self::_get_index( self::INDEX_ROW ) + 1 ); // Reset every module index inside row. self::_set_index( self::INDEX_COLUMN, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_column_inner' ) ) { self::_set_index( self::INDEX_COLUMN_INNER, self::_get_index( self::INDEX_COLUMN_INNER ) + 1 ); // Reset every module index inside column inner. self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_column' ) && -1 === self::_get_index( self::INDEX_ROW ) ) { self::_set_index( self::INDEX_COLUMN, self::_get_index( self::INDEX_COLUMN ) + 1 ); // Reset every module index inside column of specialty section. self::_set_index( self::INDEX_ROW_INNER, -1 ); self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_column' ) ) { self::_set_index( self::INDEX_COLUMN, self::_get_index( self::INDEX_COLUMN ) + 1 ); // Reset every module index inside column of regular section. self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( $is_child_module ) { self::_set_index( self::INDEX_MODULE_ITEM, self::_get_index( self::INDEX_MODULE_ITEM ) + 1 ); } else { self::_set_index( self::INDEX_MODULE, self::_get_index( self::INDEX_MODULE ) + 1 ); // Reset module item index inside module. self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } $address = self::_get_index( self::INDEX_SECTION ); if ( -1 === self::_get_index( self::INDEX_ROW ) && -1 === self::_get_index( self::INDEX_ROW_INNER ) ) { // Fullwidth & Specialty (without column inner) Section's module. $parts = array( self::_get_index( self::INDEX_COLUMN ), self::_get_index( self::INDEX_MODULE ) ); } elseif ( 0 <= self::_get_index( self::INDEX_ROW_INNER ) ) { // Specialty (inside column inner) Section's module. $parts = array( self::_get_index( self::INDEX_COLUMN ), self::_get_index( self::INDEX_ROW_INNER ), self::_get_index( self::INDEX_COLUMN_INNER ), self::_get_index( self::INDEX_MODULE ) ); } else { // Regular section's module. $parts = array( self::_get_index( self::INDEX_ROW ), self::_get_index( self::INDEX_COLUMN ), self::_get_index( self::INDEX_MODULE ) ); } foreach ( $parts as $part ) { if ( $part > -1 ) { $address .= ".{$part}"; } } if ( $is_child_module ) { $address .= '.' . self::_get_index( self::INDEX_MODULE_ITEM ); } return $address; } /** * Resolves conditional defaults * * @param array $values Fields. * @param string $render_slug Module slug. * * @return array */ public function resolve_conditional_defaults( $values, $render_slug = '' ) { // Resolve conditional defaults for the FE. $resolved = $this->get_default_props(); if ( $render_slug && $render_slug !== $this->slug ) { $module = self::get_module( $render_slug, $this->get_post_type() ); if ( $module ) { $resolved = array_merge( $resolved, $module->get_default_props() ); } } foreach ( $resolved as $field_name => $field_default ) { if ( is_array( $field_default ) && 2 === count( $field_default ) && ! empty( $field_default[0] ) ) { if ( is_array( $field_default[1] ) ) { // Looks like we have a conditional default // Get $depend_field value or use the first default if undefined. list ( $depend_field, $conditional_defaults ) = $field_default; reset( $conditional_defaults ); $default_key = isset( $values[ $depend_field ] ) ? $values[ $depend_field ] : key( $conditional_defaults ); // Set the resolved default. $resolved[ $field_name ] = isset( $conditional_defaults[ $default_key ] ) ? $conditional_defaults[ $default_key ] : null; } elseif ( 'filter' === $field_default[0] ) { $resolved[ $field_name ] = apply_filters( $field_default[1], $field_name ); } } } // Add hover attributes. if ( ! is_array( $values ) ) { return $resolved; } foreach ( $values as $attr => $value ) { // Inject module props with suffixes __hover|__hover_enabled|__sticky|__sticky_enabled|_last_edited|_tablet|_phone. if ( ! preg_match( ET_Builder_Module_Helper_MultiViewOptions::get_regex_suffix(), $attr ) ) { continue; } $resolved[ $attr ] = $value; } $skip_base_names = array( 'fb_built', '_builder_version', 'hover_enabled', 'sticky_enabled', ); $base_names = array(); foreach ( array_keys( $values ) as $attr ) { $base_name = 0 === strpos( $attr, 'content' ) ? 'content' : ET_Builder_Module_Helper_MultiViewOptions::get_name_base( $attr ); if ( in_array( $base_name, $skip_base_names, true ) ) { continue; } $base_names[ $base_name ] = $base_name; } // Set the props list that the value need to be inherited. // to get the responsive content able to display content for tablet/phone/hover only mode. foreach ( $base_names as $base_name ) { foreach ( array( 'hover', 'tablet', 'phone' ) as $mode ) { $name_by_mode = ET_Builder_Module_Helper_MultiViewOptions::get_name_by_mode( $base_name, $mode ); if ( ! isset( $values[ $name_by_mode ] ) && ! isset( $resolved[ $name_by_mode ] ) ) { // Set value inheritance flag for hover mode. $this->mv_inherited_props[ $name_by_mode ] = $name_by_mode; } elseif ( ! isset( $values[ $name_by_mode ] ) && isset( $resolved[ $name_by_mode ] ) && '' === $resolved[ $name_by_mode ] ) { // Set value inheritance flag for tablet & phone mode. $this->mv_inherited_props[ $name_by_mode ] = $name_by_mode; } } } return $resolved; } /** * Get wrapper settings. Combining module-defined wrapper settings with default wrapper settings * * @since 3.1 * * @param string $render_slug module slug. * * @return array */ protected function get_wrapper_settings( $render_slug = '' ) { global $et_fb_processing_shortcode_object; // The following defaults are used on both frontend & builder. $defaults = array( 'parallax_background' => '', 'video_background' => '', 'pattern_background' => '', 'mask_background' => '', 'attrs' => array(), 'inner_attrs' => array( 'class' => 'et_pb_module_inner', ), ); // The following defaults are only used on frontend. VB handles these on ETBuilderInjectedComponent based on live props // Note: get_parallax_image_background() and video_background() have to be called before module_classname(). if ( ! $et_fb_processing_shortcode_object ) { $_background_fields = ! empty( $this->advanced_fields['background'] ) ? $this->advanced_fields['background'] : array(); $use_background_image = isset( $_background_fields['use_background_image'] ) ? $_background_fields['use_background_image'] : false; $use_background_video = isset( $_background_fields['use_background_video'] ) ? $_background_fields['use_background_video'] : false; $use_background_pattern = isset( $_background_fields['use_background_pattern'] ) ? $_background_fields['use_background_pattern'] : false; $use_background_mask = isset( $_background_fields['use_background_mask'] ) ? $_background_fields['use_background_mask'] : false; $use_module_id = isset( $this->props['module_id'] ) ? $this->props['module_id'] : ''; // Module might disable image background. if ( $use_background_image ) { $defaults['parallax_background'] = $this->get_parallax_image_background(); } // Module might disable video background. if ( $use_background_video ) { $defaults['video_background'] = $this->video_background(); } // Module might disable pattern background. if ( $use_background_pattern ) { $defaults['pattern_background'] = $this->background_pattern(); } // Module might disable mask background. if ( $use_background_mask ) { $defaults['mask_background'] = $this->background_mask(); } // Module might intentionally has custom id fields (ie. Module items). if ( $use_module_id ) { $defaults['attrs']['id'] = $this->module_id( false ); } $defaults['attrs']['class'] = $this->module_classname( $render_slug ); } if ( ! $defaults['attrs'] ) { // Make sure we get an empty object when this is output as JSON later. $defaults['attrs'] = new stdClass(); } // Fill empty argument attributes by default values. return wp_parse_args( $this->wrapper_settings, $defaults ); } /** * Wrap module's rendered output with proper module wrapper. Ensuring module has consistent * wrapper output which compatible with module attribute and background insertion. * * @since 3.1 * * @param string $output Module's rendered output. * @param string $render_slug Slug of module that is used for rendering output. * * @return string */ protected function _render_module_wrapper( $output = '', $render_slug = '' ) { $wrapper_settings = $this->get_wrapper_settings( $render_slug ); $slug = $render_slug; $outer_wrapper_attrs = $wrapper_settings['attrs']; $inner_wrapper_attrs = $wrapper_settings['inner_attrs']; /** * Filters the HTML attributes for the module's outer wrapper. The dynamic portion of the * filter name, '$slug', corresponds to the module's slug. * * @since 3.23 Add support for responsive video background. * @since 3.1 * * @param string[] $outer_wrapper_attrs * @param ET_Builder_Element $module_instance */ $outer_wrapper_attrs = apply_filters( "et_builder_module_{$slug}_outer_wrapper_attrs", $outer_wrapper_attrs, $this ); /** * Filters the HTML attributes for the module's inner wrapper. The dynamic portion of the * filter name, '$slug', corresponds to the module's slug. * * @since 3.1 * * @param string[] $inner_wrapper_attrs * @param ET_Builder_Element $module_instance */ $inner_wrapper_attrs = apply_filters( "et_builder_module_{$slug}_inner_wrapper_attrs", $inner_wrapper_attrs, $this ); return sprintf( '<div%1$s> %2$s %3$s %6$s %7$s %8$s %9$s <div%4$s> %5$s </div> </div>', et_html_attrs( $outer_wrapper_attrs ), $wrapper_settings['parallax_background'], $wrapper_settings['video_background'], et_html_attrs( $inner_wrapper_attrs ), $output, et_()->array_get( $wrapper_settings, 'video_background_tablet', '' ), et_()->array_get( $wrapper_settings, 'video_background_phone', '' ), et_core_esc_previously( $wrapper_settings['pattern_background'] ), // #8 et_core_esc_previously( $wrapper_settings['mask_background'] ) // #9 ); } /** * Resolves the values for dynamic attributes. * * @since 3.17.2 * * @param array $original_attrs List of attributes. * * @return array Processed attributes with resolved dynamic values. */ public function process_dynamic_attrs( $original_attrs ) { global $et_fb_processing_shortcode_object; $attrs = $original_attrs; $enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $attrs ); if ( is_array( $attrs ) ) { foreach ( $attrs as $key => $value ) { $attrs[ $key ] = $this->_resolve_value( $this->get_the_ID(), $key, $value, $enabled_dynamic_attributes, $et_fb_processing_shortcode_object ); } } return $attrs; } /** * Prepares for and then calls the module's {@see self::render()} method. * * @param array $attrs List of attributes. * @param string $content Content being processed. * @param string $render_slug Slug of module that is used for rendering output. * @param string $parent_address Parent address. * @param string $global_parent Global parent. * @param string $global_parent_type Global parent type. * @param string $parent_type Parent type. * * @since 3.23 Add support for generating responsive animation. * @since 3.1 Renamed from `_shortcode_callback()` to `_render()`. * @since 1.0 * * @return string The module's HTML output. */ public function _render( $attrs, $content, $render_slug, $parent_address = '', $global_parent = '', $global_parent_type = '', $parent_type = '', $theme_builder_area = '' ) { global $et_fb_processing_shortcode_object, $et_pb_current_parent_type, $et_pb_parent_section_type, $is_parent_sticky_module, $is_inside_sticky_module; if ( $this->is_rendering ) { // Every module instance is a singleton so the TB Post Content module // can cause a section, row and/or column to call _render() multiple // times - once for each respective shortcode found in the content // rendered by the Post Content module. // Since this _render() method changes object state this leads to // props being messed up between renders so we have to clone the // base instance every time we try to render while the base // instance is still rendering. $clone = clone $this; $clone->is_rendering = false; // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found return call_user_func_array( array( $clone, '_render' ), func_get_args() ); } self::set_order_class( $render_slug ); $this->_maybe_rebuild_option_template(); $attrs = $this->_maybe_add_global_presets_settings( $attrs, $render_slug ); // Use the current layout or post ID for AB testing. This is not guaranteed to be the real // current post ID if we are rendering a TB layout. $post_interference = self::_should_respect_post_interference(); $post_id = apply_filters( 'et_is_ab_testing_active_post_id', self::get_layout_id() ); $is_main_post = $this->get_the_ID() === $post_id; if ( ! $post_interference ) { ET_Post_Stack::replace( ET_Post_Stack::get_main_post() ); } $enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $attrs ); $attrs = $this->_encode_legacy_dynamic_content( $attrs, $enabled_dynamic_attributes ); $this->attrs_unprocessed = $attrs; $attrs = $this->process_dynamic_attrs( $attrs ); $this->props = shortcode_atts( $this->resolve_conditional_defaults( $attrs, $render_slug ), $attrs ); // Only process if it's not a "global module's parent", // the render method in the global module's parent will just call the `do_shortcode()` for the global module content. // So processing the sticky should be done in the global module content render instead. $should_process_sticky = ! in_array( $render_slug, array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner' ), true ) || empty( $this->props['global_module'] ); if ( $should_process_sticky ) { // Check if current module is inside sticky module based on previous $is_parent_sticky_module. $is_inside_sticky_module = $is_parent_sticky_module; // Flag if current module rendering is sticky module or not. // This is frequently used on sticky style rendering for modifying selectors. $this->is_sticky_module = et_pb_sticky_options()->is_sticky_module( $this->props ); // If current module is sticky module, update $is_parent_sticky_module global; Module inside // current module will check $is_parent_sticky_module global before $this->is_sticky_module // property check to see if current module is inside another sticky module or not. if ( $this->is_sticky_module ) { // IMPORTANT: DO NOT use $is_parent_sticky_module global to check whether current module // is inside another sticky or not. Use is_inside_sticky_module() util instead. $is_parent_sticky_module = $this->is_sticky_module; } } else { $this->is_sticky_module = false; } $this->_decode_double_quotes( $enabled_dynamic_attributes, $et_fb_processing_shortcode_object ); $this->_maybe_remove_global_default_values_from_props(); // Some module items need to inherit value from its module parent // This inheritance needs to be done before migration to make it compatible with migration process. $this->maybe_inherit_values(); $_address = $this->generate_element_address( $render_slug ); /** * Filters Module Props. * * @param array $props Array of processed props. * @param array $attrs Array of original shortcode attrs * @param string $slug Module slug * @param string $_address Module Address * @param string $content Module content */ $this->props = apply_filters( 'et_pb_module_shortcode_attributes', $this->props, $attrs, $render_slug, $_address, $content ); // Converting icon values for custom\3rd-party modules with 'select_icon' fields. if ( false === $this->_is_official_module ) { foreach ( $this->get_fields() as $_field_name => $_field_data ) { if ( ! empty( $_field_data['type'] ) && 'select_icon' === $_field_data['type'] && ! empty( $this->props[ $_field_name ] ) ) { $all_responsive_modes_values = et_pb_responsive_options()->get_property_values( $attrs, $_field_name ); $all_modes_values = array( et_pb_responsive_options()->get_field_name( $_field_name ) => et_()->array_get( $all_responsive_modes_values, 'desktop', '' ), et_pb_responsive_options()->get_field_name( $_field_name, 'tablet' ) => et_()->array_get( $all_responsive_modes_values, 'tablet', '' ), et_pb_responsive_options()->get_field_name( $_field_name, 'phone' ) => et_()->array_get( $all_responsive_modes_values, 'phone', '' ), et_pb_hover_options()->get_hover_field( $_field_name ) => et_builder_is_hover_enabled( $_field_name, $attrs ) ? et_pb_hover_options()->get_value( $_field_name, $attrs, '' ) : null, et_pb_sticky_options()->get_sticky_field( $_field_name ) => et_pb_sticky_options()->is_sticky_module( $_field_name ) && et_pb_sticky_options()->is_enabled( $_field_name, $attrs ) ? et_pb_sticky_options()->get_value( $_field_name, $attrs ) : null, ); foreach ( $all_modes_values as $font_icon_name => $font_icon_value ) { if ( ! empty( $font_icon_value ) && et_pb_maybe_old_divi_font_icon( $font_icon_value ) ) { $this->props[ $font_icon_name ] = et_pb_build_extended_font_icon_value( $font_icon_value, false, false, true ); } } } } } $global_content = false; $ab_testing_enabled = et_is_ab_testing_active( $post_id ); $hide_subject_module = false; $hide_subject_module_cached = $hide_subject_module; $global_module_id = $this->props['global_module']; // If the section/row/module is disabled, hide it. if ( isset( $this->props['disabled'] ) && 'on' === $this->props['disabled'] && ! $et_fb_processing_shortcode_object ) { if ( ! $post_interference ) { ET_Post_Stack::restore(); } return; } // need to perform additional check and some modifications in case AB testing enabled // skip for VB since it's handled on VB side. if ( $ab_testing_enabled && ( ! $is_main_post || ! $et_fb_processing_shortcode_object ) ) { // check if ab testing enabled for this module and if it shouldn't be displayed currently. $hide_subject_module = ( ! $is_main_post || ! $et_fb_processing_shortcode_object ) && ! $this->_is_display_module( $this->props ) && ! et_pb_detect_cache_plugins(); // add class to the AB testing subject if needed. if ( isset( $this->props['ab_subject_id'] ) && '' !== $this->props['ab_subject_id'] ) { $subject_class = sprintf( ' et_pb_ab_subject et_pb_ab_subject_id-%1$s_%2$s', esc_attr( $post_id ), esc_attr( $this->props['ab_subject_id'] ) ); $this->props['module_class'] = isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ? $this->props['module_class'] . $subject_class : $subject_class; if ( et_pb_detect_cache_plugins() ) { $hide_subject_module_cached = true; } } // add class to the AB testing goal if needed. if ( isset( $this->props['ab_goal'] ) && 'on' === $this->props['ab_goal'] ) { $goal_class = sprintf( ' et_pb_ab_goal et_pb_ab_goal_id-%1$s', esc_attr( $post_id ) ); $this->props['module_class'] = isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ? $this->props['module_class'] . $goal_class : $goal_class; } } // override module attributes for global module. Skip that step while processing Frontend Builder object. if ( ! empty( $global_module_id ) && ! $et_fb_processing_shortcode_object ) { // Update render_slug when rendering global rows inside Specialty sections. $render_slug = 'et_pb_specialty_column' === $et_pb_current_parent_type && 'et_pb_row' === $render_slug ? 'et_pb_row_inner' : $render_slug; $global_module_data = et_pb_load_global_module( $global_module_id, $render_slug ); if ( '' !== $global_module_data ) { $unsynced_global_attributes = get_post_meta( $global_module_id, '_et_pb_excluded_global_options' ); $use_updated_global_sync_method = ! empty( $unsynced_global_attributes ); $unsynced_options = ! empty( $unsynced_global_attributes[0] ) ? json_decode( $unsynced_global_attributes[0], true ) : array(); $content_synced = $use_updated_global_sync_method && ! in_array( 'et_pb_content_field', $unsynced_options, true ); // support legacy selective sync system. if ( ! $use_updated_global_sync_method ) { $content_synced = ! isset( $this->props['saved_tabs'] ) || false !== strpos( $this->props['saved_tabs'], 'general' ) || 'all' === $this->props['saved_tabs']; } if ( $content_synced ) { // Set the flag showing if we load inner row. $load_inner_row = 'et_pb_row_inner' === $render_slug; $global_content = et_pb_get_global_module_content( $global_module_data, $render_slug, $load_inner_row ); } // cleanup the shortcode string to avoid the attributes messing with content. $global_content_processed = false !== $global_content ? str_replace( $global_content, '', $global_module_data ) : $global_module_data; $global_atts = shortcode_parse_atts( et_pb_remove_shortcode_content( $global_content_processed, $this->slug ) ); $global_atts = $this->_encode_legacy_dynamic_content( $global_atts, $enabled_dynamic_attributes ); // Additional content processing required for Code Modules. if ( in_array( $render_slug, array( 'et_pb_code', 'et_pb_fullwidth_code' ), true ) ) { $global_content_processed = _et_pb_code_module_prep_content( $global_content_processed ); } // reset module addresses because global items will be processed once again and address will be incremented wrongly. if ( false !== strpos( $render_slug, '_section' ) ) { self::_set_index( self::INDEX_SECTION, self::_get_index( self::INDEX_SECTION ) - 1 ); self::_set_index( self::INDEX_ROW, -1 ); self::_set_index( self::INDEX_ROW_INNER, -1 ); self::_set_index( self::INDEX_COLUMN, -1 ); self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_row_inner' ) ) { self::_set_index( self::INDEX_ROW, self::_get_index( self::INDEX_ROW ) - 1 ); self::_set_index( self::INDEX_COLUMN_INNER, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } elseif ( false !== strpos( $render_slug, '_row' ) ) { self::_set_index( self::INDEX_ROW, self::_get_index( self::INDEX_ROW ) - 1 ); self::_set_index( self::INDEX_COLUMN, -1 ); self::_set_index( self::INDEX_MODULE, -1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } else { self::_set_index( self::INDEX_MODULE, self::_get_index( self::INDEX_MODULE ) - 1 ); self::_set_index( self::INDEX_MODULE_ITEM, -1 ); } // Always unsync 'next_background_color' and 'prev_background_color' options for global sections // They should be dynamic and reflect color of top and bottom sections. if ( 'et_pb_section' === $render_slug ) { $unsynced_options = array_merge( $unsynced_options, array( 'next_background_color', 'prev_background_color' ) ); } foreach ( $this->props as $single_attr => $value ) { if ( isset( $global_atts[ $single_attr ] ) && ! in_array( $single_attr, $unsynced_options, true ) ) { // replace %22 with double quotes in options to make sure it's rendered correctly. $this->props[ $single_attr ] = is_string( $global_atts[ $single_attr ] ) && ! array_intersect( array( "et_pb_{$single_attr}", $single_attr ), $this->dbl_quote_exception_options ) ? str_replace( '%22', '"', $global_atts[ $single_attr ] ) : $global_atts[ $single_attr ]; } } $this->props = $this->process_dynamic_attrs( $this->props ); $this->_decode_double_quotes( array(), $et_fb_processing_shortcode_object ); } } $this->before_render(); $this->content_unprocessed = $this->_encode_legacy_dynamic_content_value( 'content', false !== $global_content ? $global_content : $content, $enabled_dynamic_attributes ); $content = $this->_resolve_value( $this->get_the_ID(), 'content', $this->content_unprocessed, $enabled_dynamic_attributes, $et_fb_processing_shortcode_object ); // Process sticky elements earlier to preserve the modules hierarchy during processing. if ( $should_process_sticky ) { $this->process_sticky( $render_slug ); } // Process scroll effects earlier to preserve the modules hierarchy during processing. $this->process_scroll_effects( $render_slug ); $content = apply_filters( 'et_pb_module_content', $content, $this->props, $attrs, $render_slug, $_address, $global_content ); // Set empty TinyMCE content '<br /><br />' as empty string. if ( 'ltbrgtbr' === preg_replace( '/[^a-z]/', '', $content ) ) { $content = ''; } $this->_original_content = et_pb_fix_shortcodes( $content, $this->use_raw_content ); if ( $et_fb_processing_shortcode_object ) { $this->content = et_pb_fix_shortcodes( $content, $this->use_raw_content ); } else { // Line breaks should be converted before do_shortcode to avoid legit rendered shortcode // line breaks being trimmed into one line and causing issue like broken javascript code. if ( $this->use_raw_content ) { $content = et_builder_convert_line_breaks( et_builder_replace_code_content_entities( $content ) ); } if ( ! ( isset( $this->is_structure_element ) && $this->is_structure_element ) ) { $content = et_pb_fix_shortcodes( $content, $this->use_raw_content ); $content = et_maybe_enable_embed_shortcode( $content, true ); $this->content = do_shortcode( $content ); } else { $this->content = ''; } $this->props['content'] = $this->content; } // Restart classname on shortcode callback. Module class is only called once, not on every // shortcode module appearance. Thus classname construction need to be restarted on each // module callback. $this->classname = array(); if ( method_exists( $this, 'shortcode_atts' ) ) { // Deprecated. Do not use this! $this->shortcode_atts(); } if ( ! $et_fb_processing_shortcode_object ) { $this->process_global_colors(); } $this->process_additional_options( $render_slug ); $this->process_custom_css_fields( $render_slug ); // load inline fonts if needed. if ( isset( $this->props['inline_fonts'] ) ) { $this->process_inline_fonts_option( $this->props['inline_fonts'] ); } // Automatically add slug as classname for module that uses other module's shortcode callback // This has to be added first because some classname is position-sensitive and used for // JS-based calculation (i.e. .et_pb_column in column inner). if ( $this->slug !== $render_slug ) { $this->add_classname( $this->slug ); // Apply classnames added to the module that uses other module's shortcode callback // (i.e. `process_additional_options` for the column inner). $module = self::get_module( $render_slug, $this->get_post_type() ); $this->add_classname( $module->classname ); } // Automatically add default classnames. $this->add_classname( array( 'et_pb_module', $render_slug, self::get_module_order_class( $render_slug ), ) ); // Automatically added user-defined classname if there's any. if ( isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ) { $this->add_classname( explode( ' ', $this->props['module_class'] ) ); } // Animation Styles. $animation_style = isset( $this->props['animation_style'] ) && '' !== $this->props['animation_style'] ? $this->props['animation_style'] : false; $animation_repeat = isset( $this->props['animation_repeat'] ) && '' !== $this->props['animation_repeat'] ? $this->props['animation_repeat'] : 'once'; $animation_direction = isset( $this->props['animation_direction'] ) && '' !== $this->props['animation_direction'] ? $this->props['animation_direction'] : 'center'; $animation_duration = isset( $this->props['animation_duration'] ) && '' !== $this->props['animation_duration'] ? $this->props['animation_duration'] : '500ms'; $animation_delay = isset( $this->props['animation_delay'] ) && '' !== $this->props['animation_delay'] ? $this->props['animation_delay'] : '0ms'; $animation_intensity = isset( $this->props[ "animation_intensity_{$animation_style }" ] ) && '' !== $this->props[ "animation_intensity_{$animation_style }" ] ? $this->props[ "animation_intensity_{$animation_style }" ] : '50%'; $animation_starting_opacity = isset( $this->props['animation_starting_opacity'] ) && '' !== $this->props['animation_starting_opacity'] ? $this->props['animation_starting_opacity'] : '0%'; $animation_speed_curve = isset( $this->props['animation_speed_curve'] ) && '' !== $this->props['animation_speed_curve'] ? $this->props['animation_speed_curve'] : 'ease-in-out'; // Check if animation is enabled. $animation_enabled = $this->_features_manager->get( // Has animation enabled. 'anim', function() use ( $animation_style, $animation_direction ) { return ! empty( $animation_style ) || ! empty( $animation_direction ); } ); if ( $animation_enabled ) { // Animation style and direction values for Tablet & Phone. Basically, style for tablet and // phone are same with the desktop because we only edit responsive settings for the affected // fields under animation style. Variable $animation_style_responsive need to be kept as // unmodified variable because it will be used by animation intensity. $animation_style_responsive = $animation_style; $animation_style_tablet = $animation_style; $animation_style_phone = $animation_style; $animation_direction_tablet = et_pb_responsive_options()->get_any_value( $this->props, 'animation_direction_tablet' ); $animation_direction_phone = et_pb_responsive_options()->get_any_value( $this->props, 'animation_direction_phone' ); // Check if this is an AJAX request since this is how VB loads the initial module data // et_core_is_fb_enabled() always returns `false` here. if ( $animation_style && 'none' !== $animation_style && ! wp_doing_ajax() ) { $transformed_animations = array( 'desktop' => false, 'tablet' => false, 'phone' => false, ); // Fade doesn't have direction. if ( 'fade' === $animation_style ) { $animation_direction_tablet = ''; $animation_direction_phone = ''; } else { $directions_list = array( 'top', 'right', 'bottom', 'left' ); if ( in_array( $animation_direction, $directions_list, true ) ) { $animation_style .= ucfirst( $animation_direction ); } // avoid custom animation on button because animation is applied to the wrapper so transforms do not need to combine. if ( 'et_pb_button' !== $render_slug ) { foreach ( preg_grep( '/(transform_)/', array_keys( $this->props ) ) as $index => $key ) { if ( strpos( $key, 'link' ) !== false || strpos( $key, 'hover' ) !== false || strpos( $key, 'last_edited' ) !== false ) { continue; } if ( ! empty( $this->props[ $key ] ) ) { if ( ! $transformed_animations['desktop'] && strpos( $key, 'tablet' ) === false && strpos( $key, 'phone' ) === false ) { $transformed_animations['desktop'] = true; $transformed_animations['tablet'] = true; $transformed_animations['phone'] = true; } elseif ( ! $transformed_animations['tablet'] && strpos( $key, 'tablet' ) !== false ) { $transformed_animations['tablet'] = true; $transformed_animations['phone'] = true; } elseif ( ! $transformed_animations['phone'] && strpos( $key, 'phone' ) !== false ) { $transformed_animations['phone'] = true; } if ( $transformed_animations['desktop'] && $transformed_animations['tablet'] && $transformed_animations['phone'] ) { break; } } } } } $module_class = self::get_module_order_class( $render_slug ); if ( $module_class ) { // Desktop animation data. $animation_data = array( 'class' => esc_attr( trim( $module_class ) ), 'style' => esc_html( $animation_style ), 'repeat' => esc_html( $animation_repeat ), 'duration' => esc_html( $animation_duration ), 'delay' => esc_html( $animation_delay ), 'intensity' => esc_html( $animation_intensity ), 'starting_opacity' => esc_html( $animation_starting_opacity ), 'speed_curve' => esc_html( $animation_speed_curve ), ); // Being save to generate Tablet & Phone data attributes. As default, tablet // default value will inherit desktop value and phone default value will inherit // tablet value. Ensure to pass the value only if it's different compared to // desktop value to avoid duplicate values. $animation_attributes = array( 'repeat' => 'animation_repeat', 'duration' => 'animation_duration', 'delay' => 'animation_delay', 'intensity' => "animation_intensity_{$animation_style_responsive}", 'starting_opacity' => 'animation_starting_opacity', 'speed_curve' => 'animation_speed_curve', ); foreach ( $animation_attributes as $animation_key => $animation_attribute ) { $animation_attribute_tablet = ''; $animation_attribute_phone = ''; // Ensure responsive status for current attribute is activated. if ( ! et_pb_responsive_options()->is_responsive_enabled( $this->props, $animation_attribute ) ) { continue; } // Tablet animation value. $animation_attribute_tablet = et_pb_responsive_options()->get_any_value( $this->props, "{$animation_attribute}_tablet", $animation_data[ $animation_key ] ); if ( ! empty( $animation_attribute_tablet ) ) { $animation_data[ "{$animation_key}_tablet" ] = $animation_attribute_tablet; } // Phone animation value. $animation_attribute_phone = et_pb_responsive_options()->get_any_value( $this->props, "{$animation_attribute}_phone", $animation_data[ $animation_key ] ); if ( ! empty( $animation_attribute_phone ) ) { $animation_data[ "{$animation_key}_phone" ] = $animation_attribute_phone; } } // Animation style is little bit different. We need to check the direction to get // the correct style. We need to ensure the direction is valid, then add it as // suffix for the animation style. if ( et_pb_responsive_options()->is_responsive_enabled( $this->props, 'animation_direction' ) ) { // Tablet animation style. if ( ! empty( $animation_direction_tablet ) ) { $animation_style_tablet_suffix = in_array( $animation_direction_tablet, $directions_list, true ) ? ucfirst( $animation_direction_tablet ) : ''; $animation_data['style_tablet'] = $animation_style_tablet . $animation_style_tablet_suffix; } // Phone animation style. if ( ! empty( $animation_direction_phone ) ) { $animation_style_phone_suffix = in_array( $animation_direction_phone, $directions_list, true ) ? ucfirst( $animation_direction_phone ) : ''; $animation_data['style_phone'] = $animation_style_phone . $animation_style_phone_suffix; } elseif ( ! empty( $animation_data['style_tablet'] ) ) { $animation_data['style_phone'] = $animation_data['style_tablet']; } } // overwrite animation name to match the custom animation generated on transforms options processing. if ( $transformed_animations['desktop'] ) { $animation_data['style'] = 'transformAnim'; } if ( $transformed_animations['tablet'] ) { $animation_data['style_tablet'] = 'transformAnim'; } if ( $transformed_animations['phone'] ) { $animation_data['style_phone'] = 'transformAnim'; } et_builder_handle_animation_data( $animation_data ); } // Try to apply old method for plugins without vb support. if ( ! $et_fb_processing_shortcode_object && 'on' !== $this->vb_support ) { add_filter( "{$render_slug}_shortcode_output", array( $this, 'add_et_animated_class' ), 10, 2 ); } // Only print et_animated on front-end. Avoid adding it on computed callback of post slider(s) // and modules because it'll cause the module to be visually hidden. if ( ! et_core_is_fb_enabled() ) { $this->add_classname( 'et_animated' ); } } } $has_hover_enabled = $this->_features_manager->get( // Has hover enabled. 'hov', function() { return et_has_hover_enabled( $this->props ); } ); // Add "et_hover_enabled" class to elements that have at least one hover prop enabled. if ( $has_hover_enabled ) { $this->add_classname( 'et_hover_enabled' ); } // Add sticky element module classname to determine nested sticky module. $needs_sticky_class = $this->_features_manager->get( // Needs sticky class. 'sti', function() use ( $et_fb_processing_shortcode_object, $render_slug ) { return ! $et_fb_processing_shortcode_object && et_()->array_get( self::$sticky_elements, self::get_module_order_class( $render_slug ), false ); } ); if ( $needs_sticky_class ) { $this->add_classname( 'et_pb_sticky_module' ); } // Setup link options. $link_option_url = isset( $this->props['link_option_url'] ) ? $this->props['link_option_url'] : ''; $link_option_url_new_window = isset( $this->props['link_option_url_new_window'] ) ? $this->props['link_option_url_new_window'] : false; if ( '' !== $link_option_url ) { $module_class = self::get_module_order_class( $render_slug ); if ( $module_class ) { et_builder_handle_link_options_data( array( 'class' => trim( $module_class ), 'url' => esc_url_raw( $link_option_url ), 'target' => 'on' === $link_option_url_new_window ? '_blank' : '_self', ) ); } $this->add_classname( 'et_clickable' ); } // Hide module on specific screens if needed. if ( isset( $this->props['disabled_on'] ) && '' !== $this->props['disabled_on'] ) { $disabled_on_array = explode( '|', $this->props['disabled_on'] ); $i = 0; $current_media_query = 'max_width_767'; foreach ( $disabled_on_array as $value ) { if ( 'on' === $value ) { // Added specific selector and declaration to fix the problem when // Video module is hidden for desktop the fullscreen // won't work on mobile screen size. $selector = 'et_pb_video' === $render_slug ? '.et_pb_video%%order_class%%' : '%%order_class%%'; $declaration = 'et_pb_video' === $render_slug ? 'min-height: 0; height: 0; margin: 0 !important; padding: 0; overflow: hidden;' : 'display: none !important;'; $el_style = array( 'selector' => $selector, 'declaration' => $declaration, 'media_query' => self::get_media_query( $current_media_query ), ); ET_Builder_Module::set_style( $render_slug, $el_style ); } $i++; $current_media_query = 1 === $i ? '768_980' : 'min_width_981'; } } if ( ! $et_fb_processing_shortcode_object ) { if ( 'et_pb_section' === $render_slug ) { $et_pb_current_parent_type = isset( $this->props['specialty'] ) && 'on' === $this->props['specialty'] ? 'et_pb_specialty_section' : 'et_pb_section'; $et_pb_parent_section_type = $et_pb_current_parent_type; } elseif ( 'et_pb_specialty_section' === $et_pb_current_parent_type && 'et_pb_column' === $render_slug ) { $et_pb_current_parent_type = 'et_pb_specialty_column'; } // Make sure content of Specialty Section is valid and has correct structure. Fix inner shortcode tags if needed. if ( 'et_pb_specialty_section' === $et_pb_current_parent_type ) { $content = $this->et_pb_maybe_fix_specialty_columns( $content ); } } $this->is_rendering = true; $render_method = $et_fb_processing_shortcode_object ? 'render_as_builder_data' : 'render'; // Render the module as we normally would. $output = $this->{$render_method}( $attrs, $content, $render_slug, $parent_address, $global_parent, $global_parent_type, $parent_type, $theme_builder_area ); /** * Filters every rendered module output for processing "Display Conditions" option group. * * @since 4.13.1 * * @param string $output HTML output of the rendered module. * @param string $render_method The render method used to render the module. * @param ET_Builder_Element $this The current instance of ET_Builder_Element. */ $output = apply_filters( 'et_module_process_display_conditions', $output, $render_method, $this ); $this->is_rendering = false; // Wrap 3rd party module rendered output with proper module wrapper // @TODO implement module wrapper on official module. if ( 'on' === $this->vb_support && 'render' === $render_method && ! $this->_is_official_module ) { $output = $this->_render_module_wrapper( $output, $render_slug ); } /** * Filters every builder modules shortcode output. * * @since 3.1 * * @param string $output * @param string $module_slug * @param object $this */ $output = apply_filters( 'et_module_shortcode_output', $output, $render_slug, $this ); /** * Filters builder module shortcode output. The dynamic portion of the filter name, `$render_slug`, * refers to the slug of the module for which the shortcode output was generated. * * @since 3.0.87 * @since 4.14.5 Pass module instance as 3rd argument. * * @param string $output * @param string $module_slug * @param object $this Module instance. */ $output = apply_filters( "{$render_slug}_shortcode_output", $output, $render_slug, $this ); $this->_bump_render_count(); // Reset is_parent_sticky_module global once content of current module is done rendered. if ( $this->is_sticky_module ) { $is_parent_sticky_module = false; } if ( ! $post_interference ) { ET_Post_Stack::restore(); } if ( $hide_subject_module ) { return ''; } if ( $hide_subject_module_cached ) { $previous_subjects_cache = get_post_meta( $post_id, 'et_pb_subjects_cache', true ); if ( empty( $previous_subjects_cache ) ) { $previous_subjects_cache = array(); } if ( empty( $this->template_name ) ) { $previous_subjects_cache[ $this->props['ab_subject_id'] ] = $output; } else { $previous_subjects_cache[ $this->props['ab_subject_id'] ] = $this->output(); } // update the subjects cache in post meta to use it later. update_post_meta( $post_id, 'et_pb_subjects_cache', $previous_subjects_cache ); // generate the placeholder to output on front-end instead of actual content. $subject_placeholder = sprintf( '<div class="et_pb_subject_placeholder et_pb_subject_placeholder_id_%1$s_%2$s" style="display: none;"></div>', esc_attr( $post_id ), esc_attr( $this->props['ab_subject_id'] ) ); return $subject_placeholder; } // Do not use `template_name` while processing object for VB. if ( $et_fb_processing_shortcode_object || empty( $this->template_name ) ) { return $output; } return $this->output(); } /** * Add "et_animated" class using filter. Obsolete method and only applied to old 3rd party modules without `modules_classname()` method * * @param string $output Shortcode output. * @param string $module_slug Module slug. * * @return string */ public function add_et_animated_class( $output, $module_slug ) { if ( ! is_string( $output ) || in_array( $module_slug, self::$uses_module_classname, true ) ) { return $output; } remove_filter( "{$module_slug}_shortcode_output", array( $this, 'add_et_animated_class' ), 10 ); return preg_replace( "/class=\"(.*?{$module_slug}_\d+.*?)\"/", 'class="$1 et_animated"', $output, 1 ); } /** * Delete attribute values that are equal to the global default value (if one exists). * * @return void */ protected function _maybe_remove_global_default_values_from_props() { $fields = $this->fields_unprocessed; $must_print_fields = array( 'text_orientation' ); /** * Filters Must Print attributes array. * Must Print attributes - attributes which defaults should always be printed on Front End * * @deprecated * * @param array $must_print_fields Array of attribute names. */ $must_print_fields = apply_filters( $this->slug . '_must_print_attributes', $must_print_fields ); $slug = isset( $this->global_settings_slug ) ? $this->global_settings_slug : $this->slug; $module_slug = self::$global_presets_manager->maybe_convert_module_type( $this->slug, $this->props ); $module_preset_settings = self::$global_presets_manager->get_module_presets_settings( $module_slug, $this->props ); $global_default_fields = $this->_features_manager->get( // Global default fields. 'glde', function() use ( $fields, $slug, $must_print_fields ) { $global_default_fields = []; foreach ( $fields as $field_key => $field_settings ) { $global_setting_name = "$slug-$field_key"; $global_setting_value = ET_Global_Settings::get_value( $global_setting_name ); if ( ! $global_setting_value || in_array( $field_key, $must_print_fields, true ) ) { continue; } $global_default_fields[ $field_key ] = $global_setting_value; } return $global_default_fields; } ); // make sure its at least an array if feature manager had returned false. $global_default_fields = $global_default_fields ?: []; // phpcs:ignore WordPress.PHP.DisallowShortTernary.Found -- It's PHP 5.3+ compat, so it's fine to use. foreach ( $global_default_fields as $field_key => $global_setting_value ) { $attr_value = ! empty( $this->props[ $field_key ] ) ? $this->props[ $field_key ] : ''; if ( $attr_value && $attr_value === $global_setting_value && ! array_key_exists( $field_key, $module_preset_settings ) ) { $this->props[ $field_key ] = ''; } } } /** * Intended to be overridden as needed. */ public function maybe_inherit_values() {} /** * Like {@see self::render()}, but sources the output from a template file. The template name * should be set in {@see self::$template_name}. * * Note: this functionality is not currently supported by the Visual Builder. Pages containing * modules that use this method to render their output cannot be edited using the Visual Builder * at this time. However, full support will be added in the coming months. * * @since 3.1 Renamed from `shortcode_output()` to `output()` * @since 2.4.6 * * @return string */ public function output() { if ( empty( $this->template_name ) ) { return ''; } if ( method_exists( $this, 'shortcode_output' ) ) { // Backwards compatibility. return $this->__call( 'shortcode_output', array() ); } $this->props['content'] = $this->content; // phpcs:ignore WordPress.PHP.DontExtract -- `extract` can not be removed, template depend on props variables. extract( $this->props ); ob_start(); require locate_template( $this->template_name . '.php' ); return ob_get_clean(); } /** * Generates HTML data attributes from an array of props. * * @since 3.1 Rename from `shortcode_atts_to_data_atts()` to `props_to_html_data_attrs()` * @since 1.0 * * @param array $props Propeties to be use in data attributes. * * @return string */ public function props_to_html_data_attrs( $props = array() ) { if ( empty( $props ) ) { return ''; } $output = array(); foreach ( $props as $attr ) { $output[] = 'data-' . esc_attr( $attr ) . '="' . esc_attr( $this->props[ $attr ] ) . '"'; } return implode( ' ', $output ); } /** * This method is called before {@self::_render()} for rows, columns, and modules. It can * be overridden by elements that need to perform any tasks before rendering begins. * * @since 3.1 Renamed from `pre_shortcode_content()` to `before_render()`. * @since 1.0 */ public function before_render() { if ( method_exists( $this, 'pre_shortcode_content' ) ) { // Backwards compatibility. $this->__call( 'pre_shortcode_content', array() ); } } /** * Generates the module's HTML output based on {@see self::$props}. This method should be * overridden in module classes. * * @since 3.1 Renamed from `shortcode_callback()` to `render()`. * @since 1.0 * * @param array $attrs List of unprocessed attributes. * @param string $content Content being processed. * @param string $render_slug Slug of module that is used for rendering output. * * @return string The module's HTML output. */ public function render( $attrs, $content, $render_slug ) { if ( method_exists( $this, 'shortcode_callback' ) ) { // Backwards compatibility. return $this->__call( 'shortcode_callback', array( $attrs, $content, $render_slug ) ); } return ''; } /** * Replace the et_pb_row with et_pb_row_inner and et_pb_column with et_pb_column_inner. * Used as a callback function in {@self::et_pb_maybe_fix_specialty_columns} when fixing content of Specialty Sections * * @param string $rows Row content. * * @return string Shortcode string. * @since 3.19.16 */ public function et_pb_fix_specialty_columns( $rows ) { $sanitized_shortcode = str_replace( array( 'et_pb_row ', 'et_pb_row]' ), array( 'et_pb_row_inner ', 'et_pb_row_inner]' ), $rows[0] ); $sanitized_shortcode = str_replace( array( 'et_pb_column ', 'et_pb_column]' ), array( 'et_pb_column_inner ', 'et_pb_column_inner]' ), $rows[0] ); return $sanitized_shortcode; } /** * Run regex against the Specialty Section content to find and fix invalid inner shortcodes * * @param string $section_content Section content. * * @return string Shortcode string. * @since 3.19.16 */ public function et_pb_maybe_fix_specialty_columns( $section_content ) { return preg_replace_callback( '/(\[et_pb_(row |row_inner) .*?\].*\[\/et_pb_(row |row_inner)\])/mis', array( $this, 'et_pb_fix_specialty_columns' ), $section_content ); } /** * Generates data used to render the module in the builder. * See {@see self::render()} for parameter info. * * @param array $atts Module attributes. * @param string|null $content Module content. * @param string $render_slug Module slug. * @param string $parent_address [description]. * @param string $global_parent [description]. * @param string $global_parent_type [description]. * @param string $parent_type [description]. * * @return array|string An array when called during AJAX request, an empty string otherwise. * @since 3.1 Renamed from `_shortcode_passthru_callback()` to `render_as_builder_data()` * @since 3.0.0 */ public function render_as_builder_data( $atts, $content, $render_slug, $parent_address = '', $global_parent = '', $global_parent_type = '', $parent_type = '', $theme_builder_area = '' ) { global $post; // this is called during pageload, but we want to ignore that round, as this data will be built and returned on separate ajax request instead. et_core_nonce_verified_previously(); if ( ! ( isset( $_POST['action'] ) || apply_filters( 'et_builder_module_force_render', false ) ) ) { return ''; } $attrs = array(); $fields = $this->process_fields( $this->fields_unprocessed ); $global_content = false; $function_name_processed = et_fb_prepare_tag( $render_slug ); $unsynced_global_attributes = array(); $use_updated_global_sync_method = false; $global_module_id = isset( $atts['global_module'] ) ? $atts['global_module'] : false; $is_specialty_placeholder = isset( $atts['template_type'] ) && 'section' === $atts['template_type'] && isset( $atts['specialty'] ) && 'on' === $atts['specialty'] && ( ! $content || '' === trim( $content ) ); $is_global_template = false; $real_parent_type = $parent_type; if ( $render_slug && $render_slug !== $this->slug ) { $rendering_module = self::get_module( $render_slug, $this->get_post_type() ); if ( $rendering_module ) { $fields = array_merge( $fields, $this->process_fields( $rendering_module->fields_unprocessed ) ); } } $output_render_slug = $render_slug; // When rendering specialty columns we should make sure correct tags are used for inner content // Global Rows inside may break it in some cases, so handle it. if ( 'et_pb_specialty_column' === $parent_type && 'et_pb_row' === $render_slug ) { $output_render_slug = 'et_pb_row_inner'; $function_name_processed = 'et_pb_row_inner'; } if ( 'et_pb_row_inner' === $parent_type && 'et_pb_column' === $render_slug ) { $output_render_slug = 'et_pb_column_inner'; $function_name_processed = 'et_pb_column_inner'; } $post_id = isset( $post->ID ) ? $post->ID : intval( self::$_->array_get( $_POST, 'et_post_id' ) ); $post_type = isset( $post->post_type ) ? $post->post_type : sanitize_text_field( self::$_->array_get( $_POST, 'et_post_type' ) ); $layout_type = isset( $post_type, $post_id ) && 'et_pb_layout' === $post_type ? et_fb_get_layout_type( $post_id ) : ''; if ( 'module' === $layout_type ) { // Add support of new selective sync feature for library modules in VB. $template_scope = wp_get_object_terms( $post_id, 'scope' ); $is_global_template = ! empty( $template_scope[0] ) && 'global' === $template_scope[0]->slug; if ( $is_global_template ) { $global_module_id = $post_id; } } // override module attributes for global module. if ( ! empty( $global_module_id ) ) { if ( ! in_array( $render_slug, array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner', 'et_pb_column', 'et_pb_column_inner' ), true ) ) { $processing_global_module = $global_module_id; $unsynced_global_attributes = get_post_meta( $processing_global_module, '_et_pb_excluded_global_options' ); $use_updated_global_sync_method = ! empty( $unsynced_global_attributes ); } $global_module_data = et_pb_load_global_module( $global_module_id, $function_name_processed ); if ( '' !== $global_module_data ) { $unsynced_options = ! empty( $unsynced_global_attributes[0] ) ? json_decode( $unsynced_global_attributes[0], true ) : array(); $content_synced = $use_updated_global_sync_method && ! in_array( 'et_pb_content_field', $unsynced_options, true ); $is_module_fully_global = $use_updated_global_sync_method && empty( $unsynced_options ); $unsynced_legacy_options = array(); // support legacy selective sync system. if ( ! $use_updated_global_sync_method ) { $content_synced = ! isset( $atts['saved_tabs'] ) || false !== strpos( $atts['saved_tabs'], 'general' ) || 'all' === $atts['saved_tabs']; $is_module_fully_global = ! isset( $atts['saved_tabs'] ) || 'all' === $atts['saved_tabs']; } if ( $content_synced && ! $is_global_template ) { $global_content = et_pb_get_global_module_content( $global_module_data, $function_name_processed ); // When saving global rows from specialty sections, they get saved as et_pb_row instead of et_pb_row_inner. // Handle this special case when parsing to avoid empty global row content. if ( empty( $global_content ) && 'et_pb_row_inner' === $function_name_processed ) { $global_content = et_pb_get_global_module_content( $global_module_data, 'et_pb_row', true ); } } // remove the shortcode content to avoid conflicts of parent attributes with similar attrs from child modules. if ( false !== $global_content ) { $global_content_processed = str_replace( $global_content, '', $global_module_data ); } else { $global_content_processed = $global_module_data; } // Ensuring that all possible attributes exist to avoid remaining child attributes being used by global parents' attributes // Do that only in case the module is fully global. if ( $is_module_fully_global ) { $global_atts = shortcode_parse_atts( et_pb_remove_shortcode_content( $global_content_processed, $this->slug ) ); } else { $global_atts = shortcode_parse_atts( $global_content_processed ); } // Run et_pb_module_shortcode_attributes filter to apply migration system on attributes of global module. $global_atts = apply_filters( 'et_pb_module_shortcode_attributes', $global_atts, $atts, $this->slug, $this->generate_element_address( $render_slug ), $content ); // Parse dynamic content in global attributes. $enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $global_atts ); $global_atts = $this->_encode_legacy_dynamic_content( $global_atts, $enabled_dynamic_attributes ); $global_atts = $this->process_dynamic_attrs( $global_atts ); // Parse dynamic content in global content. if ( false !== $global_content ) { $global_content = $this->_encode_legacy_dynamic_content_value( 'content', $global_content, $enabled_dynamic_attributes ); $global_content = $this->_resolve_value( $this->get_the_ID(), 'content', $global_content, $this->_get_enabled_dynamic_attributes( $global_atts ), true ); } foreach ( $this->props as $single_attr => $value ) { if ( isset( $global_atts[ $single_attr ] ) && ! in_array( $single_attr, $unsynced_options, true ) ) { // replace %22 with double quotes in options to make sure it's rendered correctly. if ( ! $is_global_template ) { $this->props[ $single_attr ] = is_string( $global_atts[ $single_attr ] ) && ! array_intersect( array( "et_pb_{$single_attr}", $single_attr ), $this->dbl_quote_exception_options ) ? str_replace( '%22', '"', $global_atts[ $single_attr ] ) : $global_atts[ $single_attr ]; } if ( 'global_colors_info' === $single_attr ) { $this->props[ $single_attr ] = str_replace( array( '%91', '%93' ), array( '[', ']' ), $this->props[ $single_attr ] ); } } elseif ( ! $use_updated_global_sync_method ) { // prepare array of unsynced options to migrate the legacy modules to new system. $unsynced_legacy_options[] = $single_attr; } else { $unsynced_global_attributes[0] = $unsynced_options; } } // migrate unsynced options to the new selective sync method. if ( ! $use_updated_global_sync_method ) { $unsynced_global_attributes[0] = $unsynced_legacy_options; // check the content and add it into list if needed. if ( ! $content_synced ) { $unsynced_global_attributes[0][] = 'et_pb_content_field'; } } else { $unsynced_global_attributes[0] = $unsynced_options; } } else { // remove global_module attr if it doesn't exist in DB. $this->props['global_module'] = ''; $global_parent = ''; } } $module_slug = self::$global_presets_manager->maybe_convert_module_type( $this->slug, $this->props ); $module_preset_settings = self::$global_presets_manager->get_module_presets_settings( $module_slug, $this->props ); foreach ( $this->props as $shortcode_attr_key => $shortcode_attr_value ) { $value = $shortcode_attr_value; // don't set the default, unless, lol, the value is literally 'default'. if ( 'default' !== $value ) { $has_preset_value = isset( $module_preset_settings[ $shortcode_attr_key ] ); if ( $has_preset_value && isset( $atts[ $shortcode_attr_key ] ) ) { $is_equal_to_preset_value = $atts[ $shortcode_attr_key ] === $module_preset_settings[ $shortcode_attr_key ]; $value = $is_equal_to_preset_value ? '' : $atts[ $shortcode_attr_key ]; } else { // handle 'preset' type of attributes. if ( isset( $fields[ $shortcode_attr_key ]['default'] ) && is_array( $fields[ $shortcode_attr_key ]['default'] ) ) { $field = $fields[ $shortcode_attr_key ]; $preset_attribute_name = $field['default'][0]; if ( 'filter' === $preset_attribute_name ) { // Functional default. if ( apply_filters( $field['default'][1], $shortcode_attr_key ) === $value ) { $value = ''; } } else { $preset_default_value = et_()->array_get( $fields[ $preset_attribute_name ], 'default', 'none' ); $preset_attribute_value = et_()->array_get( $this->props, $preset_attribute_name, $preset_default_value ); if ( ! empty( $preset_attribute_value ) ) { $value_from_preset = et_()->array_get( $fields[ $shortcode_attr_key ]['default'][1], $preset_attribute_value, '' ); if ( $value === $value_from_preset ) { $value = ''; } } } } else { $is_equal_to_default = isset( $fields[ $shortcode_attr_key ]['default'] ) && $value === $fields[ $shortcode_attr_key ]['default']; $is_equal_to_default_on_front = isset( $fields[ $shortcode_attr_key ]['default_on_front'] ) && $value === $fields[ $shortcode_attr_key ]['default_on_front']; if ( $is_equal_to_default || $is_equal_to_default_on_front ) { $value = ''; } } } } else { if ( '_module_preset' !== $shortcode_attr_key ) { $value = ''; } } // generic override, disabled=off is an unspoken default. if ( 'disabled' === $shortcode_attr_key && 'off' === $shortcode_attr_value ) { $value = ''; } // this override is necessary becuase et_pb_column and et_pb_column_inner type default is 4_4 and will get stomped // above since its default, but we need it explicitly set anyways, so we force set it. if ( in_array( $render_slug, array( 'et_pb_column', 'et_pb_column_inner' ), true ) && 'type' === $shortcode_attr_key ) { $value = $shortcode_attr_value; } $is_include_attr = false; if ( '' === $value && et_pb_hover_options()->get_field_base_name( $shortcode_attr_key ) !== $shortcode_attr_key && et_pb_hover_options()->is_enabled( et_pb_hover_options()->get_field_base_name( $shortcode_attr_key ), $atts ) ) { $is_include_attr = true; } if ( '' === $value && et_pb_responsive_options()->get_field_base_name( $shortcode_attr_key ) !== $shortcode_attr_key && et_pb_responsive_options()->is_enabled( et_pb_responsive_options()->get_field_base_name( $shortcode_attr_key ), $atts ) ) { $is_include_attr = true; } if ( '' !== $value ) { $is_include_attr = true; } if ( $is_include_attr ) { // Process Icon fields with a special `icons converting\decoding`. if ( ! empty( $fields[ $shortcode_attr_key ] ) && 'select_icon' === $fields[ $shortcode_attr_key ]['type'] && et_pb_maybe_extended_icon( $value ) && is_string( $value ) ) { $attrs[ $shortcode_attr_key ] = et_pb_check_and_convert_icon_raw_value( $value ); } else { $attrs[ $shortcode_attr_key ] = is_string( $value ) ? html_entity_decode( $value ) : $value; } } } // Decode all characters inside json object so it can be parsed correctly. if ( ! empty( $attrs['global_colors_info'] ) ) { $attrs['global_colors_info'] = str_replace( array( '%91', '%93', '%22' ), array( '[', ']', '"' ), $attrs['global_colors_info'] ); } // Format FB component path // TODO, move this to class method and property, and allow both to be overridden. $component_path = str_replace( 'et_pb_', '', $function_name_processed ); $component_path = str_replace( '_', '-', $component_path ); $_i = isset( $atts['_i'] ) ? $atts['_i'] : 0; $address = isset( $atts['_address'] ) ? $atts['_address'] : '0'; // set the global parent if exists. if ( ( ! isset( $attrs['global_module'] ) || '' === $attrs['global_module'] ) && '' !== $global_parent ) { $attrs['global_parent'] = $global_parent; } // add theme_builder_area parameter to attributes, so we know to strip this section when saving the post. if ( isset( $theme_builder_area ) && '' !== $theme_builder_area ) { $attrs['theme_builder_area'] = $theme_builder_area; } if ( isset( $this->is_structure_element ) && $this->is_structure_element ) { $this->vb_support = 'on'; } $processed_content = false !== $global_content ? $global_content : $this->content; // Determine the parent type to send it down the tree while processing shortcode // Main purpose is to know when we rendering Specialty Section content. if ( 'et_pb_section' === $render_slug ) { $parent_type = isset( $attrs['specialty'] ) && 'on' === $attrs['specialty'] ? 'et_pb_specialty_section' : 'et_pb_section'; } elseif ( 'et_pb_specialty_section' === $parent_type && 'et_pb_column' === $render_slug ) { $parent_type = 'et_pb_specialty_column'; } else { $parent_type = $render_slug; } // Make sure content of Specialty Section is valid and has correct structure. Fix inner shortcode tags if needed. if ( 'et_pb_specialty_section' === $parent_type ) { $processed_content = $this->et_pb_maybe_fix_specialty_columns( $processed_content ); } $content = array_key_exists( 'content', $this->fields_unprocessed ) || 'et_pb_code' === $function_name_processed || 'et_pb_fullwidth_code' === $function_name_processed ? $processed_content : et_fb_process_shortcode( $processed_content, $address, $global_parent, $global_parent_type, $parent_type ); // Global Code module content should be decoded before passing to VB. $is_global_code = in_array( $function_name_processed, array( 'et_pb_code', 'et_pb_fullwidth_code' ), true ); $prepared_content = $content; if ( ( ! is_array( $content ) && 'on' !== $this->vb_support && ! $this->has_line_breaks( $content ) ) || $is_global_code ) { $prepared_content = html_entity_decode( $content, ENT_COMPAT, 'UTF-8' ); } if ( empty( $attrs ) ) { // Visual Builder expects $attrs to be an object. // Associative array converted to an object by wp_json_encode correctly, but empty array is not and it causes issues. $attrs = new stdClass(); } $is_child_module = in_array( $render_slug, self::get_child_slugs( $this->get_post_type() ), true ) && false === strpos( $render_slug, '_column_inner' ) && false === strpos( $render_slug, '_column' ); $module_type = $this->type; $render_count = $is_child_module ? self::_get_index( self::INDEX_MODULE_ITEM ) : self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name_processed ) ); $child_title_var = isset( $this->child_title_var ) ? $this->child_title_var : ''; $child_title_fallback_var = isset( $this->child_title_fallback_var ) ? $this->child_title_fallback_var : ''; $advanced_setting_title_text = isset( $this->advanced_setting_title_text ) ? $this->advanced_setting_title_text : ''; // If this is a shop module use the Shop module render count // Shop module creates a new class instance which resets the $_render_count value // ( see get_shop_html() method of ET_Builder_Module_Shop class in main-modules.php ) // so we use a static property to track its proper render count. if ( 'et_pb_shop' === $render_slug ) { $render_count = self::$_shop_render_count; self::$_shop_render_count++; } // Ensuring that module which uses another module's template (i.e. accordion item uses toggle's // component) has correct values for class properties where it makes a difference. This is covered on front-end, but it causes inheriting // module uses its template's value on render_as_builder_data(). if ( isset( $rendering_module, $rendering_module->type ) ) { $module_type = $rendering_module->type; $child_title_var = isset( $rendering_module->child_title_var ) ? $rendering_module->child_title_var : $child_title_var; $child_title_fallback_var = isset( $rendering_module->child_title_fallback_var ) ? $rendering_module->child_title_fallback_var : $child_title_fallback_var; $advanced_setting_title_text = isset( $rendering_module->advanced_setting_title_text ) ? $rendering_module->advanced_setting_title_text : $advanced_setting_title_text; } // Build object. $object = array( '_i' => $_i, '_order' => $_i, // TODO make address be _address, its conflicting with 'address' prop in map module... (not sure how though, they are in diffent places...). 'address' => $address, 'child_slug' => $this->child_slug, 'parent_slug' => $real_parent_type, 'vb_support' => $this->vb_support, 'parent_address' => $parent_address, 'shortcode_index' => $render_count, 'type' => $output_render_slug, 'theme_builder_suffix' => self::_get_theme_builder_order_class_suffix(), 'wp_editor_suffix' => self::_get_wp_editor_order_class_suffix(), 'component_path' => $component_path, 'main_css_element' => $this->main_css_element, 'attrs' => $attrs, 'content' => $prepared_content, 'is_module_child' => 'child' === $module_type, 'is_structure_element' => ! empty( $this->is_structure_element ), 'is_specialty_placeholder' => $is_specialty_placeholder, 'is_official_module' => $this->_is_official_module, 'child_title_var' => $child_title_var, 'child_title_fallback_var' => $child_title_fallback_var, 'advanced_setting_title_text' => $advanced_setting_title_text, 'wrapper_settings' => $this->get_wrapper_settings( $render_slug ), ); if ( ! empty( $unsynced_global_attributes ) ) { $object['unsyncedGlobalSettings'] = $unsynced_global_attributes[0]; } if ( $is_global_template ) { $object['libraryModuleScope'] = 'global'; } if ( isset( $this->module_items_config ) ) { $object['module_items_config'] = $this->module_items_config; } return $object; } /** * Determine if provided string contain line-breaks (`\r\n`) * * @param string $content String to check. * * @return bool */ public function has_line_breaks( $content ) { return count( preg_split( '/\r\n*\n/', trim( $content ), -1, PREG_SPLIT_NO_EMPTY ) ) > 1; } /** * Additional shortcode render callback. * * Intended to be overridden as needed. * * @param array $attrs Attributes. * @param null $content Shortcode content. * @param string $render_slug Shortcode tag. */ public function additional_render( $attrs, $content, $render_slug ) { if ( method_exists( $this, 'additional_shortcode_callback' ) ) { // Backwards compatibility. $this->__call( 'additional_shortcode_callback', array( $attrs, $content, $render_slug ) ); } } /** * Intended to be overridden as needed. */ public function predefined_child_modules(){} /** * Generate global setting name * * @param string $option_slug Option slug. * * @return string Global setting name in the following format: "module_slug-option_slug" */ public function get_global_setting_name( $option_slug ) { $global_setting_name = sprintf( '%1$s-%2$s', isset( $this->global_settings_slug ) ? $this->global_settings_slug : $this->slug, $option_slug ); return $global_setting_name; } /** * Add global default values to all fields, if they don't have defaults set * * @return void */ protected function _maybe_add_global_defaults() { // Don't add default settings to "child" modules. if ( 'child' === $this->type ) { return; } $fields = $this->fields_unprocessed; $ignored_keys = array( 'custom_margin', 'custom_padding', ); // Font color settings have custom_color set to true, so add them to ignored keys array. if ( isset( $this->advanced_fields['fonts'] ) && is_array( $this->advanced_fields['fonts'] ) ) { foreach ( $this->advanced_fields['fonts'] as $font_key => $font_settings ) { $ignored_keys[] = sprintf( '%1$s_text_color', $font_key ); } } $ignored_keys = apply_filters( 'et_builder_add_defaults_ignored_keys', $ignored_keys ); foreach ( $fields as $field_key => $field_settings ) { if ( in_array( $field_key, $ignored_keys, true ) ) { continue; } $global_setting_name = $this->get_global_setting_name( $field_key ); $global_setting_value = ET_Global_Settings::get_value( $global_setting_name ); if ( ! isset( $field_settings['default'] ) && $global_setting_value ) { $fields[ $field_key ]['default'] = $global_setting_value; // Mark this default as global so VB won't print it to replicate FE behaviour. $fields[ $field_key ]['is_global_default'] = true; } } $this->fields_unprocessed = $fields; } /** * Rebuild option template in $this->fields_unprocessed property into actual field if needed. * * @since 3.28 */ protected function _maybe_rebuild_option_template() { // Once module's option template inside $this->fields_unprocessed is rebuilt, the next // module's `_render()` won't need it. Thus, skip this to speed up performance. if ( in_array( $this->slug, self::$_has_rebuilt_option_template, true ) ) { return; } foreach ( $this->fields_unprocessed as $field_name => $field ) { // If first two field name matches template prefix, it is safely assume that current // unprocessed fields is reference to option template. if ( self::$option_template->is_enabled() && self::$option_template->is_option_template_field( $field_name ) ) { // Rebuild fields. $rebuilt_fields = self::$option_template->rebuild_field_template( $field_name ); // Assign rebuilt fields to module's unprocessed fields. foreach ( $rebuilt_fields as $rebuilt_field_name => $rebuilt_field ) { $this->fields_unprocessed[ $rebuilt_field_name ] = $rebuilt_field; } // Remove option template field. unset( $this->fields_unprocessed[ $field_name ] ); } } self::$_has_rebuilt_option_template[] = $this->slug; } /** * Adds Global Presets settings. * * @since 3.26 * * @param array $attrs The list of a module attributes. * @param string $render_slug The real slug from the shortcode. * * @return array */ protected function _maybe_add_global_presets_settings( $attrs, $render_slug ) { if ( ( et_fb_is_enabled() || et_builder_bfb_enabled() ) && ! self::is_theme_builder_layout() ) { return $attrs; } $render_slug = self::$global_presets_manager->maybe_convert_module_type( $render_slug, $attrs ); $module_preset_settings = self::$global_presets_manager->get_module_presets_settings( $render_slug, $attrs ); if ( is_array( $attrs ) ) { // We need a special handler for social media child items module background color setting // as it has different default background color for each network. if ( 'et_pb_social_media_follow_network' === $render_slug && ! empty( $attrs['social_network'] ) && ! empty( $attrs['background_color'] ) && ! empty( $module_preset_settings['background_color'] ) && $attrs['background_color'] !== $module_preset_settings['background_color'] ) { $background_color_definition = self::$_->array_get( $this->get_fields(), "social_network.options.{$attrs['social_network']}.data.color" ); if ( $background_color_definition === $attrs['background_color'] ) { // Unset the background color attrs if it was default based on selected network. unset( $attrs['background_color'] ); } } return array_merge( $module_preset_settings, $attrs ); } return $module_preset_settings; } /** * Add additional option fields. * * @since 3.23 Introduce form field options set. Also, add codes to generate responsive options * set with suffix automatically. It also supports mobile_options on composite, bg * field, and computed fields as well. */ protected function _add_additional_fields() { // Setup has_advanced_fields property to adjust advanced options visibility on // module that has no VB support to avoid sudden advanced options appearances. $this->has_advanced_fields = isset( $this->advanced_fields ); // Advanced options are added by default unless module explicitly disabled it. $this->advanced_fields = $this->has_advanced_fields ? $this->advanced_fields : array(); // Advanced options have to be array. if ( ! is_array( $this->advanced_fields ) ) { return; } // Add form field options set to modules that use form as main part. $this->_add_form_field_fields(); $this->_add_font_fields(); $this->_add_background_fields(); $this->_add_borders_fields(); $this->_add_button_fields(); $this->_add_image_icon_fields(); $this->_add_box_shadow_fields(); $this->_add_transforms_fields(); $this->_add_position_fields(); $this->_add_text_fields(); $this->_add_sizing_fields(); $this->_add_overflow_fields(); $this->_add_display_conditions_fields(); $this->_add_margin_padding_fields(); // Add filter fields to modules. $this->_add_filter_fields(); // Add divider fields to section modules. $this->_add_divider_fields(); // Add animation fields to all modules. $this->_add_animation_fields(); $this->_add_additional_transition_fields(); // Add text shadow fields to all modules. $this->_add_text_shadow_fields(); // Add link options to all modules. $this->_add_link_options_fields(); $this->_add_sticky_fields(); $this->_add_scroll_effects_fields(); if ( ! isset( $this->_additional_fields_options ) ) { return false; } $additional_options = $this->_additional_fields_options; $this->_additional_fields_options = array(); $is_column_module = in_array( $this->slug, array( 'et_pb_column', 'et_pb_column_inner' ), true ); // delete second level advanced options default values. if ( isset( $this->type ) && 'child' === $this->type && ! $is_column_module && apply_filters( 'et_pb_remove_child_module_defaults', true ) ) { foreach ( $additional_options as $name => $settings ) { if ( isset( $additional_options[ $name ]['default'] ) && ! isset( $additional_options[ $name ]['default_on_child'] ) ) { $additional_options[ $name ]['default'] = ''; } } } if ( function_exists( 'et_builder_definition_sort' ) ) { et_builder_definition_sort( $additional_options ); } $additional_options = et_pb_responsive_options()->create( $additional_options ); $this->_set_fields_unprocessed( $additional_options ); } /** * Set i18n used by font fields. * * @since 4.4.9 * * @return void */ protected function set_i18n_font() { // Cache results so that translation/escaping only happens once. $i18n =& self::$i18n; if ( ! isset( $i18n['font'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['font'] = array( 'letter_spacing' => array( 'label' => esc_html__( '%1$s Letter Spacing', 'et_builder' ), 'description' => esc_html__( 'Letter spacing adjusts the distance between each letter in the %1$s.', 'et_builder' ), ), 'size' => array( 'label' => esc_html__( '%1$s Text Size', 'et_builder' ), 'description' => esc_html__( 'Increase or decrease the size of the %1$s text.', 'et_builder' ), ), 'font' => array( 'label' => esc_html__( '%1$s Font', 'et_builder' ), 'description' => esc_html__( 'Choose a custom font to use for the %1$s. All Google web fonts are available, or you can upload your own custom font files.', 'et_builder' ), ), 'color' => array( 'label' => esc_html__( '%1$s Text Color', 'et_builder' ), 'description' => esc_html__( 'Pick a color to be used for the %1$s text.', 'et_builder' ), ), 'line_height' => array( 'label' => esc_html__( '%1$s Line Height', 'et_builder' ), 'description' => esc_html__( 'Line height adjusts the distance between each line of the %1$s text. This becomes noticeable if the %1$s is long and wraps onto multiple lines.', 'et_builder' ), ), 'text_align' => array( 'label' => esc_html__( '%1$s Text Alignment', 'et_builder' ), 'description' => esc_html__( 'Align the %1$s to the left, right, center or justify.', 'et_builder' ), ), ); // phpcs:enable } } /** * Add font option fields. * * @since 3.23 Introduce block elements sub options group. Add responsive settings for font set, * text color, text alignment, and text-shadow options set. */ protected function _add_font_fields() { // Font fields are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['fonts'] = self::$_->array_get( $this->advanced_fields, 'fonts', array( 'module' => array( 'label' => esc_html__( 'Module', 'custom_module' ), 'line_height' => array( 'default' => floatval( et_get_option( 'body_font_height', '1.7' ) ) . 'em', ), 'font_size' => array( 'default' => absint( et_get_option( 'body_font_size', '14' ) ) . 'px', ), ), ) ); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Font settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'fonts' ) ) ) { return; } $advanced_font_options = $this->advanced_fields['fonts']; $additional_options = array(); $defaults = array( 'all_caps' => 'off', ); $this->set_i18n_font(); $i18n =& self::$i18n; foreach ( $advanced_font_options as $option_name => $option_settings ) { $advanced_font_options[ $option_name ]['defaults'] = $defaults; // Continue if toggle is disabled. $toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle']; if ( $toggle_disabled ) { continue; } // Block Elements - 1. Add block elements settings to fonts. // We need to add link, ul, ol, and quote settings to fonts. We also need to convert // current font setting with block_elements to be sub toggle of P. if ( isset( $option_settings['block_elements'] ) && is_array( $option_settings['block_elements'] ) ) { // Ensure target font option is exist. if ( ! isset( $advanced_font_options[ $option_name ] ) ) { continue; } // Get current block elements selector. $block_default_selector = isset( $option_settings['css']['main'] ) ? $option_settings['css']['main'] : ''; $block_elements_css = isset( $option_settings['block_elements']['css'] ) ? $option_settings['block_elements']['css'] : array(); $block_elements_selector = isset( $block_elements_css['main'] ) ? $block_elements_css['main'] : $block_default_selector; // Ensure block elements selector exist and not empty. if ( empty( $block_elements_selector ) ) { // Don't forget to disable block elements, so no sub toggles will be added. $advanced_font_options[ $option_name ]['block_elements'] = false; continue; } // Block element default settings will be used by the following sub toggles. Special // for P sub toggle, we have to use existing font settings. $existing_text_settings = $advanced_font_options[ $option_name ]; $block_elements_default_settings = array( 'line_height' => array( 'default' => '1em', ), 'font_size' => array( 'default' => '14px', ), 'toggle_slug' => $option_name, ); // Check if current module is child or not. Then append default_on_child argument. $is_child_module = isset( $this->type ) && 'child' === $this->type; if ( $is_child_module ) { // Tell font options to set default_on_child. Use fields prefix to avoid confusion. $existing_text_settings['fields_default_on_child'] = true; $block_elements_default_settings['fields_default_on_child'] = true; } // a. Paragraph - Convert main text as sub toggle P. // Convert font settings with block_elements property to be sub toggle of P as // default. So, we can avoid migration because no settings changed after we added // block elements. We also need to set default line_height and font_size. $advanced_font_options[ $option_name ] = array_merge( $existing_text_settings, array( 'line_height' => array( 'default' => floatval( et_get_option( 'body_font_height', '1.7' ) ) . 'em', ), 'font_size' => array( 'default' => absint( et_get_option( 'body_font_size', '14' ) ) . 'px', ), 'sub_toggle' => 'p', ) ); // b. Link. $link_element_selector = isset( $block_elements_css['link'] ) ? $block_elements_css['link'] : "{$block_elements_selector} a"; $advanced_font_options[ "{$option_name}_link" ] = array_merge( $block_elements_default_settings, array( 'label' => et_builder_i18n( 'Link' ), 'css' => array( 'main' => $link_element_selector, ), 'font_size' => array( 'default' => absint( et_get_option( 'body_font_size', '14' ) ) . 'px', ), 'sub_toggle' => 'a', ) ); // c. Unordered List. $ul_element_selector = et_()->array_get( $block_elements_css, 'ul', "{$block_elements_selector} ul" ); $ul_li_element_selector = et_()->array_get( $block_elements_css, 'ul_li', "{$ul_element_selector} li" ); $ul_item_indent_selector = et_()->array_get( $block_elements_css, 'ul_item_indent', $ul_element_selector ); $advanced_font_options[ "{$option_name}_ul" ] = array_merge( $block_elements_default_settings, array( 'label' => esc_html__( 'Unordered List', 'et_builder' ), 'css' => array( 'main' => $ul_li_element_selector, 'item_indent' => $ul_item_indent_selector, ), 'sub_toggle' => 'ul', ) ); // d. Ordered List. $ol_element_selector = et_()->array_get( $block_elements_css, 'ol', "{$block_elements_selector} ol" ); $ol_li_element_selector = et_()->array_get( $block_elements_css, 'ol_li', "{$ol_element_selector} li" ); $ol_item_indent_selector = et_()->array_get( $block_elements_css, 'ol_item_indent', $ol_element_selector ); $advanced_font_options[ "{$option_name}_ol" ] = array_merge( $block_elements_default_settings, array( 'label' => esc_html__( 'Ordered List', 'et_builder' ), 'css' => array( 'main' => $ol_li_element_selector, 'item_indent' => $ol_item_indent_selector, ), 'sub_toggle' => 'ol', ) ); // e. Quote. $quote_element_selector = isset( $block_elements_css['quote'] ) ? $block_elements_css['quote'] : "{$block_elements_selector} blockquote"; $advanced_font_options[ "{$option_name}_quote" ] = array_merge( $block_elements_default_settings, array( 'label' => esc_html__( 'Blockquote', 'et_builder' ), 'css' => array( 'main' => $quote_element_selector, ), 'sub_toggle' => 'quote', ) ); } } $this->advanced_fields['fonts'] = $advanced_font_options; $font_options_count = 0; foreach ( $advanced_font_options as $option_name => $option_settings ) { $font_options_count++; $option_settings = wp_parse_args( $option_settings, array( 'label' => '', 'font_size' => array(), 'letter_spacing' => array(), 'font' => array(), 'text_align' => array(), ) ); $toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle']; $tab_slug = isset( $option_settings['tab_slug'] ) ? $option_settings['tab_slug'] : 'advanced'; $toggle_slug = ''; if ( ! $toggle_disabled ) { $toggle_slug = isset( $option_settings['toggle_slug'] ) ? $option_settings['toggle_slug'] : $option_name; $sub_toggle = isset( $option_settings['sub_toggle'] ) ? $option_settings['sub_toggle'] : ''; if ( ! isset( $option_settings['toggle_slug'] ) ) { $font_toggle = array( $option_name => array( 'title' => sprintf( '%1$s %2$s', esc_html( $option_settings['label'] ), et_builder_i18n( 'Text' ) ), 'priority' => 50 + $font_options_count, ), ); $this->_add_settings_modal_toggles( $tab_slug, $font_toggle ); } } if ( isset( $option_settings['header_level'] ) ) { $additional_options[ "{$option_name}_level" ] = wp_parse_args( $option_settings['header_level'], array( 'label' => sprintf( esc_html__( '%1$s Heading Level', 'et_builder' ), $option_settings['label'] ), 'description' => sprintf( esc_html__( 'Module %1$s are created using HTML headings. You can change the heading level for this module by choosing anything from H1 through H6. Higher heading levels are smaller and less significant.', 'et_builder' ), $option_settings['label'] ), 'type' => 'multiple_buttons', 'option_category' => 'font_option', 'options' => array( 'h1' => array( 'title' => 'H1', 'icon' => 'text-h1', ), 'h2' => array( 'title' => 'H2', 'icon' => 'text-h2', ), 'h3' => array( 'title' => 'H3', 'icon' => 'text-h3', ), 'h4' => array( 'title' => 'H4', 'icon' => 'text-h4', ), 'h5' => array( 'title' => 'H5', 'icon' => 'text-h5', ), 'h6' => array( 'title' => 'H6', 'icon' => 'text-h6', ), ), 'default' => isset( $option_settings['header_level']['default'] ) ? $option_settings['header_level']['default'] : 'h2', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'advanced_fields' => true, ) ); if ( isset( $option_settings['header_level']['computed_affects'] ) ) { $additional_options[ "{$option_name}_level" ]['computed_affects'] = $option_settings['header_level']['computed_affects']; } } if ( ! isset( $option_settings['hide_font'] ) || ! $option_settings['hide_font'] ) { $additional_options[ "{$option_name}_font" ] = wp_parse_args( $option_settings['font'], array( 'label' => sprintf( $i18n['font']['font']['label'], $option_settings['label'] ), 'description' => sprintf( $i18n['font']['font']['description'], $option_settings['label'] ), 'type' => 'font', 'group_label' => et_core_esc_previously( $option_settings['label'] ), 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'mobile_options' => true, ) ); // add reference to the obsolete "all caps" option if needed. if ( isset( $option_settings['use_all_caps'] ) && $option_settings['use_all_caps'] ) { $additional_options[ "{$option_name}_font" ]['attributes'] = array( 'data-old-option-ref' => "{$option_name}_all_caps" ); } // set the depends_show_if parameter if needed. if ( isset( $option_settings['depends_show_if'] ) ) { $additional_options[ "{$option_name}_font" ]['depends_show_if'] = $option_settings['depends_show_if']; } // Set default font settings. if ( ! empty( $option_settings['font']['default'] ) ) { $additional_options[ "{$option_name}_font" ]['default'] = $option_settings['font']['default']; } // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_font" ]['default_on_child'] = true; } } if ( ! isset( $option_settings['hide_text_align'] ) || ! $option_settings['hide_text_align'] ) { $additional_options[ "{$option_name}_text_align" ] = wp_parse_args( $option_settings['text_align'], array( 'label' => sprintf( $i18n['font']['text_align']['label'], $option_settings['label'] ), 'description' => sprintf( $i18n['font']['text_align']['description'], $option_settings['label'] ), 'type' => 'text_align', 'option_category' => 'layout', 'options' => et_builder_get_text_orientation_options( array( 'justified' ), array( 'justify' => 'Justified' ) ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'advanced_fields' => true, 'mobile_options' => true, ) ); // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_text_align" ]['default_on_child'] = true; } } if ( ! isset( $option_settings['hide_text_color'] ) || ! $option_settings['hide_text_color'] ) { $label = et_()->array_get( $option_settings, 'text_color.label', false ) ? $option_settings['text_color']['label'] : sprintf( $i18n['font']['color']['label'], $option_settings['label'] ); $additional_options[ "{$option_name}_text_color" ] = wp_parse_args( self::$_->array_get( $option_settings, 'text_color', array() ), array( 'label' => $label, 'description' => sprintf( $i18n['font']['color']['description'], $option_settings['label'] ), 'type' => 'color-alpha', 'option_category' => 'font_option', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ) ); // add reference to the obsolete color option if needed. if ( self::$_->array_get( $option_settings, 'text_color.old_option_ref' ) ) { $additional_options[ "{$option_name}_text_color" ]['attributes'] = array( 'data-old-option-ref' => "{$option_settings['text_color']['old_option_ref']}" ); } // set default value if defined. if ( self::$_->array_get( $option_settings, 'text_color.default' ) ) { $additional_options[ "{$option_name}_text_color" ]['default'] = $option_settings['text_color']['default']; } // set the depends_show_if parameter if needed. if ( isset( $option_settings['depends_show_if'] ) ) { $additional_options[ "{$option_name}_text_color" ]['depends_show_if'] = $option_settings['depends_show_if']; } // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_text_color" ]['default_on_child'] = true; } } if ( ! isset( $option_settings['hide_font_size'] ) || ! $option_settings['hide_font_size'] ) { $additional_options[ "{$option_name}_font_size" ] = wp_parse_args( $option_settings['font_size'], array( 'label' => sprintf( $i18n['font']['size']['label'], $option_settings['label'] ), 'description' => sprintf( $i18n['font']['size']['description'], $option_settings['label'] ), 'type' => 'range', 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default_unit' => 'px', 'mobile_options' => true, 'sticky' => true, 'range_settings' => array( 'min' => '1', 'max' => '100', 'step' => '1', ), 'hover' => 'tabs', ) ); // set the depends_show_if parameter if needed. if ( isset( $option_settings['depends_show_if'] ) ) { $additional_options[ "{$option_name}_font_size" ]['depends_show_if'] = $option_settings['depends_show_if']; } if ( isset( $option_settings['header_level'] ) ) { $header_level_default = isset( $option_settings['header_level']['default'] ) ? $option_settings['header_level']['default'] : 'h2'; $additional_options[ "{$option_name}_font_size" ]['default_value_depends'] = "{$option_name}_level"; $additional_options[ "{$option_name}_font_size" ]['default_values_mapping'] = array( 'h1' => '30px', 'h2' => '26px', 'h3' => '22px', 'h4' => '18px', 'h5' => '16px', 'h6' => '14px', ); // remove default font-size for default header level to use option default. unset( $additional_options[ "{$option_name}_font_size" ]['default_values_mapping'][ $header_level_default ] ); } // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_font_size" ]['default_on_child'] = true; } } if ( ! isset( $option_settings['hide_letter_spacing'] ) || ! $option_settings['hide_letter_spacing'] ) { $additional_options[ "{$option_name}_letter_spacing" ] = wp_parse_args( $option_settings['letter_spacing'], array( 'label' => sprintf( $i18n['font']['letter_spacing']['label'], $option_settings['label'] ), 'description' => sprintf( $i18n['font']['letter_spacing']['description'], $option_settings['label'] ), 'type' => 'range', 'mobile_options' => true, 'sticky' => true, 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'default' => '0px', 'default_unit' => 'px', 'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'range_settings' => array( 'min' => '0', 'max' => '100', 'step' => '1', ), 'hover' => 'tabs', ) ); // set the depends_show_if parameter if needed. if ( isset( $option_settings['depends_show_if'] ) ) { $additional_options[ "{$option_name}_letter_spacing" ]['depends_show_if'] = $option_settings['depends_show_if']; } // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_letter_spacing" ]['default_on_child'] = true; } } if ( ! isset( $option_settings['hide_line_height'] ) || ! $option_settings['hide_line_height'] ) { $default_option_line_height = array( 'label' => sprintf( $i18n['font']['line_height']['label'], $option_settings['label'] ), 'description' => sprintf( $i18n['font']['line_height']['description'], $option_settings['label'] ), 'type' => 'range', 'mobile_options' => true, 'sticky' => true, 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'default_unit' => 'em', 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'range_settings' => array( 'min' => '1', 'max' => '3', 'step' => '0.1', ), 'hover' => 'tabs', ); if ( isset( $option_settings['line_height'] ) ) { $additional_options[ "{$option_name}_line_height" ] = wp_parse_args( $option_settings['line_height'], $default_option_line_height ); } else { $additional_options[ "{$option_name}_line_height" ] = $default_option_line_height; } // set the depends_show_if parameter if needed. if ( isset( $option_settings['depends_show_if'] ) ) { $additional_options[ "{$option_name}_line_height" ]['depends_show_if'] = $option_settings['depends_show_if']; } // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_line_height" ]['default_on_child'] = true; } } // Add text-shadow to font options. if ( ! isset( $option_settings['hide_text_shadow'] ) || ! $option_settings['hide_text_shadow'] ) { $option = $this->text_shadow->get_fields( wp_parse_args( self::$_->array_get( $option_settings, 'text_shadow', array() ), array( // Don't use an additional label for 'text' or else we'll end up with 'Text Text Shadow....'. 'label' => 'text' === $option_name ? '' : $option_settings['label'], 'prefix' => $option_name, 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, 'mobile_options' => true, ) ) ); $additional_options = array_merge( $additional_options, $option ); }; // The below option is obsolete. This code is for backward compatibility. if ( isset( $option_settings['use_all_caps'] ) && $option_settings['use_all_caps'] ) { $additional_options[ "{$option_name}_all_caps" ] = array( 'type' => 'hidden', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'sub_toggle' => $sub_toggle, ); } // Set options priority if it's exist and not empty. Mostly used to push a setting to // the top of font settings. For example: reorder Text Color to the top of font settings. if ( isset( $option_settings['options_priority'] ) && is_array( $option_settings['options_priority'] ) ) { $options_priority = ! empty( $option_settings['options_priority'] ) ? $option_settings['options_priority'] : array(); $temporary_options = array(); foreach ( $options_priority as $option_key => $option_priority ) { // Ensure the target is exist before adding priority. if ( isset( $additional_options[ $option_key ] ) ) { $additional_options[ $option_key ]['priority'] = $option_priority; // Keep it on temporary options and remove it from additional options. // It's needed because priority doesn't work for font settings with no // sub toggle. Basically, we will reorder the array element of font // settings here to make it works. $temporary_options[ $option_key ] = $additional_options[ $option_key ]; unset( $additional_options[ $option_key ] ); } } // Merge temporary options with additional options. $additional_options = array_merge( $temporary_options, $additional_options ); } if ( isset( $option_settings['block_elements'] ) && is_array( $option_settings['block_elements'] ) ) { // Block Elements - 2. Set sub toggles for block elements. // Add p, a, ul, ol, and quote as sub toggle of current font settings. We also // need to add tabbed_subtoggles property there. $block_elements = array( 'p' => array( 'name' => 'P', 'icon' => 'text-left', ), 'a' => array( 'name' => 'A', 'icon' => 'text-link', ), 'ul' => array( 'name' => 'UL', 'icon' => 'list', ), 'ol' => array( 'name' => 'OL', 'icon' => 'numbered-list', ), 'quote' => array( 'name' => 'QUOTE', 'icon' => 'text-quote', ), ); // Tabbed toggle & BB icons support status. $tabbed_subtoggles = isset( $option_settings['block_elements']['tabbed_subtoggles'] ) ? $option_settings['block_elements']['tabbed_subtoggles'] : false; $bb_icons_support = isset( $option_settings['block_elements']['bb_icons_support'] ) ? $option_settings['block_elements']['bb_icons_support'] : false; $this->_add_settings_modal_sub_toggles( $tab_slug, $toggle_slug, $block_elements, $tabbed_subtoggles, $bb_icons_support ); // Block Elements - 3. Set additional options for ul/ol/qoute sub toggles. // a. UL - Type, Position, and Indent. $additional_options[ "{$option_name}_ul_type" ] = array( 'label' => esc_html__( 'Unordered List Style Type', 'et_builder' ), 'description' => esc_html__( 'This setting adjusts the shape of the bullet point that begins each list item.', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'disc' => et_builder_i18n( 'Disc' ), 'circle' => et_builder_i18n( 'Circle' ), 'square' => et_builder_i18n( 'Square' ), 'none' => et_builder_i18n( 'None' ), ), 'priority' => 80, 'default' => 'disc', 'default_on_front' => '', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ul', 'mobile_options' => true, ); $additional_options[ "{$option_name}_ul_position" ] = array( 'label' => esc_html__( 'Unordered List Style Position', 'et_builder' ), 'description' => esc_html__( 'The bullet point that begins each list item can be placed either inside or outside the parent list wrapper. Placing list items inside will indent them further within the list.', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'outside' => et_builder_i18n( 'Outside' ), 'inside' => et_builder_i18n( 'Inside' ), ), 'priority' => 85, 'default' => 'outside', 'default_on_front' => '', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ul', 'mobile_options' => true, ); $additional_options[ "{$option_name}_ul_item_indent" ] = array( 'label' => esc_html__( 'Unordered List Item Indent', 'et_builder' ), 'description' => esc_html__( 'Increasing indentation will push list items further towards the center of the text content, giving the list more visible separation from the the rest of the text.', 'et_builder' ), 'type' => 'range', 'option_category' => 'configuration', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ul', 'priority' => 90, 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default' => '0px', 'default_unit' => 'px', 'default_on_front' => '', 'range_settings' => array( 'min' => '0', 'max' => '100', 'step' => '1', ), 'mobile_options' => true, ); // b. OL - Type, Position, and Indent. $additional_options[ "{$option_name}_ol_type" ] = array( 'label' => esc_html__( 'Ordered List Style Type', 'et_builder' ), 'description' => esc_html__( 'Here you can choose which types of characters are used to distinguish between each item in the ordered list.', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'decimal' => 'decimal', 'armenian' => 'armenian', 'cjk-ideographic' => 'cjk-ideographic', 'decimal-leading-zero' => 'decimal-leading-zero', 'georgian' => 'georgian', 'hebrew' => 'hebrew', 'hiragana' => 'hiragana', 'hiragana-iroha' => 'hiragana-iroha', 'katakana' => 'katakana', 'katakana-iroha' => 'katakana-iroha', 'lower-alpha' => 'lower-alpha', 'lower-greek' => 'lower-greek', 'lower-latin' => 'lower-latin', 'lower-roman' => 'lower-roman', 'upper-alpha' => 'upper-alpha', 'upper-greek' => 'upper-greek', 'upper-latin' => 'upper-latin', 'upper-roman' => 'upper-roman', 'none' => 'none', ), 'priority' => 80, 'default' => 'decimal', 'default_on_front' => '', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ol', 'mobile_options' => true, ); $additional_options[ "{$option_name}_ol_position" ] = array( 'label' => esc_html__( 'Ordered List Style Position', 'et_builder' ), 'description' => esc_html__( 'The characters that begins each list item can be placed either inside or outside the parent list wrapper. Placing list items inside will indent them further within the list.', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'inside' => et_builder_i18n( 'Inside' ), 'outside' => et_builder_i18n( 'Outside' ), ), 'priority' => 85, 'default' => 'inside', 'default_on_front' => '', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ol', 'mobile_options' => true, ); $additional_options[ "{$option_name}_ol_item_indent" ] = array( 'label' => esc_html__( 'Ordered List Item Indent', 'et_builder' ), 'description' => esc_html__( 'Increasing indentation will push list items further towards the center of the text content, giving the list more visible separation from the the rest of the text.', 'et_builder' ), 'type' => 'range', 'option_category' => 'configuration', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'ol', 'priority' => 90, 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default' => '0px', 'default_unit' => 'px', 'default_on_front' => '', 'range_settings' => array( 'min' => '0', 'max' => '100', 'step' => '1', ), 'mobile_options' => true, ); // c. Quote - Border Weight and Border Color. $additional_options[ "{$option_name}_quote_border_weight" ] = array( 'label' => esc_html__( 'Blockquote Border Weight', 'et_builder' ), 'description' => esc_html__( 'Block quotes are given a border to separate them from normal text. You can increase or decrease the size of that border using this setting.', 'et_builder' ), 'type' => 'range', 'option_category' => 'configuration', 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'quote', 'priority' => 85, 'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default' => '5px', 'default_unit' => 'px', 'default_on_front' => '', 'range_settings' => array( 'min' => '0', 'max' => '100', 'step' => '1', ), 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ); $additional_options[ "{$option_name}_quote_border_color" ] = array( 'label' => esc_html__( 'Blockquote Border Color', 'et_builder' ), 'description' => esc_html__( 'Block quotes are given a border to separate them from normal text. Pick a color to use for that border.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'configuration', 'custom_color' => true, 'tab_slug' => 'advanced', 'toggle_slug' => $option_name, 'sub_toggle' => 'quote', 'field_template' => 'color', 'priority' => 90, 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ); // Set default on child font settings. if ( ! empty( $option_settings['fields_default_on_child'] ) ) { $additional_options[ "{$option_name}_ul_type" ]['default_on_child'] = true; $additional_options[ "{$option_name}_ul_position" ]['default_on_child'] = true; $additional_options[ "{$option_name}_ul_item_indent" ]['default_on_child'] = true; $additional_options[ "{$option_name}_ol_type" ]['default_on_child'] = true; $additional_options[ "{$option_name}_ol_position" ]['default_on_child'] = true; $additional_options[ "{$option_name}_ol_item_indent" ]['default_on_child'] = true; $additional_options[ "{$option_name}_quote_border_weight" ]['default_on_child'] = true; $additional_options[ "{$option_name}_quote_border_color" ]['default_on_child'] = true; } } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add background option fields. * * @since 3.23 Add responsive settings for background settings. */ protected function _add_background_fields() { // Background fields are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['background'] = isset( $this->advanced_fields['background'] ) ? $this->advanced_fields['background'] : array(); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Bail if Background settings isn't set, or, is not array. // In Extra, there are module which set advanced fields, but without 'background'. if ( ! isset( $this->advanced_fields['background'] ) || ! is_array( $this->advanced_fields['background'] ) ) { return; } $toggle_disabled = isset( $this->advanced_fields['background']['settings']['disable_toggle'] ) ? $this->advanced_fields['background']['settings']['disable_toggle'] : false; $tab_slug = isset( $this->advanced_fields['background']['settings']['tab_slug'] ) ? $this->advanced_fields['background']['settings']['tab_slug'] : 'general'; $toggle_slug = ''; if ( ! $toggle_disabled ) { $toggle_slug = isset( $this->advanced_fields['background']['settings']['toggle_slug'] ) ? $this->advanced_fields['background']['settings']['toggle_slug'] : 'background'; $background_toggle = array( 'background' => array( 'title' => et_builder_i18n( 'Background' ), 'priority' => 80, ), ); $this->_add_settings_modal_toggles( $tab_slug, $background_toggle ); } $background_field_name = 'background'; // Possible values for use_* attributes: true, false, or 'fields_only'. $defaults = array( 'has_background_color_toggle' => false, 'use_background_color' => true, 'use_background_color_gradient' => true, 'use_background_image' => true, 'use_background_image_parallax' => true, 'use_background_video' => true, 'use_background_color_reset' => true, 'use_background_pattern' => true, 'use_background_mask' => true, ); $this->advanced_fields['background'] = wp_parse_args( $this->advanced_fields['background'], $defaults ); $additional_options = array(); if ( $this->advanced_fields['background']['use_background_color'] ) { $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'color', $tab_slug, $toggle_slug, null ) ); } // Use background color toggle was added on pre color-alpha era. Added for backward // compatibility. This option's output is printed manually on render(). if ( $this->advanced_fields['background']['has_background_color_toggle'] ) { $additional_options['use_background_color'] = self::background_field_template( 'use_color', array( 'label' => esc_html__( 'Use Background Color', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'color_option', 'options' => array( 'on' => et_builder_i18n( 'Yes' ), 'off' => et_builder_i18n( 'No' ), ), 'affects' => array( 'background_color', ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'description' => esc_html__( 'Here you can choose whether background color setting above should be used or not.', 'et_builder' ), 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); } if ( $this->advanced_fields['background']['use_background_color_gradient'] ) { $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'gradient', $tab_slug, $toggle_slug, null ) ); } if ( $this->advanced_fields['background']['use_background_image'] ) { $background_options_to_skip = ! $this->advanced_fields['background']['use_background_image_parallax'] ? array( 'parallax' ) : array(); $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'image', $tab_slug, $toggle_slug, null, $background_options_to_skip ) ); } if ( $this->advanced_fields['background']['use_background_video'] ) { $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'video', $tab_slug, $toggle_slug, null ) ); } // QF_BACKGROUND_MASKS. if ( $this->advanced_fields['background']['use_background_pattern'] ) { $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'pattern', $tab_slug, $toggle_slug ) ); } if ( $this->advanced_fields['background']['use_background_mask'] ) { $additional_options = array_merge( $additional_options, $this->generate_background_options( 'background', 'mask', $tab_slug, $toggle_slug ) ); } // Allow module to configure specific options. $background_options = isset( $this->advanced_fields['background']['options'] ) ? $this->advanced_fields['background']['options'] : false; if ( $background_options ) { foreach ( $background_options as $option_slug => $options ) { if ( ! is_array( $options ) ) { continue; } foreach ( $options as $option_name => $option_value ) { $additional_options[ $option_slug ][ $option_name ] = $option_value; } } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add text option fields. * * @since 3.23 Add responsive settings for text orientation and layout settings. */ protected function _add_text_fields() { // Text fields are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['text'] = self::$_->array_get( $this->advanced_fields, 'text', array() ); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Text settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'text' ) ) ) { return; } $text_settings = $this->advanced_fields['text']; $tab_slug = isset( $text_settings['tab_slug'] ) ? $text_settings['tab_slug'] : 'advanced'; $toggle_slug = isset( $text_settings['toggle_slug'] ) ? $text_settings['toggle_slug'] : 'text'; $sub_toggle = isset( $text_settings['sub_toggle'] ) ? $text_settings['sub_toggle'] : ''; $orientation_exclude_options = isset( $text_settings['text_orientation'] ) && isset( $text_settings['text_orientation']['exclude_options'] ) ? $text_settings['text_orientation']['exclude_options'] : array(); // Make sure we can exclude text_orientation from Advanced/Text. $setting_defaults = array( 'use_text_orientation' => true, 'use_background_layout' => false, ); $text_settings = wp_parse_args( $text_settings, $setting_defaults ); $this->_add_settings_modal_toggles( $tab_slug, array( $toggle_slug => array( 'title' => et_builder_i18n( 'Text' ), 'priority' => 49, ), ) ); $additional_options = array(); if ( $text_settings['use_text_orientation'] ) { $default_on_front = self::$_->array_get( $text_settings, 'options.text_orientation.default_on_front', '' ); $additional_options = array( 'text_orientation' => wp_parse_args( self::$_->array_get( $text_settings, 'options.text_orientation', array() ), array( 'label' => esc_html__( 'Text Alignment', 'et_builder' ), 'type' => 'text_align', 'option_category' => 'layout', 'options' => et_builder_get_text_orientation_options( $orientation_exclude_options ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'description' => esc_html__( 'This controls how your text is aligned within the module.', 'et_builder' ), 'advanced_fields' => true, 'default' => self::$_->array_get( $text_settings, 'options.text_orientation.default', $default_on_front ), 'mobile_options' => true, ) ), ); if ( '' !== $sub_toggle ) { $additional_options['text_orientation']['sub_toggle'] = $sub_toggle; } } // Background layout works by setting text to light/dark color. This was added before text // color has its own colorpicker as a simple mechanism for picking color. // New module should not use this option. This is kept for backward compatibility. if ( $text_settings['use_background_layout'] ) { $additional_options['background_layout'] = array( 'label' => esc_html__( 'Text Color', 'et_builder' ), 'type' => 'select', 'option_category' => 'color_option', 'options' => array( 'dark' => et_builder_i18n( 'Light' ), 'light' => et_builder_i18n( 'Dark' ), ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'description' => esc_html__( 'Here you can choose whether your text should be light or dark. If you are working with a dark background, then your text should be light. If your background is light, then your text should be set to dark.', 'et_builder' ), 'mobile_options' => true, 'sticky' => true, ); if ( '' !== $sub_toggle ) { $additional_options['background_layout']['sub_toggle'] = $sub_toggle; } } // Allow module to configure specific options. if ( isset( $text_settings['options'] ) && is_array( $text_settings['options'] ) ) { foreach ( $text_settings['options'] as $option_slug => $options ) { if ( ! is_array( $options ) ) { continue; } foreach ( $options as $option_name => $option_value ) { if ( isset( $additional_options[ $option_slug ] ) ) { $additional_options[ $option_slug ][ $option_name ] = $option_value; } } } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add Border & Border Radius fields to each module. Default borders option are added on with * Borders fields group on Design tab. However, module can add more borders field by adding * more settings on $this->advanced_fields['borders'] * * @since 3.1 * * {@internal * border options are initially defined via _add_additional_border_fields() method and adding * more border options require overwriting it on module's class. This is repetitive so * the fields registration mechanics were simplified mimicing advanced fonts field mechanism.} */ protected function _add_borders_fields() { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } // Get borders settings. Fallback to default if needed. Borders are added to all modules by default // unless the module explicitly disabled it // Backward compatibility. Use `border` settings as default if exist. $legacy_border = self::$_->array_get( $this->advanced_fields, 'border', array() ); $borders_fields = self::$_->array_get( $this->advanced_fields, 'borders', array( 'default' => $legacy_border, ) ); // Borders settings have to be array. if ( ! is_array( $borders_fields ) ) { return; } $i18n =& self::$i18n; if ( ! isset( $i18n['border'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['border'] = array( 'title' => esc_html__( 'Border', 'et_builder' ), ); // phpcs:enable } // Loop border settings, enable multiple border fields declaration in one place. foreach ( $borders_fields as $border_fields_name => $border_fields ) { // Enable module to disable border options by setting it to false. if ( false === $border_fields ) { continue; } // Make sure that border fields has minimum attribute required. $border_fields_defaults = array( 'tab_slug' => 'advanced', 'toggle_slug' => 'border', ); $border_fields = wp_parse_args( $border_fields, $border_fields_defaults ); // Check for default border options. $is_default_border_options = 'default' === $border_fields_name; if ( $is_default_border_options ) { // Default border fields doesn't have toggle for itself, thus register new toggle. $this->_add_settings_modal_toggles( $border_fields['tab_slug'], array( $border_fields['toggle_slug'] => array( 'title' => $i18n['border']['title'], 'priority' => 95, ), ) ); } // Add suffix to border fields settings. $suffix = $is_default_border_options ? '' : "_{$border_fields_name}"; $border_fields['suffix'] = $suffix; // Assign CSS setting to advanced options. if ( isset( $border_fields['css'] ) ) { $this->advanced_fields[ "border{$suffix}" ]['css'] = $border_fields['css']; } // Add border fields to advanced_fields. Each border fields (style + radii) has its own attribute // registered on $this->advanced_fields. self::$_->array_set( $this->advanced_fields, "border{$suffix}", $border_fields ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'Border' )->get_fields( $border_fields ) ); // Add module defined fields that needs to be added after existing border options. if ( isset( $border_fields['fields_after'] ) ) { $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $border_fields['fields_after'] ); } // Loop radii and styles and add fields to advanced_fields. foreach ( array( 'border_radii', 'border_styles' ) as $border_key ) { $border_key_name = $border_key . $suffix; if ( isset( $this->advanced_fields[ "border{$suffix}" ][ $border_key_name ] ) ) { // Backward compatibility. Properly handle existing 3rd party module that // directly defines border via direct $this->advanced_fields["border{$suffix}"]. $this->advanced_fields[ "border{$suffix}" ][ $border_key_name ] = array_merge( $this->advanced_fields[ "border{$suffix}" ][ $border_key_name ], $this->_additional_fields_options[ $border_key_name ] ); $message = "You're Doing It Wrong! You shouldn't define border settings in 'advanced_fields' directly. All the Border settings should be defined via provided API"; et_debug( $message ); } // Border used to rely on $this->advanced_fields complete configuration for // rendering. Since option template update, border style rendering fetches // border setting based on rebuilt fields on demand for performance reason. } } if ( method_exists( $this, '_add_additional_border_fields' ) ) { // Backwards compatibility should go after all the fields added to emulate behavior of old version. $this->_add_additional_border_fields(); $message = "You're Doing It Wrong! '_add_additional_border_fields' is deprecated. All the Border settings should be defined via provided API"; et_debug( $message ); } } /** * Add transform fields. */ protected function _add_transforms_fields() { $i18n =& self::$i18n; if ( ! isset( $i18n['transforms'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['transforms'] = array( 'title' => esc_html__( 'Transform', 'et_builder' ), ); // phpcs:enable } $this->advanced_fields['transform'] = self::$_->array_get( $this->advanced_fields, 'transform', array() ); // Transforms Disabled. if ( false === $this->advanced_fields['transform'] ) { return; } // Transforms settings have to be array. if ( ! is_array( $this->advanced_fields['transform'] ) ) { return; } $this->settings_modal_toggles['advanced']['toggles']['transform'] = array( 'title' => $i18n['transforms']['title'], 'priority' => 109, ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, /** Get transform fields. @see ET_Builder_Module_Field_Transform::get_fields() */ ET_Builder_Module_Fields_Factory::get( 'Transform' )->get_fields() ); } /** * Add sizing option fields. */ protected function _add_sizing_fields() { // Maybe someone did overwrite this function. $this->_add_max_width_fields(); $additional_options = array(); $features = array( 'max_width' => 'MaxWidth', 'height' => 'Height', ); foreach ( $features as $name => $fields_name ) { if ( $this->has_vb_support() ) { $this->advanced_fields[ $name ] = self::$_->array_get( $this->advanced_fields, $name, array() ); } elseif ( ! $this->has_advanced_fields ) { return; } if ( ! is_array( self::$_->array_get( $this->advanced_fields, $name ) ) ) { return; } $extra = self::$_->array_get( $this->advanced_fields[ $name ], 'extra', array() ); $fields = array_merge( array( '' => $this->advanced_fields[ $name ] ), $extra ); foreach ( $fields as $prefix => $settings ) { $prefix = et_builder_add_prefix( $prefix, '' ); $tab_slug = isset( $settings['tab_slug'] ) ? $settings['tab_slug'] : 'advanced'; $toggle_slug = isset( $settings['toggle_slug'] ) ? $settings['toggle_slug'] : 'width'; $toggle_title = isset( $settings['toggle_title'] ) ? $settings['toggle_title'] : et_builder_i18n( 'Sizing' ); $toggle_priority = isset( $settings['toggle_priority'] ) ? $settings['toggle_priority'] : 80; $settings['prefix'] = $prefix; $this->_add_settings_modal_toggles( $tab_slug, array( $toggle_slug => array( 'title' => $toggle_title, 'priority' => $toggle_priority, ), ) ); $additional_options = array_merge( $additional_options, ET_Builder_Module_Fields_Factory::get( $fields_name )->get_fields( $settings ) ); // Allow module to configure specific options. if ( isset( $settings['options'] ) && is_array( $settings['options'] ) ) { foreach ( $settings['options'] as $option_slug => $options ) { if ( ! is_array( $options ) ) { continue; } foreach ( $options as $option_name => $option_value ) { $additional_options[ $prefix . $option_slug ][ $option_name ] = $option_value; } } } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } } // phpcs:ignore Generic.Commenting.DocComment -- Deprecated function. /** * @deprecated */ public function _add_max_width_fields() { } /** * Add overflow option fields. */ protected function _add_overflow_fields() { if ( is_array( self::$_->array_get( $this->advanced_fields, 'overflow', array() ) ) ) { $default_overflow = self::$_->array_get( $this->advanced_fields, 'overflow.default', ET_Builder_Module_Helper_Overflow::OVERFLOW_DEFAULT ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'Overflow' )->get_fields( array( 'default' => $default_overflow ) ) ); } } /** * Add display conditions option fields. */ protected function _add_display_conditions_fields() { /** * Filters "Display Conditions" option visibility to determine whether to add its field to the Visual Builder or not. * * Useful for displaying/hiding the option on the Visual Builder. * * @since 4.13.1 * * @param boolean True to make the option visible on VB, False to make it hidden. */ $is_display_conditions_enabled = apply_filters( 'et_is_display_conditions_option_visible', true ); if ( ! $is_display_conditions_enabled ) { return; } if ( is_array( et_()->array_get( $this->advanced_fields, 'display_conditions', array() ) ) ) { $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'DisplayConditions' )->get_fields() ); } } /** * Return Scroll effects option fields. * * @return array */ public function get_scroll_effects_options() { // cache translations. $prefix = 'scroll_'; $i18n =& self::$i18n; if ( ! isset( $i18n['motion'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['motion'] = array( 'vertical' => array( 'label' => __( 'Vertical Motion', 'et_builder' ), 'description' => __( 'Give this element vertical motion so that is moves faster or slower than the elements around it as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Offset', 'et_builder' ), 'middleValueTitle' => __( 'Mid Offset', 'et_builder' ), 'endValueTitle' => __( 'Ending Offset', 'et_builder' ), ), 'horizontal' => array( 'label' => __( 'Horizontal Motion', 'et_builder' ), 'description' => __( 'Give this element horizontal motion so that it slides left or right as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Offset', 'et_builder' ), 'middleValueTitle' => __( 'Mid Offset', 'et_builder' ), 'endValueTitle' => __( 'Ending Offset', 'et_builder' ), ), 'fade' => array( 'label' => __( 'Fading In and Out', 'et_builder' ), 'description' => __( 'Give this element an opacity effect so that it fades in and out as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Opacity', 'et_builder' ), 'middleValueTitle' => __( 'Mid Opacity', 'et_builder' ), 'endValueTitle' => __( 'Ending Opacity', 'et_builder' ), ), 'scaling' => array( 'label' => __( 'Scaling Up and Down', 'et_builder' ), 'description' => __( 'Give this element a scale effect so that it grows and shrinks as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Scale', 'et_builder' ), 'middleValueTitle' => __( 'Mid Scale', 'et_builder' ), 'endValueTitle' => __( 'Ending Scale', 'et_builder' ), ), 'rotating' => array( 'label' => __( 'Rotating', 'et_builder' ), 'description' => __( 'Give this element rotating motion so that it spins as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Rotation', 'et_builder' ), 'middleValueTitle' => __( 'Mid Rotation', 'et_builder' ), 'endValueTitle' => __( 'Ending Rotation', 'et_builder' ), ), 'blur' => array( 'label' => __( 'Blur', 'et_builder' ), 'description' => __( 'Give this element a blur effect so that it moves in and out of focus as the viewer scrolls through the page.', 'et_builder' ), 'startValueTitle' => __( 'Starting Blur', 'et_builder' ), 'middleValueTitle' => __( 'Mid Blur', 'et_builder' ), 'endValueTitle' => __( 'Ending Blur', 'et_builder' ), ), ); // phpcs:enable } return array( "{$prefix}vertical_motion" => array( 'label' => $i18n['motion']['vertical']['label'], 'description' => $i18n['motion']['vertical']['description'], 'startValueTitle' => $i18n['motion']['vertical']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['vertical']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['vertical']['endValueTitle'], 'icon' => 'vertical-motion', 'resolver' => 'translateY', 'default' => '0|50|50|100|4|0|-4', ), "{$prefix}horizontal_motion" => array( 'label' => $i18n['motion']['horizontal']['label'], 'description' => $i18n['motion']['horizontal']['description'], 'startValueTitle' => $i18n['motion']['horizontal']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['horizontal']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['horizontal']['endValueTitle'], 'icon' => 'horizontal-motion', 'resolver' => 'translateX', 'default' => '0|50|50|100|4|0|-4', ), "{$prefix}fade" => array( 'label' => $i18n['motion']['fade']['label'], 'description' => $i18n['motion']['fade']['description'], 'startValueTitle' => $i18n['motion']['fade']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['fade']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['fade']['endValueTitle'], 'icon' => 'animation-fade', 'resolver' => 'opacity', 'default' => '0|50|50|100|0|100|100', ), "{$prefix}scaling" => array( 'label' => $i18n['motion']['scaling']['label'], 'description' => $i18n['motion']['scaling']['description'], 'startValueTitle' => $i18n['motion']['scaling']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['scaling']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['scaling']['endValueTitle'], 'icon' => 'resize', 'resolver' => 'scale', 'default' => '0|50|50|100|70|100|100', ), "{$prefix}rotating" => array( 'label' => $i18n['motion']['rotating']['label'], 'description' => $i18n['motion']['rotating']['description'], 'startValueTitle' => $i18n['motion']['rotating']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['rotating']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['rotating']['endValueTitle'], 'icon' => 'rotate', 'resolver' => 'rotate', 'default' => '0|50|50|100|90|0|0', ), "{$prefix}blur" => array( 'label' => $i18n['motion']['blur']['label'], 'description' => $i18n['motion']['blur']['description'], 'startValueTitle' => $i18n['motion']['blur']['startValueTitle'], 'middleValueTitle' => $i18n['motion']['blur']['middleValueTitle'], 'endValueTitle' => $i18n['motion']['blur']['endValueTitle'], 'icon' => 'blur', 'resolver' => 'blur', 'default' => '0|40|60|100|10|0|0', ), ); } /** * Add sticky fields to the additional fields options. * * @since 4.6.0 * * @return void */ protected function _add_sticky_fields() { if ( is_array( self::$_->array_get( $this->advanced_fields, 'sticky', array() ) ) ) { $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'Sticky' )->get_fields( array( 'module_slug' => $this->slug, ) ) ); } } /** * Add scroll effects option fields. */ protected function _add_scroll_effects_fields() { if ( is_array( self::$_->array_get( $this->advanced_fields, 'scroll_effects', array() ) ) ) { $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'Scroll' )->get_fields( array( 'options' => $this->get_scroll_effects_options(), 'grid_support' => self::$_->array_get( $this->advanced_fields, 'scroll_effects.grid_support', 'no' ), ) ) ); } } /** * Add margin & padding option fields. * * @since 3.23 Add allowed CSS units for margin and padding. */ protected function _add_margin_padding_fields() { // Margin-Padding fields are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['margin_padding'] = self::$_->array_get( $this->advanced_fields, 'margin_padding', array() ); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Margin settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'margin_padding' ) ) ) { return; } $additional_options = array(); $defaults = array( 'use_margin' => true, 'draggable_margin' => true, 'use_padding' => true, 'draggable_padding' => true, ); $this->advanced_fields['margin_padding'] = wp_parse_args( $this->advanced_fields['margin_padding'], $defaults ); $tab_slug = isset( $this->advanced_fields['margin_padding']['tab_slug'] ) ? $this->advanced_fields['margin_padding']['tab_slug'] : 'advanced'; $toggle_disabled = isset( $this->advanced_fields['margin_padding']['disable_toggle'] ) && $this->advanced_fields['margin_padding']['disable_toggle']; $toggle_slug = isset( $this->advanced_fields['margin_padding']['toggle_slug'] ) ? $this->advanced_fields['margin_padding']['toggle_slug'] : 'margin_padding'; $sub_toggle = et_()->array_get( $this->advanced_fields['margin_padding'], 'sub_toggle', null ); $i18n =& self::$i18n; if ( ! isset( $i18n['margin'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['margin'] = array( 'toggle' => array( 'title' => esc_html__( 'Spacing', 'et_builder' ), ), 'margin' => array( 'label' => esc_html__( 'Margin', 'et_builder' ), 'description' => esc_html__( 'Margin adds extra space to the outside of the element, increasing the distance between the element and other items on the page.', 'et_builder' ), ), 'padding' => array( 'label' => esc_html__( 'Padding', 'et_builder' ), 'description' => esc_html__( 'Padding adds extra space to the inside of the element, increasing the distance between the edge of the element and its inner contents.', 'et_builder' ), ), ); // phpcs:enable } if ( ! $toggle_disabled ) { $margin_toggle = array( $toggle_slug => array( 'title' => $i18n['margin']['toggle']['title'], 'priority' => 90, ), ); $this->_add_settings_modal_toggles( $tab_slug, $margin_toggle ); } if ( $this->advanced_fields['margin_padding']['use_margin'] ) { $additional_options['custom_margin'] = array( 'label' => $i18n['margin']['margin']['label'], 'description' => $i18n['margin']['margin']['description'], 'type' => 'custom_margin', 'mobile_options' => true, 'sticky' => true, 'option_category' => 'layout', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), ); $additional_options['custom_margin_tablet'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, ); $additional_options['custom_margin_phone'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); // Only adds `sub_toggle` for the margin settings when it's not empty. if ( ! empty( $sub_toggle ) ) { $additional_options['custom_margin']['sub_toggle'] = $sub_toggle; } // make it possible to override/add options. if ( ! empty( $this->advanced_fields['margin_padding']['custom_margin'] ) ) { $additional_options['custom_margin'] = array_merge( $additional_options['custom_margin'], $this->advanced_fields['margin_padding']['custom_margin'] ); } $additional_options['custom_margin_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options['padding_1_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options['padding_2_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options['padding_3_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options['padding_4_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); } if ( $this->advanced_fields['margin_padding']['use_padding'] ) { $additional_options['custom_padding'] = array( 'label' => $i18n['margin']['padding']['label'], 'description' => $i18n['margin']['padding']['description'], 'type' => 'custom_padding', 'mobile_options' => true, 'sticky' => true, 'option_category' => 'layout', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), ); $additional_options['custom_padding_tablet'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options['custom_padding_phone'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); // Only adds `sub_toggle` for the padding settings when it's not empty. if ( ! empty( $sub_toggle ) ) { $additional_options['custom_padding']['sub_toggle'] = $sub_toggle; } // make it possible to override/add options. if ( ! empty( $this->advanced_fields['margin_padding']['custom_padding'] ) ) { $additional_options['custom_padding'] = array_merge( $additional_options['custom_padding'], $this->advanced_fields['margin_padding']['custom_padding'] ); } $additional_options['custom_padding_last_edited'] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add button option fields. * * @since 3.23 Add custom padding for button options set. Add allowed CSS units. Set custom * default for text size and border width. Add responsive settings for button * settings. Set custom group label. Add ability hide and show the icon settings. */ protected function _add_button_fields() { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_advanced_fields ) { return; } // Button settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'button' ) ) ) { return; } $this->set_i18n_font(); $i18n =& self::$i18n; // Auto-add attributes toggle. $toggles_custom_css_tab = isset( $this->settings_modal_toggles['custom_css'] ) ? $this->settings_modal_toggles['custom_css'] : array(); if ( ! isset( $toggles_custom_css_tab['toggles'] ) || ! isset( $toggles_custom_css_tab['toggles']['attributes'] ) ) { $this->_add_settings_modal_toggles( 'custom_css', array( 'attributes' => array( 'title' => esc_html__( 'Attributes', 'et_builder' ), 'priority' => 95, ), ) ); } $additional_options = array(); $hover = et_pb_hover_options(); $default_toggle_priority = 70; foreach ( $this->advanced_fields['button'] as $option_name => $option_settings ) { $tab_slug = self::$_->array_get( $option_settings, 'tab_slug', 'advanced' ); $toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle']; $toggle_slug = ''; if ( ! $toggle_disabled ) { $toggle_slug = self::$_->array_get( $option_settings, 'toggle_slug', $option_name ); $toggle_priority = self::$_->array_get( $option_settings, 'toggle_priority', $default_toggle_priority ); $button_toggle = array( $option_name => array( 'title' => esc_html( $option_settings['label'] ), 'priority' => $toggle_priority, ), ); $this->_add_settings_modal_toggles( $tab_slug, $button_toggle ); } // Custom default values defined on module. $text_size_default = self::$_->array_get( $option_settings, 'text_size.default', '' ); $border_width_default = self::$_->array_get( $option_settings, 'border_width.default', '' ); $additional_options[ "custom_{$option_name}" ] = array( 'label' => sprintf( esc_html__( 'Use Custom Styles For %1$s ', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( "If you would like to customize the appearance of this module's button, you must first enable custom button styles.", 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'button', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'affects' => array( "{$option_name}_text_color", "{$option_name}_text_size", "{$option_name}_border_width", "{$option_name}_border_radius", "{$option_name}_letter_spacing", "{$option_name}_spacing", "{$option_name}_bg_color", "{$option_name}_border_color", "{$option_name}_use_icon", "{$option_name}_font", $hover->get_hover_field( "{$option_name}_text_color" ), $hover->get_hover_field( "{$option_name}_border_color" ), $hover->get_hover_field( "{$option_name}_border_radius" ), $hover->get_hover_field( "{$option_name}_letter_spacing" ), "{$option_name}_text_shadow_style", // Add Text Shadow to button options. "{$option_name}_custom_margin", "{$option_name}_custom_padding", ), 'default_on_front' => 'off', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $additional_options[ "{$option_name}_text_size" ] = array( 'label' => sprintf( $i18n['font']['size']['label'], $option_settings['label'] ), 'description' => esc_html__( 'Increase or decrease the size of the button text.', 'et_builder' ), 'type' => 'range', 'range_settings' => array( 'min' => '1', 'max' => '100', 'step' => '1', ), 'option_category' => 'button', 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default' => ! empty( $text_size_default ) ? $text_size_default : ET_Global_Settings::get_value( 'all_buttons_font_size' ), 'default_unit' => 'px', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'mobile_options' => true, 'sticky' => true, 'depends_show_if' => 'on', 'hover' => 'tabs', ); $additional_options[ "{$option_name}_text_color" ] = array( 'label' => sprintf( $i18n['font']['color']['label'], $option_settings['label'] ), 'description' => esc_html__( 'Pick a color to be used for the button text.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'button', 'custom_color' => true, 'default' => '', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_bg_color" ] = array( 'label' => sprintf( esc_html__( '%1$s Background', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Adjust the background style of the button by customizing the background color, gradient, and image.', 'et_builder' ), 'type' => 'background-field', 'base_name' => "{$option_name}_bg", 'context' => "{$option_name}_bg_color", 'option_category' => 'button', 'custom_color' => true, 'default' => ET_Global_Settings::get_value( 'all_buttons_bg_color' ), 'default_on_front' => '', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'background_fields' => $this->generate_background_options( "{$option_name}_bg", 'button', $tab_slug, $toggle_slug, "{$option_name}_bg_color" ), 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_bg_color" ]['background_fields'][ "{$option_name}_bg_color" ]['default'] = ET_Global_Settings::get_value( 'all_buttons_bg_color' ); $additional_options = array_merge( $additional_options, $this->generate_background_options( "{$option_name}_bg", 'skip', $tab_slug, $toggle_slug, "{$option_name}_bg_color" ) ); $additional_options[ "{$option_name}_border_width" ] = array( 'label' => sprintf( esc_html__( '%1$s Border Width', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Increase or decrease the thickness of the border around the button. Setting this value to 0 will remove the border entirely.', 'et_builder' ), 'type' => 'range', 'option_category' => 'button', 'default' => ! empty( $border_width_default ) ? $border_width_default : ET_Global_Settings::get_value( 'all_buttons_border_width' ), 'default_unit' => 'px', 'default_on_front' => '', 'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_border_color" ] = array( 'label' => sprintf( esc_html__( '%1$s Border Color', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Pick a color to be used for the button border.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'button', 'custom_color' => true, 'default' => '', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_border_radius" ] = array( 'label' => sprintf( esc_html__( '%1$s Border Radius', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( "Increasing the border radius will increase the roundness of the button's corners. Setting this value to 0 will result in squared corners.", 'et_builder' ), 'type' => 'range', 'option_category' => 'button', 'default' => ET_Global_Settings::get_value( 'all_buttons_border_radius' ), 'default_unit' => 'px', 'default_on_front' => '', 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_letter_spacing" ] = array( 'label' => sprintf( $i18n['font']['letter_spacing']['label'], $option_settings['label'] ), 'description' => esc_html__( 'Letter spacing adjusts the distance between each letter in the button.', 'et_builder' ), 'type' => 'range', 'option_category' => 'button', 'default' => ET_Global_Settings::get_value( 'all_buttons_spacing' ), 'default_unit' => 'px', 'default_on_front' => '', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'mobile_options' => true, 'sticky' => true, 'depends_show_if' => 'on', 'hover' => 'tabs', ); $additional_options[ "{$option_name}_font" ] = array( 'label' => sprintf( $i18n['font']['font']['label'], $option_settings['label'] ), 'description' => esc_html__( 'Choose a custom font to use for the button. All Google web fonts are available, or you can upload your own custom font files.', 'et_builder' ), 'group_label' => esc_html( $option_settings['label'] ), 'type' => 'font', 'option_category' => 'button', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'mobile_options' => true, ); // Hide show button icon. $hide_icon = isset( $option_settings['hide_icon'] ) ? $option_settings['hide_icon'] : false; if ( false === $hide_icon ) { $additional_options[ "{$option_name}_use_icon" ] = array( 'label' => sprintf( esc_html__( 'Show %1$s Icon', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'When enabled, this will add a custom icon within the button.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'button', 'default' => 'on', 'options' => array( 'on' => et_builder_i18n( 'Yes' ), 'off' => et_builder_i18n( 'No' ), ), 'affects' => array( "{$option_name}_icon_color", "{$option_name}_icon_placement", "{$option_name}_on_hover", "{$option_name}_icon", ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', ); $additional_options[ "{$option_name}_icon" ] = array( 'label' => sprintf( esc_html__( '%1$s Icon', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Pick an icon to be used for the button.', 'et_builder' ), 'type' => 'select_icon', 'option_category' => 'button', 'class' => array( 'et-pb-font-icon' ), 'default' => '', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if_not' => 'off', 'mobile_options' => true, ); $additional_options[ "{$option_name}_icon_color" ] = array( 'label' => sprintf( esc_html__( '%1$s Icon Color', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Here you can define a custom color for the button icon.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'button', 'custom_color' => true, 'default' => '', 'hover' => 'tabs', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if_not' => 'off', 'mobile_options' => true, 'sticky' => true, ); $additional_options[ "{$option_name}_icon_placement" ] = array( 'label' => sprintf( esc_html__( '%1$s Icon Placement', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Choose where the button icon should be displayed within the button.', 'et_builder' ), 'type' => 'select', 'option_category' => 'button', 'options' => array( 'right' => et_builder_i18n( 'Right' ), 'left' => et_builder_i18n( 'Left' ), ), 'default' => 'right', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if_not' => 'off', 'mobile_options' => true, ); $additional_options[ "{$option_name}_on_hover" ] = array( 'label' => sprintf( esc_html__( 'Only Show Icon On Hover for %1$s', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'By default, button icons are displayed on hover. If you would like button icons to always be displayed, then you can enable this option.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'button', 'default' => 'on', 'options' => array( 'on' => et_builder_i18n( 'Yes' ), 'off' => et_builder_i18n( 'No' ), ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if_not' => 'off', 'mobile_options' => true, ); } if ( isset( $option_settings['use_alignment'] ) && $option_settings['use_alignment'] ) { $additional_options[ "{$option_name}_alignment" ] = array( 'label' => esc_html__( 'Button Alignment', 'et_builder' ), 'description' => esc_html__( 'Align your button to the left, right or center of the module.', 'et_builder' ), 'type' => 'text_align', 'option_category' => 'layout', 'options' => et_builder_get_text_orientation_options( array( 'justified' ) ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'mobile_options' => true, ); } // The configurable rel attribute field is added by default. if ( ! isset( $option_settings['no_rel_attr'] ) ) { $additional_options[ "{$option_name}_rel" ] = array( 'label' => sprintf( esc_html__( '%1$s Relationship', 'et_builder' ), $option_settings['label'] ), 'type' => 'multiple_checkboxes', 'option_category' => 'configuration', 'options' => $this->get_rel_values(), 'description' => et_get_safe_localization( __( "Specify the value of your link's <em>rel</em> attribute. The <em>rel</em> attribute specifies the relationship between the current document and the linked document.<br><strong>Tip:</strong> Search engines can use this attribute to get more information about a link.", 'et_builder' ) ), 'tab_slug' => 'custom_css', 'toggle_slug' => 'attributes', 'shortcut_index' => $option_name, ); } // Add text-shadow to button options. $option = $this->text_shadow->get_fields( array( 'label' => $option_settings['label'], 'prefix' => $option_name, 'option_category' => 'font_option', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_show_if' => 'on', 'show_if' => array( "custom_{$option_name}" => 'on', ), ) ); $additional_options = array_merge( $additional_options, $option ); // Conditionally add box-shadow options to button options. Get box shadow settings for advanced button fields. $button_box_shadow_options = self::$_->array_get( $option_settings, 'box_shadow', array() ); // Enable module to remove box shadow from advanced button fields by declaring false value to box // shadow attribute (i.e. button module). if ( false !== $button_box_shadow_options ) { $button_box_shadow_options = wp_parse_args( $button_box_shadow_options, array( 'label' => esc_html__( 'Button Box Shadow', 'et_builder' ), 'option_category' => 'layout', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'show_if' => array( "custom_{$option_name}" => 'on', ), ) ); // Only print box shadow styling if custom_* attribute is equal to "on" by adding show_iff attribute. $button_visibility_condition = array( "custom_{$option_name}" => 'on' ); self::$_->array_set( $button_box_shadow_options, 'css.show_if', $button_visibility_condition ); // Automatically add default box shadow fields if box shadow attribute hasn't even defined yet. // No attribute found is considered true for default thus if this about to add the first advanced // box shadow, add the default first. if ( ! isset( $this->advanced_fields['box_shadow'] ) ) { $button_box_shadow_options_default = array(); self::$_->array_set( $this->advanced_fields, 'box_shadow.default', $button_box_shadow_options_default ); } // Box shadow fields are generated after button fields being added. Thus, adding $this->advanced_fields // is sufficient to insert the box shadow fields. self::$_->array_set( $this->advanced_fields, "box_shadow.{$option_name}", $button_box_shadow_options ); } // Add custom margin-padding to form field options. $margin_padding = self::$_->array_get( $option_settings, 'margin_padding', true ); if ( $margin_padding ) { $margin_padding_module_args = is_array( $margin_padding ) ? $margin_padding : array(); $margin_padding_args = wp_parse_args( $margin_padding_module_args, array( 'label' => $option_settings['label'], 'prefix' => $option_name, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ) ); $margin_padding_options = $this->margin_padding->get_fields( $margin_padding_args ); $additional_options = array_merge( $additional_options, $margin_padding_options ); } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add special image_icon option fields. * * @since ? Add custom margin-padding for image_icon field. */ protected function _add_image_icon_fields() { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_advanced_fields ) { return; } // image_icon settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'image_icon' ) ) ) { return; } $this->set_i18n_font(); $i18n =& self::$i18n; $additional_options = array(); $hover = et_pb_hover_options(); foreach ( $this->advanced_fields['image_icon'] as $option_name => $option_settings ) { $tab_slug = isset( $option_settings['tab_slug'] ) ? $option_settings['tab_slug'] : 'advanced'; $toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle']; $toggle_slug = ''; if ( ! $toggle_disabled ) { $toggle_slug = isset( $option_settings['toggle_slug'] ) ? $option_settings['toggle_slug'] : $option_name; $image_icon_toggle = array( $option_name => array( 'title' => esc_html( $option_settings['label'] ), 'priority' => 70, ), ); $this->_add_settings_modal_toggles( $tab_slug, $image_icon_toggle ); } // Add custom margin-padding to form field options. $margin_padding = self::$_->array_get( $option_settings, 'margin_padding', true ); if ( $margin_padding ) { $margin_padding_module_args = is_array( $margin_padding ) ? $margin_padding : array(); $margin_padding_args = wp_parse_args( $margin_padding_module_args, array( 'label' => $option_settings['label'], 'prefix' => $option_name, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ) ); $margin_padding_options = $this->margin_padding->get_fields( $margin_padding_args ); $additional_options = array_merge( $additional_options, $margin_padding_options ); } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add animation option fields. * * @since 3.23 Introduce responsive settings on all animation options. Rename Animation label * for et_pb_team_member module as Image Animation. Reorder animation repeat option * to the bottom of animation settings. */ protected function _add_animation_fields() { // Animation fields are added by default on all module. $this->advanced_fields['animation'] = self::$_->array_get( $this->advanced_fields, 'animation', array() ); // Animation Disabled. if ( false === $this->advanced_fields['animation'] ) { return; } $classname = get_class( $this ); // Child modules do not support the Animation settings except for Columns. if ( isset( $this->type ) && 'child' === $this->type && ! in_array( $this->slug, array( 'et_pb_column', 'et_pb_column_inner' ), true ) ) { return; } // Cache results so that translation/escaping only happens once. $i18n =& self::$i18n; if ( ! isset( $i18n['animation'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['animation'] = array( 'toggle' => array( 'title' => esc_html__( 'Animation', 'et_builder' ), ), 'style' => array( 'label' => esc_html__( 'Animation Style', 'et_builder' ), 'description' => esc_html__( 'Pick an animation style to enable animations for this element. Once enabled, you will be able to customize your animation style further. To disable animations, choose the None option.', 'et_builder' ), 'options' => array( 'fade' => et_builder_i18n( 'Fade' ), 'slide' => et_builder_i18n( 'Slide' ), 'bounce' => esc_html__( 'Bounce', 'et_builder' ), 'zoom' => esc_html__( 'Zoom', 'et_builder' ), 'flip' => et_builder_i18n( 'Flip' ), 'fold' => esc_html__( 'Fold', 'et_builder' ), 'roll' => esc_html__( 'Roll', 'et_builder' ), ), ), 'direction' => array( 'label' => esc_html__( 'Animation Direction', 'et_builder' ), 'description' => esc_html__( 'Pick from up to five different animation directions, each of which will adjust the starting and ending position of your animated element.', 'et_builder' ), ), 'duration' => array( 'label' => esc_html__( 'Animation Duration', 'et_builder' ), 'description' => esc_html__( 'Speed up or slow down your animation by adjusting the animation duration. Units are in milliseconds and the default animation duration is one second.', 'et_builder' ), ), 'delay' => array( 'label' => esc_html__( 'Animation Delay', 'et_builder' ), 'description' => esc_html__( 'If you would like to add a delay before your animation runs you can designate that delay here in milliseconds. This can be useful when using multiple animated modules together.', 'et_builder' ), ), 'opacity' => array( 'label' => esc_html__( 'Animation Starting Opacity', 'et_builder' ), 'description' => esc_html__( 'By increasing the starting opacity, you can reduce or remove the fade effect that is applied to all animation styles.', 'et_builder' ), ), 'speed' => array( 'label' => esc_html__( 'Animation Speed Curve', 'et_builder' ), 'description' => esc_html__( 'Here you can adjust the easing method of your animation. Easing your animation in and out will create a smoother effect when compared to a linear speed curve.', 'et_builder' ), ), 'repeat' => array( 'label' => esc_html__( 'Animation Repeat', 'et_builder' ), 'description' => esc_html__( 'By default, animations will only play once. If you would like to loop your animation continuously you can choose the Loop option here.', 'et_builder' ), 'options' => array( 'once' => esc_html__( 'Once', 'et_builder' ), 'loop' => esc_html__( 'Loop', 'et_builder' ), ), ), 'menu' => array( 'label' => esc_html__( 'Dropdown Menu Animation', 'et_builder' ), 'description' => esc_html__( 'Select an animation to be used when dropdown menus appear. Dropdown menus appear when hovering over links with sub items.', 'et_builder' ), ), 'intensity' => array( 'label' => esc_html__( 'Animation Intensity', 'et_builder' ), 'description' => esc_html__( 'Intensity effects how subtle or aggressive your animation will be. Lowering the intensity will create a smoother and more subtle animation while increasing the intensity will create a snappier more aggressive animation.', 'et_builder' ), ), ); // phpcs:enable } $this->settings_modal_toggles['advanced']['toggles']['animation'] = array( 'title' => $i18n['animation']['toggle']['title'], 'priority' => 110, ); $additional_options = array(); $animations_intensity_fields = array( 'animation_intensity_slide', 'animation_intensity_zoom', 'animation_intensity_flip', 'animation_intensity_fold', 'animation_intensity_roll', ); $additional_options['animation_style'] = array( 'label' => $i18n['animation']['style']['label'], 'description' => $i18n['animation']['style']['description'], 'type' => 'select_animation', 'option_category' => 'configuration', 'default' => 'none', 'options' => array( 'none' => et_builder_i18n( 'None' ), 'fade' => $i18n['animation']['style']['options']['fade'], 'slide' => $i18n['animation']['style']['options']['slide'], 'bounce' => $i18n['animation']['style']['options']['bounce'], 'zoom' => $i18n['animation']['style']['options']['zoom'], 'flip' => $i18n['animation']['style']['options']['flip'], 'fold' => $i18n['animation']['style']['options']['fold'], 'roll' => $i18n['animation']['style']['options']['roll'], ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'affects' => array_merge( array( 'animation_repeat', 'animation_direction', 'animation_duration', 'animation_delay', 'animation_starting_opacity', 'animation_speed_curve', ), $animations_intensity_fields ), ); $additional_options['animation_direction'] = array( 'label' => $i18n['animation']['direction']['label'], 'description' => $i18n['animation']['direction']['description'], 'type' => 'select', 'option_category' => 'configuration', 'default' => 'center', 'options' => array( 'center' => et_builder_i18n( 'Center' ), 'left' => et_builder_i18n( 'Right' ), 'right' => et_builder_i18n( 'Left' ), 'bottom' => et_builder_i18n( 'Up' ), 'top' => et_builder_i18n( 'Down' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => array( 'none', 'fade' ), 'mobile_options' => true, ); $additional_options['animation_duration'] = array( 'label' => $i18n['animation']['duration']['label'], 'description' => $i18n['animation']['duration']['description'], 'type' => 'range', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 2000, 'step' => 50, ), 'default' => '1000ms', 'validate_unit' => true, 'fixed_unit' => 'ms', 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => 'none', 'reset_animation' => true, 'mobile_options' => true, ); $additional_options['animation_delay'] = array( 'label' => $i18n['animation']['delay']['label'], 'description' => $i18n['animation']['delay']['description'], 'type' => 'range', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 3000, 'step' => 50, ), 'default' => '0ms', 'validate_unit' => true, 'fixed_unit' => 'ms', 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => 'none', 'reset_animation' => true, 'mobile_options' => true, ); foreach ( $animations_intensity_fields as $animations_intensity_field ) { $animation_style = str_replace( 'animation_intensity_', '', $animations_intensity_field ); $additional_options[ $animations_intensity_field ] = array( 'label' => $i18n['animation']['intensity']['label'], 'description' => $i18n['animation']['intensity']['description'], 'type' => 'range', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => '50%', 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if' => $animation_style, 'reset_animation' => true, 'mobile_options' => true, ); } $additional_options['animation_starting_opacity'] = array( 'label' => $i18n['animation']['opacity']['label'], 'description' => $i18n['animation']['opacity']['description'], 'type' => 'range', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, 'min_limit' => 0, 'max_limit' => 100, ), 'default' => '0%', 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => 'none', 'reset_animation' => true, 'mobile_options' => true, ); $additional_options['animation_speed_curve'] = array( 'label' => $i18n['animation']['speed']['label'], 'description' => $i18n['animation']['speed']['description'], 'type' => 'select', 'option_category' => 'configuration', 'default' => 'ease-in-out', 'options' => array( 'ease-in-out' => et_builder_i18n( 'Ease-In-Out' ), 'ease' => et_builder_i18n( 'Ease' ), 'ease-in' => et_builder_i18n( 'Ease-In' ), 'ease-out' => et_builder_i18n( 'Ease-Out' ), 'linear' => et_builder_i18n( 'Linear' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => 'none', 'mobile_options' => true, ); $additional_options['animation_repeat'] = array( 'label' => $i18n['animation']['repeat']['label'], 'description' => $i18n['animation']['repeat']['description'], 'type' => 'select', 'option_category' => 'configuration', 'default' => 'once', 'options' => array( 'once' => $i18n['animation']['repeat']['options']['once'], 'loop' => $i18n['animation']['repeat']['options']['loop'], ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'depends_show_if_not' => 'none', 'mobile_options' => true, ); if ( isset( $this->slug ) && in_array( $this->slug, array( 'et_pb_menu', 'et_pb_fullwidth_menu' ), true ) ) { $additional_options['dropdown_menu_animation'] = array( 'label' => $i18n['animation']['menu']['label'], 'description' => $i18n['animation']['menu']['description'], 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'fade' => et_builder_i18n( 'Fade' ), 'expand' => et_builder_i18n( 'Expand' ), 'slide' => et_builder_i18n( 'Slide' ), 'flip' => et_builder_i18n( 'Flip' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'default' => 'fade', ); } // Move existing "Animation" section fields under the new animations UI. if ( isset( $this->slug ) && 'et_pb_fullwidth_portfolio' === $this->slug ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Carousel Rotation', 'et_builder' ), 'description' => esc_html__( 'If you the carousel layout option is chosen and you would like the carousel to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', ), 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Carousel Rotation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the carousel rotates, if 'Automatic Carousel Rotation' option is enabled above. The higher the number the longer the pause between each rotation. (Ex. 1000 = 1 sec)", 'et_builder' ), 'default' => '7000', ); } if ( isset( $this->slug ) && 'et_pb_fullwidth_slider' === $this->slug ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Animation', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', 'auto_ignore_hover', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ), 'default' => '7000', ); $additional_options['auto_ignore_hover'] = array( 'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ), 'default' => 'off', ); } if ( isset( $this->slug ) && 'et_pb_fullwidth_post_slider' === $this->slug ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Animation', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', 'auto_ignore_hover', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ), 'default' => '7000', ); $additional_options['auto_ignore_hover'] = array( 'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ), 'default' => 'off', ); } if ( isset( $this->slug ) && in_array( $this->slug, array( 'et_pb_gallery', 'et_pb_wc_images', 'et_pb_wc_gallery' ), true ) ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Animation', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', ), 'depends_show_if' => 'on', 'depends_on' => array( 'fullwidth', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ), 'default' => '7000', ); } if ( isset( $this->slug ) && 'et_pb_blurb' === $this->slug ) { $additional_options['animation'] = array( 'label' => esc_html__( 'Image/Icon Animation', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'top' => esc_html__( 'Top To Bottom', 'et_builder' ), 'left' => esc_html__( 'Left To Right', 'et_builder' ), 'right' => esc_html__( 'Right To Left', 'et_builder' ), 'bottom' => esc_html__( 'Bottom To Top', 'et_builder' ), 'off' => esc_html__( 'No Animation', 'et_builder' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'This controls the direction of the lazy-loading animation.', 'et_builder' ), 'default' => 'top', 'mobile_options' => true, ); } if ( isset( $this->slug ) && 'et_pb_slider' === $this->slug ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Animation', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', 'auto_ignore_hover', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ), 'default' => '7000', ); $additional_options['auto_ignore_hover'] = array( 'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ), 'default' => 'off', ); } if ( isset( $this->slug ) && 'et_pb_post_slider' === $this->slug ) { $additional_options['auto'] = array( 'label' => esc_html__( 'Automatic Animation', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'affects' => array( 'auto_speed', 'auto_ignore_hover', ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ), 'default' => 'off', ); $additional_options['auto_speed'] = array( 'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ), 'type' => 'text', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ), 'default' => '7000', ); $additional_options['auto_ignore_hover'] = array( 'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'configuration', 'depends_show_if' => 'on', 'options' => array( 'off' => et_builder_i18n( 'Off' ), 'on' => et_builder_i18n( 'On' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ), 'default' => 'off', ); } if ( isset( $this->slug ) && 'et_pb_team_member' === $this->slug ) { $additional_options['animation'] = array( 'label' => esc_html__( 'Image Animation', 'et_builder' ), 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'off' => esc_html__( 'No Animation', 'et_builder' ), 'fade_in' => esc_html__( 'Fade In', 'et_builder' ), 'left' => esc_html__( 'Left To Right', 'et_builder' ), 'right' => esc_html__( 'Right To Left', 'et_builder' ), 'top' => esc_html__( 'Top To Bottom', 'et_builder' ), 'bottom' => esc_html__( 'Bottom To Top', 'et_builder' ), ), 'tab_slug' => 'advanced', 'toggle_slug' => 'animation', 'description' => esc_html__( 'This controls the direction of the lazy-loading animation.', 'et_builder' ), 'default' => 'off', ); } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add additional transition fields. e.x hover transition fields. */ private function _add_additional_transition_fields() { $i18n =& self::$i18n; if ( ! isset( $i18n['transition'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['transition'] = array( 'toggle' => array( 'title' => esc_html__( 'Transitions', 'et_builder' ), ), 'duration' => array( 'label' => esc_html__( 'Transition Duration', 'et_builder' ), 'description' => esc_html__( 'This controls the transition duration of the hover animation.', 'et_builder' ), ), 'delay' => array( 'label' => esc_html__( 'Transition Delay', 'et_builder' ), 'description' => esc_html__( 'This controls the transition delay of the hover animation.', 'et_builder' ), ), 'curve' => array( 'label' => esc_html__( 'Transition Speed Curve', 'et_builder' ), 'description' => esc_html__( 'This controls the transition speed curve of the hover animation.', 'et_builder' ), ), ); // phpcs:enable } $this->settings_modal_toggles['custom_css']['toggles']['hover_transitions'] = array( 'title' => $i18n['transition']['toggle']['title'], 'priority' => 120, ); $additional_options = array(); $additional_options['hover_transition_duration'] = array( 'label' => $i18n['transition']['duration']['label'], 'description' => $i18n['transition']['duration']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 2000, 'step' => 50, ), 'default' => '300ms', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => 'ms', 'fixed_range' => true, 'tab_slug' => 'custom_css', 'toggle_slug' => 'hover_transitions', 'depends_default' => null, 'mobile_options' => true, ); $additional_options['hover_transition_delay'] = array( 'label' => $i18n['transition']['delay']['label'], 'description' => $i18n['transition']['delay']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 300, 'step' => 50, ), 'default' => '0ms', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => 'ms', 'fixed_range' => true, 'tab_slug' => 'custom_css', 'toggle_slug' => 'hover_transitions', 'depends_default' => null, 'mobile_options' => true, ); $additional_options['hover_transition_speed_curve'] = array( 'label' => $i18n['transition']['curve']['label'], 'description' => $i18n['transition']['curve']['description'], 'type' => 'select', 'option_category' => 'layout', 'default' => 'ease', 'default_on_child' => true, 'options' => array( 'ease-in-out' => et_builder_i18n( 'Ease-In-Out' ), 'ease' => et_builder_i18n( 'Ease' ), 'ease-in' => et_builder_i18n( 'Ease-In' ), 'ease-out' => et_builder_i18n( 'Ease-Out' ), 'linear' => et_builder_i18n( 'Linear' ), ), 'tab_slug' => 'custom_css', 'toggle_slug' => 'hover_transitions', 'depends_default' => null, 'mobile_options' => true, ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add CSS position controls affects top,right,bottom,left,position and transform translate CSS properties. * * @return void * @since 4.2 */ private function _add_position_fields() { /** Position field class instance. @var $class ET_Builder_Module_Field_Position */ $class = ET_Builder_Module_Fields_Factory::get( 'Position' ); $this->advanced_fields[ $class::TOGGLE_SLUG ] = self::$_->array_get( $this->advanced_fields, $class::TOGGLE_SLUG, array() ); $this->advanced_fields['z_index'] = self::$_->array_get( $this->advanced_fields, 'z_index', array() ); // Position and Z Index Disabled. if ( ! is_array( $this->advanced_fields[ $class::TOGGLE_SLUG ] ) && ! is_array( $this->advanced_fields['z_index'] ) ) { return; } $this->settings_modal_toggles[ $class::TAB_SLUG ]['toggles'][ $class::TOGGLE_SLUG ] = array( 'title' => et_builder_i18n( 'Position' ), 'priority' => 190, ); $default_position = self::$_->array_get( $this->advanced_fields[ $class::TOGGLE_SLUG ], 'default', 'none' ); $default_z_index = self::$_->array_get( $this->advanced_fields['z_index'], 'default', '' ); $args = array( 'defaults' => array( 'positioning' => $default_position, // none | relative | absolute | fixed. 'position_origin' => 'top_left', 'vertical_offset' => '', 'horizontal_offset' => '', 'z_index' => $default_z_index, ), 'hide_position_fields' => false === $this->advanced_fields[ $class::TOGGLE_SLUG ], 'hide_z_index_fields' => false === $this->advanced_fields['z_index'], ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $class->get_fields( $args ) ); } /** * Add CSS filter controls (i.e. saturation, brightness, opacity) to the `_additional_fields_options` array. * * @since 3.23 Introduce responsive settings on all animation options. Add allowed CSS unit for * Blur option. * * @return void */ protected function _add_filter_fields() { // Filter fields are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['filters'] = self::$_->array_get( $this->advanced_fields, 'filters', array() ); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Module has to explicitly set false to disable filters options. if ( false === self::$_->array_get( $this->advanced_fields, 'filters', false ) ) { return; } $i18n =& self::$i18n; if ( ! isset( $i18n['filter'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['filter'] = array( 'toggle' => array( 'title' => esc_html__( 'Filters', 'et_builder' ), ), 'hue' => array( 'description' => esc_html__( 'Shift all colors by this amount.', 'et_builder' ), ), 'saturate' => array( 'description' => esc_html__( 'Define how intense the color saturation should be.', 'et_builder' ), ), 'brightness' => array( 'label' => esc_html__( 'Brightness', 'et_builder' ), 'description' => esc_html__( 'Define how bright the colors should be.', 'et_builder' ), ), 'contrast' => array( 'label' => esc_html__( 'Contrast', 'et_builder' ), 'description' => esc_html__( 'Define how distinct bright and dark areas should be.', 'et_builder' ), ), 'invert' => array( 'label' => esc_html__( 'Invert', 'et_builder' ), 'description' => esc_html__( 'Invert the hue, saturation, and brightness by this amount.', 'et_builder' ), ), 'sepia' => array( 'label' => esc_html__( 'Sepia', 'et_builder' ), 'description' => esc_html__( 'Travel back in time by this amount.', 'et_builder' ), ), 'opacity' => array( 'label' => esc_html__( 'Opacity', 'et_builder' ), 'description' => esc_html__( 'Define how transparent or opaque this should be.', 'et_builder' ), ), 'blur' => array( 'description' => esc_html__( 'Blur by this amount.', 'et_builder' ), ), 'blend' => array( 'label' => esc_html__( 'Blend Mode', 'et_builder' ), 'description' => esc_html__( 'Modify how this element blends with any layers beneath it. To reset, choose the "Normal" option.', 'et_builder' ), ), ); // phpcs:enable } $filter_settings = self::$_->array_get( $this->advanced_fields, 'filters' ); $tab_slug = self::$_->array_get( $filter_settings, 'tab_slug', 'advanced' ); $toggle_slug = self::$_->array_get( $filter_settings, 'toggle_slug', 'filters' ); $toggle_name = self::$_->array_get( $filter_settings, 'toggle_name', $i18n['filter']['toggle']['title'] ); $sub_toggle = self::$_->array_get( $filter_settings, 'sub_toggle', 'filters' ); $modal_toggles = array( $toggle_slug => array( 'title' => $toggle_name, 'priority' => 105, ), ); $this->_add_settings_modal_toggles( $tab_slug, $modal_toggles ); $additional_options = array(); $additional_options['filter_hue_rotate'] = array( 'label' => et_builder_i18n( 'Hue' ), 'description' => $i18n['filter']['hue']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 359, 'step' => 1, ), 'default' => '0deg', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => 'deg', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_saturate'] = array( 'label' => et_builder_i18n( 'Saturation' ), 'description' => $i18n['filter']['saturate']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_brightness'] = array( 'label' => $i18n['filter']['brightness']['label'], 'description' => $i18n['filter']['brightness']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_contrast'] = array( 'label' => $i18n['filter']['contrast']['label'], 'description' => $i18n['filter']['contrast']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_invert'] = array( 'label' => $i18n['filter']['invert']['label'], 'description' => $i18n['filter']['invert']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => '0%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_sepia'] = array( 'label' => $i18n['filter']['sepia']['label'], 'description' => $i18n['filter']['sepia']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => '0%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_opacity'] = array( 'label' => $i18n['filter']['opacity']['label'], 'description' => $i18n['filter']['opacity']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, 'min_limit' => 0, 'max_limit' => 100, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['filter_blur'] = array( 'label' => et_builder_i18n( 'Blur' ), 'description' => $i18n['filter']['blur']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 50, 'step' => 1, ), 'default' => '0px', 'default_unit' => 'px', 'default_on_child' => true, 'validate_unit' => true, 'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default_unit' => 'px', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); $additional_options['mix_blend_mode'] = array( 'label' => $i18n['filter']['blend']['label'], 'description' => $i18n['filter']['blend']['description'], 'type' => 'select', 'option_category' => 'layout', 'default' => 'normal', 'default_on_child' => true, 'options' => array( 'normal' => et_builder_i18n( 'Normal' ), 'multiply' => et_builder_i18n( 'Multiply' ), 'screen' => et_builder_i18n( 'Screen' ), 'overlay' => et_builder_i18n( 'Overlay' ), 'darken' => et_builder_i18n( 'Darken' ), 'lighten' => et_builder_i18n( 'Lighten' ), 'color-dodge' => et_builder_i18n( 'Color Dodge' ), 'color-burn' => et_builder_i18n( 'Color Burn' ), 'hard-light' => et_builder_i18n( 'Hard Light' ), 'soft-light' => et_builder_i18n( 'Soft Light' ), 'difference' => et_builder_i18n( 'Difference' ), 'exclusion' => et_builder_i18n( 'Exclusion' ), 'hue' => et_builder_i18n( 'Hue' ), 'saturation' => et_builder_i18n( 'Saturation' ), 'color' => et_builder_i18n( 'Color' ), 'luminosity' => et_builder_i18n( 'Luminosity' ), ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'reset_animation' => false, 'mobile_options' => true, ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); // Maybe add child filters (i.e. targeting only images within a module). if ( ! isset( $this->advanced_fields['filters']['child_filters_target'] ) ) { return; } $child_filter = $this->advanced_fields['filters']['child_filters_target']; // Allow to modify child filter options label. Default is Image. $child_filter_label = isset( $child_filter['label'] ) ? $child_filter['label'] : et_builder_i18n( 'Image' ); $child_filter_sub_toggle = et_()->array_get( $child_filter, 'sub_toggle', null ); $additional_child_options = array( 'child_filter_hue_rotate' => array( 'label' => $child_filter_label . ' ' . et_builder_i18n( 'Hue' ), 'description' => $i18n['filter']['hue']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 359, 'step' => 1, ), 'default' => '0deg', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => 'deg', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_saturate' => array( 'label' => $child_filter_label . ' ' . et_builder_i18n( 'Saturation' ), 'description' => $i18n['filter']['saturate']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_brightness' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['brightness']['label'], 'description' => $i18n['filter']['brightness']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_contrast' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['contrast']['label'], 'description' => $i18n['filter']['contrast']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 200, 'step' => 1, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_invert' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['invert']['label'], 'description' => $i18n['filter']['invert']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => '0%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_sepia' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['sepia']['label'], 'description' => $i18n['filter']['sepia']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => '0%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_opacity' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['opacity']['label'], 'description' => $i18n['filter']['opacity']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, 'min_limit' => 0, 'max_limit' => 100, ), 'default' => '100%', 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => '%', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_filter_blur' => array( 'label' => $child_filter_label . ' ' . et_builder_i18n( 'Blur' ), 'description' => $i18n['filter']['blur']['description'], 'type' => 'range', 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'max' => 50, 'step' => 1, ), 'default' => '0px', 'default_on_child' => true, 'validate_unit' => true, 'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ), 'default_unit' => 'px', 'fixed_range' => true, 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), 'child_mix_blend_mode' => array( 'label' => $child_filter_label . ' ' . $i18n['filter']['blend']['label'], 'description' => $i18n['filter']['blend']['description'], 'type' => 'select', 'option_category' => 'layout', 'default' => 'normal', 'default_on_child' => true, 'options' => array( 'normal' => et_builder_i18n( 'Normal' ), 'multiply' => et_builder_i18n( 'Multiply' ), 'screen' => et_builder_i18n( 'Screen' ), 'overlay' => et_builder_i18n( 'Overlay' ), 'darken' => et_builder_i18n( 'Darken' ), 'lighten' => et_builder_i18n( 'Lighten' ), 'color-dodge' => et_builder_i18n( 'Color Dodge' ), 'color-burn' => et_builder_i18n( 'Color Burn' ), 'hard-light' => et_builder_i18n( 'Hard Light' ), 'soft-light' => et_builder_i18n( 'Soft Light' ), 'difference' => et_builder_i18n( 'Difference' ), 'exclusion' => et_builder_i18n( 'Exclusion' ), 'hue' => et_builder_i18n( 'Hue' ), 'saturation' => et_builder_i18n( 'Saturation' ), 'color' => et_builder_i18n( 'Color' ), 'luminosity' => et_builder_i18n( 'Luminosity' ), ), 'tab_slug' => $child_filter['tab_slug'], 'toggle_slug' => $child_filter['toggle_slug'], 'sub_toggle' => $child_filter_sub_toggle, 'reset_animation' => false, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => false, ), ); if ( isset( $child_filter['depends_show_if'] ) ) { foreach ( $additional_child_options as $option => $value ) { $additional_child_options[ $option ]['depends_show_if'] = $child_filter['depends_show_if']; } } if ( isset( $child_filter['depends_show_if_not'] ) ) { foreach ( $additional_child_options as $option => $value ) { $additional_child_options[ $option ]['depends_show_if_not'] = $child_filter['depends_show_if_not']; } } if ( isset( $child_filter['depends_on'] ) ) { foreach ( $additional_child_options as $option => $value ) { $additional_child_options[ $option ]['depends_on'] = $child_filter['depends_on']; } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_child_options ); } /** * Add the divider options to the additional_fields_options array. */ protected function _add_divider_fields() { if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } // Make sure we only add this to sections. if ( 'et_pb_section' !== $this->slug ) { return; } $tab_slug = 'advanced'; $toggle_slug = 'dividers'; $divider_toggle = array( $toggle_slug => array( 'title' => esc_html__( 'Dividers', 'et_builder' ), 'priority' => 65, ), ); // Add the toggle sections. $this->_add_settings_modal_toggles( $tab_slug, $divider_toggle ); if ( ! isset( $this->advanced_fields['dividers'] ) ) { $this->advanced_fields['dividers'] = array(); } $additional_options = ET_Builder_Module_Fields_Factory::get( 'Divider' )->get_fields( array( 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ) ); // Return our merged options and toggles. $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Add additional Text Shadow fields to all modules */ protected function _add_text_shadow_fields() { // Get text shadow settings. Fallback to default if needed. $this->advanced_fields['text_shadow'] = self::$_->array_get( $this->advanced_fields, 'text_shadow', array( 'default' => array(), ) ); // Text shadow settings have to be array. if ( ! is_array( $this->advanced_fields['text_shadow'] ) ) { return; } // Loop test settings, do multiple text shadow field declaration in one palce. foreach ( $this->advanced_fields['text_shadow'] as $text_shadow_name => $text_shadow_fields ) { // Enable module to disable text shadow. Also disable text shadow if no text group is // found because default text shadow lives on text group. if ( 'default' === $text_shadow_name && ( false === $text_shadow_fields || empty( $this->settings_modal_toggles['advanced']['toggles']['text'] ) ) ) { return; } if ( 'default' !== $text_shadow_name ) { // Automatically add prefix and toggle slug. $text_shadow_fields['prefix'] = $text_shadow_name; $text_shadow_fields['toggle_slug'] = $text_shadow_name; } // Add text shadow fields. $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $this->text_shadow->get_fields( $text_shadow_fields ) ); } } /** * Add box shadow fields based on configuration on $this->advanced_fields['box_shadow'] * * @since 3.1 */ protected function _add_box_shadow_fields() { // BOX shadow fields are added by default to all modules. $this->advanced_fields['box_shadow'] = self::$_->array_get( $this->advanced_fields, 'box_shadow', array( 'default' => array(), ) ); // Box shadow settings have to be array. if ( ! is_array( $this->advanced_fields['box_shadow'] ) ) { return; } $i18n =& self::$i18n; if ( ! isset( $i18n['box_shadow'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['box_shadow'] = array( 'title' => esc_html__( 'Box Shadow', 'et_builder' ), ); // phpcs:enable } // Loop box shadow settings. foreach ( $this->advanced_fields['box_shadow'] as $fields_name => $settings ) { // Enable module to disable box shadow. if ( false === $settings ) { continue; } $is_box_shadow_default = 'default' === $fields_name; // Add Box Shadow toggle for default Box Shadow fields. if ( $is_box_shadow_default ) { $this->settings_modal_toggles['advanced']['toggles']['box_shadow'] = array( 'title' => $i18n['box_shadow']['title'], 'priority' => 100, ); } // Ensure box settings has minimum settings required. $default_settings = array( 'option_category' => 'layout', 'tab_slug' => 'advanced', 'toggle_slug' => 'box_shadow', ); $settings = wp_parse_args( $settings, $default_settings ); // Automatically add suffix attribute. $settings['suffix'] = $is_box_shadow_default ? '' : "_{$fields_name}"; // Add default Box Shadow fields. $this->_additional_fields_options = array_merge( $this->_additional_fields_options, ET_Builder_Module_Fields_Factory::get( 'BoxShadow' )->get_fields( $settings ) ); } } /** * Add form field fields based on configuration on $this->advanced_fields['field']. * * @since 3.23 */ protected function _add_form_field_fields() { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_advanced_fields ) { return; } // Form field settings have to be an array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'form_field' ) ) ) { return; } $additional_options = array(); $hover = et_pb_hover_options(); $this->set_i18n_font(); $i18n =& self::$i18n; // Fetch the form field. foreach ( $this->advanced_fields['form_field'] as $option_name => $option_settings ) { $toggle_slug = ''; $tab_slug = isset( $option_settings['tab_slug'] ) ? $option_settings['tab_slug'] : 'advanced'; $toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle']; // Add form field options group if it's enabled. if ( ! $toggle_disabled ) { $toggle_slug = isset( $option_settings['toggle_slug'] ) ? $option_settings['toggle_slug'] : $option_name; $toggle_priority = isset( $option_settings['toggle_priority'] ) ? $option_settings['toggle_priority'] : 20; $field_toggle = array( $option_name => array( 'title' => esc_html( $option_settings['label'] ), 'priority' => $toggle_priority, ), ); $this->_add_settings_modal_toggles( $tab_slug, $field_toggle ); } // Background Color. $bg_color_options = isset( $option_settings['background_color'] ) ? $option_settings['background_color'] : true; if ( $bg_color_options ) { $bg_color_args = is_array( $bg_color_options ) ? $bg_color_options : array(); $additional_options[ "{$option_name}_background_color" ] = array_merge( array( 'label' => sprintf( esc_html__( '%1$s Background Color', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'Pick a color to fill the module\'s input fields.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'field', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), $bg_color_args ); } // Alternating Background Color. $alternating_bg_color_options = isset( $option_settings['alternating_background_color'] ) ? $option_settings['alternating_background_color'] : false; if ( $alternating_bg_color_options ) { $alternating_bg_color_args = is_array( $alternating_bg_color_options ) ? $alternating_bg_color_options : array(); // phpcs:ignore WordPress.Arrays.ArrayKeySpacingRestrictions.NoSpacesAroundArrayKeys -- The key is not a variable. $additional_options["{$option_name}_alternating_background_color"] = array_merge( array( 'label' => sprintf( esc_html__( '%1$s Alternating Background Color', 'et_builder' ), $option_settings['label'] ), 'description' => sprintf( esc_html__( 'Pick a color to be used for the alternating %1$s', 'et_builder' ), $option_settings['label'] ), 'type' => 'color-alpha', 'option_category' => 'field', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), $alternating_bg_color_args ); } // Text Color. $text_color_options = isset( $option_settings['text_color'] ) ? $option_settings['text_color'] : true; if ( $text_color_options ) { $text_color_args = is_array( $text_color_options ) ? $text_color_options : array(); // phpcs:ignore WordPress.Arrays.ArrayKeySpacingRestrictions.NoSpacesAroundArrayKeys -- The key is not a variable. $additional_options["{$option_name}_text_color"] = array_merge( array( 'label' => sprintf( $i18n['font']['color']['label'], $option_settings['label'] ), 'description' => esc_html__( 'Pick a color to be used for the text written inside input fields.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'field', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ), $text_color_args ); } // Focus Background Color. $focus_background_color_options = isset( $option_settings['focus_background_color'] ) ? $option_settings['focus_background_color'] : true; if ( $focus_background_color_options ) { // phpcs:ignore WordPress.Arrays.ArrayKeySpacingRestrictions.NoSpacesAroundArrayKeys -- The key is not a variable. $additional_options["{$option_name}_focus_background_color"] = array( 'label' => sprintf( esc_html__( '%1$s Focus Background Color', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'When a visitor clicks into an input field, it becomes focused. You can pick a color to be used for the input field background while focused.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'field', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); } // Focus Text Color. $focus_text_color_options = isset( $option_settings['focus_text_color'] ) ? $option_settings['focus_text_color'] : true; if ( $focus_text_color_options ) { // phpcs:ignore WordPress.Arrays.ArrayKeySpacingRestrictions.NoSpacesAroundArrayKeys -- The key is not a variable. $additional_options["{$option_name}_focus_text_color"] = array( 'label' => sprintf( esc_html__( '%1$s Focus Text Color', 'et_builder' ), $option_settings['label'] ), 'description' => esc_html__( 'When a visitor clicks into an input field, it becomes focused. You can pick a color to be used for the input text while focused.', 'et_builder' ), 'type' => 'color-alpha', 'option_category' => 'field', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'hover' => 'tabs', 'mobile_options' => true, 'sticky' => true, ); } // Font - Add current font settings into advanced fields. The font_field is basically // combination of fonts (options group) + fields (type), but plural suffix is removed // because there are some case we just need one field declaration for child module. $font_options = isset( $option_settings['font_field'] ) ? $option_settings['font_field'] : true; if ( $font_options ) { $font_args = is_array( $font_options ) ? $font_options : array(); $font_settings = array_merge( array( 'label' => esc_html( $option_settings['label'] ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, // Text color will be handled by form field function. 'hide_text_color' => true, ), $font_args ); self::$_->array_set( $this->advanced_fields, "fonts.{$option_name}", $font_settings ); } // Add custom margin-padding to form field options. $margin_padding_options = isset( $option_settings['margin_padding'] ) ? $option_settings['margin_padding'] : true; if ( $margin_padding_options ) { $margin_padding_args = is_array( $margin_padding_options ) ? $margin_padding_options : array(); $margin_padding_settings = array_merge( array( 'label' => $option_settings['label'], 'prefix' => $option_name, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ), $margin_padding_args ); $additional_options = array_merge( $additional_options, $this->margin_padding->get_fields( $margin_padding_settings ) ); } // Border Styles - Ensure borders attribute is exist in advanced fields. If it's not, // add borders property and set empty default. $borders_options = isset( $option_settings['border_styles'] ) ? $option_settings['border_styles'] : true; if ( $borders_options ) { if ( ! isset( $this->advanced_fields['borders'] ) ) { self::$_->array_set( $this->advanced_fields, 'borders.default', array() ); } // Border Styles - Add current borders settings into advanced fields. $border_style_options = self::$_->array_get( $option_settings, "border_styles.{$option_name}", array() ); $border_style_name = ! empty( $border_style_options['name'] ) ? $border_style_options['name'] : $option_name; $use_focus_borders = self::$_->array_get( $borders_options, "{$border_style_name}.use_focus_borders", true ); $settings = array( 'option_category' => 'field', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'defaults' => array( 'border_radii' => 'on|3px|3px|3px|3px', 'border_styles' => array( 'width' => '0px', 'color' => '#333333', 'style' => 'solid', ), ), ); if ( $use_focus_borders ) { $settings = array_merge( $settings, array( 'fields_after' => array( 'use_focus_border_color' => array( 'label' => esc_html__( 'Use Focus Borders', 'et_builder' ), 'description' => esc_html__( 'Enabling this option will add borders to input fields when focused.', 'et_builder' ), 'type' => 'yes_no_button', 'option_category' => 'color_option', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'affects' => array( "border_radii_{$toggle_slug}_focus", "border_styles_{$toggle_slug}_focus", ), 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'default_on_front' => 'off', ), ), ) ); } $border_style_settings = array_merge( $settings, $border_style_options ); self::$_->array_set( $this->advanced_fields, "borders.{$border_style_name}", $border_style_settings ); // Border Styles Focus - Add current borders focus settings into advanced fields. $border_style_focus_options = self::$_->array_get( $option_settings, "border_styles.{$option_name}_focus", array() ); $border_style_focus_settings = array_merge( array( 'option_category' => 'field', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'depends_on' => array( 'use_focus_border_color' ), 'depends_show_if' => 'on', 'defaults' => array( 'border_radii' => 'on|3px|3px|3px|3px', 'border_styles' => array( 'width' => '0px', 'color' => '#333333', 'style' => 'solid', ), ), ), $border_style_focus_options ); self::$_->array_set( $this->advanced_fields, "borders.{$border_style_name}_focus", $border_style_focus_settings ); } // Box Shadow - Ensure box shadow attribute is exist in advanced fields. If it's not, // add box_shadow property and set empty default. $box_shadow_options = isset( $option_settings['box_shadow'] ) ? $option_settings['box_shadow'] : true; if ( $box_shadow_options ) { if ( ! isset( $this->advanced_fields['box_shadow'] ) ) { self::$_->array_set( $this->advanced_fields, 'box_shadow.default', array() ); } $box_shadow_args = is_array( $box_shadow_options ) ? $box_shadow_options : array(); $box_shadow_name = ! empty( $box_shadow_options['name'] ) ? $box_shadow_options['name'] : $option_name; // Box Shadow - Add current box shadow settings into advanced fields. $box_shadow_settings = array_merge( array( 'label' => sprintf( esc_html__( '%1$s Box Shadow', 'et_builder' ), $option_settings['label'] ), 'option_category' => 'layout', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ), $box_shadow_args ); self::$_->array_set( $this->advanced_fields, "box_shadow.{$box_shadow_name}", $box_shadow_settings ); } } $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Get css transition properties for box shadow fields. * * @param string|null $module Module slug. * * @return array */ public function get_transition_box_shadow_fields_css_props( $module = 'default' ) { /** * Instance of box shadow field class. @var ET_Builder_Module_Field_BoxShadow $box_shadow */ $box_shadow = ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); $selector = self::$_->array_get( $this->advanced_fields, "box_shadow.$module.css.main", '%%order_class%%' ); $overlay = self::$_->array_get( $this->advanced_fields, "box_shadow.$module.css.overlay", false ); $suffix = 'default' === $module ? '' : "_$module"; if ( in_array( $overlay, array( 'inset', 'always' ), true ) ) { $selector .= ', ' . $box_shadow->get_overlay_selector( $selector ); } return array( "box_shadow_horizontal{$suffix}" => array( 'box-shadow' => $selector ), "box_shadow_vertical{$suffix}" => array( 'box-shadow' => $selector ), "box_shadow_blur{$suffix}" => array( 'box-shadow' => $selector ), "box_shadow_spread{$suffix}" => array( 'box-shadow' => $selector ), "box_shadow_color{$suffix}" => array( 'box-shadow' => $selector ), ); } /** * Get css transition properties for text shadow fields. * * @param string|null $module Module slug. * * @return array */ public function get_transition_text_shadow_fields_css_props( $module = null ) { $source = null === $module ? 'text.css' : "fonts.$module.css"; $default = self::$_->array_get( $this->advanced_fields, "$source.main", '%%order_class%%' ); $selector = self::$_->array_get( $this->advanced_fields, "$source.text_shadow", $default ); $prefix = null === $module ? '' : "{$module}_"; return array( "{$prefix}text_shadow_horizontal_length" => array( 'text-shadow' => $selector ), "{$prefix}text_shadow_vertical_length" => array( 'text-shadow' => $selector ), "{$prefix}text_shadow_blur_strength" => array( 'text-shadow' => $selector ), "{$prefix}text_shadow_color" => array( 'text-shadow' => $selector ), ); } /** * Get css transition properties for filters fields. * * @param string|null $module Module slug. * * @return array */ public function get_transition_filters_fields_css_props( $module = null ) { $slug = empty( $module ) ? 'filter' : 'child_filter'; $source = empty( $module ) ? 'filters.css.main' : "$module.css.main"; $filters = array( 'hue_rotate', 'saturate', 'brightness', 'contrast', 'invert', 'sepia', 'opacity', 'blur' ); $fields = array(); $main = self::$_->array_get( $this->advanced_fields, $source, '%%order_class%%' ); $selector = $module ? self::$_->array_get( $this->advanced_fields, 'filters.child_filters_target.css.main', $main ) : $main; foreach ( $filters as $filter ) { $fields[ "{$slug}_{$filter}" ] = array( 'filter' => $selector ); } return $fields; } /** * Get css transition properties for borders fields. * * @param string|null $module Module slug. * * @return array */ public function get_transition_borders_fields_css_props( $module = 'default' ) { $suffix = 'default' === $module ? '' : "_$module"; $radius = self::$_->array_get( $this->advanced_fields, "borders.$module.css.main.border_radii", '%%order_class%%' ); $style = self::$_->array_get( $this->advanced_fields, "borders.$module.css.main.border_styles", '%%order_class%%' ); return array( "border_radii{$suffix}" => array( 'border-radius' => implode( ', ', array( $radius ) ) ), "border_width_all{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_color_all{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_width_top{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_color_top{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_width_right{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_color_right{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_width_bottom{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_color_bottom{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_width_left{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), "border_color_left{$suffix}" => array( 'border' => implode( ', ', array( $style ) ) ), ); } /** * Get margin and padding transition css properties. * * @param string|null $module Module slug. * * @return array */ public function get_transition_margin_padding_fields_css_props( $module = null ) { $key = empty( $module ) ? '' : "$module."; $suffix = empty( $module ) ? '' : "_$module"; $margin = self::$_->array_get( $this->advanced_fields, "margin_padding.{$key}css.margin", '%%order_class%%' ); $padding = self::$_->array_get( $this->advanced_fields, "margin_padding.{$key}css.padding", '%%order_class%%' ); return array( "custom_margin{$suffix}" => array( 'margin' => implode( ', ', array( $margin ) ) ), "custom_padding{$suffix}" => array( 'padding' => implode( ', ', array( $padding ) ) ), ); } /** * Get transform transition css properties. * * @param string|null $module Module slug. * * @return array */ public function get_transition_transform_css_props( $module = null ) { $key = empty( $module ) ? '' : "$module."; $suffix = empty( $module ) ? '' : "_$module"; $selector = self::$_->array_get( $this->advanced_fields, "transform.{$key}css.main", '%%order_class%%' ); /** Transform field. @see ET_Builder_Module_Field_Transform */ $defaults = array( 'scale', 'translate', 'rotate', 'skew', 'origin' ); $fields = array(); foreach ( $defaults as $name ) { $fields += array( "transform_{$name}{$suffix}" => array( 'transform' => implode( ', ', (array) $selector ) ) ); } return $fields; } /** * Get position transition css properties. * * @param string|null $module Module slug. * * @return array */ public function get_transition_position_css_props( $module = null ) { $key = empty( $module ) ? '' : "$module."; $suffix = empty( $module ) ? '' : "_$module"; $selector = self::$_->array_get( $this->advanced_fields, "position_fields.{$key}css.main", '%%order_class%%' ); $string_selector = implode( ', ', (array) $selector ); $fields = array(); $fields += array( "horizontal_offset{$suffix}" => array( 'left' => $string_selector, 'right' => $string_selector, ), ); $fields += array( "vertical_offset{$suffix}" => array( 'top' => $string_selector, 'bottom' => $string_selector, ), ); return $fields; } /** * Get font transition css properties. * * @return array */ public function get_transition_font_fields_css_props() { $items = ! empty( $this->advanced_fields['fonts'] ) ? $this->advanced_fields['fonts'] : false; if ( ! $items ) { return array(); } $font_options = array( array( 'option' => 'text_color', 'slug' => 'color', 'prop' => 'color', ), array( 'option' => 'font_size', 'slug' => 'font_size', 'prop' => 'font-size', ), array( 'option' => 'line_height', 'slug' => 'line_height', 'prop' => 'line-height', ), array( 'option' => 'letter_spacing', 'slug' => 'letter_spacing', 'prop' => 'letter-spacing', ), array( 'option' => 'text_shadow_horizontal_length', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ), array( 'option' => 'text_shadow_vertical_length', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ), array( 'option' => 'text_shadow_blur_strength', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ), array( 'option' => 'text_shadow_color', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ), array( 'option' => 'border_weight', 'slug' => 'quote', 'prop' => 'border-width', ), array( 'option' => 'border_color', 'slug' => 'quote', 'prop' => 'border-color', ), ); $fields = array(); foreach ( $items as $item => $field ) { foreach ( $font_options as $key ) { $default = ! empty( $field['css'] ) && ! empty( $field['css']['main'] ) ? $field['css']['main'] : '%%order_class%%'; $value = ! empty( $field['css'] ) && ! empty( $field['css'][ $key['slug'] ] ) ? $field['css'][ $key['slug'] ] : $default; $fields[ "{$item}_{$key['option']}" ] = array( $key['prop'] => $value, ); } } return $fields; } /** * Get height transition css properties. * * @param string $prefix The prefix string that may be added to field name. * * @return array */ public function get_transition_height_fields_css_props( $prefix = '' ) { $options = self::$_->array_get( $this->advanced_fields, 'height' ); if ( ! is_array( $options ) ) { return array(); } $height = et_pb_height_options( $prefix ); $max_height = et_pb_max_height_options( $prefix ); $selector = self::$_->array_get( $options, 'css.main', '%%order_class%%' ); return array( $height->get_field() => array( 'height' => $selector ), $max_height->get_field() => array( 'max-height' => $selector ), ); } /** * Get css transition properties for image fields. * * @return array */ public function get_transition_image_fields_css_props() { $fields = array(); $fields = array_merge( $this->get_transition_filters_fields_css_props( 'image' ), $fields ); $fields = array_merge( $this->get_transition_borders_fields_css_props( 'image' ), $fields ); $fields = array_merge( $this->get_transition_box_shadow_fields_css_props( 'image' ), $fields ); return $fields; } /** * Get css transition properties for button fields. * * * * @return array */ public function get_transition_button_fields_css_props() { $buttons = self::$_->array_get( $this->advanced_fields, 'button', array() ); $fields = array(); if ( empty( $buttons ) ) { return array(); } foreach ( $buttons as $key => $button ) { $selector = self::$_->array_get( $button, 'css.main', '%%order_class%%' ); $box_shadow_style = array( $selector, $this->add_suffix_to_selectors( ' > .box-shadow-overlay', $selector ), ); $field = array( "{$key}_text_color" => array( 'color' => $selector ), "{$key}_text_size" => array( 'font-size' => $selector, 'line-height' => $selector, 'padding' => $selector, ), "{$key}_bg_color" => array( 'background-color' => $selector ), "{$key}_border_width" => array( 'border' => $selector ), "{$key}_border_color" => array( 'border' => $selector ), "{$key}_border_radius" => array( 'border-radius' => $selector ), "{$key}_letter_spacing" => array( 'letter-spacing' => $selector ), "{$key}text_shadow_horizontal_length" => array( 'text-shadow' => $selector ), "{$key}text_shadow_vertical_length" => array( 'text-shadow' => $selector ), "{$key}text_shadow_blur_strength" => array( 'text-shadow' => $selector ), "{$key}text_shadow_color" => array( 'text-shadow' => $selector ), "box_shadow_style_$key" => array( 'box-shadow' => implode( ', ', $box_shadow_style ) ), ); $fields = array_merge( $fields, $field ); } return $fields; } /** * Get transition form field CSS props. * * @since 3.23 * * @return array Selector for each fields. */ public function get_transition_form_field_fields_css_props() { $fields_input = self::$_->array_get( $this->advanced_fields, 'form_field', array() ); $fields = array(); // Ensure fields input is exist. if ( empty( $fields_input ) ) { return array(); } foreach ( $fields_input as $key => $form_field ) { $selector = self::$_->array_get( $form_field, 'css.main', '%%order_class%% input' ); $placeholders = "$selector::placeholder, $selector::-webkit-input-placeholder, $selector::-moz-placeholder, $selector::-ms-input-placeholder"; // Set all individual fields that need transition during hover event. $fields = array_merge( $fields, array( "{$key}_background_color" => array( 'background-color' => $selector ), "{$key}_text_color" => array( 'color' => implode( ', ', array( $placeholders, $selector ) ) ), "{$key}_focus_background_color" => array( 'background-color' => $selector ), "{$key}_focus_text_color" => array( 'color' => implode( ', ', array( $placeholders, $selector ) ) ), "{$key}_custom_margin" => array( 'margin' => $selector ), "{$key}_custom_padding" => array( 'padding' => $selector ), ) ); // Merge group fields such as borders, box shadow, and text shadow. $fields = array_merge( $fields, $this->get_transition_borders_fields_css_props( $key ), $this->get_transition_borders_fields_css_props( "{$key}_focus" ), $this->get_transition_box_shadow_fields_css_props( $key ) ); } return $fields; } /** * Get css transition properties for gutter wudth fields. * * * * @return array */ public function get_transition_gutter_fields_css_props() { $gutter_selector = 'et_pb_section' === $this->slug ? '%%order_class%% .et_pb_gutter_hover *' : '%%order_class%%.et_pb_gutter_hover *'; // animate width, padding and margin if gutter width has hover options. return array( 'gutter_width' => array( 'width' => $gutter_selector, 'margin' => $gutter_selector, 'padding' => $gutter_selector, ), ); } /** * Get CSS fields transition. * * @since 3.23 Add form field options group and background image on the fields list. */ public function get_transition_fields_css_props() { $default = $this->main_css_element; $text_main = self::$_->array_get( $this->advanced_fields, 'text.css.main', $default ); $fields = array( 'background_layout' => array( 'color' => $text_main ), 'background' => array( 'background-color' => self::$_->array_get( $this->advanced_fields, 'background.css.main', $default ), 'background-image' => self::$_->array_get( $this->advanced_fields, 'background.css.main', $default ), ), 'max_width' => array( 'max-width' => $default ), 'width' => array( 'width' => $default ), 'text_color' => array( 'color' => self::$_->array_get( $this->advanced_fields, 'text.css.color', $text_main ), ), ); $fields = array_merge( $this->get_transition_filters_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_box_shadow_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_text_shadow_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_image_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_borders_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_margin_padding_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_button_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_form_field_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_font_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_gutter_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_height_fields_css_props(), $fields ); $fields = array_merge( $this->get_transition_transform_css_props(), $fields ); $fields = array_merge( $this->get_transition_position_css_props(), $fields ); return apply_filters( 'et_builder_hover_transitions_map', $fields ); } /** * Add link options fields to all modules * * @since 3.15.1 */ protected function _add_link_options_fields() { // Link Options are added by default if module has partial or full VB support. if ( $this->has_vb_support() ) { $this->advanced_fields['link_options'] = self::$_->array_get( $this->advanced_fields, 'link_options', array() ); } elseif ( ! $this->has_advanced_fields ) { // Disable if module doesn't set advanced_fields property and has no VB support. return; } // Set link_options to false to disable Link options. if ( false === self::$_->array_get( $this->advanced_fields, 'link_options' ) ) { return; } // Link options settings have to be array. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'link_options' ) ) ) { return; } $this->settings_modal_toggles['general']['toggles']['link_options'] = array( 'title' => et_builder_i18n( 'Link' ), 'priority' => 70, ); $additional_options = array(); $i18n =& self::$i18n; if ( ! isset( $i18n['link'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['link'] = array( 'url' => array( 'label' => esc_html__( 'Module Link URL', 'et_builder' ), 'description' => esc_html__( 'When clicked the module will link to this URL.', 'et_builder' ), ), 'target' => array( 'label' => esc_html__( 'Module Link Target', 'et_builder' ), 'description' => esc_html__( 'Here you can choose whether or not your link opens in a new window', 'et_builder' ), 'options' => array( 'off' => esc_html__( 'In The Same Window', 'et_builder' ), 'on' => esc_html__( 'In The New Tab', 'et_builder' ), ), ), ); // phpcs:enable } // Translate the whole label as a phrase instead of replacing placeholder with section / row / module translation // Less error prone for translator and the translation. Phrase might be structured differently in some language. switch ( $this->slug ) { case 'et_pb_section': $url_label = esc_html__( 'Section Link URL', 'et_builder' ); $target_label = esc_html__( 'Section Link Target', 'et_builder' ); break; case 'et_pb_row': case 'et_pb_row_inner': $url_label = esc_html__( 'Row Link URL', 'et_builder' ); $target_label = esc_html__( 'Row Link Target', 'et_builder' ); break; case 'et_pb_column': case 'et_pb_column_inner': $url_label = esc_html__( 'Column Link URL', 'et_builder' ); $target_label = esc_html__( 'Column Link Target', 'et_builder' ); break; default: $url_label = $i18n['link']['url']['label']; $target_label = $i18n['link']['target']['label']; break; } $additional_options['link_option_url'] = array( 'label' => $url_label, 'description' => $i18n['link']['url']['description'], 'type' => 'text', 'option_category' => 'configuration', 'toggle_slug' => 'link_options', 'dynamic_content' => 'url', ); $additional_options['link_option_url_new_window'] = array( 'label' => $target_label, 'description' => $i18n['link']['target']['description'], 'type' => 'select', 'option_category' => 'configuration', 'options' => array( 'off' => $i18n['link']['target']['options']['off'], 'on' => $i18n['link']['target']['options']['on'], ), 'toggle_slug' => 'link_options', 'default_on_front' => 'off', ); $this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options ); } /** * Get transition style. * * @param array $props Transition css properties. * @param string $device Device. * * @since 3.23 Add $device parameter to support responsive settings. * * @return string */ public function get_transition_style( array $props = array(), $device = 'desktop' ) { $duration = et_pb_transition_options()->get_duration( $this->props, $device ); $easing = et_pb_transition_options()->get_easing( $this->props, $device ); $delay = et_pb_transition_options()->get_delay( $this->props, $device ); $transition_css = array(); foreach ( $props as $prop ) { $transition_css[] = sprintf( '%1$s %2$s %3$s %4$s', esc_attr( $prop ), esc_attr( $duration ), esc_attr( $easing ), esc_attr( $delay ) ); } // Sticky module that has custom width for sticky style will need its `left` property to // be animated as well due to fixed positioning used for sticky state. if ( $this->is_sticky_module && in_array( 'width', $props, true ) ) { $transition_css[] = sprintf( 'left %1$s %2$s %3$s', esc_attr( $duration ), esc_attr( $easing ), esc_attr( $delay ) ); } return 'transition: ' . implode( ', ', $transition_css ) . ';'; } /** * Setup hover transitions. * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support */ public function setup_hover_transitions( $function_name ) { $transitions_map = $this->get_transition_fields_css_props(); $selectors = array(); $transitions = array(); $hover = et_pb_hover_options(); $hover_suffix = $hover->get_suffix(); $sticky = et_pb_sticky_options(); $sticky_suffix = $sticky->get_suffix(); // we need to loop transitions array so cases of prefixed prop names can also be caught. foreach ( $transitions_map as $prop_name => $css_props ) { $prop_name_hover = "{$prop_name}{$hover_suffix}"; $prop_name_sticky = "{$prop_name}{$sticky_suffix}"; // background is a special case because it also contains the "background_color" property. if ( 'background' === $prop_name ) { // we can continue if hover background color or background image is not set. if ( ! $hover->get_value( 'background_color', $this->props ) && ! $hover->get_value( 'background_image', $this->props ) && ! $sticky->get_value( 'background_color', $this->props ) && ! $sticky->get_value( 'background_image', $this->props ) ) { continue; } } elseif ( empty( $this->props[ $prop_name_hover ] ) && empty( $this->props[ $prop_name_sticky ] ) ) { // continue if hover or sticky value is empty. continue; } // continue if sticky / hover option is not enabled. $is_hover_option_enabled = $hover->is_enabled( $prop_name, $this->props ); $is_sticky_option_enabled = $sticky->is_enabled( $prop_name, $this->props ); $is_sticky_transition_enabled = 'on' === et_()->array_get( $this->props, 'sticky_transition' ); if ( ! $is_hover_option_enabled && ! ( $is_sticky_transition_enabled && $is_sticky_option_enabled ) ) { continue; } // add the css property for the transition. $transitions = array_merge( $transitions, array_keys( $css_props ) ); foreach ( $css_props as $selector ) { $selector = is_array( $selector ) ? $selector : array( $selector ); $selectors = array_merge( $selectors, $selector ); } } return [ $transitions, $selectors ]; } /** * Process hover transitions. * * @param string $function_name Module alias. * * @since 4.6.0 add sticky style support */ public function process_hover_transitions( $function_name ) { list( $transitions, $selectors ) = $this->setup_hover_transitions( $function_name ); // don't apply transitions if none are needed. if ( empty( $transitions ) ) { return; } $transitions = array_unique( $transitions ); $transition_selectors = implode( ', ', array_unique( $selectors ) ); $transition_style = $this->get_transition_style( $transitions ); $el_style = array( 'selector' => $transition_selectors, 'declaration' => esc_html( $transition_style ), ); self::set_style( $function_name, $el_style ); // Tablet. $transition_style_tablet = $this->get_transition_style( $transitions, 'tablet' ); if ( $transition_style_tablet !== $transition_style ) { $el_style = array( 'selector' => $transition_selectors, 'declaration' => esc_html( $transition_style_tablet ), 'media_query' => self::get_media_query( 'max_width_980' ), ); self::set_style( $function_name, $el_style ); } // Phone. $transition_style_phone = $this->get_transition_style( $transitions, 'phone' ); if ( $transition_style_phone !== $transition_style || $transition_style_phone !== $transition_style_tablet ) { $el_style = array( 'selector' => $transition_selectors, 'declaration' => esc_html( $transition_style_phone ), 'media_query' => self::get_media_query( 'max_width_767' ), ); self::set_style( $function_name, $el_style ); } } /** * Add custom css fields. e.g before, main_element and after. */ protected function _add_custom_css_fields() { if ( isset( $this->custom_css_tab ) && ! $this->custom_css_tab ) { return; } $custom_css_fields_processed = array(); $current_module_unique_class = '.' . $this->slug . '_' . "<%= typeof( module_order ) !== 'undefined' ? module_order : '<span class=\"et_pb_module_order_placeholder\"></span>' %>"; $main_css_element_output = isset( $this->main_css_element ) ? $this->main_css_element : '%%order_class%%'; $main_css_element_output = str_replace( '%%order_class%%', $current_module_unique_class, $main_css_element_output ); $custom_css_default_options = array( 'before' => array( 'label' => et_builder_i18n( 'Before' ), 'selector' => ':before', 'no_space_before_selector' => true, ), 'main_element' => array( 'label' => et_builder_i18n( 'Main Element' ), ), 'after' => array( 'label' => et_builder_i18n( 'After' ), 'selector' => ':after', 'no_space_before_selector' => true, ), 'free_form' => array( 'label' => et_builder_i18n( 'CSS' ), ), ); $custom_css_fields = apply_filters( 'et_default_custom_css_fields', $custom_css_default_options ); $this->custom_css_fields = $this->get_custom_css_fields_config(); if ( $this->custom_css_fields ) { $custom_css_fields = array_merge( $custom_css_fields, $this->custom_css_fields ); } $this->custom_css_fields = apply_filters( 'et_custom_css_fields_' . $this->slug, $custom_css_fields ); // optional settings names in custom css options. $additional_option_slugs = array( 'description', 'priority' ); foreach ( $custom_css_fields as $slug => $option ) { $selector_value = isset( $option['selector'] ) ? $option['selector'] : ''; $selector_contains_module_class = false !== strpos( $selector_value, '%%order_class%%' ) ? true : false; $selector_output = '' !== $selector_value ? str_replace( '%%order_class%%', $current_module_unique_class, $option['selector'] ) : ''; $custom_css_fields_processed[ "custom_css_{$slug}" ] = array( 'label' => sprintf( '%1$s:<span>%2$s%3$s%4$s</span>', $option['label'], ! $selector_contains_module_class ? $main_css_element_output : '', ! isset( $option['no_space_before_selector'] ) && isset( $option['selector'] ) ? ' ' : '', $selector_output ), 'type' => 'custom_css', 'tab_slug' => 'custom_css', 'toggle_slug' => 'custom_css', 'option_category' => 'layout', 'no_colon' => true, ); // update toggle slug and option category for $this->custom_css_fields. $this->custom_css_fields[ $slug ]['toggle_slug'] = 'custom_css'; $this->custom_css_fields[ $slug ]['option_category'] = 'layout'; // add optional settings if needed. foreach ( $additional_option_slugs as $option_slug ) { if ( isset( $option[ $option_slug ] ) ) { $custom_css_fields_processed[ "custom_css_{$slug}" ][ $option_slug ] = $option[ $option_slug ]; } } } if ( ! empty( $custom_css_fields_processed ) ) { $this->fields_unprocessed = array_merge( $this->fields_unprocessed, $custom_css_fields_processed ); } $i18n =& self::$i18n; if ( ! isset( $i18n['css'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['css'] = array( 'classes' => esc_html__( 'CSS ID & Classes', 'et_builder' ), ); // phpcs:enable } $default_custom_css_toggles = array( 'classes' => $i18n['css']['classes'], 'custom_css' => et_builder_i18n( 'Custom CSS' ), ); $this->_add_settings_modal_toggles( 'custom_css', $default_custom_css_toggles ); } /** * Add toggles in settings modal. * * @param string $tab_slug Toggle tab slug. * @param string $toggles_array Toggles. */ protected function _add_settings_modal_toggles( $tab_slug, $toggles_array ) { if ( ! isset( $this->settings_modal_toggles[ $tab_slug ] ) ) { $this->settings_modal_toggles[ $tab_slug ] = array(); } if ( ! isset( $this->settings_modal_toggles[ $tab_slug ]['toggles'] ) ) { $this->settings_modal_toggles[ $tab_slug ]['toggles'] = array(); } // get the only toggles which do not exist. $processed_toggles = array_diff_key( $toggles_array, $this->settings_modal_toggles[ $tab_slug ]['toggles'] ); $this->settings_modal_toggles[ $tab_slug ]['toggles'] = array_merge( $this->settings_modal_toggles[ $tab_slug ]['toggles'], $processed_toggles ); } /** * Add settings under sub toggles. * * @since 3.23 * @since 3.26.7 Add support to set custom icons on sub toggles. * * @param string $tab_slug Current tab slug. * @param string $toggle_slug Current toggle slug. * @param array $sub_toggle_items Sub toggles settings need to be added. * @param boolean $tabbed_subtoggles Tabbed sub toggle status. * @param boolean $bb_icons_support BB icons support status. */ protected function _add_settings_modal_sub_toggles( $tab_slug, $toggle_slug, $sub_toggle_items, $tabbed_subtoggles = false, $bb_icons_support = false ) { // Ensure tab slug is exist. if ( ! isset( $this->settings_modal_toggles[ $tab_slug ] ) ) { $this->settings_modal_toggles[ $tab_slug ] = array(); } // Ensure toggles is exist. if ( ! isset( $this->settings_modal_toggles[ $tab_slug ]['toggles'] ) ) { $this->settings_modal_toggles[ $tab_slug ]['toggles'] = array(); } // Stop the process here if the toggle slug doesn't exist. It should exist before we add // sub toggles. if ( ! isset( $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { return; } // Don't replace existing sub toggles. $toggle = $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ]; $sub_toggles = isset( $toggle['sub_toggles'] ) ? $toggle['sub_toggles'] : array(); if ( ! empty( $sub_toggles ) ) { return; } // Set sub toggles. $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ]['sub_toggles'] = $sub_toggle_items; // Set tabbed sub toggles status. if ( $tabbed_subtoggles ) { $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ]['tabbed_subtoggles'] = $tabbed_subtoggles; } // Set BB icons support status. if ( $bb_icons_support ) { $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ]['bb_icons_support'] = $bb_icons_support; } } /** * Get all the fields. * * @return array|mixed|void */ private function _get_fields() { $this->fields = array(); $this->fields = $this->fields_unprocessed; $this->fields = $this->process_fields( $this->fields ); $this->fields = apply_filters( 'et_builder_module_fields_' . $this->slug, $this->fields ); foreach ( $this->fields as $field_name => $field ) { $this->fields[ $field_name ] = apply_filters( 'et_builder_module_fields_' . $this->slug . '_field_' . $field_name, $field ); // Option template replaces field's array configuration into string which refers to // saved template data & template id. if ( is_array( $this->fields[ $field_name ] ) ) { $this->fields[ $field_name ]['name'] = $field_name; } } return $this->fields; } /** * Checks if the field value equals its default value * * @param string $name Field name. * @param mixed $value Field value. * * @return bool */ protected function _is_field_default( $name, $value ) { if ( ! isset( $this->fields_unprocessed[ $name ] ) ) { // field does not exist. return false; } $field = $this->fields_unprocessed[ $name ]; $default = self::$_->array_get( $field, 'default', '' ); $default_on_front = self::$_->array_get( $field, 'default_on_front', 'not_found' ); if ( 'not_found' !== $default_on_front ) { return $default_on_front === $value; } if ( is_array( $default ) && ! empty( $default[0] ) && is_array( $default[1] ) ) { // This is a conditional default. Let's try to resolve it. list ( $depend_field, $conditional_defaults ) = $default; $default_key = self::$_->array_get( $this->props, $depend_field, key( $conditional_defaults ) ); $default = self::$_->array_get( $conditional_defaults, $default_key, null ); } return $default === $value; } /** * Intended to be overridden as needed. * * @param array $fields Module fields. * * @return mixed|void */ public function process_fields( $fields ) { return apply_filters( 'et_pb_module_processed_fields', $fields, $this->slug ); } /** * Get the settings fields data for this element. * * @since 1.0 * @todo Finish documenting return value's structure. * * @return array[] { * Settings Fields * * @type mixed[] $setting_field_key { * Setting Field Data * * @type string $type Setting field type. * @type string $id CSS id for the setting. * @type string $label Text label for the setting. Translatable. * @type string $description Description for the settings. Translatable. * @type string $class Optional. Css class for the settings. * @type string[] $affects Optional. The keys of all settings that depend on this setting. * @type string[] $depends_on Optional. The keys of all settings that this setting depends on. * @type string $depends_show_if Optional. Only show this setting when the settings * on which it depends has a value equal to this. * @type string $depends_show_if_not Optional. Only show this setting when the settings * on which it depends has a value that is not equal to this. * ... * } * ... * } */ public function get_fields() { return array(); } /** * Returns props value by provided key, if the value is empty, returns the default value * * @param string $prop Prop key. * @param mixed $default Default value. * * @return mixed|null */ public function prop( $prop, $default = null ) { return et_builder_module_prop( $prop, $this->props, $default ); } /** * Get module defined fields + automatically generated fields * * @since 3.23 Add auto generate responsive settings suffix based on mobile_options parameter. * * @internal Added to make get_fields() lighter. Initially added during BFB's 3rd party support * * @return array */ public function get_complete_fields() { $fields = $this->get_fields(); $responsive_suffixes = array( 'tablet', 'phone', 'last_edited' ); // Loop fields and modify it if needed. foreach ( $fields as $field_name => $field ) { // Automatically generate responsive fields. $supports_responsive = ( isset( $field['responsive'] ) && $field['responsive'] ) || ( isset( $field['mobile_options'] ) && $field['mobile_options'] ); if ( $supports_responsive ) { // Get tab and toggle slugs value. $tab_slug = isset( $field['tab_slug'] ) ? $field['tab_slug'] : ''; $toggle_slug = isset( $field['toggle_slug'] ) ? $field['toggle_slug'] : ''; $priority = isset( $field['priority'] ) ? $field['priority'] : false; foreach ( $responsive_suffixes as $responsive_suffix ) { $responsive_field_name = "{$field_name}_{$responsive_suffix}"; $fields[ $responsive_field_name ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); if ( false !== $priority ) { $fields[ $responsive_field_name ]['priority'] = $priority; } } } } // Add general fields for modules including Columns. if ( ( ! isset( $this->type ) || 'child' !== $this->type ) || in_array( $this->slug, array( 'et_pb_column', 'et_pb_column_inner' ), true ) ) { $i18n =& self::$i18n; if ( ! isset( $i18n['complete'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['complete'] = array( 'section' => esc_html__( 'section', 'et_builder' ), 'row' => esc_html__( 'row', 'et_builder' ), 'module' => esc_html__( 'module', 'et_builder' ), 'disabled' => array( 'label' => esc_html__( 'Disable on', 'et_builder' ), 'description' => esc_html__( 'This will disable the %1$s on selected devices', 'et_builder' ), ), 'admin' => array( 'description' => esc_html__( 'This will change the label of the module in the builder for easy identification.', 'et_builder' ), ), 'id' => array( 'label' => esc_html__( 'CSS ID', 'et_builder' ), 'description' => esc_html__( "Assign a unique CSS ID to the element which can be used to assign custom CSS styles from within your child theme or from within Divi's custom CSS inputs.", 'et_builder' ), ), 'class' => array( 'label' => esc_html__( 'CSS Class', 'et_builder' ), 'description' => esc_html__( "Assign any number of CSS Classes to the element, separated by spaces, which can be used to assign custom CSS styles from within your child theme or from within Divi's custom CSS inputs.", 'et_builder' ), ), '' => array(), ); // phpcs:enable } $disabled_on_fields = array(); $slug_labels = array( 'et_pb_section' => $i18n['complete']['section'], 'et_pb_row' => $i18n['complete']['row'], ); $disable_label = isset( $slug_labels[ $this->slug ] ) ? $slug_labels[ $this->slug ] : $i18n['complete']['module']; $disabled_on_fields = array( 'disabled_on' => array( 'label' => $i18n['complete']['disabled']['label'], 'description' => sprintf( $i18n['complete']['disabled']['description'], $disable_label ), 'type' => 'multiple_checkboxes', 'options' => array( 'phone' => et_builder_i18n( 'Phone' ), 'tablet' => et_builder_i18n( 'Tablet' ), 'desktop' => et_builder_i18n( 'Desktop' ), ), 'additional_att' => 'disable_on', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'visibility', ), ); $common_general_fields = array( 'admin_label' => array( 'label' => et_builder_i18n( 'Admin Label' ), 'description' => $i18n['complete']['admin']['description'], 'type' => 'text', 'option_category' => 'configuration', 'toggle_slug' => 'admin_label', ), 'module_id' => array( 'label' => $i18n['complete']['id']['label'], 'description' => $i18n['complete']['id']['description'], 'type' => 'text', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'classes', 'option_class' => 'et_pb_custom_css_regular', ), 'module_class' => array( 'label' => $i18n['complete']['class']['label'], 'description' => $i18n['complete']['class']['description'], 'type' => 'text', 'option_category' => 'configuration', 'tab_slug' => 'custom_css', 'toggle_slug' => 'classes', 'option_class' => 'et_pb_custom_css_regular', ), ); $general_fields = array_merge( $disabled_on_fields, $common_general_fields ); $fields = array_merge( $fields, apply_filters( 'et_builder_module_general_fields', $general_fields ) ); } return $fields; } /** * Get configuration for module's advanced fields. This method is meant to be overridden in module classes. * * @since 3.1 * * @return array[] {@see self::$advanced_fields} */ public function get_advanced_fields_config() { return $this->advanced_fields; } /** * Get configuration for module's custom css fields. This method is meant to be overridden in module classes. * * @since 3.1 * * @return array[] {@see self::$custom_css_fields} */ public function get_custom_css_fields_config() { return $this->custom_css_fields; } /** * Returns Global Presets settings */ public static function get_global_presets() { return self::$global_presets_manager->get_global_presets(); } /** * Get custom tabs for the module's settings modal. This method is meant to be overridden in module classes. * * @since 3.1 * * @return array[] {@see self::$settings_modal_tabs} */ public function get_settings_modal_tabs() { return $this->settings_modal_tabs; } /** * Get toggles for the module's settings modal. This method is meant to be overridden in module classes. * * @since 3.1 * * @return array[] {@see self::$settings_modal_toggles} */ public function get_settings_modal_toggles() { return $this->settings_modal_toggles; } /** * Generate column fields. * * @param integer $column_number number of column. * @param array $base_fields base fields for column. * * @return array column fields */ public function get_column_fields( $column_number = 1, $base_fields = array() ) { $fields = array(); // Loop column's base fields. foreach ( $base_fields as $field_name => $field ) { // Loop (number of column) times. for ( $index = 1; $index <= $column_number; $index++ ) { // Some attribute's id is not located at the bottom of the attribute name. if ( isset( $field['has_custom_index_location'] ) && $field['has_custom_index_location'] ) { $column_name = str_replace( '%column_index%', $index, $field_name ); } else { $column_name = "{$field_name}_{$index}"; } $fields[ $column_name ] = array( 'type' => 'skip', ); // Most column field is an empty-type attribute. Non-empty attribute are likely // attribute for computed field which needs to have suffix ID. if ( ! empty( $field ) ) { // Append suffix to the module variable. foreach ( $field as $attr_name => $attr_value ) { if ( 'has_custom_index_location' === $attr_name ) { continue; } if ( is_array( $attr_value ) && 'computed_callback' !== $attr_name ) { $attr_value = $this->_append_suffix( $attr_value, $index ); } $fields[ $column_name ][ $attr_name ] = $attr_value; } } } } return $fields; } /** * Append suffix to simple array value. * * @param array $values array value. * @param string $suffix intended suffix for output's array. * * @return array suffixed value */ public function _append_suffix( $values, $suffix ) { $output = array(); foreach ( $values as $value ) { $output[] = "{$value}_{$suffix}"; } return $output; } /** * Returns unprocessed attrs. * * @since 4.15.0 * * @return mixed[] */ public function get_attrs_unprocessed() { return $this->attrs_unprocessed; } /** * Returns module style priority. * * @return int */ public function get_style_priority() { return $this->_style_priority; } /** * Get current post's post type. * * @return string */ public function get_post_type() { global $post, $et_builder_post_type; // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( isset( $_POST['et_post_type'] ) && ! $et_builder_post_type ) { $et_builder_post_type = sanitize_text_field( $_POST['et_post_type'] ); } // phpcs:enable if ( is_a( $post, 'WP_POST' ) && ( is_admin() || ! isset( $et_builder_post_type ) ) ) { return $post->post_type; } else { $layout_type = self::get_theme_builder_layout_type(); if ( $layout_type ) { return $layout_type; } return isset( $et_builder_post_type ) ? $et_builder_post_type : 'post'; } } /** * Removed extra tabs/newlines from template. * * @param string $content Template content. * * @return string|string[] */ public static function optimize_bb_chunk( $content ) { if ( ! ET_BUILDER_OPTIMIZE_TEMPLATES ) { return $content; } return str_replace( self::$_unique_bb_strip, '', $content ); } /** * Optimize template content. * * @param string $content Template content. * * @return string|string[] */ public static function get_unique_bb_key( $content ) { if ( ! ET_BUILDER_OPTIMIZE_TEMPLATES ) { return $content; } $content = self::optimize_bb_chunk( $content ); if ( isset( self::$_unique_bb_keys_map[ $content ] ) ) { $key = self::$_unique_bb_keys_map[ $content ]; } else { self::$_unique_bb_keys_values[] = $content; $key = count( self::$_unique_bb_keys_values ) - 1; self::$_unique_bb_keys_map[ $content ] = $key; } $content = "<!-- $key -->"; return $content; } /** * Wrap settings option in wrapper div e.g `.et-pb-option-standard`. * * @param string $option_output Option markup. * @param array $field Field settings. * @param string $name Field name. * * @return string|string[] */ public function wrap_settings_option( $option_output, $field, $name = '' ) { // Option template convert array field into string id; return early to prevent error. if ( is_string( $field ) ) { return self::get_unique_bb_key( $option_output ); } $depends = false; $new_depends = isset( $field['show_if'] ) || isset( $field['show_if_not'] ); $depends_attr = ''; if ( ! $new_depends && ( isset( $field['depends_show_if'] ) || isset( $field['depends_show_if_not'] ) ) ) { $depends = true; if ( isset( $field['depends_show_if_not'] ) ) { $depends_show_if_not = is_array( $field['depends_show_if_not'] ) ? implode( ',', $field['depends_show_if_not'] ) : $field['depends_show_if_not']; $depends_attr = sprintf( ' data-depends_show_if_not="%s"', esc_attr( $depends_show_if_not ) ); } else { $depends_attr = sprintf( ' data-depends_show_if="%s"', esc_attr( $field['depends_show_if'] ) ); } } if ( isset( $field['depends_on_responsive'] ) ) { $depends_attr .= sprintf( ' data-depends_on_responsive="%s"', esc_attr( implode( ',', $field['depends_on_responsive'] ) ) ); } // Overriding background color's attribute, turning it into appropriate background attributes. if ( isset( $field['type'] ) && isset( $field['name'] ) && 'background_color' === $field['name'] && ! self::$_->array_get( $field, 'skip_background_ui' ) ) { $field['type'] = 'background'; // Removing depends default variable which hides background color for unified background field UI. if ( isset( $field['depends_show_if'] ) ) { unset( $field['depends_show_if'] ); } } $output = sprintf( '%6$s<div class="et-pb-option et-pb-option--%10$s%1$s%2$s%3$s%8$s%9$s%12$s%13$s"%4$s tabindex="-1" data-option_name="%11$s">%5$s</div>%7$s', ( ! empty( $field['type'] ) && 'tiny_mce' === $field['type'] ? ' et-pb-option-main-content' : '' ), $depends || $new_depends ? ' et-pb-depends' : '', ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? ' et_pb_hidden' : '' ), ( $depends ? $depends_attr : '' ), "\n\t\t\t\t" . $option_output . "\n\t\t\t", "\t", "\n\n\t\t", ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? esc_attr( sprintf( ' et-pb-option-%1$s', $field['name'] ) ) : '' ), ( ! empty( $field['option_class'] ) ? ' ' . $field['option_class'] : '' ), isset( $field['type'] ) ? esc_attr( $field['type'] ) : '', esc_attr( $field['name'] ), isset( $field['specialty_only'] ) && 'yes' === $field['specialty_only'] ? ' et-pb-specialty-only-option' : '', $new_depends ? ' et-pb-new-depends' : '' ); if ( ! empty( $field['hover'] ) ) { if ( 'tabs' === $field['hover'] ) { $this->last_hover_tab_field = $name; } $hover = $this->last_hover_tab_field; if ( $hover ) { $begin = '<div class="et-pb-option '; $pos = strpos( $output, $begin ); if ( $pos >= 0 ) { $output = substr_replace( $output, "<div data-depends_hover=\"$hover\" class=\"et-pb-option-standard et-pb-option ", $pos, strlen( $begin ) ); } } } return self::get_unique_bb_key( $output ); } /** * Get field renderer data e.g renderer method. * * @param array $field Field options. * * @return array|mixed|void */ public function get_field_renderer( $field ) { if ( ! isset( $field['type'] ) && ! isset( $field['renderer'] ) ) { return array(); } // Make it backward compatible with old 3rd party modules which use custom render methods. $renderer_method = isset( $field['renderer'] ) ? $field['renderer'] : ''; $renderer_with_field = isset( $field['renderer_with_field'] ) ? $field['renderer_with_field'] : false; $renderer_data = array(); if ( isset( $field['type'] ) ) { switch ( $field['type'] ) { case 'categories': // after 3rd party support release taxonomy name for Shop module has been changed to `product_cat` // so check also for `product_category` for backward compatibility. if ( isset( $field['taxonomy_name'] ) && self::$_->includes( $field['taxonomy_name'], 'product' ) ) { $renderer_method = 'et_builder_include_categories_shop_option'; } else { $renderer_method = 'et_builder_include_categories_option'; } break; case 'select_sidebar': $renderer_method = 'et_builder_get_widget_areas'; break; case 'select_icon': if ( isset( $field['renderer_options'] ) && isset( $field['renderer_options']['icons_list'] ) && 'icon_down' === $field['renderer_options']['icons_list'] ) { $renderer_method = 'et_pb_get_font_down_icon_list'; } else { $renderer_method = 'et_pb_get_font_icon_list'; } $renderer_with_field = true; break; case 'upload_gallery': $renderer_method = 'et_builder_get_gallery_settings'; break; case 'center_map': $renderer_method = 'et_builder_generate_center_map_setting'; break; case 'border-radius': $renderer_method = array( 'class' => 'ET_Builder_Module_Field_Template_Border_Radius', ); break; case 'composite': if ( isset( $field['composite_type'] ) && 'default' === $field['composite_type'] ) { $renderer_method = array( 'class' => 'ET_Builder_Module_Field_Template_Tabbed', ); } elseif ( isset( $field['composite_type'] ) && 'tabbed' === $field['composite_type'] && 'border' === $field['option_category'] ) { $renderer_method = array( 'class' => 'ET_Builder_Module_Field_Template_Border_Styles', ); } break; } } if ( '' !== $renderer_method ) { $renderer_data = array( 'renderer' => $renderer_method, 'renderer_options' => isset( $field['renderer_options'] ) ? $field['renderer_options'] : array(), 'renderer_with_field' => $renderer_with_field, ); } return apply_filters( 'et_bb_field_renderer_data', $renderer_data, $field ); } /** * Prepare module field (option) for use within BB microtemplates. * The own field renderer can be used. * * @param array $field Module field. * * @param string $name Unused name param. * * @return mixed|string Html code of the field */ public function wrap_settings_option_field( $field, $name = '' ) { $use_container_wrapper = isset( $field['use_container_wrapper'] ) && ! $field['use_container_wrapper'] ? false : true; $field_renderer = $this->get_field_renderer( $field ); if ( ! empty( $field_renderer ) && is_array( $field_renderer['renderer'] ) && ! empty( $field_renderer['renderer']['class'] ) ) { // cut off 'ET_Builder_Module_Field_Template_' part from renderer definition. $class_name_without_prefix = strtolower( str_replace( 'ET_Builder_Module_Field_Template_', '', $field_renderer['renderer']['class'] ) ); // split class name string by underscore symbol. $file_name_parts = explode( '_', $class_name_without_prefix ); if ( ! empty( $file_name_parts ) ) { // the first symbol of class name must be uppercase. $last_index = count( $file_name_parts ) - 1; $file_name_parts[ $last_index ] = ucwords( $file_name_parts[ $last_index ] ); // load renderer class from 'module/field/template/' directory accordingly class name and class directory hierarchy. require_once ET_BUILDER_DIR . 'module/field/template/' . implode( DIRECTORY_SEPARATOR, $file_name_parts ) . '.php'; $renderer = new $field_renderer['renderer']['class'](); // before calling the 'render' method make sure the instantiated class is child of 'ET_Builder_Module_Field_Template_Base'. if ( is_subclass_of( $field_renderer['renderer']['class'], 'ET_Builder_Module_Field_Template_Base' ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found $field_el = call_user_func( array( $renderer, 'render' ), $field, $this ); } } } elseif ( ! empty( $field_renderer ) ) { $renderer_options = ! empty( $field_renderer['renderer_options'] ) ? $field_renderer['renderer_options'] : $field; $default_value = isset( $field['default'] ) ? $field['default'] : ''; // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found $field_el = is_callable( $field_renderer['renderer'] ) ? call_user_func( $field_renderer['renderer'], $renderer_options, $default_value ) : $field_renderer['renderer']; if ( ! empty( $field_renderer['renderer_with_field'] ) && $field_renderer['renderer_with_field'] ) { $field_el .= $this->render_field( $field, $name ); } } else { $field_el = $this->render_field( $field, $name ); } $description = ! empty( $field['description'] ) ? sprintf( '%2$s<p class="description">%1$s</p>', $field['description'], "\n\t\t\t\t\t" ) : ''; if ( '' === $description && ! $use_container_wrapper ) { $output = $field_el; } else { $output = sprintf( '%3$s<div class="et-pb-option-container et-pb-option-container--%6$s%5$s"> %1$s %2$s %4$s</div>', $field_el, $description, "\n\n\t\t\t\t", "\t", ( isset( $field['type'] ) && 'custom_css' === $field['type'] ? ' et-pb-custom-css-option' : '' ), isset( $field['type'] ) ? esc_attr( $field['type'] ) : '' ); } $dynamic_content_notice = et_get_safe_localization( sprintf( __( 'This field contains a dynamic value which requires the Visual Builder. <a href="#" class="%1$s">Open Visual Builder</a>', 'et_builder' ), 'et-pb-dynamic-content-fb-switch' ) ); // Conditionally wrap fields depending on whether their values represent dynamic content or not. $output = sprintf( '<%% var isDynamic = typeof %1$s !== \'undefined\' && ET_PageBuilder.isDynamicContent(%1$s); %%> <%% if (isDynamic) { %%> <div class="et-pb-dynamic-content"> <div class="et-pb-dynamic-content__message"> %2$s </div> <div class="et-pb-dynamic-content__field"> <%% } %%> %3$s <%% if (isDynamic) { %%> </div> </div> <%% } %%>', et_core_intentionally_unescaped( $this->get_field_variable_name( $field ), 'underscore_template' ), et_core_intentionally_unescaped( $this->get_icon( 'lock' ) . $dynamic_content_notice, 'underscore_template' ), et_core_intentionally_unescaped( $output, 'underscore_template' ) ); return $output; } /** * Wrap setting option label. * * @param array $field Field settings. * * @return mixed|string */ public function wrap_settings_option_label( $field ) { if ( ! empty( $field['label'] ) ) { $label = $field['label']; } else { return ''; } $field_name = $this->get_field_name( $field ); if ( isset( $field['type'] ) && 'font' === $field['type'] ) { $field_name .= '_select'; } $required = ! empty( $field['required'] ) ? '<span class="required">*</span>' : ''; $attributes = ! ( isset( $field['type'] ) && in_array( $field['type'], array( 'custom_margin', 'custom_padding' ), true ) ) ? sprintf( ' for="%1$s"', esc_attr( $field_name ) ) : ' class="et_custom_margin_label"'; $label = sprintf( '<label%1$s>%2$s%4$s %3$s</label>', et_core_esc_previously( $attributes ), et_core_intentionally_unescaped( $label, 'html' ), et_core_intentionally_unescaped( $required, 'fixed_string' ), isset( $field['no_colon'] ) && true === $field['no_colon'] ? '' : ':' ); return $label; } /** * Get svg icon as string. * * @param string $icon_name icon name. * * @return string div-wrapped svg icon. */ public function get_icon( $icon_name ) { $all_svg_icons = et_pb_get_svg_icons_list(); $icon = isset( $all_svg_icons[ $icon_name ] ) ? $all_svg_icons[ $icon_name ] : ''; if ( '' === $icon ) { return ''; } return '<div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision">' . $icon . '</svg> </div>'; } /** * Get structure of background UI tabs * * @param string $base_name Background base. * * @return array */ public function get_background_fields_structure( $base_name = 'background' ) { $is_background_attr = 'background' === $base_name; $use_background_color_gradient = $is_background_attr ? 'use_background_color_gradient' : "{$base_name}_use_color_gradient"; $prefix = $is_background_attr ? '' : "{$base_name}_"; $structure = array( 'color' => array( "{$base_name}_color", ), 'gradient' => array( $use_background_color_gradient, "{$base_name}_color_gradient_repeat", "{$base_name}_color_gradient_type", "{$base_name}_color_gradient_direction", "{$base_name}_color_gradient_direction_radial", "{$base_name}_color_gradient_stops", "{$base_name}_color_gradient_unit", "{$base_name}_color_gradient_overlays_image", // Deprecated. "{$base_name}_color_gradient_start", "{$base_name}_color_gradient_start_position", "{$base_name}_color_gradient_end", "{$base_name}_color_gradient_end_position", ), 'image' => array( "{$base_name}_image", "{$prefix}parallax", "{$prefix}parallax_method", "{$base_name}_size", "{$base_name}_image_width", "{$base_name}_image_height", "{$base_name}_position", "{$base_name}_horizontal_offset", "{$base_name}_vertical_offset", "{$base_name}_repeat", "{$base_name}_blend", ), 'video' => array( "{$base_name}_video_mp4", "{$base_name}_video_webm", "{$base_name}_video_width", "{$base_name}_video_height", "{$prefix}allow_player_pause", "{$base_name}_video_pause_outside_viewport", ), 'pattern' => array( "{$base_name}_pattern_style", "{$base_name}_pattern_color", "{$base_name}_pattern_transform", "{$base_name}_pattern_size", "{$base_name}_pattern_width", "{$base_name}_pattern_height", "{$base_name}_pattern_repeat_origin", "{$base_name}_pattern_horizontal_offset", "{$base_name}_pattern_vertical_offset", "{$base_name}_pattern_repeat", "{$base_name}_pattern_blend_mode", ), 'mask' => array( "{$base_name}_mask_style", "{$base_name}_mask_color", "{$base_name}_mask_transform", "{$base_name}_mask_aspect_ratio", "{$base_name}_mask_size", "{$base_name}_mask_width", "{$base_name}_mask_height", "{$base_name}_mask_position", "{$base_name}_mask_horizontal_offset", "{$base_name}_mask_vertical_offset", "{$base_name}_mask_blend_mode", ), ); if ( $is_background_attr ) { $structure['color'][] = 'use_background_color'; $structure['image'][] = 'bg_img'; // Column. } return $structure; } /** * Get list of background fields names in one dimensional array * * @return array */ public function get_background_fields_names() { $background_structure = $this->get_background_fields_structure(); $fields_names = array(); foreach ( $background_structure as $tab_name ) { foreach ( $tab_name as $field_name ) { $fields_names[] = $field_name; } } return $fields_names; } /** * Get / extract background fields from all modules fields * * @param array $all_fields All modules fields. * * @param string $base_name Field base name. * * @return array background fields multidimensional array grouped based on its tab */ public function get_background_fields( $all_fields, $base_name = 'background' ) { $background_fields_structure = $this->get_background_fields_structure( $base_name ); $background_tab_names = array_keys( $background_fields_structure ); $background_fields = array_fill_keys( $background_tab_names, array() ); foreach ( $all_fields as $field_name => $field ) { // Multiple foreaches seem overkill. Use single foreach with little bit if conditions // redundancy to get background fields grouped into multi-dimensional tab-based array. if ( in_array( $field_name, $background_fields_structure['color'], true ) ) { $background_fields['color'][ $field_name ] = $field; } if ( in_array( $field_name, $background_fields_structure['gradient'], true ) ) { $background_fields['gradient'][ $field_name ] = $field; } if ( in_array( $field_name, $background_fields_structure['image'], true ) ) { $background_fields['image'][ $field_name ] = $field; } if ( in_array( $field_name, $background_fields_structure['video'], true ) ) { $background_fields['video'][ $field_name ] = $field; } // QF_BACKGROUND_MASKS. if ( in_array( $field_name, $background_fields_structure['pattern'], true ) ) { $background_fields['pattern'][ $field_name ] = $field; } if ( in_array( $field_name, $background_fields_structure['mask'], true ) ) { $background_fields['mask'][ $field_name ] = $field; } } return $background_fields; } /** * Generate background fields based on base name * * @since 3.23 Add allowed CSS units for gradient start and end position. Override computed callback. * * @param string $base_name background base name. * @param string $background_tab background tab name. * @param string $tab_slug field's tab slug. * @param string $toggle_slug field's toggle slug. * @param string $context field's context. * @param array $options_filters list of filters that should be used to skip some options. * * @return array of background fields */ public function generate_background_options( $base_name, $background_tab, $tab_slug, $toggle_slug, $context = null, $options_filters = array() ) { $baseless_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $options = array(); $i18n =& self::$i18n; if ( ! isset( $i18n['background'] ) ) { // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment $i18n['background'] = array( 'color' => array( 'label' => esc_html__( 'Background Color', 'et_builder' ), ), 'gradient' => array( 'label' => esc_html__( 'Use Background Color Gradient', 'et_builder' ), ), 'gradient_repeat' => array( 'label' => esc_html__( 'Repeat Gradient', 'et_builder' ), 'description' => esc_html__( 'If enabled, defined gradient stops will be repeated until the outer boundary of the background is reached.', 'et_builder' ), ), 'gradient_type' => array( 'label' => esc_html__( 'Gradient Type', 'et_builder' ), 'description' => esc_html__( 'Linear gradients radiate in a single direction across one axis. Radial gradients radiate from the center of the background in the shape of a circle.', 'et_builder' ), ), 'gradient_direction' => array( 'label' => esc_html__( 'Gradient Direction', 'et_builder' ), 'description' => esc_html__( 'Change the direction of the gradient by choosing a starting position within a 360 degree range.', 'et_builder' ), ), 'gradient_direction_radial' => array( 'label' => esc_html__( 'Gradient Position', 'et_builder' ), 'description' => esc_html__( 'Change the direction of the gradient by choosing a starting position within a 360 degree range.', 'et_builder' ), ), 'gradient_stops' => array( 'label' => esc_html__( 'Gradient Stops', 'et_builder' ), 'description' => esc_html__( 'Add two or more color stops to your gradient background. Each stop can be dragged to any position on the gradient bar. From each color stop to the next the color is interpolated into a smooth gradient.', 'et_builder' ), ), 'gradient_unit' => array( 'label' => esc_html__( 'Gradient Unit', 'et_builder' ), 'description' => esc_html__( 'Define the units of your gradient stop positions.', 'et_builder' ), ), 'gradient_overlay' => array( 'label' => esc_html__( 'Place Gradient Above Background Image', 'et_builder' ), 'description' => esc_html__( 'If enabled, gradient will be positioned on top of background-image', 'et_builder' ), ), // Deprecated. 'gradient_start' => array( 'label' => esc_html__( 'Gradient Start', 'et_builder' ), ), // Deprecated. 'gradient_start_position' => array( 'label' => esc_html__( 'Start Position', 'et_builder' ), 'description' => esc_html__( 'By adjusting the starting position of the gradient, you can control how quickly or slowly each color transitions, and where the transition begins.', 'et_builder' ), ), // Deprecated. 'gradient_end' => array( 'label' => esc_html__( 'Gradient End', 'et_builder' ), ), // Deprecated. 'gradient_end_position' => array( 'label' => esc_html__( 'End Position', 'et_builder' ), 'description' => esc_html__( 'By adjusting the ending position of the gradient, you can control how quickly or slowly each color transitions, and where the transition begins.', 'et_builder' ), ), 'image' => array( 'label' => esc_html__( 'Background Image', 'et_builder' ), 'choose_text' => esc_attr__( 'Choose a Background Image', 'et_builder' ), 'update_text' => esc_attr__( 'Set As Background', 'et_builder' ), ), 'parallax' => array( 'label' => esc_html__( 'Use Parallax Effect', 'et_builder' ), 'description' => esc_html__( 'If enabled, your background image will stay fixed as your scroll, creating a fun parallax-like effect.', 'et_builder' ), ), 'parallax_method' => array( 'label' => esc_html__( 'Parallax Method', 'et_builder' ), 'description' => esc_html__( 'Define the method, used for the parallax effect.', 'et_builder' ), 'options' => array( 'on' => esc_html__( 'True Parallax', 'et_builder' ), 'off' => esc_html__( 'CSS', 'et_builder' ), ), ), 'size' => array( 'label' => esc_html__( 'Background Image Size', 'et_builder' ), 'description' => esc_html__( 'Choosing "Cover" will force the image to fill the entire background area, clipping the image when necessary. Choosing "Fit" will ensure that the entire image is always visible, but can result in blank spaces around the image. When set to "Actual Size," the image will not be resized at all.', 'et_builder' ), 'options' => array( 'cover' => et_builder_i18n( 'Cover' ), 'contain' => et_builder_i18n( 'Fit' ), 'initial' => et_builder_i18n( 'Actual Size' ), 'stretch' => et_builder_i18n( 'Stretch to Fill' ), 'custom' => et_builder_i18n( 'Custom Size' ), ), ), 'image_width' => array( 'label' => esc_html__( 'Background Image Width', 'et_builder' ), ), 'image_height' => array( 'label' => esc_html__( 'Background Image Height', 'et_builder' ), ), 'position' => array( 'label' => esc_html__( 'Background Image Position', 'et_builder' ), 'description' => esc_html__( "Choose where you would like the background image to be positioned within this element. You may want to position the background based on the the image's focus point.", 'et_builder' ), ), 'horizontal_offset' => array( 'label' => esc_html__( 'Background Image Horizontal Offset', 'et_builder' ), ), 'vertical_offset' => array( 'label' => esc_html__( 'Background Image Vertical Offset', 'et_builder' ), ), 'repeat' => array( 'label' => esc_html__( 'Background Image Repeat', 'et_builder' ), 'description' => esc_html__( 'If the background image is smaller than the size of the element, you may want the image to repeat. This result will result in a background image pattern.', 'et_builder' ), ), 'blend' => array( 'label' => esc_html__( 'Background Image Blend', 'et_builder' ), 'description' => esc_html__( 'Background images can be blended with the background color, merging the two and creating unique effects.', 'et_builder' ), ), 'mp4' => array( 'label' => esc_html__( 'Background Video MP4', 'et_builder' ), 'description' => esc_html__( 'All videos should be uploaded in both .MP4 .WEBM formats to ensure maximum compatibility in all browsers. Upload the .MP4 version here.', 'et_builder' ), 'upload_button_text' => esc_attr__( 'Upload a video', 'et_builder' ), 'choose_text' => esc_attr__( 'Choose a Background Video MP4 File', 'et_builder' ), 'update_text' => esc_attr__( 'Set As Background Video', 'et_builder' ), ), 'webm' => array( 'label' => esc_html__( 'Background Video Webm', 'et_builder' ), 'description' => esc_html__( 'All videos should be uploaded in both .MP4 .WEBM formats to ensure maximum compatibility in all browsers. Upload the .WEBM version here.', 'et_builder' ), 'upload_button_text' => esc_attr__( 'Upload a video', 'et_builder' ), 'choose_text' => esc_attr__( 'Choose a Background Video WEBM File', 'et_builder' ), 'update_text' => esc_attr__( 'Set As Background Video', 'et_builder' ), ), 'video_width' => array( 'label' => esc_html__( 'Background Video Width', 'et_builder' ), 'description' => esc_html__( 'In order for videos to be sized correctly, you must input the exact width (in pixels) of your video here.', 'et_builder' ), ), 'video_height' => array( 'label' => esc_html__( 'Background Video Height', 'et_builder' ), 'description' => esc_html__( 'In order for videos to be sized correctly, you must input the exact height (in pixels) of your video here.', 'et_builder' ), ), 'pause' => array( 'label' => esc_html__( 'Pause Video When Another Video Plays', 'et_builder' ), 'description' => esc_html__( 'Allow video to be paused by other players when they begin playing', 'et_builder' ), ), 'viewport' => array( 'label' => esc_html__( 'Pause Video While Not In View', 'et_builder' ), 'description' => esc_html__( 'Allow video to be paused while it is not in the visible area.', 'et_builder' ), ), ); // phpcs:enable } // Not included on skip background tab because background-field is expected to be registered under "background_color" field. if ( in_array( $background_tab, array( 'all', 'button', 'color' ), true ) ) { $options[ "{$base_name}_color" ] = self::background_field_template( 'color', array( 'label' => $i18n['background']['color']['label'], 'type' => 'color-alpha', 'option_category' => 'configuration', 'custom_color' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color', 'hover' => 'tabs', 'last_edited' => 'background', 'mobile_options' => true, 'sticky' => true, ) ); // This option is used to enable or disable background color on VB or FE. This option has // different function with use_background_color. Option background_enable_color won't hide // background color option like what use_background_color does. It's used to ensure if // current background should be rendered or not by inheriting or applying custom color. $options[ "{$base_name}_enable_color" ] = self::background_field_template( 'enable_color', array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'default' => 'on', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); } if ( in_array( $background_tab, array( 'all', 'button', 'skip', 'gradient' ), true ) ) { $use_background_color_gradient_name = 'background' === $base_name ? 'use_background_color_gradient' : "{$base_name}_use_color_gradient"; $options[ $use_background_color_gradient_name ] = self::background_field_template( 'use_color_gradient', array( 'label' => $i18n['background']['gradient']['label'], 'description' => '', 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'use_color_gradient', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$base_name}_color_gradient_repeat" ] = self::background_field_template( 'color_gradient_repeat', array( 'label' => $i18n['background']['gradient_repeat']['label'], 'description' => $i18n['background']['gradient_repeat']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_repeat' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_repeat', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'on', ), ) ); $options[ "{$base_name}_color_gradient_type" ] = self::background_field_template( 'color_gradient_type', array( 'label' => $i18n['background']['gradient_type']['label'], 'description' => $i18n['background']['gradient_type']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'configuration', 'options' => array( 'linear' => et_builder_i18n( 'Linear' ), 'circular' => et_builder_i18n( 'Circular' ), 'elliptical' => et_builder_i18n( 'Elliptical' ), 'conic' => et_builder_i18n( 'Conical' ), ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_type' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_type', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'on', ), ) ); $options[ "{$base_name}_color_gradient_direction" ] = self::background_field_template( 'color_gradient_direction', array( 'label' => $i18n['background']['gradient_direction']['label'], 'description' => $i18n['background']['gradient_direction']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'range', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 1, 'max' => 360, 'step' => 1, ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_direction' ), 'default_on_child' => true, 'validate_unit' => true, 'fixed_unit' => 'deg', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_direction', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "{$base_name}_color_gradient_type" => array( 'linear', 'conic', ), ), ) ); $options[ "{$base_name}_color_gradient_direction_radial" ] = self::background_field_template( 'color_gradient_direction_radial', array( 'label' => $i18n['background']['gradient_direction_radial']['label'], 'description' => $i18n['background']['gradient_direction_radial']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'configuration', 'options' => array( 'center' => et_builder_i18n( 'Center' ), 'top left' => et_builder_i18n( 'Top Left' ), 'top' => et_builder_i18n( 'Top' ), 'top right' => et_builder_i18n( 'Top Right' ), 'right' => et_builder_i18n( 'Right' ), 'bottom right' => et_builder_i18n( 'Bottom Right' ), 'bottom' => et_builder_i18n( 'Bottom' ), 'bottom left' => et_builder_i18n( 'Bottom Left' ), 'left' => et_builder_i18n( 'Left' ), ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_direction_radial' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_direction_radial', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "{$base_name}_color_gradient_type" => array( 'radial', 'circular', 'elliptical', 'conic', ), ), ) ); $options[ "{$base_name}_color_gradient_stops" ] = self::background_field_template( 'color_gradient_stops', array( 'label' => $i18n['background']['gradient_stops']['label'], 'description' => $i18n['background']['gradient_stops']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'gradient-stops', 'option_category' => 'configuration', 'default' => ET_Global_Settings::get_value( 'all_background_gradient_stops' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_stops', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'on', ), ) ); $options[ "{$base_name}_color_gradient_unit" ] = self::background_field_template( 'color_gradient_unit', array( 'label' => $i18n['background']['gradient_unit']['label'], 'description' => $i18n['background']['gradient_unit']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'configuration', 'options' => array( '%' => et_builder_i18n( 'Percent' ), 'px' => et_builder_i18n( 'Pixels' ), 'em' => et_builder_i18n( 'Font Size (em)' ), 'rem' => et_builder_i18n( 'Root-level Font Size (rem)' ), 'ex' => et_builder_i18n( 'X-Height (ex)' ), 'ch' => et_builder_i18n( 'Zero-width (ch)' ), 'pc' => et_builder_i18n( 'Picas (pc)' ), 'pt' => et_builder_i18n( 'Points (pt)' ), 'cm' => et_builder_i18n( 'Centimeters (cm)' ), 'mm' => et_builder_i18n( 'Millimeters (mm)' ), 'in' => et_builder_i18n( 'Inches (in)' ), 'vh' => et_builder_i18n( 'Viewport Height (vh)' ), 'vw' => et_builder_i18n( 'Viewport Width (vw)' ), 'vmin' => et_builder_i18n( 'Viewport Minimum (vmin)' ), 'vmax' => et_builder_i18n( 'Viewport Maximum (vmax)' ), ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_unit' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_unit', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( // Do not render this control for conic gradients. "{$base_name}_color_gradient_type" => array( 'linear', 'radial', 'circular', 'elliptical', ), ), ) ); $options[ "{$base_name}_color_gradient_overlays_image" ] = self::background_field_template( 'color_gradient_overlays_image', array( 'label' => $i18n['background']['gradient_overlay']['label'], 'description' => $i18n['background']['gradient_overlay']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_overlays_image' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_overlays_image', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'on', ), ) ); // Deprecated. $options[ "{$base_name}_color_gradient_start" ] = self::background_field_template( 'color_gradient_start', array( 'label' => $i18n['background']['gradient_start']['label'], 'description' => '', 'type' => 'skip', 'option_category' => 'configuration', 'default' => ET_Global_Settings::get_value( 'all_background_gradient_start' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_start', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'deprecated', ), ) ); // Deprecated. $options[ "{$base_name}_color_gradient_start_position" ] = self::background_field_template( 'color_gradient_start_position', array( 'label' => $i18n['background']['gradient_start_position']['label'], 'description' => $i18n['background']['gradient_start_position']['description'], 'type' => 'skip', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_start_position' ), 'default_on_child' => true, 'validate_unit' => true, 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pc', 'ex', 'vh', 'vw' ), 'default_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_start_position', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'deprecated', ), ) ); // Deprecated. $options[ "{$base_name}_color_gradient_end" ] = self::background_field_template( 'color_gradient_end', array( 'label' => $i18n['background']['gradient_end']['label'], 'description' => '', 'type' => 'skip', 'option_category' => 'configuration', 'default' => ET_Global_Settings::get_value( 'all_background_gradient_end' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_end', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'deprecated', ), ) ); // Deprecated. $options[ "{$base_name}_color_gradient_end_position" ] = self::background_field_template( 'color_gradient_end_position', array( 'label' => $i18n['background']['gradient_end_position']['label'], 'description' => $i18n['background']['gradient_end_position']['description'], 'type' => 'skip', 'option_category' => 'configuration', 'range_settings' => array( 'min' => 0, 'max' => 100, 'step' => 1, ), 'default' => ET_Global_Settings::get_value( 'all_background_gradient_end_position' ), 'default_on_child' => true, 'validate_unit' => true, 'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pc', 'ex', 'vh', 'vw' ), 'default_unit' => '%', 'fixed_range' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'color_gradient_end_position', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "$use_background_color_gradient_name" => 'deprecated', ), ) ); } if ( in_array( $background_tab, array( 'all', 'button', 'skip', 'image' ), true ) ) { $options[ "{$base_name}_image" ] = self::background_field_template( 'image', array( 'label' => $i18n['background']['image']['label'], 'choose_text' => $i18n['background']['image']['choose_text'], 'update_text' => $i18n['background']['image']['update_text'], 'upload_button_text' => et_builder_i18n( 'Upload an image' ), 'type' => 'skip' === $background_tab ? 'skip' : 'upload', 'option_category' => 'configuration', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'image', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); // This option is used to enable or disable background image on VB or FE. It's used to // ensure if current background should be rendered or not by inheriting or applying // custom image. $options[ "{$base_name}_enable_image" ] = self::background_field_template( 'enable_image', array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'default' => 'on', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); if ( 'button' !== $background_tab && ! in_array( 'parallax', $options_filters, true ) ) { $options[ "{$baseless_prefix}parallax" ] = self::background_field_template( 'parallax', array( 'label' => $i18n['background']['parallax']['label'], 'description' => $i18n['background']['parallax']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'parallax', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$baseless_prefix}parallax_method" ] = self::background_field_template( 'parallax_method', array( 'label' => $i18n['background']['parallax_method']['label'], 'description' => $i18n['background']['parallax_method']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'configuration', 'options' => array( 'on' => $i18n['background']['parallax_method']['options']['on'], 'off' => $i18n['background']['parallax_method']['options']['off'], ), 'default' => self::$_->array_get( $this->advanced_fields, "background.options.{$baseless_prefix}parallax_method.default", 'on' ), 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'parallax_method', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "{$baseless_prefix}parallax" => 'on', ), ) ); } $options[ "{$base_name}_size" ] = self::background_field_template( 'size', array( 'label' => $i18n['background']['size']['label'], 'description' => $i18n['background']['size']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'layout', 'options' => array( 'cover' => $i18n['background']['size']['options']['cover'], 'contain' => $i18n['background']['size']['options']['contain'], 'initial' => $i18n['background']['size']['options']['initial'], 'stretch' => $i18n['background']['size']['options']['stretch'], 'custom' => $i18n['background']['size']['options']['custom'], ), 'default' => 'cover', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'size', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'show_if' => array( "{$baseless_prefix}parallax" => 'off', ), ) ); $options[ "{$base_name}_image_width" ] = self::background_field_template( 'image_width', array( 'allow_empty' => true, 'allowed_units' => et_pb_get_background_field_allowed_units(), 'allowed_values' => et_builder_get_acceptable_css_string_values( 'background-size' ), 'default' => 'auto', 'default_unit' => '%', 'default_on_child' => true, 'fixed_range' => true, 'hover' => 'tabs', 'label' => $i18n['background']['image_width']['label'], 'mobile_options' => true, 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'min_limit' => 0, 'max' => 100, 'step' => 1, ), 'show_if' => array( "{$base_name}_size" => 'custom', ), 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'sticky' => true, 'type' => 'range', 'validate_unit' => true, ) ); $options[ "{$base_name}_image_height" ] = self::background_field_template( 'image_height', array( 'allow_empty' => true, 'allowed_units' => et_pb_get_background_field_allowed_units(), 'allowed_values' => et_builder_get_acceptable_css_string_values( 'background-size' ), 'default' => 'auto', 'default_unit' => '%', 'default_on_child' => true, 'fixed_range' => true, 'hover' => 'tabs', 'label' => $i18n['background']['image_height']['label'], 'mobile_options' => true, 'option_category' => 'layout', 'range_settings' => array( 'min' => 0, 'min_limit' => 0, 'max' => 100, 'step' => 1, ), 'show_if' => array( "{$base_name}_size" => 'custom', ), 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'sticky' => true, 'type' => 'range', 'validate_unit' => true, ) ); $options[ "{$base_name}_position" ] = self::background_field_template( 'position', array( 'label' => $i18n['background']['position']['label'], 'description' => $i18n['background']['position']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'layout', 'options' => et_pb_get_background_position_options(), 'default' => 'center', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'position', 'mobile_options' => true, 'sticky' => true, 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'hover' => 'tabs', ) ); $options[ "{$base_name}_horizontal_offset" ] = self::background_field_template( 'horizontal_offset', array( 'allowed_units' => et_pb_get_background_field_allowed_units(), 'default' => '0', 'default_unit' => '%', 'default_on_child' => true, 'fixed_range' => true, 'hover' => 'tabs', 'label' => $i18n['background']['horizontal_offset']['label'], 'mobile_options' => true, 'option_category' => 'layout', 'range_settings' => array( 'min' => - 100, 'max' => 100, 'step' => 1, ), 'show_if' => array( "{$base_name}_position" => array( 'top_left', 'top_right', 'center_left', 'center_right', 'bottom_left', 'bottom_right', ), ), 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'sticky' => true, 'type' => 'range', 'validate_unit' => true, ) ); $options[ "{$base_name}_vertical_offset" ] = self::background_field_template( 'vertical_offset', array( 'allowed_units' => et_pb_get_background_field_allowed_units(), 'default' => '0', 'default_unit' => '%', 'default_on_child' => true, 'fixed_range' => true, 'hover' => 'tabs', 'label' => $i18n['background']['vertical_offset']['label'], 'mobile_options' => true, 'option_category' => 'layout', 'range_settings' => array( 'min' => - 100, 'max' => 100, 'step' => 1, ), 'show_if' => array( "{$base_name}_position" => array( 'top_left', 'top_center', 'top_right', 'bottom_left', 'bottom_center', 'bottom_right', ), ), 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'sticky' => true, 'type' => 'range', 'validate_unit' => true, ) ); $options[ "{$base_name}_repeat" ] = self::background_field_template( 'repeat', array( 'label' => $i18n['background']['repeat']['label'], 'description' => $i18n['background']['repeat']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'layout', 'options' => et_pb_get_background_repeat_options(), 'default' => 'no-repeat', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'repeat', 'mobile_options' => true, 'show_if' => array( "{$base_name}_size" => array( 'cover', 'contain', 'initial', 'custom', ), ), 'show_if_not' => array( "{$baseless_prefix}parallax" => 'on', ), 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$base_name}_blend" ] = self::background_field_template( 'blend', array( 'label' => $i18n['background']['blend']['label'], 'description' => $i18n['background']['blend']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'select', 'option_category' => 'layout', 'options' => et_pb_get_background_blend_mode_options(), 'default' => 'normal', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'blend', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); } if ( in_array( $background_tab, array( 'all', 'skip', 'video' ), true ) ) { $options[ "{$base_name}_video_mp4" ] = self::background_field_template( 'video_mp4', array( 'label' => $i18n['background']['mp4']['label'], 'description' => $i18n['background']['mp4']['description'], 'upload_button_text' => $i18n['background']['mp4']['upload_button_text'], 'choose_text' => $i18n['background']['mp4']['choose_text'], 'update_text' => $i18n['background']['mp4']['update_text'], 'type' => 'skip' === $background_tab ? 'skip' : 'upload', 'option_category' => 'configuration', 'data_type' => 'video', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'computed_affects' => array( "__video_{$base_name}", ), 'field_template' => 'video_mp4', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'affects_mobile' => true, ) ); // This option is used to enable or disable background MP4 video on VB or FE. It's used // to ensure if current background should be rendered or not by inheriting or applying // custom MP4 video. $options[ "{$base_name}_enable_video_mp4" ] = self::background_field_template( 'enable_video_mp4', array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'default' => 'on', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$base_name}_video_webm" ] = self::background_field_template( 'video_webm', array( 'label' => $i18n['background']['webm']['label'], 'description' => $i18n['background']['webm']['description'], 'upload_button_text' => $i18n['background']['webm']['upload_button_text'], 'choose_text' => $i18n['background']['webm']['choose_text'], 'update_text' => $i18n['background']['webm']['update_text'], 'type' => 'skip' === $background_tab ? 'skip' : 'upload', 'option_category' => 'configuration', 'data_type' => 'video', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'computed_affects' => array( "__video_{$base_name}", ), 'field_template' => 'video_webm', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'affects_mobile' => true, ) ); // This option is used to enable or disable background Webm video on VB or FE. It's used // to ensure if current background should be rendered or not by inheriting or applying // custom Webm video. $options[ "{$base_name}_enable_video_webm" ] = self::background_field_template( 'enable_video_webm', array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'default' => 'on', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$base_name}_video_width" ] = self::background_field_template( 'video_width', array( 'label' => $i18n['background']['video_width']['label'], 'description' => $i18n['background']['video_width']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'text', 'option_category' => 'configuration', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'computed_affects' => array( "__video_{$base_name}", ), 'field_template' => 'video_width', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'affects_mobile' => true, ) ); $options[ "{$base_name}_video_height" ] = self::background_field_template( 'video_height', array( 'label' => $i18n['background']['video_height']['label'], 'description' => $i18n['background']['video_height']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'text', 'option_category' => 'configuration', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'computed_affects' => array( "__video_{$base_name}", ), 'field_template' => 'video_height', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', 'affects_mobile' => true, ) ); $options[ "{$baseless_prefix}allow_player_pause" ] = self::background_field_template( 'allow_player_pause', array( 'label' => $i18n['background']['pause']['label'], 'description' => $i18n['background']['pause']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'off', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'allow_player_pause', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "{$base_name}_video_pause_outside_viewport" ] = self::background_field_template( 'video_pause_outside_viewport', array( 'label' => $i18n['background']['viewport']['label'], 'description' => $i18n['background']['viewport']['description'], 'type' => 'skip' === $background_tab ? 'skip' : 'yes_no_button', 'option_category' => 'configuration', 'options' => array( 'off' => et_builder_i18n( 'No' ), 'on' => et_builder_i18n( 'Yes' ), ), 'default' => 'on', 'default_on_child' => true, 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'field_template' => 'video_pause_outside_viewport', 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); $options[ "__video_{$base_name}" ] = self::background_field_template( 'video_computed', array( 'type' => 'computed', 'computed_callback' => array( 'ET_Builder_Module_Helper_ResponsiveOptions', 'get_video_background' ), 'computed_depends_on' => array( "{$base_name}_video_mp4", "{$base_name}_video_webm", "{$base_name}_video_width", "{$base_name}_video_height", ), 'computed_minimum' => array( "{$base_name}_video_mp4", "{$base_name}_video_webm", ), 'computed_variables' => array( 'base_name' => $base_name, ), 'mobile_options' => true, 'sticky' => true, 'hover' => 'tabs', ) ); } // QF_BACKGROUND_MASKS :: Mask Options. // Not included on skip background tab because pattern should only load for background, not // for background-fields (i.e button). if ( in_array( $background_tab, array( 'all', 'pattern' ), true ) ) { $pattern_fields = et_pb_get_pattern_fields( $base_name ); $options = array_merge( $options, $pattern_fields ); unset( $pattern_fields ); } // Not included on skip background tab because mask should only load for background, not for // background-fields (i.e button). if ( in_array( $background_tab, array( 'all', 'mask' ), true ) ) { $mask_fields = et_pb_get_mask_fields( $base_name ); $options = array_merge( $options, $mask_fields ); unset( $mask_fields ); } foreach ( $options as $option_name => &$option ) { $option['context'] = null === $context ? $base_name : $context; } return $options; } /** * Get string of background fields UI. Used in place of background_color fields UI. * * @param array $all_fields list of all module fields. * * @param string $base_name background base name. * * @return string background fields UI */ public function wrap_settings_background_fields( $all_fields, $base_name = 'background' ) { $tab_structure = $this->get_background_fields_structure( $base_name ); $tab_names = array_keys( $tab_structure ); $background_fields = $this->get_background_fields( $all_fields, $base_name ); // Concatenate background fields UI. $background = ''; // Label. $background .= sprintf( '<label for="et_pb_background">%1$s</label>', esc_html__( 'Background:', 'et_builder' ) ); // Field wrapper. $background .= sprintf( '<div class="et-pb-option-container et-pb-option-container-inner et-pb-option-container--background" data-base_name="%s">', esc_attr( $base_name ) ); $tab_names_processed = array(); foreach ( $tab_names as $tab_nav_name ) { if ( ! empty( $background_fields[ $tab_nav_name ] ) ) { $tab_names_processed[] = sanitize_text_field( $tab_nav_name ); } } // Apply background UI if the module has more than one backgroundFields to avoid 3rd party module's field which uses `background_color` field and incorrectly rendered as background UI. if ( count( $tab_names_processed ) < 2 ) { return ''; } // Tab Nav. $background .= sprintf( '<%%= window.et_builder.options_template_output("background_tabs_nav",%1$s) %%>', wp_json_encode( $tab_names_processed ) ); // Tabs. foreach ( $tab_names as $tab_name ) { $background .= sprintf( '<div class="et_pb_background-tab et_pb_background-tab--%1$s" data-tab="%1$s">', esc_attr( $tab_name ) ); // Get tab's fields. $tab_fields = $background_fields[ $tab_name ]; // Render gradient tab's preview. if ( 'gradient' === $tab_name ) { $background .= '<%= window.et_builder.options_template_output("background_gradient_buttons") %>'; } // Tab's fields. foreach ( $tab_fields as $tab_field_name => $tab_field ) { if ( 'skip' === $tab_field['type'] ) { continue; } $preview_class = ''; // Append field name. $tab_field['name'] = $tab_field_name; // Append preview class name. if ( in_array( $tab_field['name'], array( "{$base_name}_color", "{$base_name}_image", "{$base_name}_url", "{$base_name}_video_mp4", "{$base_name}_video_webm" ), true ) ) { $tab_field['has_preview'] = true; $preview_class = ' et-pb-option--has-preview'; } // Prepare field list attribute. $depends = false; $depends_attr = ''; if ( isset( $tab_field['depends_show_if'] ) || isset( $tab_field['depends_show_if_not'] ) ) { $depends = true; if ( isset( $tab_field['depends_show_if_not'] ) ) { $depends_show_if_not = is_array( $tab_field['depends_show_if_not'] ) ? implode( ',', $tab_field['depends_show_if_not'] ) : $tab_field['depends_show_if_not']; $depends_attr = sprintf( ' data-depends_show_if_not="%s"', esc_attr( $depends_show_if_not ) ); } else { $depends_attr = sprintf( ' data-depends_show_if="%s"', esc_attr( $tab_field['depends_show_if'] ) ); } } // Append fields UI. $background .= sprintf( '<div class="et_pb_background-option et_pb_background-option--%1$s et_pb_background-template--%6$s %5$s et-pb-option--%1$s%2$s"%3$s data-option_name="%4$s">', esc_attr( $tab_field_name ), esc_attr( $preview_class ), $depends_attr, esc_attr( $tab_field['name'] ), "{$base_name}_color" === $tab_field['name'] && 'background' !== $base_name ? 'et-pb-option-main' : 'et-pb-option', isset( $tab_field['field_template'] ) ? esc_attr( $tab_field['field_template'] ) : '' ); // This adds a CSS class based on whether it is `true` or `false`. $this->is_background = true; $background .= $this->wrap_settings_option_label( $tab_field ); $background .= $this->wrap_settings_option_field( $tab_field ); $this->is_background = false; $background .= '</div>'; } $background .= '</div>'; } // End of field wrapper. $background .= '</div>'; return $background; } /** * Get field name with prefix. * * @param array $field Field. * * @return mixed|string */ public function get_field_name( $field ) { $prefix = 'et_pb_'; // Option template convert array field into string id; return early to prevent error. if ( is_string( $field ) ) { return $prefix . 'option_template_' . $field; } // Don't add 'et_pb_' prefix to the "Admin Label" field. if ( 'admin_label' === $field['name'] ) { return $field['name']; } // Make sure the prefix is not doubled. if ( strpos( $field['name'], $prefix ) === 0 ) { return $field['name']; } return $prefix . $field['name']; } /** * Get field name for use in underscore templates. * * @since 3.17.2 * * @param array $field Field. * * @return string */ public function get_field_variable_name( $field ) { $name = $this->get_field_name( $field ); if ( isset( $this->type ) && 'child' === $this->type ) { $name = "data.{$name}"; } $name = str_replace( '-', '_', $name ); return $name; } /** * Process field attributes into markup. * * @param array $field Field. * @param array $attributes Field attributes. */ public function process_html_attributes( $field, &$attributes ) { if ( is_array( $field['attributes'] ) ) { foreach ( $field['attributes'] as $attribute_key => $attribute_value ) { $attributes .= ' ' . esc_attr( $attribute_key ) . '="' . esc_attr( $attribute_value ) . '"'; } } else { $attributes = ' ' . $field['attributes']; } } /** * Returns an underscore template for the options settings. * * @since 3.23 Disable mobile options (responsive settings) on unsupported field types. It's * added to adapt Options Harmony v2. Fix unexpected token because composite fields * with range type load empty last edited value. * * @param array $field { * Associative array. * * @type 'id' => (int), * @type string 'label' => (string), * @type string 'description' => (string), * @type string 'class' => (string), * @type string 'type' => (string), * @type string 'validate_input' => (bool), * @type string 'name' => (string), * @type string 'default' => (string), * @type string 'defaults' => (array), * @type string 'options' => (array), * @type string 'option_category' => (string), * @type string 'attributes' => (string), * @type string 'affects' => (string), * @type string 'before' => (string), * @type string 'after' => (string), * @type string 'display_if' => (string), * @type string 'depends_on' => (string), * @type string 'depends_show_if' => (string), * @type string 'depends_show_if_not' => (string), * @type string 'show_if' => (string), * @type string 'show_if_not' => (string), * @type string 'tab_slug' => (string), * @type string 'toggle_slug' => (string), * @type string 'composite_type' => (string), * @type string 'composite_structure' => (array), * } * @param string $name Field name. * * @return string HTML underscore template. */ public function render_field( $field, $name = '' ) { $classes = array(); $hidden_field = ''; $field_el = ''; $is_custom_color = isset( $field['custom_color'] ) && $field['custom_color']; $reset_button_html = '<span class="et-pb-reset-setting"></span>'; $need_mobile_options = isset( $field['mobile_options'] ) && $field['mobile_options'] ? true : false; $only_options = isset( $field['only_options'] ) ? $field['only_options'] : false; $is_child = isset( $this->type ) && 'child' === $this->type; // Option template convert array field into string id; return early to prevent error. if ( is_string( $field ) ) { return ''; } // Make sure 'type' is always set to prevent PHP notices. if ( empty( $field['type'] ) ) { $field['type'] = 'no-type'; } // Disable mobile options for unsupported types. Before Options Harmony v2, only custom // margin/padding, text/number, and range support responsive settings. Later on, we added // responsive settings to all settings. However BB is no longer supported, so we need to // disable mobile options on those selected field types. $unsupported_mobile_options = array( 'upload-gallery', 'background-field', 'warning', 'tiny_mce', 'codemirror', 'textarea', 'custom_css', 'options_list', 'sortable_list', 'conditional_logic', 'text_align', 'align', 'select', 'divider', 'yes_no_button', 'multiple_buttons', 'font', 'select_with_option_groups', 'select_animation', 'presets_shadow', 'select_box_shadow', 'presets', 'color', 'color-alpha', 'upload', 'checkbox', 'multiple_checkboxes', 'hidden' ); if ( $need_mobile_options && in_array( $field['type'], $unsupported_mobile_options, true ) ) { $need_mobile_options = false; } if ( $need_mobile_options ) { $mobile_settings_tabs = et_pb_generate_mobile_settings_tabs(); } if ( 0 !== strpos( $field['type'], 'select' ) ) { $classes = array( 'regular-text' ); } foreach ( $this->get_validation_class_rules() as $rule ) { if ( ! empty( $field[ $rule ] ) ) { $this->validation_in_use = true; $classes[] = $rule; } } if ( isset( $field['validate_unit'] ) && $field['validate_unit'] ) { $classes[] = 'et-pb-validate-unit'; } if ( ! empty( $field['class'] ) ) { if ( is_string( $field['class'] ) ) { $field['class'] = array( $field['class'] ); } $classes = array_merge( $classes, $field['class'] ); } $field['class'] = implode( ' ', $classes ); $field_name = $this->get_field_name( $field ); $field['id'] = ! empty( $field['id'] ) ? $field['id'] : $field_name; $field['name'] = $field_name; if ( $is_child ) { $field_name = "data.{$field_name}"; } $field_var_name = $this->get_field_variable_name( $field ); $default_on_front = self::$_->array_get( $field, 'default_on_front', '' ); $default_arr = self::$_->array_get( $field, 'default', $default_on_front ); // Inform that default value is array and last edited value maybe empty string. Decided to // create new variable, just in case $default_arr will be modified later. $default_last_edited_is_arr = false; if ( is_array( $default_arr ) && isset( $default_arr[1] ) && is_array( $default_arr[1] ) ) { list($default_parent_id, $defaults_list) = $default_arr; $default_parent_id = sprintf( '%1$set_pb_%2$s', $is_child ? 'data.' : '', $default_parent_id ); $default = esc_attr( wp_json_encode( $default_arr ) ); $default_value = sprintf( '(typeof(%1$s) !== \'undefined\' ? ( typeof(%1$s) === \'object\' ? (%2$s)[jQuery(%1$s).val()] : (%2$s)[%1$s] ) : \'\')', $default_parent_id, wp_json_encode( $defaults_list ) ); $default_is_arr = true; $default_last_edited_is_arr = true; } else { $default_value = $default_arr; $default = $default_value; $default_is_arr = false; } if ( 'font' === $field['type'] ) { $default = '' === $default ? '||||||||' : $default; $default_value = '' === $default_value ? '||||||||' : $default_value; } $font_icon_options = array( 'et_pb_font_icon', 'et_pb_button_icon', 'et_pb_button_one_icon', 'et_pb_button_two_icon', 'et_pb_hover_icon' ); if ( in_array( $field_name, $font_icon_options, true ) ) { $field_value = esc_attr( $field_var_name ); } else { $field_value = esc_attr( $field_var_name ) . '.replace(/%91/g, "[").replace(/%93/g, "]").replace(/%22/g, "\"")'; } $value_html = $default_is_arr ? ' value="<%%- typeof( %1$s ) !== \'undefined\' ? %2$s : %3$s %%>" ' : ' value="<%%- typeof( %1$s ) !== \'undefined\' ? %2$s : \'%3$s\' %%>" '; $value = sprintf( $value_html, esc_attr( $field_var_name ), $field_value, $default_value ); $attributes = ''; if ( ! empty( $field['attributes'] ) ) { $this->process_html_attributes( $field, $attributes ); } if ( ! empty( $field['affects'] ) ) { $field['class'] .= ' et-pb-affects'; $attributes .= sprintf( ' data-affects="%s"', esc_attr( implode( ', ', $field['affects'] ) ) ); } if ( ! empty( $field['responsive_affects'] ) ) { $field['class'] .= ' et-pb-responsive-affects'; $attributes .= sprintf( ' data-responsive-affects="%1$s" data-responsive-desktop-name="%2$s"', esc_attr( implode( ', ', $field['responsive_affects'] ) ), esc_attr( $field['name'] ) ); } if ( 'font' === $field['type'] ) { $field['class'] .= ' et-pb-font-select'; } if ( in_array( $field['type'], array( 'font', 'hidden', 'multiple_checkboxes', 'select_with_option_groups', 'select_animation', 'presets', 'presets_shadow', 'select_box_shadow' ), true ) && ! $only_options ) { $hidden_field = sprintf( '<input type="hidden" name="%1$s" id="%2$s" class="et-pb-main-setting %3$s" data-default="%4$s" %5$s %6$s/>', esc_attr( $field['name'] ), esc_attr( $field['id'] ), esc_attr( $field['class'] ), esc_attr( $default ), $value, $attributes ); if ( 'select_with_option_groups' === $field['type'] ) { // Since we are using a hidden field to manage the value, we need to clear the data-affects attribute so that // it doesn't appear on both the `$field` AND the hidden field. This should probably be done for all of these // field types but don't want to risk breaking anything :-/. $attributes = preg_replace( '/data-affects="[\w\s,-]*"/', 'data-affects=""', $attributes ); } } foreach ( $this->get_validation_attr_rules() as $rule ) { if ( ! empty( $field[ $rule ] ) ) { $this->validation_in_use = true; $attributes .= ' data-rule-' . esc_attr( $rule ) . '="' . esc_attr( $field[ $rule ] ) . '"'; } } if ( isset( $field['before'] ) && ! $only_options ) { $field_el .= $this->render_field_before_after_element( $field['before'] ); } switch ( $field['type'] ) { case 'upload-gallery': $field_el .= sprintf( '<input type="button" class="button button-upload et-pb-gallery-button" value="%1$s" />' . '<input type="hidden" name="%3$s" id="%4$s" class="et-pb-gallery" %2$s />', esc_attr__( 'Update Gallery', 'et_builder' ), $value, esc_attr( $field['name'] ), esc_attr( $field['id'] ) ); break; case 'background-field': $field_el .= $this->wrap_settings_background_fields( $field['background_fields'], $field['base_name'] ); break; case 'warning': $field_el .= sprintf( '<div class="et-pb-option-warning" data-name="%2$s" data-display_if="%3$s">%1$s</div>', html_entity_decode( esc_html( $field['message'] ) ), esc_attr( $field['name'] ), esc_attr( $field['display_if'] ) ); break; case 'tiny_mce': if ( ! empty( $field['tiny_mce_html_mode'] ) ) { $field['class'] .= ' html_mode'; } $main_content_field_name = 'et_pb_content'; $main_content_property_name = $main_content_field_name; if ( isset( $this->type ) && 'child' === $this->type ) { $main_content_property_name = "data.{$main_content_property_name}"; } if ( 'et_pb_signup' === $this->slug ) { $main_content_field_name = $field['name']; $main_content_property_name = $main_content_field_name; } $field_el .= sprintf( '<div id="%1$s" class="et_pb_tiny_mce_field"><%%= typeof( %2$s ) !== \'undefined\' ? %2$s : \'\' %%></div>', esc_attr( $main_content_field_name ), esc_html( $main_content_property_name ) ); break; case 'codemirror': case 'textarea': case 'custom_css': case 'options_list': case 'sortable_list': $field_custom_value = esc_html( $field_var_name ); if ( in_array( $field['type'], array( 'custom_css', 'options_list', 'sortable_list' ), true ) ) { $field_custom_value .= '.replace( /\|\|/g, "\n" ).replace( /%22/g, """ ).replace( /%92/g, "\\\" )'; $field_custom_value .= '.replace( /%91/g, "[" ).replace( /%93/g, "]" )'; } if ( in_array( $field_name, array( 'et_pb_custom_message' ), true ) ) { // escape html to make sure it's not rendered inside the Textarea field in Settings Modal. $field_custom_value = sprintf( '_.escape( %1$s )', $field_custom_value ); } $field_el .= sprintf( '<textarea class="et-pb-main-setting large-text code%1$s" rows="4" cols="50" id="%2$s"><%%= typeof( %3$s ) !== \'undefined\' ? %4$s : \'\' %%></textarea>', esc_attr( $field['class'] ), esc_attr( $field['id'] ), esc_html( $field_var_name ), et_core_esc_previously( $field_custom_value ) ); if ( 'options_list' === $field['type'] || 'sortable_list' === $field['type'] ) { $radio_check = ''; $row_class = 'et_options_list_row'; if ( isset( $field['checkbox'] ) && true === $field['checkbox'] ) { $radio_check = '<a href="#" class="et_options_list_check"></a>'; $row_class .= ' et_options_list_row_checkbox'; } if ( isset( $field['radio'] ) && true === $field['radio'] ) { $radio_check = '<a href="#" class="et_options_list_check"></a>'; $row_class .= ' et_options_list_row_radio'; } $field_el = sprintf( '<div class="et_options_list"> <div class="%5$s"> %6$s <input type="text" /> <div class="et_options_list_actions"> <a href="#" class="et_options_list_move"></a> <a href="#" class="et_options_list_copy"></a> <a href="#" class="et_options_list_remove"></a> </div> </div> <textarea class="et-pb-main-setting large-text code%1$s" rows="4" cols="50" id="%2$s"><%%= typeof( %3$s ) !== \'undefined\' ? %4$s : \'\' %%></textarea> <a href="#" class="et-pb-add-sortable-option"><span>%7$s</span></a> </div>', esc_attr( $field['class'] ), esc_attr( $field['id'] ), esc_html( $field_var_name ), et_core_esc_previously( $field_custom_value ), esc_attr( $row_class ), $radio_check, esc_html__( 'Add New Item', 'et_builder' ) ); } break; case 'conditional_logic': $field_custom_value = esc_html( $field_var_name ); $field_custom_value .= '.replace( /\|\|/g, "\n" ).replace( /%22/g, """ ).replace( /%92/g, "\\\" )'; $field_custom_value .= '.replace( /%91/g, "[" ).replace( /%93/g, "]" )'; $field_selects = sprintf( '<select class="et_conditional_logic_field"></select> <select class="et_conditional_logic_condition"> <option value="is">%1$s</option> <option value="is not">%2$s</option> <option value="is greater">%3$s</option> <option value="is less">%4$s</option> <option value="contains">%5$s</option> <option value="does not contain">%6$s</option> <option value="is empty">%7$s</option> <option value="is not empty">%8$s</option> </select>', esc_html__( 'equals', 'et_builder' ), esc_html__( 'does not equal', 'et_builder' ), esc_html__( 'is greater than', 'et_builder' ), esc_html__( 'is less than', 'et_builder' ), esc_html__( 'contains', 'et_builder' ), esc_html__( 'does not contain', 'et_builder' ), esc_html__( 'is empty', 'et_builder' ), esc_html__( 'is not empty', 'et_builder' ) ); $field_el = sprintf( '<div class="et_options_list et_conditional_logic" data-checked="%6$s" data-unchecked="%7$s"> <div class="et_options_list_row"> %5$s <a href="#" class="et_options_list_remove"></a> </div> <textarea class="et-pb-main-setting large-text code%1$s" rows="4" cols="50" id="%2$s"><%%= typeof( %3$s ) !== \'undefined\' ? %4$s : \'\' %%></textarea> <a href="#" class="et-pb-add-sortable-option"><span>%8$s</span></a> </div>', esc_attr( $field['class'] ), esc_attr( $field['id'] ), esc_html( $field_var_name ), et_core_esc_previously( $field_custom_value ), $field_selects, esc_html__( 'checked', 'et_builder' ), esc_html__( 'not checked', 'et_builder' ), esc_html__( 'Add New Rule', 'et_builder' ) ); break; case 'align': case 'text_align': case 'select': case 'divider': case 'yes_no_button': case 'multiple_buttons': case 'font': case 'select_with_option_groups': $is_align = in_array( $field['type'], array( 'text_align', 'align' ), true ); if ( 'font' === $field['type'] ) { $field['id'] .= '_select'; $field_name .= '_select'; $field['class'] .= ' et-pb-helper-field'; $field['options'] = array(); } if ( $is_align ) { $field['class'] = 'et-pb-text-align-select'; } $button_options = array(); if ( 'yes_no_button' === $field['type'] ) { $button_options = isset( $field['button_options'] ) ? $field['button_options'] : array(); } if ( $default ) { $attributes .= sprintf( ' data-default="%1$s"', esc_attr( $default ) ); } // If default is an array, then $default_value value is an js expression, so it doesn't need to be encoded // In other case it needs to be encoded. $select_default = $default_is_arr ? $default_value : wp_json_encode( $default_value ); if ( 'font' === $field['type'] ) { $group_label = isset( $field['group_label'] ) ? $field['group_label'] : ''; $select = $this->render_font_select( $field_name, $field['id'], $group_label ); } elseif ( 'multiple_buttons' === $field['type'] ) { if ( isset( $field['toggleable'] ) && $field['toggleable'] ) { $attributes .= ' data-toggleable="yes"'; } if ( isset( $field['multi_selection'] ) && $field['multi_selection'] ) { $attributes .= ' data-multi="yes"'; } $select = $this->render_multiple_buttons( $field_name, $field['options'], $field['id'], $field['class'], $attributes, $value, $default_value ); } else { $select = $this->render_select( $field_name, $field['options'], $field['id'], $field['class'], $attributes, $field['type'], $button_options, $select_default, $only_options ); } if ( $only_options ) { $field_el = $select; } else { $field_el .= $select; } if ( 'font' === $field['type'] ) { $font_style_button_html = sprintf( '<%%= window.et_builder.options_template_output("font_buttons",%1$s) %%>', wp_json_encode( array( 'italic', 'uppercase', 'capitalize', 'underline', 'line_through' ) ) ); $field_el .= sprintf( '<div class="et_builder_font_styles mce-toolbar"> %1$s </div>', $font_style_button_html ); $field_el .= '<%= window.et_builder.options_template_output("font_line_styles") %>'; $field_el .= $hidden_field; } if ( $is_align ) { $text_align_options = ! empty( $field['options'] ) ? array_keys( $field['options'] ) : array( 'left', 'center', 'right', 'justified' ); $is_module_alignment = 'align' === $field['type'] || ( in_array( $field['name'], array( 'et_pb_module_alignment', 'et_pb_button_alignment' ), true ) || ( isset( $field['options_icon'] ) && 'module_align' === $field['options_icon'] ) ); $text_align_style_button_html = sprintf( '<%%= window.et_builder.options_text_align_buttons_output(%1$s, "%2$s") %%>', wp_json_encode( $text_align_options ), $is_module_alignment ? 'module' : 'text' ); $field_el .= sprintf( '<div class="et_builder_text_aligns mce-toolbar"> %1$s </div>', $text_align_style_button_html ); $field_el .= $hidden_field; } if ( 'select_with_option_groups' === $field['type'] ) { $field_el .= $hidden_field; } break; case 'select_animation': $options = $field['options']; $animation_buttons_array = array(); foreach ( $options as $option_name => $option_title ) { $animation_buttons_array[ $option_name ] = sanitize_text_field( $option_title ); } $animation_buttons = sprintf( '<%%= window.et_builder.options_template_output("animation_buttons",%1$s) %%>', wp_json_encode( $animation_buttons_array ) ); $field_el = sprintf( '<div class="et_select_animation et-pb-main-setting" data-default="none"> %1$s %2$s </div>', $animation_buttons, $hidden_field ); break; case 'presets_shadow': case 'select_box_shadow': case 'presets': $presets = $field['presets']; $presets_buttons = ''; foreach ( $presets as $preset ) { $fields = isset( $preset['fields'] ) ? htmlspecialchars( wp_json_encode( $preset['fields'] ), ENT_QUOTES, 'UTF-8' ) : '[]'; $presets_buttons .= sprintf( '<div class="et-preset" data-value="%1$s" data-fields="%2$s">', esc_attr( $preset['value'] ), esc_attr( $fields ) ); if ( isset( $preset['title'] ) && ! empty( $preset['title'] ) ) { $presets_buttons .= sprintf( '<span class="et-preset-title" >%1$s</span>', $preset['title'] ); } if ( isset( $preset['icon'] ) && ! empty( $preset['icon'] ) ) { $presets_buttons .= sprintf( '<span class="et-preset-icon">%1$s</span>', $this->get_icon( $preset['icon'] ) ); } if ( isset( $preset['content'] ) && ! empty( $preset['content'] ) ) { if ( is_array( $preset['content'] ) ) { $content = isset( $preset['content']['content'] ) ? $preset['content']['content'] : ''; $class = isset( $preset['content']['class'] ) ? ' ' . $preset['content']['class'] : ''; } else { $content = $preset['content']; $class = ''; } $presets_buttons .= sprintf( '<span class="et-preset-content%2$s">%1$s</span>', $content, $class ); } $presets_buttons .= '</div>'; } $field_el = sprintf( '<div class="et-presets et-preset-container et-pb-main-setting %3$s" data-default="none"> %1$s %2$s </div>', $presets_buttons, $hidden_field, esc_attr( $field['type'] ) ); break; case 'color': case 'color-alpha': $field['default'] = ! empty( $field['default'] ) ? $field['default'] : ''; if ( $is_custom_color && ( ! isset( $field['default'] ) || '' === $field['default'] ) ) { $field['default'] = ''; } $default = ! empty( $field['default'] ) ? sprintf( ' data-default-color="%1$s" data-default="%1$s"', esc_attr( $field['default'] ) ) : ''; $color_id = sprintf( ' id="%1$s"', esc_attr( $field['id'] ) ); $color_value_html = '<%%- typeof( %1$s ) !== \'undefined\' && %1$s !== \'\' ? %1$s : \'%2$s\' %%>'; $main_color_value = sprintf( $color_value_html, esc_attr( $field_var_name ), $field['default'] ); $hidden_color_value = sprintf( $color_value_html, esc_attr( $field_var_name ), '' ); $has_preview = isset( $field['has_preview'] ) && $field['has_preview']; $field_el = sprintf( '<input%1$s class="et-pb-color-picker-hex%5$s%8$s%10$s" type="text"%6$s%7$s placeholder="%9$s" data-selected-value="%2$s" value="%2$s"%3$s /> %4$s', ( ! $is_custom_color || $has_preview ? $color_id : '' ), $main_color_value, $default, ( ! empty( $field['additional_code'] ) ? $field['additional_code'] : '' ), ( 'color-alpha' === $field['type'] ? ' et-pb-color-picker-hex-alpha' : '' ), ( 'color-alpha' === $field['type'] ? ' data-alpha="true"' : '' ), ( 'color' === $field['type'] ? ' maxlength="7"' : '' ), ( ! $is_custom_color ? ' et-pb-main-setting' : '' ), esc_attr__( 'Hex Value', 'et_builder' ), $has_preview ? esc_attr( ' et-pb-color-picker-hex-has-preview' ) : '' ); if ( $is_custom_color && ! $has_preview ) { $field_el = sprintf( '<span class="et-pb-custom-color-button et-pb-choose-custom-color-button"><span>%1$s</span></span> <div class="et-pb-custom-color-container et_pb_hidden"> %2$s <input%3$s class="et-pb-main-setting et-pb-custom-color-picker" type="hidden" value="%4$s" %6$s /> %5$s </div>', esc_html__( 'Choose Custom Color', 'et_builder' ), $field_el, $color_id, $hidden_color_value, $reset_button_html, $attributes ); } break; case 'upload': $field_data_type = ! empty( $field['data_type'] ) ? $field['data_type'] : 'image'; $field['upload_button_text'] = ! empty( $field['upload_button_text'] ) ? $field['upload_button_text'] : esc_attr__( 'Upload', 'et_builder' ); $field['choose_text'] = ! empty( $field['choose_text'] ) ? $field['choose_text'] : esc_attr__( 'Choose image', 'et_builder' ); $field['update_text'] = ! empty( $field['update_text'] ) ? $field['update_text'] : esc_attr__( 'Set image', 'et_builder' ); $field['class'] = ! empty( $field['class'] ) ? ' ' . $field['class'] : ''; $field_additional_button = ! empty( $field['additional_button'] ) ? "\n\t\t\t\t\t" . $field['additional_button'] : ''; $field_el .= sprintf( '<input id="%1$s" type="text" class="et-pb-main-setting regular-text et-pb-upload-field%8$s" value="<%%- typeof( %2$s ) !== \'undefined\' ? %2$s : \'\' %%>" %9$s /> <input type="button" class="button button-upload et-pb-upload-button" value="%3$s" data-choose="%4$s" data-update="%5$s" data-type="%6$s" />%7$s', esc_attr( $field['id'] ), esc_attr( $field_var_name ), esc_attr( $field['upload_button_text'] ), esc_attr( $field['choose_text'] ), esc_attr( $field['update_text'] ), esc_attr( $field_data_type ), $field_additional_button, esc_attr( $field['class'] ), $attributes ); break; case 'checkbox': $field_el .= sprintf( '<input type="checkbox" name="%1$s" id="%2$s" class="et-pb-main-setting" value="on" <%%- typeof( %3$s ) !== \'undefined\' && %3$s === \'on\' ? checked="checked" : "" %%>>', esc_attr( $field['name'] ), esc_attr( $field['id'] ), esc_attr( str_replace( '-', '_', $field['name'] ) ) ); break; case 'multiple_checkboxes': $checkboxes_set = '<div class="et_pb_checkboxes_wrapper">'; if ( ! empty( $field['options'] ) ) { foreach ( $field['options'] as $option_value => $option_label ) { $checkboxes_set .= sprintf( '%3$s<label><input type="checkbox" class="et_pb_checkbox_%1$s" value="%1$s"> %2$s</label><br/>', esc_attr( $option_value ), esc_html( $option_label ), "\n\t\t\t\t\t" ); } } // additional option for disable_on option for backward compatibility. if ( isset( $field['additional_att'] ) && 'disable_on' === $field['additional_att'] ) { $et_pb_disabled_value = sprintf( $value_html, esc_attr( 'et_pb_disabled' ), esc_attr( 'et_pb_disabled' ), '' ); $checkboxes_set .= sprintf( '<input type="hidden" id="et_pb_disabled" class="et_pb_disabled_option"%1$s>', $et_pb_disabled_value ); } $field_el .= $checkboxes_set . $hidden_field . '</div>'; break; case 'hidden': $field_el .= $hidden_field; break; case 'custom_margin': case 'custom_padding': $custom_margin_class = ''; // Fill the array of values for tablet and phone. if ( $need_mobile_options ) { $mobile_values_array = array(); $has_saved_value = array(); $mobile_desktop_class = ' et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active'; $mobile_desktop_data = ' data-device="desktop"'; foreach ( array( 'tablet', 'phone' ) as $device ) { $mobile_values_array[] = sprintf( $value_html, esc_attr( $field_var_name . '_' . $device ), esc_attr( $field_var_name . '_' . $device ), $default_value ); $has_saved_value[] = sprintf( ' data-has_saved_value="<%%- typeof( %1$s ) !== \'undefined\' ? \'yes\' : \'no\' %%>" ', esc_attr( $field_var_name . '_' . $device ) ); } $value_last_edited = sprintf( $value_html, esc_attr( $field_var_name . '_last_edited' ), esc_attr( $field_var_name . '_last_edited' ), '' ); // additional field to save the last edited field which will be opened automatically. $additional_mobile_fields = sprintf( '<input id="%1$s" type="hidden" class="et_pb_mobile_last_edited_field"%2$s>', esc_attr( $field_name . '_last_edited' ), $value_last_edited ); } // Add auto_important class to field which automatically append !important tag. if ( isset( $this->advanced_fields['margin_padding']['css']['important'] ) ) { $custom_margin_class .= ' auto_important'; } $has_responsive_affects = isset( $field['responsive_affects'] ); $single_fields_settings = array( 'side' => '', 'label' => '', 'need_mobile' => $need_mobile_options ? 'need_mobile' : '', 'class' => esc_attr( $custom_margin_class ), ); $field_el .= sprintf( '<div class="et_margin_padding"> %6$s %7$s %8$s %9$s <input type="hidden" name="%1$s" data-default="%5$s" id="%2$s" class="et_custom_margin_main et-pb-main-setting%11$s%14$s"%12$s %3$s %4$s/> %10$s %13$s </div>', esc_attr( $field['name'] ), esc_attr( $field['id'] ), $value, $attributes, esc_attr( $default ), // #5 ! isset( $field['sides'] ) || ( ! empty( $field['sides'] ) && in_array( 'top', $field['sides'], true ) ) ? sprintf( '<%%= window.et_builder.options_template_output("padding",%1$s) %%>', wp_json_encode( array_merge( $single_fields_settings, array( 'side' => 'top', 'label' => et_builder_i18n( 'Top' ), ) ) ) ) : '', ! isset( $field['sides'] ) || ( ! empty( $field['sides'] ) && in_array( 'right', $field['sides'], true ) ) ? sprintf( '<%%= window.et_builder.options_template_output("padding",%1$s) %%>', wp_json_encode( array_merge( $single_fields_settings, array( 'side' => 'right', 'label' => et_builder_i18n( 'Right' ), ) ) ) ) : '', ! isset( $field['sides'] ) || ( ! empty( $field['sides'] ) && in_array( 'bottom', $field['sides'], true ) ) ? sprintf( '<%%= window.et_builder.options_template_output("padding",%1$s) %%>', wp_json_encode( array_merge( $single_fields_settings, array( 'side' => 'bottom', 'label' => et_builder_i18n( 'Bottom' ), ) ) ) ) : '', ! isset( $field['sides'] ) || ( ! empty( $field['sides'] ) && in_array( 'left', $field['sides'], true ) ) ? sprintf( '<%%= window.et_builder.options_template_output("padding",%1$s) %%>', wp_json_encode( array_merge( $single_fields_settings, array( 'side' => 'left', 'label' => et_builder_i18n( 'Left' ), ) ) ) ) : '', $need_mobile_options ? sprintf( '<input type="hidden" name="%1$s_tablet" data-default="%4$s" id="%2$s_tablet" class="et-pb-main-setting et_custom_margin_main et_pb_setting_mobile et_pb_setting_mobile_tablet%9$s" data-device="tablet" %5$s %3$s %7$s/> <input type="hidden" name="%1$s_phone" data-default="%4$s" id="%2$s_phone" class="et-pb-main-setting et_custom_margin_main et_pb_setting_mobile et_pb_setting_mobile_phone%9$s" data-device="phone" %6$s %3$s %8$s/>', esc_attr( $field['name'] ), esc_attr( $field['id'] ), $attributes, esc_attr( $default ), $mobile_values_array[0], $mobile_values_array[1], $has_saved_value[0], $has_saved_value[1], $has_responsive_affects ? ' et-pb-responsive-affects' : '' ) : '', // #10 $need_mobile_options ? esc_attr( $mobile_desktop_class ) : '', $need_mobile_options ? $mobile_desktop_data : '', $need_mobile_options ? $additional_mobile_fields : '', $has_responsive_affects ? ' et-pb-responsive-affects' : '' // #14 ); break; case 'text': case 'number': case 'date_picker': case 'range': default: $validate_number = isset( $field['number_validation'] ) && $field['number_validation'] ? true : false; if ( 'date_picker' === $field['type'] ) { $field['class'] .= ' et-pb-date-time-picker'; } $field['class'] .= 'range' === $field['type'] ? ' et-pb-range-input' : ' et-pb-main-setting'; $type = in_array( $field['type'], array( 'text', 'number' ), true ) ? $field['type'] : 'text'; $unit = isset( $field['default_unit'] ) ? 'data-unit="' . esc_attr( $field['default_unit'] ) . '"' : ''; $field_el .= sprintf( '<input id="%1$s" type="%11$s" class="%2$s%5$s%9$s"%6$s%3$s%8$s%10$s %4$s %12$s/>%7$s', esc_attr( $field['id'] ), esc_attr( $field['class'] ), $value, $attributes, ( $validate_number ? ' et-validate-number' : '' ), ( $validate_number ? ' maxlength="3"' : '' ), ( ! empty( $field['additional_button'] ) ? $field['additional_button'] : '' ), ( '' !== $default ? sprintf( ' data-default="%1$s"', esc_attr( $default ) ) : '' ), $need_mobile_options ? ' et_pb_setting_mobile et_pb_setting_mobile_active et_pb_setting_mobile_desktop' : '', $need_mobile_options ? ' data-device="desktop"' : '', $type, $unit ); // generate additional fields for mobile settings switcher if needed. if ( $need_mobile_options ) { $additional_fields = ''; foreach ( array( 'tablet', 'phone' ) as $device_type ) { $value_mobile = sprintf( $value_html, esc_attr( $field_var_name . '_' . $device_type ), esc_attr( $field_var_name . '_' . $device_type ), $default_value ); // additional data attribute to handle default values for the responsive options. $has_saved_value = sprintf( ' data-has_saved_value="<%%- typeof( %1$s ) !== \'undefined\' ? \'yes\' : \'no\' %%>" ', esc_attr( $field_var_name . '_' . $device_type ) ); $additional_fields .= sprintf( '<input id="%2$s" type="%11$s" class="%3$s%5$s et_pb_setting_mobile et_pb_setting_mobile_%9$s"%6$s%8$s%1$s data-device="%9$s" %4$s%10$s/>%7$s', $value_mobile, esc_attr( $field['id'] ) . '_' . $device_type, esc_attr( $field['class'] ), $attributes, ( $validate_number ? ' et-validate-number' : '' ), // #5 ( $validate_number ? ' maxlength="3"' : '' ), ( ! empty( $field['additional_button'] ) ? $field['additional_button'] : '' ), ( '' !== $default ? sprintf( ' data-default="%1$s"', esc_attr( $default ) ) : '' ), esc_attr( $device_type ), $has_saved_value, // #10, $type ); } // Replace value HTML of last edited field. Last edited value maybe an empty // string on some range input under Shadow settings. $last_edited_value_html = $default_last_edited_is_arr ? ' value="<%%- typeof( %1$s ) !== \'undefined\' ? %2$s : \'%3$s\' %%>" ' : $value_html; $value_last_edited = sprintf( $last_edited_value_html, esc_attr( $field_var_name . '_last_edited' ), esc_attr( $field_var_name . '_last_edited' ), '' ); $class_last_edited = array( 'et_pb_mobile_last_edited_field', ); $attrs = ''; if ( ! empty( $field['responsive_affects'] ) ) { $class_last_edited[] = 'et-pb-responsive-affects'; $attrs .= sprintf( ' data-responsive-affects="%1$s" data-responsive-desktop-name="%2$s"', esc_attr( implode( ', ', $field['responsive_affects'] ) ), esc_attr( $field['name'] ) ); } // additional field to save the last edited field which will be opened automatically. $additional_fields .= sprintf( '<input id="%1$s" type="hidden" class="%3$s"%2$s%4$s>', esc_attr( $field_name . '_last_edited' ), $value_last_edited, esc_attr( implode( ' ', $class_last_edited ) ), $attrs ); } if ( 'range' === $field['type'] ) { $range_value_html = $default_is_arr ? ' value="<%%- typeof( %1$s ) !== \'undefined\' ? %2$s :parseFloat(%3$s) %%>" ' : ' value="<%%- typeof( %1$s ) !== \'undefined\' ? %2$s : parseFloat(\'%3$s\') %%>" '; $value = sprintf( $range_value_html, esc_attr( $field_var_name ), esc_attr( sprintf( 'parseFloat( %1$s )', $field_var_name ) ), $default_value ); $fixed_range = isset( $field['fixed_range'] ) && $field['fixed_range']; $range_settings_html = ''; $range_properties = apply_filters( 'et_builder_range_properties', array( 'min', 'max', 'step' ) ); foreach ( $range_properties as $property ) { if ( isset( $field['range_settings'][ $property ] ) ) { $range_settings_html .= sprintf( ' %2$s="%1$s"', esc_attr( $field['range_settings'][ $property ] ), esc_html( $property ) ); } } $range_el = sprintf( '<input type="range" data-name="%7$s" class="et-pb-main-setting et-pb-range%4$s%6$s" data-default="%2$s"%1$s%3$s%5$s %8$s />', $value, esc_attr( $default ), $range_settings_html, $need_mobile_options ? ' et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active' : '', $need_mobile_options ? ' data-device="desktop"' : '', $fixed_range ? ' et-pb-fixed-range' : '', esc_attr( $field['name'] ), $unit ); if ( $need_mobile_options ) { foreach ( array( 'tablet', 'phone' ) as $device_type ) { // additional data attribute to handle default values for the responsive options. $has_saved_value = sprintf( ' data-has_saved_value="<%%- typeof( %1$s ) !== \'undefined\' ? \'yes\' : \'no\' %%>" ', esc_attr( $field_var_name . '_' . $device_type ) ); $value_mobile_range = sprintf( $value_html, esc_attr( $field_var_name . '_' . $device_type ), esc_attr( sprintf( 'parseFloat( %1$s )', $field_var_name . '_' . $device_type ) ), $default_value ); $range_el .= sprintf( '<input type="range" class="et-pb-main-setting et-pb-range et_pb_setting_mobile et_pb_setting_mobile_%3$s%6$s" data-default="%1$s"%4$s%2$s data-device="%3$s"%5$s %7$s/>', esc_attr( $default ), $range_settings_html, esc_attr( $device_type ), $value_mobile_range, $has_saved_value, $fixed_range ? ' et-pb-fixed-range' : '', $unit ); } } $field_el = $range_el . "\n" . $field_el; } if ( $need_mobile_options ) { $field_el = $field_el . $additional_fields; } break; } if ( isset( $field['has_preview'] ) && $field['has_preview'] ) { $field_el = sprintf( '<%%= window.et_builder.options_template_output("option_preview_buttons") %%> %1$s', $field_el ); } if ( $need_mobile_options ) { $field_el = $mobile_settings_tabs . "\n" . $field_el; $field_el .= '<span class="et-pb-mobile-settings-toggle"></span>'; } if ( isset( $field['type'] ) && isset( $field['tab_slug'] ) && 'advanced' === $field['tab_slug'] && ! $is_custom_color ) { $field_el .= $reset_button_html; } if ( isset( $field['after'] ) && ! $only_options ) { $field_el .= $this->render_field_before_after_element( $field['after'] ); } return "\t" . $field_el; } /** * Returns an underscore template for the field before and after elements. * * @param array $elements Render field after/before these elements. * * @return string */ public function render_field_before_after_element( $elements ) { $field_el = ''; $elements = is_array( $elements ) ? $elements : array( $elements ); foreach ( $elements as $element ) { $attributes = ''; if ( ! empty( $element['attributes'] ) ) { $this->process_html_attributes( $element, $attributes ); } switch ( $element['type'] ) { case 'button': $class = isset( $element['class'] ) ? esc_attr( $element['class'] ) : ''; $text = isset( $element['text'] ) ? et_core_esc_previously( $element['text'] ) : ''; $field_el .= sprintf( '<button class="button %1$s"%2$s>%3$s</button>', $class, $attributes, $text ); break; } } return $field_el; } /** * Returns an underscore template for the font select options settings. * * @param string $name Field name. * @param string $id Field id. * @param string $group_label Field group label. * * @return string */ public function render_font_select( $name, $id, $group_label ) { $options_output = '<%= window.et_builder.fonts_template() %>'; $font_weight_output = '<%= window.et_builder.fonts_weight_template() %>'; $output = sprintf( '<div class="et-pb-select-font-outer" data-group_label="%6$s"> <div class="et-pb-settings-custom-select-wrapper et-pb-settings-option-select-searchable"> <div class="et_pb_select_placeholder"></div> <ul class="et-pb-settings-option-select et-pb-settings-option-select-advanced et-pb-main-setting"> <li class="et-pb-select-options-filter"> <input type="text" class="et-pb-settings-option-input et-pb-main-setting regular-text et-pb-menu-filter" placeholder="Search Fonts"> </li> <li class="et_pb_selected_item_container select-option-item"> </li> <li class="et-pb-option-subgroup et-pb-recent-fonts"> <p class="et-pb-subgroup-title">%4$s</p> <ul> </ul> </li> %3$s </ul> </div> </div> %5$s', esc_attr( $name ), ( ! empty( $id ) ? sprintf( ' id="%s"', esc_attr( $id ) ) : '' ), $options_output . "\n\t\t\t\t\t", esc_html__( 'Recent', 'et_builder' ), $font_weight_output, esc_attr( $group_label ) ); return $output; } /** * Returns an underscore template for the select options settings. * * @param string $name Option name. * @param array $options Option settings. * @param string $id Option id. * @param string $class Option css class. * @param string $attributes Option html attributes. * @param string $field_type Whether select field is with option groups. * @param array $button_options Yes/No button option. * @param string $default Option default value. * @param bool $only_options Whether to return only option without <select>. * * @return string */ public function render_select( $name, $options, $id = '', $class = '', $attributes = '', $field_type = '', $button_options = array(), $default = '', $only_options = false ) { $options_output = ''; $processed_options = $options; if ( 'select_with_option_groups' === $field_type ) { foreach ( $processed_options as $option_group_name => $option_group ) { $option_group_name = esc_attr( $option_group_name ); $options_output .= '0' !== $option_group_name ? "<optgroup label='{$option_group_name}'>" : ''; $options_output .= sprintf( '<%%= window.et_builder.options_template_output("select",%1$s,this.model.toJSON()) %%>', sprintf( '{select_name: "%1$s", list: %2$s, default: %3$s, }', $name, wp_json_encode( $option_group ), $default ) ); $options_output .= '0' !== $option_group_name ? '</optgroup>' : ''; } $class = rtrim( $class ); $id = ''; $name = $id; } else { $class = rtrim( 'et-pb-main-setting ' . $class ); $options_output .= sprintf( '<%%= window.et_builder.options_template_output("select",%1$s,this.model.toJSON()) %%>', sprintf( '{select_name: "%1$s", list: %2$s, default: %3$s, }', $name, wp_json_encode( $options ), $default ) ); } $output = sprintf( '%6$s <select name="%1$s"%2$s%3$s%4$s class="%3$s %8$s"%9$s>%5$s</select> %7$s', esc_attr( $name ), ( ! empty( $id ) ? sprintf( ' id="%s"', esc_attr( $id ) ) : '' ), ( ! empty( $class ) ? esc_attr( $class ) : '' ), ( ! empty( $attributes ) ? $attributes : '' ), $options_output . "\n\t\t\t\t\t", 'yes_no_button' === $field_type ? sprintf( '<div class="et_pb_yes_no_button_wrapper %2$s"> %1$s', sprintf( '<%%= window.et_builder.options_template_output("yes_no_button",%1$s) %%>', wp_json_encode( array( 'on' => esc_html( $processed_options['on'] ), 'off' => esc_html( $processed_options['off'] ), ) ) ), ( ! empty( $button_options['button_type'] ) && 'equal' === $button_options['button_type'] ? ' et_pb_button_equal_sides' : '' ) ) : '', 'yes_no_button' === $field_type ? '</div>' : '', esc_attr( $field_type ), '' !== $name ? sprintf( ' data-saved_value="<%%= typeof( %1$s ) !== \'undefined\' ? %1$s : \'\' %%>"', esc_attr( str_replace( '-', '_', $name ) ) ) : '' ); return $only_options ? $options_output : $output; } /** * Returns an underscore template for the multiple button options settings. * * @param string $name Option name. * @param array $options Option settings. * @param string $id Option id. * @param string $class Option css class. * @param string $attributes Option html attributes. * @param string $value Option value. * @param string $default Option default value. * * @return string */ public function render_multiple_buttons( $name, $options, $id = '', $class = '', $attributes = '', $value = '', $default = '' ) { $class = rtrim( 'et-pb-main-setting ' . $class ); $output = sprintf( '<div class="et_pb_multiple_buttons_wrapper"> <input id="%1$s" name="%7$s" type="hidden" class="%2$s" %3$s%5$s %4$s/> %6$s </div>', esc_attr( $id ), esc_attr( $class ), $value, $attributes, ( '' !== $default ? sprintf( ' data-default=%1$s', esc_attr( $default ) ) : '' ), sprintf( '<%%= window.et_builder.options_template_output("multiple_buttons",%1$s) %%>', wp_json_encode( $options ) ), esc_attr( $name ) ); return $output; } // phpcs:disable -- Deprecated function. /** * @deprecated */ public function get_main_tabs() { $tabs = array( 'general' => et_builder_i18n( 'Content' ), 'advanced' => et_builder_i18n( 'Design' ), 'custom_css' => et_builder_i18n( 'Advanced' ), ); return apply_filters( 'et_builder_main_tabs', $tabs ); } // phpcs:enable /** * Get validation rule attribute. * * @return array */ public function get_validation_attr_rules() { return array( 'minlength', 'maxlength', 'min', 'max', ); } /** * Get validation class name to use in underscore template. * * @return array */ public function get_validation_class_rules() { return array( 'required', 'email', 'url', 'date', 'dateISO', 'number', 'digits', 'creditcard', ); } /** * Sorting options by priority. * * @param array $fields Fields. * * @return array */ public function sort_fields( $fields ) { $tabs_fields = array(); $sorted_fields = array(); $i = 0; // Sort fields array by tab name. foreach ( $fields as $field_slug => $field_options ) { // Option template replaces field's array configuration into string which refers to // saved template data & template id; thus add index order if $field_options is array. if ( is_array( $field_options ) ) { $field_options['_order_number'] = $i; } $tab_slug = ! empty( $field_options['tab_slug'] ) ? $field_options['tab_slug'] : 'general'; $tabs_fields[ $tab_slug ][ $field_slug ] = $field_options; $i++; } // Sort fields within tabs by priority. foreach ( $tabs_fields as $tab_fields ) { uasort( $tab_fields, array( 'ET_Builder_Element', 'compare_by_priority' ) ); $sorted_fields = array_merge( $sorted_fields, $tab_fields ); } return $sorted_fields; } /** * Return setting options markup. * * @return string */ public function get_options() { $output = ''; $toggle_all_options_slug = 'all_options'; $toggles_used = isset( $this->settings_modal_toggles ); $tabs_output = array( 'general' => array() ); $all_fields = $this->sort_fields( $this->_get_fields() ); $all_fields_keys = array_keys( $all_fields ); $background_fields_names = $this->get_background_fields_names(); $module_has_background_color_field = in_array( 'background_color', $all_fields_keys, true ); $all_toggles = self::get_toggles( 'post' ); foreach ( $all_fields as $field_name => $field ) { if ( ! empty( $field['type'] ) && ( 'skip' === $field['type'] || 'computed' === $field['type'] ) ) { continue; } if ( ! self::$_->array_get( $field, 'bb_support', true ) ) { continue; } // add only options allowed for current user. if ( ( ! et_pb_is_allowed( 'edit_colors' ) && ( ! empty( $field['type'] ) && in_array( $field['type'], array( 'color', 'color-alpha' ), true ) || ( ! empty( $field['option_category'] ) && 'color_option' === $field['option_category'] ) ) ) || ( ! et_pb_is_allowed( 'edit_content' ) && ! empty( $field['option_category'] ) && 'basic_option' === $field['option_category'] ) || ( ! et_pb_is_allowed( 'edit_layout' ) && ! empty( $field['option_category'] ) && 'layout' === $field['option_category'] ) || ( ! et_pb_is_allowed( 'edit_configuration' ) && ! empty( $field['option_category'] ) && 'configuration' === $field['option_category'] ) || ( ! et_pb_is_allowed( 'edit_fonts' ) && ! empty( $field['option_category'] ) && ( 'font_option' === $field['option_category'] || ( 'button' === $field['option_category'] && ! empty( $field['type'] ) && 'font' === $field['type'] ) ) ) || ( ! et_pb_is_allowed( 'edit_buttons' ) && ! empty( $field['option_category'] ) && 'button' === $field['option_category'] ) || ( ! et_pb_is_allowed( 'edit_borders' ) && ! empty( $field['option_category'] ) && 'border' === $field['option_category'] ) ) { continue; } // check for allowed 3rd party custom options categories. if ( ! empty( $field['option_category'] ) && ! et_pb_is_allowed( $field['option_category'] ) ) { continue; } $option_output = ''; if ( 'background_color' === $field_name ) { $background_fields_ui = $this->wrap_settings_background_fields( $all_fields ); // Append background fields UI if applicable. Append standard option otherwise. if ( '' !== $background_fields_ui ) { // unset depends_show_if because background fields visibility handled in Background UI. unset( $field['depends_show_if'] ); // append background UI. $option_output .= $background_fields_ui; } else { $field['skip_background_ui'] = true; $option_output .= $this->wrap_settings_option_label( $field ); $option_output .= $this->wrap_settings_option_field( $field ); } } elseif ( $module_has_background_color_field && in_array( $field_name, $background_fields_names, true ) ) { // remove background-related fields from setting modals since it'll be printed by background UI. continue; } else { // append normal fields. $option_output .= $this->wrap_settings_option_label( $field ); $option_output .= $this->wrap_settings_option_field( $field ); } $tab_slug = ! empty( $field['tab_slug'] ) ? $field['tab_slug'] : 'general'; $is_toggle_option = isset( $field['toggle_slug'] ) && $toggles_used && isset( $this->settings_modal_toggles[ $tab_slug ] ); $toggle_slug = $is_toggle_option ? $field['toggle_slug'] : $toggle_all_options_slug; $sub_toggle_slug = 'all_options' !== $toggle_slug && isset( $field['sub_toggle'] ) && '' !== $field['sub_toggle'] ? $field['sub_toggle'] : 'main'; $tabs_output[ $tab_slug ][ $toggle_slug ][ $sub_toggle_slug ][] = $this->wrap_settings_option( $option_output, $field, $field_name ); if ( isset( $field['toggle_slug'] ) && ! isset( $this->settings_modal_toggles[ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { $toggle = self::$_->array_get( $all_toggles, "{$this->slug}.{$tab_slug}.toggles.{$field['toggle_slug']}" ); if ( $toggle ) { self::$_->array_set( $this->settings_modal_toggles, "{$tab_slug}.toggles.{$toggle_slug}", $toggle ); } } } $default_tabs_keys = array_keys( $this->main_tabs ); $module_tabs_keys = array_keys( $tabs_output ); $module_default_tabs = array_intersect( $default_tabs_keys, $module_tabs_keys ); $module_custom_tabs = array_diff( $module_tabs_keys, $default_tabs_keys ); // Make sure tabs order is correct for BB, i.e. custom tabs goes after default tabs and default tabs in following order: // `Content`, `Design`, `Advanced`. $module_tabs_sorted = array_merge( $module_default_tabs, $module_custom_tabs ); $tabs_output_processed = array(); // reorder tabs to be sure they're correct. foreach ( $module_tabs_sorted as $tab_slug ) { $tabs_output_processed[ $tab_slug ] = $tabs_output[ $tab_slug ]; } foreach ( $tabs_output_processed as $tab_slug => $tab_settings ) { // Add only tabs allowed for current user. if ( ! et_pb_is_allowed( $tab_slug . '_settings' ) ) { continue; } $tab_output = ''; $this->used_tabs[] = $tab_slug; $i = 0; if ( isset( $tabs_output_processed[ $tab_slug ] ) ) { // Group field with no explicit toggle_slug then append it on top of other toggles. if ( isset( $tabs_output_processed[ $tab_slug ][ $toggle_all_options_slug ] ) ) { $toggle_unclassified_output = ''; foreach ( $tabs_output_processed[ $tab_slug ][ $toggle_all_options_slug ] as $no_toggle_option_data ) { foreach ( $no_toggle_option_data as $subtoggle_id => $no_toggle_option_output ) { $toggle_unclassified_output .= $no_toggle_option_output; } } $tab_output .= sprintf( '<div class="et-pb-options-toggle-container et-pb-options-toggle-disabled"> <h3 class="et-pb-option-toggle-title">%1$s</h3> <div class="et-pb-option-toggle-content"> %2$s </div> </div>', esc_html( $this->name ), et_core_esc_previously( $toggle_unclassified_output ), 'et-pb-options-toggle-disabled' ); } if ( isset( $this->settings_modal_toggles[ $tab_slug ] ) ) { $this->settings_modal_toggles[ $tab_slug ]['toggles'] = self::et_pb_order_toggles_by_priority( $this->settings_modal_toggles[ $tab_slug ]['toggles'] ); foreach ( $this->settings_modal_toggles[ $tab_slug ]['toggles'] as $toggle_slug => $toggle_data ) { $toggle_heading = is_array( $toggle_data ) ? $toggle_data['title'] : $toggle_data; if ( ! isset( $tabs_output_processed[ $tab_slug ][ $toggle_slug ] ) ) { continue; } $i++; $toggle_output = ''; $is_accordion_enabled = isset( $this->settings_modal_toggles[ $tab_slug ]['settings']['bb_toggles_enabeld'] ) && $this->settings_modal_toggles[ $tab_slug ]['settings']['bb_toggles_enabled'] ? true : false; $is_tabbed_subtoggles = isset( $toggle_data['tabbed_subtoggles'] ); $is_bb_icons_support = isset( $toggle_data['bb_icons_support'] ); $subtoggle_tabs_nav = ''; if ( is_array( $toggle_data ) && ! empty( $toggle_data ) ) { if ( ! isset( $toggle_data['sub_toggles'] ) ) { $toggle_data['sub_toggles'] = array( 'main' => '' ); } foreach ( $toggle_data['sub_toggles'] as $sub_toggle_id => $sub_toggle_data ) { if ( ! isset( $tabs_output_processed[ $tab_slug ][ $toggle_slug ][ $sub_toggle_id ] ) ) { continue; } if ( $is_tabbed_subtoggles ) { $subtoggle_tabs_nav .= sprintf( '<li class="subtoggle_tabs_nav_item"><a class="subtoggle_tabs_nav_item_inner%3$s" data-tab_id="%1$s">%2$s</a></li>', $sub_toggle_id, $is_bb_icons_support ? '' : esc_html( $sub_toggle_data['name'] ), $is_bb_icons_support ? sprintf( ' subtoggle_tabs_nav_icon subtoggle_tabs_nav_icon-%1$s', esc_attr( $sub_toggle_data['icon'] ) ) : '' ); } $subtoggle_options = ''; foreach ( $tabs_output_processed[ $tab_slug ][ $toggle_slug ][ $sub_toggle_id ] as $toggle_option_output ) { $subtoggle_options .= $toggle_option_output; } if ( 'main' === $sub_toggle_id ) { $toggle_output .= $subtoggle_options; } else { $toggle_output .= sprintf( '<div class="et_pb_subtoggle_section%2$s"%3$s> %1$s </div>', $subtoggle_options, $is_tabbed_subtoggles ? ' et_pb_tabbed_subtoggle' : '', $is_tabbed_subtoggles ? sprintf( ' data-tab_id="%1$s"', esc_attr( $sub_toggle_id ) ) : '' ); } } } else { foreach ( $tabs_output_processed[ $tab_slug ][ $toggle_slug ] as $toggle_option_id => $toggle_option_data ) { foreach ( $toggle_option_data as $toggle_option_output ) { $toggle_output .= $toggle_option_output; } } } if ( '' === $toggle_output ) { continue; } $toggle_output = sprintf( '<div class="et-pb-options-toggle-container%3$s%4$s%5$s"> <h3 class="et-pb-option-toggle-title">%1$s</h3> %6$s <div class="et-pb-option-toggle-content"> %2$s </div> </div>', esc_html( $toggle_heading ), $toggle_output, ( $is_accordion_enabled ? ' et-pb-options-toggle-enabled' : ' et-pb-options-toggle-disabled' ), ( 1 === $i && $is_accordion_enabled ? ' et-pb-option-toggle-content-open' : '' ), $is_tabbed_subtoggles ? ' et_pb_contains_tabbed_subtoggle' : '', $is_tabbed_subtoggles && '' !== $subtoggle_tabs_nav ? sprintf( '<ul class="subtoggle_tabs_nav">%1$s</ul>', $subtoggle_tabs_nav ) : '' ); $tab_output .= $toggle_output; } } } $output .= sprintf( '<div class="et-pb-options-tab et-pb-options-tab-%1$s"> %3$s %2$s </div>', esc_attr( $tab_slug ), $tab_output, ( 'general' === $tab_slug ? $this->children_settings() : '' ) ); } // return error message if no tabs allowed for current user. if ( '' === $output ) { $output = esc_html__( "You don't have sufficient permissions to access the settings", 'et_builder' ); } return $output; } /** * Return children(general) setting options markup. * * @return string */ public function children_settings() { $output = ''; if ( ! empty( $this->child_slug ) ) { $child_module = self::get_module( $this->child_slug ); if ( isset( $child_module->bb_support ) && ! $child_module->bb_support ) { return $output; } $output = sprintf( '%6$s<div class="et-pb-option-advanced-module-settings" data-module_type="%1$s"> <ul class="et-pb-sortable-options"> </ul> %2$s </div> <div class="et-pb-option et-pb-option-main-content et-pb-option-advanced-module"> <label for="et_pb_content">%3$s</label> <div class="et-pb-option-container"> <div id="et_pb_content"><%%= typeof( et_pb_content )!== \'undefined\' && \'\' !== et_pb_content.trim() ? et_pb_content : \'%7$s\' %%></div> <p class="description">%4$s</p> </div> </div>%5$s', esc_attr( $this->child_slug ), ! in_array( $this->child_slug, array( 'et_pb_column', 'et_pb_column_inner' ), true ) ? sprintf( '<a href="#" class="et-pb-add-sortable-option"><span>%1$s</span></a>', esc_html( $this->add_new_child_text() ) ) : '', et_builder_i18n( 'Content' ), esc_html__( 'Here you can define the content that will be placed within the current tab.', 'et_builder' ), "\n\n", "\t", $this->predefined_child_modules() ); } return $output; } /** * Return add new item(module) text. * * @return mixed|string|null */ public function add_new_child_text() { $child_slug = ! empty( $this->child_item_text ) ? $this->child_item_text : ''; $child_slug = '' === $child_slug ? esc_html__( 'Add New Item', 'et_builder' ) : sprintf( esc_html__( 'Add New %s', 'et_builder' ), $child_slug ); return $child_slug; } /** * Wrap module settings underscore template. * * @param string $output Module template content. * * @return string */ public function wrap_settings( $output ) { $tabs_output = ''; $i = 0; $tabs = array(); // General Settings Tab should be added to all modules if allowed. if ( et_pb_is_allowed( 'general_settings' ) ) { $tabs['general'] = isset( $this->main_tabs['general'] ) ? $this->main_tabs['general'] : esc_html__( 'General Settings', 'et_builder' ); } foreach ( $this->used_tabs as $tab_slug ) { if ( 'general' === $tab_slug ) { continue; } // Add only tabs allowed for current user. if ( et_pb_is_allowed( $tab_slug . '_settings' ) ) { if ( isset( $this->main_tabs[ $tab_slug ] ) ) { // if it's one of 3 default tabs. $tabs[ $tab_slug ] = $this->main_tabs[ $tab_slug ]; } else { // Use tab name if it's properly registered custom tab. Fallback to tab slug otherwise. $tabs[ $tab_slug ] = isset( $this->settings_modal_tabs ) && isset( $this->settings_modal_tabs[ $tab_slug ] ) ? $this->settings_modal_tabs[ $tab_slug ]['name'] : $tab_slug; } } } $tabs_array = array(); $tabs_json = ''; foreach ( $tabs as $tab_slug => $tab_name ) { $i++; $tabs_array[ $i ] = array( 'slug' => $tab_slug, 'label' => $tab_name, ); $tabs_json = wp_json_encode( $tabs_array ); } $tabs_output = sprintf( '<%%= window.et_builder.settings_tabs_output(%1$s) %%>', $tabs_json ); $preview_tabs_output = '<%= window.et_builder.preview_tabs_output() %>'; $output = sprintf( '%2$s %3$s <div class="et-pb-options-tabs"> %1$s </div> <div class="et-pb-preview-tab"></div> ', $output, $tabs_output, $preview_tabs_output ); return sprintf( '%2$s<div class="et-pb-main-settings">%1$s</div>%3$s', "\n\t\t" . $output, "\n\t\t", "\n" ); } /** * Wrap module template into validation form. * * @param string $output Module template. * * @return string */ public function wrap_validation_form( $output ) { return '<form class="et-builder-main-settings-form validate">' . $output . '</form>'; } /** * Get the module's props mapped to their default values. * * @since 3.1 Renamed from `get_shortcode_fields()` to `get_default_props()`. * @since 1.0 * * @return array */ public function get_default_props() { if ( method_exists( $this, 'get_shortcode_fields' ) ) { // Backwards compatibility. return $this->__call( 'get_shortcode_fields', array() ); } // Get module's default props from static property If current module's default props // have been generated before. if ( isset( self::$_default_props[ $this->slug ] ) ) { return self::$_default_props[ $this->slug ]; } $fields = array(); // Resolve option template. foreach ( $this->process_fields( $this->fields_unprocessed ) as $field_name => $field ) { $value = ''; if ( isset( $field['composite_type'], $field['composite_structure'] ) ) { require_once ET_BUILDER_DIR . 'module/field/attribute/composite/Parser.php'; $composite_atts = ET_Builder_Module_Field_Attribute_Composite_Parser::parse( $field['composite_type'], $field['composite_structure'] ); $fields = array_merge( $fields, $composite_atts ); } else { if ( isset( $field['default_on_front'] ) ) { $value = $field['default_on_front']; } elseif ( isset( $field['default'] ) ) { $value = $field['default']; } $fields[ $field_name ] = $value; } } $fields['disabled'] = 'off'; $fields['disabled_on'] = ''; $fields['global_module'] = ''; $fields['temp_global_module'] = ''; $fields['global_parent'] = ''; $fields['temp_global_parent'] = ''; $fields['saved_tabs'] = ''; $fields['ab_subject'] = ''; $fields['ab_subject_id'] = ''; $fields['ab_goal'] = ''; $fields['locked'] = ''; $fields['template_type'] = ''; $fields['inline_fonts'] = ''; $fields['collapsed'] = ''; $fields['global_colors_info'] = '{}'; $fields['display_conditions'] = ''; // Default props of each modules are always identical; thus saves it as static prop // so the next same modules doesn't need to process all of these again repetitively. self::$_default_props[ $this->slug ] = $fields; return $fields; } /** * Get module data attributes. * * @return string */ public function get_module_data_attributes() { $attributes = apply_filters( "{$this->slug}_data_attributes", array(), $this->props, $this->render_count() ); $data_attributes = ''; if ( ! empty( $attributes ) ) { foreach ( $attributes as $name => $value ) { $data_attributes .= sprintf( ' data-%1$s="%2$s"', sanitize_title( $name ), esc_attr( $value ) ); } } return $data_attributes; } /** * Build underscore template for the module. * * @return string */ public function build_microtemplate() { $this->validation_in_use = false; $template_output = ''; if ( 'child' === $this->type ) { $id_attr = sprintf( 'et-builder-advanced-setting-%s', $this->slug ); } else { $id_attr = sprintf( 'et-builder-%s-module-template', $this->slug ); } if ( ! isset( $this->settings_text ) ) { $settings_text = sprintf( __( '%1$s %2$s Settings', 'et_builder' ), esc_html( $this->name ), 'child' === $this->type ? esc_html__( 'Item', 'et_builder' ) : esc_html__( 'Module', 'et_builder' ) ); } else { $settings_text = $this->settings_text; } if ( file_exists( ET_BUILDER_DIR . 'microtemplates/' . $this->slug . '.php' ) ) { ob_start(); include ET_BUILDER_DIR . 'microtemplates/' . $this->slug . '.php'; $output = ob_get_clean(); } else { $output = $this->get_options(); } $output = $this->wrap_settings( $output ); if ( $this->validation_in_use ) { $output = $this->wrap_validation_form( $output ); } $template_output = sprintf( '<script type="text/template" id="%1$s"> <h3 class="et-pb-settings-heading">%2$s</h3> %3$s </script>', esc_attr( $id_attr ), esc_html( $settings_text ), et_core_intentionally_unescaped( $output, 'html' ) ); if ( 'child' === $this->type ) { $title_var = esc_js( $this->child_title_var ); $title_var = false === strpos( $title_var, 'et_pb_' ) && 'admin_label' !== $title_var ? 'et_pb_' . $title_var : $title_var; $title_fallback_var = esc_js( $this->child_title_fallback_var ); $title_fallback_var = false === strpos( $title_fallback_var, 'et_pb_' ) ? 'et_pb_' . $title_fallback_var : $title_fallback_var; $add_new_text = isset( $this->advanced_setting_title_text ) ? $this->advanced_setting_title_text : $this->add_new_child_text(); $template_output .= sprintf( '%6$s<script type="text/template" id="et-builder-advanced-setting-%1$s-title"> <%% if ( typeof( %2$s ) !== \'undefined\' && typeof( %2$s ) === \'string\' && %2$s !== \'\' ) { %%> <%% if ( ET_PageBuilder.isDynamicContent(%2$s) ) { %%> %7$s <%% } else { %%> <%%- %2$s.replace( /%%91/g, "[" ).replace( /%%93/g, "]" ) %%> <%% } %%> <%% } else if ( typeof( %3$s ) !== \'undefined\' && typeof( %3$s ) === \'string\' && %3$s !== \'\' ) { %%> <%% if ( ET_PageBuilder.isDynamicContent(%3$s) ) { %%> %7$s <%% } else { %%> <%%- %3$s.replace( /%%91/g, "[" ).replace( /%%93/g, "]" ) %%> <%% } %%> <%% } else { %%> <%%- \'%4$s\' %%> <%% } %%> </script>%5$s', esc_attr( $this->slug ), esc_html( $title_var ), esc_html( $title_fallback_var ), esc_html( $add_new_text ), "\n\n", "\t", $this->get_icon( 'lock' ) . esc_html__( 'Dynamic Content', 'et_builder' ) ); } return $template_output; } /** * Generate gradient background. * * @param array $args Settings. * * @return string */ public function get_gradient( $args ) { $defaults = apply_filters( 'et_pb_default_gradient', array( 'repeat' => ET_Global_Settings::get_value( 'all_background_gradient_repeat' ), 'type' => ET_Global_Settings::get_value( 'all_background_gradient_type' ), 'direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction' ), 'radial_direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction_radial' ), 'stops' => ET_Global_Settings::get_value( 'all_background_gradient_stops' ), 'unit' => ET_Global_Settings::get_value( 'all_background_gradient_unit' ), ) ); $args = wp_parse_args( array_filter( $args ), $defaults ); $stops = str_replace( '|', ', ', $args['stops'] ); switch ( $args['type'] ) { case 'conic': $type = 'conic'; $direction = "from {$args['direction']} at {$args['radial_direction']}"; break; case 'elliptical': $type = 'radial'; $direction = "ellipse at {$args['radial_direction']}"; break; case 'radial': case 'circular': $type = 'radial'; $direction = "circle at {$args['radial_direction']}"; break; case 'linear': default: $type = 'linear'; $direction = $args['direction']; } // Apply gradient repeat (if set). if ( 'on' === $args['repeat'] ) { $type = 'repeating-' . $type; } return esc_html( "{$type}-gradient( {$direction}, {$stops} )" ); } /** * Get values for the rel attribute. * * @return array */ public function get_rel_values() { return array( 'bookmark', 'external', 'nofollow', 'noreferrer', 'noopener', ); } /** * Get rel attributes. * * @param string $saved_value Rel values. * @param bool $add_tag Whether to add rel attribute. * * @return string */ public function get_rel_attributes( $saved_value, $add_tag = true ) { $rel_attributes = array(); if ( $saved_value ) { $rel_values = $this->get_rel_values(); $selected_rels = explode( '|', $saved_value ); foreach ( $selected_rels as $index => $selected_rel ) { if ( ! $selected_rel || 'off' === $selected_rel ) { continue; } $rel_attributes[] = $rel_values[ $index ]; } } $attr = empty( $rel_attributes ) ? '' : implode( ' ', $rel_attributes ); return ( $add_tag && '' !== $attr ) ? sprintf( ' rel="%1$s"', esc_attr( $attr ) ) : $attr; } /** * Get text orientation. * * @since 3.23 Add device and desktop default parameter to get responsive value. * * @param string $device Device name. * @param string $desktop_default Default desktop value. * @return string RTL ready text alignment value. */ public function get_text_orientation( $device = 'desktop', $desktop_default = '' ) { $text_orientation = 'desktop' === $device && isset( $this->props['text_orientation'] ) ? $this->props['text_orientation'] : ''; if ( 'desktop' !== $device ) { $text_orientation = et_pb_responsive_options()->get_any_value( $this->props, "text_orientation_{$device}", $desktop_default ); } return et_pb_get_alignment( $text_orientation ); } /** * Get text orientation class. * * @param bool $print_default Whether. * * @return string Text orientation class names. * @since 3.23 Generate text orientation classes for tablet and phone. */ public function get_text_orientation_classname( $print_default = false ) { $text_orientation = $this->get_text_orientation(); $text_orientation_tablet = $this->get_text_orientation( 'tablet' ); $text_orientation_phone = $this->get_text_orientation( 'phone' ); // Should be `justified` instead of justify in classname. $text_orientation = 'justify' === $text_orientation ? 'justified' : $text_orientation; $text_orientation_tablet = 'justify' === $text_orientation_tablet ? 'justified' : $text_orientation_tablet; $text_orientation_phone = 'justify' === $text_orientation_phone ? 'justified' : $text_orientation_phone; $default_classname = $print_default ? ' et_pb_text_align_left' : ''; $text_orientation_classname = ''; if ( '' !== $text_orientation ) { $text_orientation_classname .= " et_pb_text_align_{$text_orientation}"; } if ( '' !== $text_orientation_tablet ) { $text_orientation_classname .= " et_pb_text_align_{$text_orientation_tablet}-tablet"; } if ( '' !== $text_orientation_phone ) { $text_orientation_classname .= " et_pb_text_align_{$text_orientation_phone}-phone"; } return '' !== $text_orientation_classname ? $text_orientation_classname : $default_classname; } /** * Intended to be overridden as needed. * * @return string */ public function get_max_width_additional_css() { return ''; } /** * Get type of element */ public function get_type() { return $this->type; } /** * Remove suffix of a string * * @param string $string Input string. * @param string $separator Suffix to remove. * * @return string */ public function remove_suffix( $string, $separator = '_' ) { $string_as_array = explode( $separator, $string ); array_pop( $string_as_array ); return implode( $separator, $string_as_array ); } /** * Determine field visibility against its dependency. * * @param array $field Field. * * @return bool */ protected function _is_field_applicable( $field ) { $result = true; // Field can be undefined/empty in some 3rd party modules without VB support. Handle this situation. if ( ! $field ) { return $result; } $depends_on = self::$_->array_get( $field, 'depends_on', false ); $depends_show_if = self::$_->array_get( $field, 'depends_show_if', false ); if ( $depends_on && $depends_show_if ) { foreach ( $depends_on as $attr_name ) { if ( $result && self::$_->array_get( $this->props, $attr_name ) !== $depends_show_if ) { $result = false; break; } } } return $result; } /** * Update global colors info to match replace with value. * * @since 4.9.0 * * @return void */ protected function _prepare_global_colors_info() { // Retrive global_colors_info from post meta, which saved as string[][]. $props_gc_info = str_replace( array( '[', ']' ), array( '[', ']' ), $this->props['global_colors_info'] ); $gc_info = json_decode( $props_gc_info, true ); $global_colors = et_builder_get_all_global_colors(); if ( empty( $gc_info ) || ! is_array( $gc_info ) || ! is_array( $global_colors ) ) { return; } $gc_info = $this->_remove_inactive_global_colors_module_info( $gc_info, $global_colors ); foreach ( $gc_info as $key => $old_info ) { if ( empty( $global_colors[ $key ] ) || ! is_array( $global_colors[ $key ] ) || ! array_key_exists( 'replaced_with', $global_colors[ $key ] ) ) { continue; } $replaced_id = $global_colors[ $key ]['replaced_with']; $new_info = array(); if ( isset( $gc_info[ $replaced_id ] ) ) { $new_info = $gc_info[ $replaced_id ]; } // remove data from prev global color id. $gc_info[ $key ] = array(); // add data to new global color id. $gc_info[ $replaced_id ] = array_merge( $old_info, $new_info ); } $this->props['global_colors_info'] = wp_json_encode( $gc_info ); } /** * Remove inactive global colors info from module. * * @since 4.9.0 * * @param string $gc_info Module's global colors info. * @param array $global_colors Global colors. * * @return array */ protected function _remove_inactive_global_colors_module_info( $gc_info, $global_colors ) { foreach ( $gc_info as $key => $info ) { if ( isset( $global_colors[ $key ]['active'] ) && 'no' === $global_colors[ $key ]['active'] ) { // Empty out module's global color info by global color ID. $gc_info[ $key ] = array(); } } return $gc_info; } /** * Process global colors. * If there is a global color id need to be replaced, that is done here. * * @since 4.9.0 * * @return void */ public function process_global_colors() { if ( empty( $this->props['global_colors_info'] ) ) { return; } $this->_prepare_global_colors_info(); foreach ( $this->props as $attr_key => $attr_value ) { if ( empty( $attr_key ) || strpos( $attr_key, 'color' ) === false ) { continue; } // Don't convert anything in the `global_colors_info` array. if ( 'global_colors_info' === $attr_key ) { continue; } // if color value includes `gcid-`, check for associated Global Color value. if ( empty( $attr_value ) || false === strpos( $attr_value, 'gcid-' ) ) { continue; } $global_color_info = et_builder_get_all_global_colors( true ); // If there are no matching Global Colors, return null. if ( ! is_array( $global_color_info ) ) { continue; } foreach ( $global_color_info as $gcid => $details ) { if ( false !== strpos( $attr_value, $gcid ) ) { // Match substring (needed for attrs like gradient stops). $this->props[ $attr_key ] = str_replace( $gcid, $details['color'], $this->props[ $attr_key ] ); } } // Finally, escape the output. if ( ! empty( $global_color_info['color'] ) ) { $this->props[ $attr_key ] = esc_attr( $this->props[ $attr_key ] ); } } } /** * Process the fields. * * @since 3.23 Add function to process advanced form field options set. * * @param string $function_name String of the function_name. * @return void */ public function process_additional_options( $function_name ) { $module = $this; if ( $function_name && $function_name !== $this->slug ) { $module = self::get_module( $function_name, $this->get_post_type() ); if ( ! $module ) { $module = $this; } else { $module->props = $this->props; $module->classname = $this->classname; } } if ( ! isset( $module->advanced_fields ) || false === $module->advanced_fields ) { return; } $module->process_advanced_fonts_options( $function_name ); // Process Text Shadow CSS. $this->process_text_shadow( $function_name ); $module->process_advanced_background_options( $function_name ); $module->process_advanced_text_options( $function_name ); $module->process_advanced_borders_options( $function_name ); $module->process_advanced_filter_options( $function_name ); $module->process_height_options( $function_name ); $module->process_overflow_options( $function_name ); $module->process_advanced_custom_margin_options( $function_name ); $module->process_max_width_options( $function_name ); $module->process_advanced_button_options( $function_name ); // Process Form Field CSS. $module->process_advanced_form_field_options( $function_name ); $this->process_box_shadow( $function_name ); $this->process_position( $function_name ); $this->process_transform( $function_name ); // Process Margin & Padding CSS. $this->process_margin_padding_advanced_css( $function_name ); $this->process_hover_transitions( $function_name ); } /** * Process inline fonts options into CSS style. * * @param string $fonts_list Font list. */ public function process_inline_fonts_option( $fonts_list ) { if ( '' === $fonts_list ) { return; } $fonts_list_array = explode( ',', $fonts_list ); foreach ( $fonts_list_array as $font_name ) { et_builder_enqueue_font( $font_name ); } } /** * Process advanced font styles. * * @since 3.23 Add support to generate responsive styles of font, text color, and text align. * And also process styles of block elements sub options group. * @since 4.6.0 Add sticky style support * * @param string $function_name Module slug. */ public function process_advanced_fonts_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } if ( ! self::$_->array_get( $this->advanced_fields, 'fonts', false ) ) { return; } $font_options = array(); $slugs = array( 'font', 'font_size', 'text_color', 'letter_spacing', 'line_height', 'text_align', ); $mobile_options_slugs = array( 'font_tablet', 'font_phone', 'font_size_tablet', 'font_size_phone', 'text_color_tablet', 'text_color_phone', 'line_height_tablet', 'line_height_phone', 'letter_spacing_tablet', 'letter_spacing_phone', 'text_align_tablet', 'text_align_phone', ); $slugs = array_merge( $slugs, $mobile_options_slugs ); // merge all slugs into single array to define them in one place. // Separetely defined and merged *_last_edited slugs. It needs to be merged as reference but shouldn't be looped for calling mobile attributes. $mobile_options_last_edited_slugs = array( 'font_last_edited', 'text_color_last_edited', 'font_size_last_edited', 'line_height_last_edited', 'letter_spacing_last_edited', 'text_align_last_edited', ); $slugs = array_merge( $slugs, $mobile_options_last_edited_slugs ); $is_enabled = $this->_features_manager->get( // Is custom font options enabled. 'foop', function() use ( $slugs ) { return $this->font_options_are_used( $slugs ); } ); if ( ! $is_enabled ) { return; } foreach ( $this->advanced_fields['fonts'] as $option_name => $option_settings ) { $style = ''; $hover_style = ''; $sticky_style = ''; $important_options = array(); $is_important_set = isset( $option_settings['css']['important'] ); $is_placeholder = isset( $option_settings['css']['placeholder'] ); $use_global_important = $is_important_set && 'all' === $option_settings['css']['important']; if ( ! $use_global_important && $is_important_set && 'plugin_only' === $option_settings['css']['important'] && et_builder_has_limitation( 'force_use_global_important' ) ) { $use_global_important = true; } if ( $is_important_set && is_array( $option_settings['css']['important'] ) ) { $important_options = $option_settings['css']['important']; if ( et_builder_has_limitation( 'force_use_global_important' ) && in_array( 'plugin_all', $option_settings['css']['important'], true ) ) { $use_global_important = true; } } foreach ( $slugs as $font_option_slug ) { if ( isset( $this->props[ "{$option_name}_{$font_option_slug}" ] ) ) { $font_options[ "{$option_name}_{$font_option_slug}" ] = $this->props[ "{$option_name}_{$font_option_slug}" ]; } } $field_key = "{$option_name}_{$slugs[0]}"; $global_setting_name = $this->get_global_setting_name( $field_key ); $global_setting_value = ET_Global_Settings::get_value( $global_setting_name ); // Add default parameter to override global setting value, just in case we need to // use another default. $field_option_default = isset( $this->fields_unprocessed[ $field_key ]['default'] ) ? $this->fields_unprocessed[ $field_key ]['default'] : $global_setting_value; $field_option_value = isset( $font_options[ $field_key ] ) ? $font_options[ $field_key ] : ''; if ( '' !== $field_option_value || ! $global_setting_value ) { $important = in_array( 'font', $important_options, true ) || $use_global_important ? ' !important' : ''; $font_styles = et_builder_set_element_font( $field_option_value, ( '' !== $important ), $field_option_default ); // Get font custom breakpoint if needed on desktop. $font_custom_desktop_breakpoint = et_pb_font_options()->get_breakpoint_by_font_value( $font_options, $field_key ); if ( isset( $option_settings['css']['font'] ) || ! empty( $font_custom_desktop_breakpoint ) ) { // Prepare font styles args. $font_styles_args = array( 'selector' => et_pb_font_options()->get_font_selector( $option_settings, $this->main_css_element ), 'declaration' => rtrim( $font_styles ), 'priority' => $this->_style_priority, ); // Set custom media query if needed. if ( ! empty( $font_custom_desktop_breakpoint ) ) { $font_styles_args['media_query'] = self::get_media_query( $font_custom_desktop_breakpoint ); } self::set_style( $function_name, $font_styles_args ); } else { $style .= $font_styles; } } $size_option_name = "{$option_name}_{$slugs[1]}"; $default_size = isset( $this->fields_unprocessed[ $size_option_name ]['default'] ) ? $this->fields_unprocessed[ $size_option_name ]['default'] : ''; $size_option_value = ''; if ( isset( $font_options[ $size_option_name ] ) && ! in_array( trim( $font_options[ $size_option_name ] ), array( '', 'px', $default_size ), true ) ) { $important = in_array( 'size', $important_options, true ) || $use_global_important ? ' !important' : ''; $size_option_value = et_builder_process_range_value( $font_options[ $size_option_name ] ); $style .= sprintf( 'font-size: %1$s%2$s; ', esc_html( $size_option_value ), esc_html( $important ) ); } // Hover font size. $size_hover = trim( et_pb_hover_options()->get_value( $size_option_name, $this->props, '' ) ); if ( ! in_array( $size_hover, array( '', 'px', $size_option_value ), true ) ) { $important = in_array( 'size', $important_options, true ) || $use_global_important ? ' !important' : ''; $hover_style .= sprintf( 'font-size: %1$s%2$s; ', esc_html( et_builder_process_range_value( $size_hover ) ), esc_html( $important ) ); } // Sticky font size. $size_sticky = trim( et_pb_sticky_options()->get_value( $size_option_name, $this->props, '' ) ); if ( ! in_array( $size_sticky, array( '', 'px', $size_option_value ), true ) ) { $important = in_array( 'size', $important_options, true ) || $use_global_important ? ' !important' : ''; $sticky_style .= sprintf( 'font-size: %1$s%2$s; ', esc_html( et_builder_process_range_value( $size_sticky ) ), esc_html( $important ) ); } $text_color_option_name = "{$option_name}_{$slugs[2]}"; $color_selector = isset( $option_settings['css']['color'] ) ? $option_settings['css']['color'] : ''; // Ensure if text color option is not disabled on current font options. $hide_text_color = isset( $option_settings['hide_text_color'] ) && true === $option_settings['hide_text_color']; // handle the value from old option. $old_option_ref = isset( $option_settings['text_color'] ) && isset( $option_settings['text_color']['old_option_ref'] ) ? $option_settings['text_color']['old_option_ref'] : ''; $old_option_val = '' !== $old_option_ref && isset( $this->props[ $old_option_ref ] ) ? $this->props[ $old_option_ref ] : ''; $default_value = '' !== $old_option_val && isset( $option_settings['text_color'] ) && isset( $option_settings['text_color']['default'] ) ? $option_settings['text_color']['default'] : ''; if ( isset( $font_options[ $text_color_option_name ] ) && '' !== $font_options[ $text_color_option_name ] && ! $hide_text_color ) { $important = ' !important'; if ( $default_value !== $font_options[ $text_color_option_name ] ) { if ( ! empty( $color_selector ) ) { $el_style = array( 'selector' => $color_selector, 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $font_options[ $text_color_option_name ] ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } else { $style .= sprintf( 'color: %1$s%2$s; ', esc_html( $font_options[ $text_color_option_name ] ), esc_html( $important ) ); } } } // Text Color Hover. $text_color_hover = et_pb_hover_options()->get_value( $text_color_option_name, $this->props ); $hover_has_text_color = $default_value !== $text_color_hover && ! empty( $text_color_hover ) && ! $hide_text_color; $default_hover_color_selector = et_pb_hover_options()->add_hover_to_selectors( $color_selector ); $hover_text_color_selector = self::$_->array_get( $option_settings, 'css.color_hover', $default_hover_color_selector ); if ( $hover_has_text_color ) { $important = ' !important'; if ( ! empty( $color_selector ) ) { $el_style = array( 'selector' => $hover_text_color_selector, 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $text_color_hover ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } else { $hover_style .= sprintf( 'color: %1$s%2$s; ', esc_html( $text_color_hover ), esc_html( $important ) ); } } // Text Color Sticky. $text_color_sticky = et_pb_sticky_options()->get_value( $text_color_option_name, $this->props ); if ( $default_value !== $text_color_sticky && ! empty( $text_color_sticky ) && ! $hide_text_color ) { $important = ' !important'; if ( ! empty( $color_selector ) ) { $sel = et_pb_sticky_options()->add_sticky_to_selectors( $color_selector, $this->is_sticky_module ); $el_style = array( 'selector' => et_()->array_get( $option_settings, 'css.color_sticky', $sel ), 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $text_color_sticky ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); // Add hover style in sticky state. if ( $hover_has_text_color ) { $el_style = array( 'selector' => et_pb_sticky_options()->add_sticky_to_selectors( $hover_text_color_selector, $this->is_sticky_module ), 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $text_color_hover ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } else { $sticky_style .= sprintf( 'color: %1$s%2$s; ', esc_html( $text_color_sticky ), esc_html( $important ) ); } } $letter_spacing_option_name = "{$option_name}_{$slugs[3]}"; $default_letter_spacing = isset( $this->fields_unprocessed[ $letter_spacing_option_name ]['default'] ) ? $this->fields_unprocessed[ $letter_spacing_option_name ]['default'] : ''; $letter_spacing_selector = isset( $option_settings['css']['letter_spacing'] ) ? $option_settings['css']['letter_spacing'] : ''; $letter_spacing_value = ''; if ( isset( $font_options[ $letter_spacing_option_name ] ) && ! in_array( trim( $font_options[ $letter_spacing_option_name ] ), array( '', 'px', $default_letter_spacing ), true ) ) { $important = in_array( 'letter-spacing', $important_options, true ) || $use_global_important ? ' !important' : ''; $letter_spacing_value = et_builder_process_range_value( $font_options[ $letter_spacing_option_name ], 'letter_spacing' ); $style .= sprintf( 'letter-spacing: %1$s%2$s; ', esc_html( $letter_spacing_value ), esc_html( $important ) ); if ( ! empty( $letter_spacing_selector ) ) { $el_style = array( 'selector' => $letter_spacing_selector, 'declaration' => sprintf( 'letter-spacing: %1$s%2$s;', esc_html( $letter_spacing_value ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } // Hover letter spacing. $letter_spacing_hover = trim( et_pb_hover_options()->get_value( $letter_spacing_option_name, $this->props, '' ) ); $hover_has_letter_spacing = ! in_array( $letter_spacing_hover, array( '', 'px', $letter_spacing_value ), true ); $hover_letter_spacing_selector = isset( $option_settings['css']['letter_spacing_hover'] ) ? $option_settings['css']['letter_spacing_hover'] : ''; if ( $hover_has_letter_spacing ) { $important = in_array( 'letter-spacing', $important_options, true ) || $use_global_important ? ' !important' : ''; if ( et_builder_is_hover_enabled( $letter_spacing_option_name, $this->props ) ) { if ( ! empty( $hover_letter_spacing_selector ) ) { $el_style = array( 'selector' => $hover_letter_spacing_selector, 'declaration' => sprintf( 'letter-spacing: %1$s%2$s;', esc_html( et_builder_process_range_value( $letter_spacing_hover ) ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } else { $hover_style .= sprintf( 'letter-spacing: %1$s%2$s; ', esc_html( et_builder_process_range_value( $letter_spacing_hover ) ), esc_html( $important ) ); } } if ( isset( $option_settings['css']['letter_spacing'] ) ) { if ( et_builder_is_hover_enabled( $letter_spacing_option_name, $this->props ) ) { if ( $default_letter_spacing !== $letter_spacing_hover ) { if ( isset( $option_settings['css']['color'] ) ) { $sel = et_pb_hover_options()->add_hover_to_selectors( $option_settings['css']['letter_spacing'] ); $el_style = array( 'selector' => self::$_->array_get( $option_settings, 'css.letter_spacing_hover', $sel ), 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $letter_spacing_hover ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } } } // Sticky letter spacing. $letter_spacing_sticky = trim( et_pb_sticky_options()->get_value( $letter_spacing_option_name, $this->props, '' ) ); if ( ! in_array( $letter_spacing_sticky, array( '', 'px', $letter_spacing_value ), true ) ) { $important = in_array( 'letter-spacing', $important_options, true ) || $use_global_important ? ' !important' : ''; if ( et_pb_sticky_options()->is_enabled( $letter_spacing_option_name, $this->props ) ) { if ( isset( $option_settings['css']['letter_spacing_sticky'] ) ) { $el_style = array( 'selector' => $option_settings['css']['letter_spacing_sticky'], 'declaration' => sprintf( 'letter-spacing: %1$s%2$s;', esc_html( et_builder_process_range_value( $letter_spacing_sticky ) ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); // Add hover style in sticky state. if ( $hover_has_letter_spacing && ! empty( $hover_letter_spacing_selector ) ) { $el_style = array( 'selector' => et_pb_sticky_options()->add_sticky_to_selectors( $hover_letter_spacing_selector, $this->is_sticky_module ), 'declaration' => sprintf( 'letter-spacing: %1$s%2$s;', esc_html( et_builder_process_range_value( $letter_spacing_hover ) ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } else { $sticky_style .= sprintf( 'letter-spacing: %1$s%2$s; ', esc_html( et_builder_process_range_value( $letter_spacing_sticky ) ), esc_html( $important ) ); } } if ( isset( $option_settings['css']['letter_spacing'] ) ) { if ( et_builder_is_sticky_enabled( $letter_spacing_option_name, $this->props ) ) { if ( $default_letter_spacing !== $letter_spacing_sticky ) { if ( isset( $option_settings['css']['color'] ) ) { $sel = et_pb_sticky_options()->add_sticky_to_selectors( $option_settings['css']['letter_spacing'], $this->is_sticky_module ); $el_style = array( 'selector' => self::$_->array_get( $option_settings, 'css.letter_spacing_sticky', $sel ), 'declaration' => sprintf( 'color: %1$s%2$s;', esc_html( $letter_spacing_sticky ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } } } $line_height_option_name = "{$option_name}_{$slugs[4]}"; $line_height_selector = isset( $option_settings['css']['line_height'] ) ? $option_settings['css']['line_height'] : ''; $line_height_value = ''; if ( isset( $font_options[ $line_height_option_name ] ) ) { $default_line_height = isset( $this->fields_unprocessed[ $line_height_option_name ]['default'] ) ? $this->fields_unprocessed[ $line_height_option_name ]['default'] : ''; if ( ! in_array( trim( $font_options[ $line_height_option_name ] ), array( '', 'px', $default_line_height ), true ) ) { $important = in_array( 'line-height', $important_options, true ) || $use_global_important ? ' !important' : ''; $line_height_value = et_builder_process_range_value( $font_options[ $line_height_option_name ], 'line_height' ); $style .= sprintf( 'line-height: %1$s%2$s; ', esc_html( $line_height_value ), esc_html( $important ) ); if ( isset( $option_settings['css']['line_height'] ) ) { $el_style = array( 'selector' => $option_settings['css']['line_height'], 'declaration' => sprintf( 'line-height: %1$s%2$s;', esc_html( $line_height_value ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } // Hover line height. $line_height_hover = trim( et_pb_hover_options()->get_value( $line_height_option_name, $this->props, '' ) ); $hover_has_line_height = ! in_array( $line_height_hover, array( '', 'px', $line_height_value ), true ); $hover_line_height_selector = self::$_->array_get( $option_settings, 'css.line_height_hover', et_pb_hover_options()->add_hover_to_selectors( $line_height_selector ) ); if ( $hover_has_line_height ) { $important = in_array( 'line-height', $important_options, true ) || $use_global_important ? ' !important' : ''; if ( et_builder_is_hover_enabled( $line_height_option_name, $this->props ) ) { $hover_style .= sprintf( 'line-height: %1$s%2$s; ', esc_html( et_builder_process_range_value( $line_height_hover, 'line_height' ) ), esc_html( $important ) ); } if ( ! empty( $line_height_selector ) ) { if ( et_builder_is_hover_enabled( $line_height_option_name, $this->props ) ) { if ( isset( $option_settings['css']['color'] ) ) { $el_style = array( 'selector' => $hover_line_height_selector, 'declaration' => sprintf( 'line-height: %1$s%2$s;', esc_html( $line_height_hover ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } } // Sticky line height. $line_height_sticky = trim( et_pb_sticky_options()->get_value( $line_height_option_name, $this->props, '' ) ); if ( ! in_array( $line_height_sticky, array( '', 'px', $line_height_value ), true ) ) { $important = in_array( 'line-height', $important_options, true ) || $use_global_important ? ' !important' : ''; if ( et_pb_sticky_options()->is_enabled( $line_height_option_name, $this->props ) ) { $sticky_style .= sprintf( 'line-height: %1$s%2$s; ', esc_html( et_builder_process_range_value( $line_height_sticky, 'line_height' ) ), esc_html( $important ) ); } if ( ! empty( $line_height_selector ) ) { if ( et_pb_sticky_options()->is_enabled( $line_height_option_name, $this->props ) ) { if ( isset( $option_settings['css']['color'] ) ) { $sel = et_pb_sticky_options()->add_sticky_to_selectors( $line_height_selector, $this->is_sticky_module ); $el_style = array( 'selector' => et_()->array_get( $option_settings, 'css.line_height_sticky', $sel ), 'declaration' => sprintf( 'line-height: %1$s%2$s;', esc_html( $line_height_sticky ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); // Add hover style in sticky state. if ( $hover_has_line_height ) { $el_style = array( 'selector' => et_pb_sticky_options()->add_sticky_to_selectors( $hover_line_height_selector, $this->is_sticky_module ), 'declaration' => sprintf( 'line-height: %1$s%2$s;', esc_html( $line_height_hover ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } } } $text_align_option_name = "{$option_name}_{$slugs[5]}"; // Ensure to not print text alignment if current font hide text alignment option. $hide_text_align = self::$_->array_get( $option_settings, 'hide_text_align', false ); if ( isset( $font_options[ $text_align_option_name ] ) && '' !== $font_options[ $text_align_option_name ] && ! $hide_text_align ) { $important = in_array( 'text-align', $important_options, true ) || $use_global_important ? ' !important' : ''; $text_align = et_pb_get_alignment( $font_options[ $text_align_option_name ] ); if ( isset( $option_settings['css']['text_align'] ) ) { $el_style = array( 'selector' => $option_settings['css']['text_align'], 'declaration' => sprintf( 'text-align: %1$s%2$s;', esc_html( $text_align ), esc_html( $important ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } else { $style .= sprintf( 'text-align: %1$s%2$s; ', esc_html( $text_align ), esc_html( $important ) ); } } if ( isset( $option_settings['use_all_caps'] ) && $option_settings['use_all_caps'] && 'on' === $this->props[ "{$option_name}_all_caps" ] ) { $important = in_array( 'all_caps', $important_options, true ) || $use_global_important ? ' !important' : ''; $style .= sprintf( 'text-transform: uppercase%1$s; ', esc_html( $important ) ); } // apply both default and hover styles. $style_states = array( 'default', 'hover', 'sticky' ); foreach ( $style_states as $style_state ) { $is_hover = 'hover' === $style_state; $is_sticky = 'sticky' === $style_state; $style = $is_hover ? $hover_style : $style; if ( $is_hover ) { $style = $hover_style; } if ( $is_sticky ) { $style = $sticky_style; } if ( '' !== $style ) { $css_element = ! empty( $option_settings['css']['main'] ) ? $option_settings['css']['main'] : $this->main_css_element; // use different selector for plugin if defined. if ( et_builder_has_limitation( 'use_limited_main' ) && ! empty( $option_settings['css']['limited_main'] ) ) { $css_element = $option_settings['css']['limited_main']; } // $css_element might be an array, for example to apply the css for placeholders if ( is_array( $css_element ) ) { foreach ( $css_element as $selector ) { $hover_selector = self::$_->array_get( $option_settings, 'css.hover', $this->add_hover_to_selectors( $selector, $is_hover ) ); if ( $is_hover ) { $selector = $hover_selector; } if ( $is_sticky ) { $selector = et_()->array_get( $option_settings, 'css.sticky', et_pb_sticky_options()->add_sticky_to_selectors( $selector, $this->is_sticky_module ) ); // Add hover style in sticky state. if ( '' !== $hover_style && '' !== $hover_selector ) { $sticky_hover_selector = et_pb_sticky_options()->add_sticky_to_selectors( $hover_selector, $this->is_sticky_module ); $el_style = array( 'selector' => $sticky_hover_selector, 'declaration' => et_core_esc_previously( rtrim( $hover_style ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } $el_style = array( 'selector' => $selector, 'declaration' => et_core_esc_previously( rtrim( $style ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); $this->maybe_push_element_to_letter_spacing_fix_list( $selector, array( 'body.safari ', 'body.iphone ', 'body.uiwebview ' ), rtrim( $style ), $default_letter_spacing ); } } else { $hover_selector = self::$_->array_get( $option_settings, 'css.hover', $this->add_hover_to_selectors( $css_element, $is_hover ) ); if ( $is_hover ) { $css_element = $hover_selector; } if ( $is_sticky ) { $css_element = et_()->array_get( $option_settings, 'css.sticky', et_pb_sticky_options()->add_sticky_to_selectors( $css_element, $this->is_sticky_module ) ); // Add hover style in sticky state. if ( '' !== $hover_style && '' !== $hover_selector ) { $sticky_hover_selector = et_pb_sticky_options()->add_sticky_to_selectors( $hover_selector, $this->is_sticky_module ); $el_style = array( 'selector' => $sticky_hover_selector, 'declaration' => et_core_esc_previously( rtrim( $hover_style ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } $el_style = array( 'selector' => $css_element, 'declaration' => et_core_esc_previously( rtrim( $style ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); $this->maybe_push_element_to_letter_spacing_fix_list( $css_element, array( 'body.safari ', 'body.iphone ', 'body.uiwebview ' ), rtrim( $style ), $default_letter_spacing ); if ( $is_placeholder ) { $el_style = array( 'selector' => $this->_maybe_add_hover_to_order_class( $css_element . '::-webkit-input-placeholder', $is_hover ), 'declaration' => rtrim( $style ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => $this->_maybe_add_hover_to_order_class( $css_element . '::-moz-placeholder', $is_hover ), 'declaration' => rtrim( $style ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => $this->_maybe_add_hover_to_order_class( $css_element . '::-ms-input-placeholder', $is_hover ), 'declaration' => rtrim( $style ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } } } // process mobile options. foreach ( $mobile_options_slugs as $mobile_option ) { $current_option_name = "{$option_name}_{$mobile_option}"; if ( isset( $font_options[ $current_option_name ] ) && '' !== $font_options[ $current_option_name ] ) { $current_desktop_option = $this->remove_suffix( $mobile_option ); $current_last_edited_slug = "{$option_name}_{$current_desktop_option}_last_edited"; $current_last_edited = isset( $font_options[ $current_last_edited_slug ] ) ? $font_options[ $current_last_edited_slug ] : ''; $current_responsive_status = et_pb_get_responsive_status( $current_last_edited ); // Don't print mobile styles if responsive UI isn't toggled on. if ( ! $current_responsive_status ) { continue; } $current_media_query = false === strpos( $mobile_option, 'phone' ) ? 'max_width_980' : 'max_width_767'; $main_option_name = str_replace( array( '_tablet', '_phone' ), '', $mobile_option ); // 1. Generate CSS property. $css_property = str_replace( '_', '-', $main_option_name ); if ( 'text_color' === $main_option_name ) { $css_property = 'color'; } // 2. Custom important. $css_option_name = 'font-size' === $css_property ? 'size' : $css_property; $important = in_array( $css_option_name, $important_options, true ) || $use_global_important ? ' !important' : ''; // As default, text color should be important on tablet and phone. if ( 'text_color' === $main_option_name ) { $important = ' !important'; } // Allow specific selector tablet and mobile, simply add _tablet or _phone suffix. if ( isset( $option_settings['css'][ $mobile_option ] ) && '' !== $option_settings['css'][ $mobile_option ] ) { $selector = $option_settings['css'][ $mobile_option ]; } elseif ( 'text_color' === $main_option_name && ! empty( $option_settings['css']['color'] ) ) { // We define custom selector for text color as 'color', not 'text_color'. $selector = $option_settings['css']['color']; } elseif ( isset( $option_settings['css'][ $main_option_name ] ) || isset( $option_settings['css']['main'] ) ) { $selector = isset( $option_settings['css'][ $main_option_name ] ) ? $option_settings['css'][ $main_option_name ] : $option_settings['css']['main']; } elseif ( et_builder_has_limitation( 'use_limited_main' ) && ! empty( $option_settings['css']['limited_main'] ) ) { $selector = $option_settings['css']['limited_main']; } else { $selector = $this->main_css_element; } // 3. Process value based on property name. $text_range_inputs = array( 'font_size', 'line_height', 'letter_spacing' ); $processed_value = $font_options[ $current_option_name ]; if ( in_array( $main_option_name, $text_range_inputs, true ) ) { $processed_value = et_builder_process_range_value( $font_options[ $current_option_name ] ); } // 4. Declare CSS property, value, and important status. if ( 'font' === $main_option_name ) { $global_font_name = $this->get_global_setting_name( $current_option_name ); $global_font_value = ET_Global_Settings::get_value( $global_font_name ); $declaration = et_builder_set_element_font( $processed_value, ( '' !== $important ), $global_font_value ); } else { $declaration = sprintf( '%1$s: %2$s%3$s;', esc_html( $css_property ), esc_html( $processed_value ), esc_html( $important ) ); } // Reset font style: italic/normal, uppercase/normal/smallcaps, underline/ // linethrough. There is a case where a font option group inherit font style // value from another font option group. Most of the time, we can't toggle // on/off the inherited options. if ( 'font' === $main_option_name ) { $processed_prev_value = et_pb_responsive_options()->get_default_value( $this->props, $current_option_name ); $reset_declaration = et_builder_set_reset_font_style( $processed_value, $processed_prev_value, '' !== $important ); $declaration .= ! empty( $reset_declaration ) ? $reset_declaration : ''; } // $selector might be an array, for example to apply the css for placeholders if ( is_array( $selector ) ) { foreach ( $selector as $selector_item ) { $el_style = array( 'selector' => $selector_item, 'declaration' => $declaration, 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } } else { $el_style = array( 'selector' => $selector, 'declaration' => $declaration, 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); if ( ! empty( $selector ) && in_array( $mobile_option, array( 'letter_spacing_phone', 'letter_spacing_tablet' ), true ) ) { switch ( $mobile_option ) { case 'letter_spacing_phone': $css_prefix = 'body.iphone '; break; case 'letter_spacing_tablet': $css_prefix = 'body.uiwebview '; break; } $this->maybe_push_element_to_letter_spacing_fix_list( $selector, $css_prefix, $declaration, $default_letter_spacing ); } if ( $is_placeholder ) { $el_style = array( 'selector' => $selector . '::-webkit-input-placeholder', 'declaration' => $declaration, 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => $selector . '::-moz-placeholder', 'declaration' => $declaration, 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => $selector . '::-ms-input-placeholder', 'declaration' => $declaration, 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } } } } $sub_toggle = isset( $option_settings['sub_toggle'] ) ? $option_settings['sub_toggle'] : ''; // Ignore the process if the current module is Text since the process will be handled // by the module itself. if ( 'et_pb_text' !== $function_name ) { // Build sub toggle selector. $sub_toggle_selector = ''; if ( et_builder_has_limitation( 'use_limited_main' ) && ! empty( $option_settings['css']['limited_main'] ) ) { $sub_toggle_selector = $option_settings['css']['limited_main']; } elseif ( isset( $option_settings['css']['main'] ) ) { $sub_toggle_selector = $option_settings['css']['main']; } // Additional ul and ol option slugs. if ( in_array( $sub_toggle, array( 'ul', 'ol' ), true ) ) { $list_selector = '' !== $sub_toggle_selector ? $sub_toggle_selector : "{$this->main_css_element} {$sub_toggle}"; // Option ul / ol type. $list_type_name = "{$option_name}_type"; $is_list_type_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $list_type_name ); $list_type_values = array( 'desktop' => esc_html( et_pb_responsive_options()->get_any_value( $this->props, $list_type_name, '', false, 'desktop' ) ), 'tablet' => $is_list_type_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_type_name}_tablet" ) ) : '', 'phone' => $is_list_type_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_type_name}_tablet" ) ) : '', ); et_pb_responsive_options()->generate_responsive_css( $list_type_values, $list_selector, 'list-style-type', $function_name, ' !important;', 'select' ); // Option ul / ol position. $list_position_name = "{$option_name}_position"; $is_list_position_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $list_position_name ); $list_position_values = array( 'desktop' => esc_html( et_pb_responsive_options()->get_any_value( $this->props, $list_position_name ) ), 'tablet' => $is_list_position_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_position_name}_tablet" ) ) : '', 'phone' => $is_list_position_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_position_name}_phone" ) ) : '', ); et_pb_responsive_options()->generate_responsive_css( $list_position_values, $list_selector, 'list-style-position', $function_name, '', 'select' ); // Option ul / ol indent. $list_indent_name = "{$option_name}_item_indent"; $is_list_indent_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $list_indent_name ); $list_indent_values = array( 'desktop' => esc_html( et_pb_responsive_options()->get_any_value( $this->props, $list_indent_name ) ), 'tablet' => $is_list_indent_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_indent_name}_tablet" ) ) : '', 'phone' => $is_list_indent_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$list_indent_name}_phone" ) ) : '', ); $list_item_indent_selector = et_()->array_get( $option_settings, 'css.item_indent', $list_selector ); et_pb_responsive_options()->generate_responsive_css( $list_indent_values, $list_item_indent_selector, 'padding-left', $function_name, ' !important;' ); } // Additional quote option slugs. if ( 'quote' === $sub_toggle ) { $quote_selector = '' !== $sub_toggle_selector ? $sub_toggle_selector : "{$this->main_css_element} blockquote"; // Option quote border weight. $border_weight_name = "{$option_name}_border_weight"; $is_border_weight_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $border_weight_name ); $border_weight_values = array( 'desktop' => esc_html( et_pb_responsive_options()->get_any_value( $this->props, $border_weight_name ) ), 'tablet' => $is_border_weight_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$border_weight_name}_tablet" ) ) : '', 'phone' => $is_border_weight_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$border_weight_name}_phone" ) ) : '', ); et_pb_responsive_options()->generate_responsive_css( $border_weight_values, $quote_selector, 'border-width', $function_name ); // Option quote border weight on hover. $border_weight_hover_value = et_pb_hover_options()->get_value( $border_weight_name, $this->props ); if ( '' !== $border_weight_hover_value && et_builder_is_hover_enabled( $border_weight_name, $this->props ) ) { $el_style = array( 'selector' => "{$quote_selector}:hover", 'declaration' => sprintf( 'border-width: %1$s%2$s;', esc_html( et_builder_process_range_value( $border_weight_hover_value ) ), esc_html( $important ) ), ); self::set_style( $function_name, $el_style ); } // Option quote border color. $border_color_name = "{$option_name}_border_color"; $is_border_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $border_color_name ); $border_color_values = array( 'desktop' => esc_html( et_pb_responsive_options()->get_any_value( $this->props, $border_color_name ) ), 'tablet' => $is_border_color_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$border_color_name}_tablet" ) ) : '', 'phone' => $is_border_color_responsive ? esc_html( et_pb_responsive_options()->get_any_value( $this->props, "{$border_color_name}_phone" ) ) : '', ); et_pb_responsive_options()->generate_responsive_css( $border_color_values, $quote_selector, 'border-color', $function_name, '', 'color' ); // Option quote border weight on hover. $border_color_hover_value = et_pb_hover_options()->get_value( $border_color_name, $this->props ); if ( '' !== $border_color_hover_value && et_builder_is_hover_enabled( $border_color_name, $this->props ) ) { $el_style = array( 'selector' => "{$quote_selector}:hover", 'declaration' => sprintf( 'border-color: %1$s%2$s;', esc_html( $border_color_hover_value ), esc_html( $important ) ), ); self::set_style( $function_name, $el_style ); } } } } // sets ligatures disabling for all selectors // from the list $this->letter_spacing_fix_selectors. foreach ( $this->letter_spacing_fix_selectors as $selector_with_prefix ) { $el_style = array( 'selector' => $selector_with_prefix, 'declaration' => 'font-variant-ligatures: no-common-ligatures;', 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } /** * Maybe push element to the letter spacing fix list * * @since 4.4.7 Checks a element for the having of the letter-spacing property, * adds a prefix to all its selectors, push prefixed selector * to the array ($this->letter_spacing_fix_selectors) of elements * that need to have ligature fix (same elements will be overridden). * * @param string $selector CSS selector of the current element. * @param array $css_prefixes array or string of CSS prefixes which will be added to the current element selector. * @param string $declaration CSS declaration of the current element. * @param string $default_letter_spacing default letter-spacing value at the current element. */ public function maybe_push_element_to_letter_spacing_fix_list( $selector, $css_prefixes, $declaration, $default_letter_spacing ) { if ( false === strpos( trim( $declaration ), 'letter-spacing' ) || empty( $css_prefixes ) || empty( $selector ) ) { return; } $selectors = ! is_array( $selector ) ? array( $selector ) : $selector; foreach ( $selectors as $selector ) { if ( empty( $selector ) ) { continue; } $css_value = str_replace( 'letter-spacing', '', $declaration ); $css_value = preg_replace( '/[^a-zA-Z0-9]/', '', $css_value ); if ( ! is_array( $css_prefixes ) ) { $css_prefixes = array( $css_prefixes ); } foreach ( $css_prefixes as $css_prefix ) { $selector_with_prefix = ''; $selector_elements = explode( ',', $selector ); if ( is_array( $selector_elements ) && count( $selector_elements ) > 0 ) { $selector_with_prefix = implode( ',', preg_filter( '/^/', $css_prefix, $selector_elements ) ); } if ( ! empty( $selector_with_prefix ) ) { $hash_id_for_fix_selectors = crc32( $selector_with_prefix ); // Checking: if the current value of the sector is the default, // given that the default value can be set in a different css-unit // (px, em, rem... etc) than the current value // (for example, the predefined default value can be '0px', while the current selector value is '0em'). $maybe_selector_has_default_value = 0 === intval( $default_letter_spacing ) && 0 === intval( $css_value ) || ( $css_value === $default_letter_spacing ); if ( ! ( $maybe_selector_has_default_value ) ) { $this->letter_spacing_fix_selectors[ $hash_id_for_fix_selectors ] = $selector_with_prefix; } elseif ( isset( $this->letter_spacing_fix_selectors[ $hash_id_for_fix_selectors ] ) ) { // If the selector has a default value, should delete it from // array of selectors ($this->letter_spacing_fix_selectors) that need to be fixed, // if it was added earlier. unset( $this->letter_spacing_fix_selectors[ $hash_id_for_fix_selectors ] ); } } } } } /** * Process background CSS styles. * * @since 3.23 Add responsive support. * @since 4.6.0 Add sticky style support. * @since 4.15.0 Use et_pb_background_options()->get_background_style() to process. * * @param string $function_name Module slug. */ public function process_advanced_background_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } if ( ! self::$_->array_get( $this->advanced_fields, 'background', false ) ) { return; } $base_prop_name = 'background'; $settings = $this->advanced_fields[ $base_prop_name ]; $css_element = ! empty( $settings['css']['main'] ) ? $settings['css']['main'] : $this->main_css_element; $pattern_selector = $this->add_suffix_to_selectors( ' > .et_pb_background_pattern', $css_element ); $mask_selector = $this->add_suffix_to_selectors( ' > .et_pb_background_mask', $css_element ); $css_element_pattern = ! empty( $settings['css']['pattern'] ) ? $settings['css']['pattern'] : $pattern_selector; $css_element_mask = ! empty( $settings['css']['mask'] ) ? $settings['css']['mask'] : $mask_selector; $css_element_hover = ! empty( $settings['css']['hover'] ) ? $settings['css']['hover'] : ''; $args = array( 'base_prop_name' => $base_prop_name, 'props' => $this->props, 'selector' => $css_element, 'selector_pattern' => $css_element_pattern, 'selector_mask' => $css_element_mask, 'selector_hover' => $css_element_hover, 'function_name' => $function_name, 'fields_definition' => $this->fields_unprocessed, 'important' => isset( $settings['css']['important'] ) && $settings['css']['important'] ? ' !important' : '', 'has_background_color_toggle' => $settings['has_background_color_toggle'], 'use_background_color' => $settings['use_background_color'], 'use_background_color_gradient' => $settings['use_background_color_gradient'], 'use_background_image' => $settings['use_background_image'], 'use_background_video' => $settings['use_background_video'], 'use_background_pattern' => ! empty( $settings['use_background_pattern'] ) ? $settings['use_background_pattern'] : false, 'use_background_mask' => ! empty( $settings['use_background_mask'] ) ? $settings['use_background_mask'] : false, 'use_background_color_reset' => et_()->array_get( $settings, 'use_background_color_reset', true ), ); // Process background style. et_pb_background_options()->get_background_style( $args ); } /** * Process margin and padding advanced css. * * @since 4.10.0 * * @param string $function_name Module slug. */ public function process_margin_padding_advanced_css( $function_name ) { $is_enabled = $this->_features_manager->get( // Margin padding css is enabled. 'mapac', function() { return $this->margin_padding->is_used( $this->props ); } ); if ( ! $is_enabled ) { return; } $this->margin_padding->process_advanced_css( $this, $function_name ); } /** * Process text shadow options. * * @since 4.10.0 * * @param string $function_name Module slug. */ public function process_text_shadow( $function_name ) { $is_enabled = $this->_features_manager->get( // Text shadow is enabled. 'tesh', function() { return $this->text_shadow->is_used( $this->props ); } ); if ( ! $is_enabled ) { return; } $this->text_shadow->process_advanced_css( $this, $function_name ); } /** * Process advanced text options. * * @since 3.23 Add support to generate responsive styles of text orientation. * * @param string $function_name Module slug. */ public function process_advanced_text_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } if ( ! self::$_->array_get( $this->advanced_fields, 'text', false ) ) { return; } $text_options = $this->advanced_fields['text']; if ( isset( $text_options['css'] ) && is_array( $text_options['css'] ) ) { $text_css = $text_options['css']; $text_orientation_default = isset( $this->fields_unprocessed['text_orientation']['default'] ) ? $this->fields_unprocessed['text_orientation']['default'] : ''; $text_orientation = $this->get_text_orientation() !== $text_orientation_default ? $this->get_text_orientation() : ''; $text_orientation_tablet = $this->get_text_orientation( 'tablet', $text_orientation_default ); $text_orientation_phone = $this->get_text_orientation( 'phone', $text_orientation_default ); // Normally, text orientation attr adds et_pb_text_align_* class name to its module wrapper // In some cases, it needs to target particular children inside the module. Thus, only prints // styling if selector is given. if ( isset( $text_css['text_orientation'] ) ) { $text_orientation_values = array( 'desktop' => esc_attr( $text_orientation ), 'tablet' => esc_attr( $text_orientation_tablet ), 'phone' => esc_attr( $text_orientation_phone ), ); et_pb_responsive_options()->generate_responsive_css( $text_orientation_values, $text_css['text_orientation'], 'text-align', $function_name, '', 'alignment', $this->_style_priority ); } } } /** * Output border and border radius styling * * @param string $function_name Function name. * * @since 3.23 Add support to generate responsive styles of border styles and radii.] * @since 4.6.0 Add sticky style support. */ public function process_advanced_borders_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } /** * Border field * * @var ET_Builder_Module_Field_Border $border_field */ $border_field = ET_Builder_Module_Fields_Factory::get( 'Border' ); $is_enabled = $this->_features_manager->get( // Border is enabled. 'bor', function() use ( $border_field ) { return $border_field->has_any_border_attrs( $this->props ); } ); if ( ! $is_enabled ) { return; } global $et_fb_processing_shortcode_object; $borders = self::$_->array_get( $this->advanced_fields, 'borders', array( 'default' => array() ) ); $sticky = et_pb_sticky_options(); if ( is_array( $borders ) && ! empty( $borders ) ) { foreach ( $borders as $border_name => $border ) { if ( false === $border ) { continue; } if ( 'fullwidth' === $border_name && 'et_pb_blog' === $this->slug && 'on' !== self::$_->array_get( $this->props, 'fullwidth' ) ) { continue; } if ( 'fields_focus' === $border_name && in_array( $this->slug, array( 'et_pb_login', 'et_pb_signup' ), true ) && 'on' !== self::$_->array_get( $this->props, 'use_focus_border_color' ) ) { continue; } // Enable module to disable border options. // Blurb image specific adjustment. // Blog specific adjustment. // Login & signup specific adjustment. // Check field visibility against its dependency. if ( ! $this->_is_field_applicable( $border ) ) { continue; } $is_border_default = 'default' === $border_name; $suffix = $is_border_default ? '' : "_{$border_name}"; if ( $is_border_default && $this->slug !== $function_name ) { // This module's shortcode callback is being used to render another module (like accordion item // uses toggle ) so we need to make sure border option overrides are taken from the other module // instead of this one. $fields = self::get_advanced_fields( $this->get_post_type(), 'all', $function_name ); $border = self::$_->array_get( $fields, 'advanced_common.border', array() ); } // Backward compatibility. For 3rd party modules which define `_add_additional_border_fields` and do not have `process_advanced_border_options`. if ( $is_border_default && method_exists( $this, '_add_additional_border_fields' ) ) { $border = self::$_->array_get( $this->advanced_fields, 'border', array() ); } // Do not add overflow:hidden for some modules. $no_overflow_module = array( 'et_pb_social_media_follow', 'et_pb_social_media_follow_network', 'et_pb_menu', 'et_pb_fullwidth_menu', ); $overflow = ! in_array( $function_name, $no_overflow_module, true ); $overflow_x = ! in_array( self::$_->array_get( $this->props, 'overflow-x' ), array( '', 'hidden' ), true ); $overflow_y = ! in_array( self::$_->array_get( $this->props, 'overflow-y' ), array( '', 'hidden' ), true ); // Remove "overflow: hidden" if both overflow-x and overflow-y are not empty or not set to "hidden" // Add "overflow-y: hidden" if overflow-x is not empty or not set to "hidden" (or vice versa). if ( $overflow_x && $overflow_y ) { $overflow = false; } elseif ( $overflow_x ) { $overflow = 'overflow-y'; } elseif ( $overflow_y ) { $overflow = 'overflow-x'; } /** * Filters if overflow should be set along with border radius. * * @param bool|string $overflow If overflow is enabled (true) or disabled (false) or -x or -y. * @param string $function_name Module slug (e.g. et_pb_section). * @param ET_Builder_Element $this Module object. * * @since 4.17.4 */ $overflow = apply_filters( 'et_builder_process_advanced_borders_options_radii_overflow_enabled', $overflow, $function_name, $this ); // Render border radii for all devices. foreach ( et_pb_responsive_options()->get_modes() as $device ) { $border_radii_attrs = array( 'selector' => self::$_->array_get( $border, 'css.main.border_radii', $this->main_css_element ), 'declaration' => $border_field->get_radii_style( $this->props, $this->advanced_fields, $suffix, $overflow, false, $device ), 'priority' => $this->_style_priority, ); // Set media query attribute for non-desktop. if ( 'desktop' !== $device ) { $media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $border_radii_attrs['media_query'] = self::get_media_query( $media_query ); } self::set_style( $function_name, $border_radii_attrs ); } if ( et_builder_is_hover_enabled( "border_radii$suffix", $this->props ) ) { $main = self::$_->array_get( $border, 'css.hover', $this->main_css_element ); $main = self::$data_utils->array_get( $border, 'css.main.border_radii', $main ); $main = 'default' !== $border_name ? et_pb_hover_options()->add_hover_to_selectors( $main ) : et_pb_hover_options()->add_hover_to_order_class( $main ); $selector = self::$data_utils->array_get( $border, 'css.main.border_radii_hover', $main ); $el_style = array( 'selector' => $selector, 'declaration' => $border_field->get_radii_style( $this->props, $this->advanced_fields, $suffix, $overflow, true ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } if ( $sticky->is_enabled( "border_radii$suffix", $this->props ) ) { $main = et_()->array_get( $border, 'css.sticky', $this->main_css_element ); $main = et_()->array_get( $border, 'css.main.border_radii', $main ); $main = 'default' !== $border_name ? $sticky->add_sticky_to_selectors( $main, $this->is_sticky_module ) : $sticky->add_sticky_to_order_class( $main, $this->is_sticky_module ); $selector = et_()->array_get( $border, 'css.main.border_radii_sticky', $main ); $el_style = array( 'selector' => $selector, 'declaration' => $border_field->get_radii_style( $this->props, $this->advanced_fields, $suffix, $overflow, false, 'desktop', true ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } // Render border styles for all devices. foreach ( et_pb_responsive_options()->get_modes() as $device ) { $border_styles_attrs = array( 'selector' => self::$_->array_get( $border, 'css.main.border_styles', $this->main_css_element ), 'declaration' => $border_field->get_borders_style( $this->props, $this->advanced_fields, $suffix, false, $device ), 'priority' => $this->_style_priority, ); // Set media query attribute for non-desktop. if ( 'desktop' !== $device ) { $media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $border_styles_attrs['media_query'] = self::get_media_query( $media_query ); } self::set_style( $function_name, $border_styles_attrs ); } $main = self::$_->array_get( $border, 'css.hover', $this->main_css_element ); $main = self::$data_utils->array_get( $border, 'css.main.border_styles', $main ); $main = 'default' !== $border_name ? et_pb_hover_options()->add_hover_to_selectors( $main ) : et_pb_hover_options()->add_hover_to_order_class( $main ); $selector = self::$data_utils->array_get( $border, 'css.main.border_styles_hover', $main ); $el_style = array( 'selector' => $selector, 'declaration' => $border_field->get_borders_style( $this->props, $this->advanced_fields, $suffix, true ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); $main = et_()->array_get( $border, 'css.sticky', $this->main_css_element ); $main = et_()->array_get( $border, 'css.main.border_styles', $main ); $main = 'default' !== $border_name ? $sticky->add_sticky_to_selectors( $main, $this->is_sticky_module ) : $sticky->add_sticky_to_order_class( $main, $this->is_sticky_module ); $selector = et_()->array_get( $border, 'css.main.border_styles_sticky', $main ); $el_style = array( 'selector' => $selector, 'declaration' => $border_field->get_borders_style( $this->props, $this->advanced_fields, $suffix, false, 'desktop', true ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } } if ( ! $et_fb_processing_shortcode_object && $border_field->needs_border_reset_class( $function_name, $this->props ) ) { // Try to apply old method for plugins without vb support. if ( 'on' !== $this->vb_support ) { add_filter( "{$function_name}_shortcode_output", array( $border_field, 'add_border_reset_class' ), 10, 2 ); } $this->add_classname( 'et_pb_with_border' ); } if ( method_exists( $this, 'process_advanced_border_options' ) ) { // Backwards Compatibility // Call it after processing default fields because it's additional processing and is not replacement. $this->process_advanced_border_options( $function_name ); } } /** * Get active position locations. * * @return array */ public function get_position_locations() { return $this->position_locations; } /** * Set active position locations. * * @param string $locations Location name e.x center_center, top_left_is_default. */ public function set_position_locations( $locations ) { $this->position_locations = $locations; } /** * Process transform options. * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support. */ public function process_transform( $function_name ) { $transform = self::$_->array_get( $this->advanced_fields, 'transform', array() ); if ( false === $transform || ! is_array( $transform ) ) { return; } // @codingStandardsIgnoreLine $selector = self::$_->array_get( $transform, 'css.main', '%%order_class%%' ); /** * The "a" element of the button module is the one that should be * scaled and not its wrapper */ if ( 'et_pb_button' === $function_name ) { $selector .= ' a'; } $important = self::$_->array_get( $transform, 'css.important', false ); $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $is_hover_enabled = $hover->is_enabled( 'transform_styles', $this->props ); $is_sticky_enabled = $sticky->is_enabled( 'transform_styles', $this->props ); $is_responsive_enabled = isset( $this->props['transform_styles_last_edited'] ) && et_pb_get_responsive_status( $this->props['transform_styles_last_edited'] ); $responsive_direction = isset( $this->props['animation_direction_last_edited'] ) && et_pb_get_responsive_status( $this->props['animation_direction_last_edited'] ); $animation_type = self::$_->array_get( $this->props, 'animation_style', 'none' ); /** * Transform instance. * * @var ET_Builder_Module_Field_Transform $class Transform field class instance. */ $class = ET_Builder_Module_Fields_Factory::get( 'Transform' ); $class->set_props( $this->props + array( 'transforms_important' => $important ) ); $position_locations = $this->get_position_locations(); $is_enabled = $this->_features_manager->get( // Transforms is enabled. 'tra', function() use ( $class, $position_locations ) { return $class->is_used( $this->props, $position_locations ); } ); if ( ! $is_enabled ) { return; } $views = array( 'desktop' ); if ( $is_hover_enabled || isset( $position_locations['hover'] ) ) { array_push( $views, 'hover' ); } if ( $is_sticky_enabled || isset( $position_locations['sticky'] ) ) { array_push( $views, 'sticky' ); } if ( $is_responsive_enabled || ( 'none' !== $animation_type && $responsive_direction ) || ( isset( $position_locations['hover'] ) || isset( $position_locations['phone'] ) ) ) { array_push( $views, 'tablet', 'phone' ); } foreach ( $views as $view ) { $view_selector = $selector; $device = $view; if ( ! $is_responsive_enabled && in_array( $view, array( 'phone', 'tablet' ), true ) || ( 'hover' === $view && ! $is_hover_enabled ) || ( 'sticky' === $view && ! $is_sticky_enabled ) ) { $device = 'desktop'; } $elements = $class->get_elements( $device ); $media_query = array(); if ( 'hover' === $view ) { $view_selector = $selector . ':hover'; } elseif ( 'sticky' === $view ) { $view_selector = $sticky->add_sticky_to_selectors( $selector, $this->is_sticky_module ); } elseif ( 'tablet' === $view ) { $media_query = array( 'media_query' => self::get_media_query( 'max_width_980' ), ); } elseif ( 'phone' === $view ) { $media_query = array( 'media_query' => self::get_media_query( 'max_width_767' ), ); } if ( isset( $position_locations[ $view ] ) ) { $default_strpos = strpos( $position_locations[ $view ], '_is_default' ); $location = $position_locations[ $view ]; if ( false !== $default_strpos ) { $location = substr( $position_locations[ $view ], 0, $default_strpos ); } if ( ! isset( $elements['transform']['translateX'] ) ) { if ( in_array( $location, array( 'top_center', 'bottom_center', 'center_center' ), true ) ) { $elements['transform']['translateX'] = '-50%'; } elseif ( 'desktop' !== $view ) { $elements['transform']['translateX'] = '0px'; } } if ( ! isset( $elements['transform']['translateY'] ) ) { if ( in_array( $location, array( 'center_left', 'center_right', 'center_center' ), true ) ) { $elements['transform']['translateY'] = '-50%'; } elseif ( 'desktop' !== $view ) { $elements['transform']['translateY'] = '0px'; } } } if ( ! empty( $elements['transform'] ) || ! empty( $elements['origin'] ) ) { if ( 'hover' !== $view && 'sticky' !== $view && ! empty( $animation_type ) && 'none' !== $animation_type && 'fade' !== $animation_type ) { $transformed_animation = $class->transformedAnimation( $animation_type, $elements, $function_name, $device ); if ( ! empty( $transformed_animation ) ) { self::set_style( $function_name, $transformed_animation['keyframe'] + $media_query ); self::set_style( $function_name, $transformed_animation['animationRules'] + $media_query ); $el_style = array( 'selector' => $view_selector, 'declaration' => $transformed_animation['declaration'], 'priority' => $this->_style_priority, ) + $media_query; self::set_style( $function_name, $el_style ); } } else { $declaration = ''; if ( ! empty( $elements['transform'] ) ) { $declaration .= $class->getTransformDeclaration( $elements['transform'], $view ); } if ( ! empty( $elements['origin'] ) ) { if ( $important ) { array_push( $elements['origin'], '!important' ); } $declaration .= sprintf( 'transform-origin:%s;', implode( ' ', $elements['origin'] ) ); } $el_style = array( 'selector' => $view_selector, 'declaration' => $declaration, 'priority' => $this->_style_priority, ) + $media_query; self::set_style( $function_name, $el_style ); // Flag sticky module that uses transform uses classname on module wrapper // so its offset calculation can be adjusted (jQuery offsets() gets the edge // of transformed module so its offset will be outside its parent). if ( 'sticky' !== $view && $this->is_sticky_module ) { $this->add_classname( 'et_pb_sticky--has-transform' ); } } } } } /** * Process position options. * * @param string $function_name Module slug. */ public function process_position( $function_name ) { global $et_fb_processing_shortcode_object; /** * Position field instance. * * @var $position_class of `ET_Builder_Module_Field_Position`. */ $position_class = ET_Builder_Module_Fields_Factory::get( 'Position' ); $position_class->set_module( $this ); $is_enabled = $this->_features_manager->get( // Position is enabled. 'pos', function() use ( $position_class ) { return $position_class->is_used( $this->props ); } ); $this->set_position_locations( [] ); if ( $is_enabled ) { $position_class->process( $function_name ); } // Expose position settings on layout block preview so necesary adjustment can be applied. if ( ET_GB_Block_Layout::is_layout_block_preview() ) { $layout_block_settings = $position_class->get_layout_block_settings( $function_name ); if ( is_array( $layout_block_settings ) && ! empty( $layout_block_settings ) ) { self::$layout_block_assistive_settings['position'][] = array( 'selector' => '.' . self::get_module_order_class( $function_name ), 'settings' => $layout_block_settings, ); } } // Exposes `position: relative` offsets settings. Offsets need to be rendered as inline // style because `top`, `right`, `bottom`, and `left` are used to render sticky state // in the module as inline module. $responsive = et_pb_responsive_options(); $sticky = et_pb_sticky_options(); $positioning = $responsive->get_checked_property_value( $this->props, 'positioning', '', true ); $is_relative = is_string( $positioning ) && 'relative' === $positioning; $has_relative = is_array( $positioning ) && in_array( 'relative', $positioning, true ); if ( ! $et_fb_processing_shortcode_object && $this->is_sticky_module && ( $is_relative || $has_relative ) ) { $order_class = self::get_module_order_class( $this->slug ); // Set position style. et_()->array_set( self::$sticky_elements, $order_class . '.styles.positioning', $positioning ); // Attributes list. $relative_attributes = array( 'position_origin_r' => array( 'default' => 'top_left', ), 'horizontal_offset' => array( 'default' => '', ), 'vertical_offset' => array( 'default' => '', ), ); // Loop over attributes list and save sticky style. foreach ( $relative_attributes as $attr_name => $attr ) { // Position origin should fallback to non-sticky value to avoid broken calculation. $is_relative = 'position_origin_r' === $attr_name; $attr_value_default = $is_relative ? et_()->array_get( $this->props, $attr_name, '' ) : ''; $attr_value = $sticky->get_value( $attr_name, $this->props, $attr_value_default ); et_()->array_set( self::$sticky_elements, $order_class . '.stickyStyles.' . $attr_name, $attr_value ); } } } /** * Adds Filter styles to the page custom css code * * Wrapper for `generate_css_filters` used for module defaults * * @param string $function_name Module slug. * * @return string|void */ public function process_advanced_filter_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } // Module has to explicitly set false to disable filters options. if ( false === self::$_->array_get( $this->advanced_fields, 'filters', false ) ) { return; } // Needed to use custom css selectors for Filter in Icon Module. if ( $this->slug === $function_name && 'et_pb_icon' === $function_name && ! empty( $this->advanced_fields['filters']['css']['main'] ) ) { return $this->generate_css_filters( $function_name, '', $this->advanced_fields['filters']['css']['main'] ); } $is_enabled = $this->_features_manager->get( // Filters are enabled. 'fil', function() { return $this->are_filters_used(); } ); if ( ! $is_enabled ) { return; } return $this->generate_css_filters( $function_name ); } /** * Determine if max_width is used * * @since 4.10.0 * * @param string $slug Module slug. * @return bool */ public function max_width_is_used( $slug ) { foreach ( $this->props as $attr => $value ) { if ( ! $value ) { continue; } $is_attr = false !== strpos( $attr, 'max_width' ) || false !== strpos( $attr, 'width' ); // Ignore default value for section. if ( 'et_pb_section' === $slug && 'inner_max_width' === $attr && '1080px' === $value ) { continue; } // Ignore default value for row. if ( 'et_pb_row' === $slug && 'max_width' === $attr && '1080px' === $value ) { continue; } $default_values = [ 'none', ]; if ( $is_attr && ! in_array( $value, $default_values, true ) ) { return true; } } return false; } /** * Process max width options * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support. */ public function process_max_width_options( $function_name ) { global $et_fb_processing_shortcode_object; // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'max_width', false ) ) ) { return; } $is_enabled = $this->_features_manager->get( // Max width is enabled. 'mawi', function() use ( $function_name ) { return $this->max_width_is_used( $function_name ); } ); if ( ! $is_enabled ) { return; } $max_width = self::$_->array_get( $this->advanced_fields, 'max_width', array() ); $fields = array_merge( array( '' => $max_width ), self::$_->array_get( $max_width, 'extra', array() ) ); $order_class = self::get_module_order_class( $this->slug ); foreach ( $fields as $prefix => $field ) { $is_customized = ! self::$_->array_get( $field, 'use_max_width', true ) && ! self::$_->array_get( $field, 'use_width', true ); $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); if ( 'et_pb_section' === $this->slug && 'inner' === $prefix && 'on' !== $this->prop( 'specialty' ) ) { // https://github.com/elegantthemes/Divi/issues/14445 // This is a hot fix due to the fact that in near future // modules will be processed and rendered in VB // The real solution requires handling modules fields dependencies in FE // As section inner sizing depends on section `speciality`. continue; } // Max width. foreach ( array( 'width', 'max_width' ) as $key ) { if ( ! self::$_->array_get( $field, "use_$key", true ) ) { continue; } $slug = et_builder_add_prefix( $prefix, $key ); $css_prop = $this->field_to_css_prop( $key ); $option = self::$_->array_get( $this->fields_unprocessed, $slug, array() ); $width_options_css = self::$_->array_get( $field, 'css', array() ); $default_selector = self::$_->array_get( $width_options_css, 'main', '%%order_class%%' ); $selector = self::$_->array_get( $width_options_css, $key, $default_selector ); $desktop_default = self::$_->array_get( $option, 'default' ); $default = $desktop_default; $width = $this->prop( $slug, $default ); $default_tablet = self::$_->array_get( $option, 'default_tablet', $width ); $width_tablet = $this->prop( "{$slug}_tablet", $default_tablet ); $default_phone = self::$_->array_get( $option, 'default_phone', $width_tablet ); $width_phone = $this->prop( "{$slug}_phone", $default_phone ); $width_hover = $hover->get_value( $slug, $this->props, '' ); $width_sticky = $sticky->get_value( $slug, $this->props, '' ); $width_last_edited = $this->prop( "{$slug}_last_edited", '' ); $width_responsive_active = et_pb_get_responsive_status( $width_last_edited ); $width = $width === $default ? '' : $width; $width_tablet = $width_tablet === $default_tablet ? '' : $width_tablet; $width_phone = $width_phone === $default_phone ? '' : $width_phone; if ( '' !== $width_tablet || '' !== $width_phone || '' !== $width ) { $additional_css = $this->get_max_width_additional_css(); $width_attrs = array( $slug ); // Append !important tag. if ( isset( $width_options_css['important'] ) ) { $additional_css = ' !important;'; } if ( $width_responsive_active ) { $width_values = array( 'desktop_only' => $width, 'tablet' => $width_tablet, 'phone' => $width_phone, ); $selector_values = array( 'desktop_only' => $selector, 'tablet' => $selector, 'phone' => $selector, ); $width_attrs = array_merge( $width_attrs, array( "{$slug}_tablet", "{$slug}_phone" ) ); } else { $width_values = array( 'desktop' => $width, ); $selector_values = array( 'desktop' => $selector, ); } // Update $is_max_width_customized if one of max_width* value is modified. foreach ( $width_attrs as $width_attr ) { if ( $is_customized ) { break; } if ( ! in_array( self::$_->array_get( $this->props, $width_attr ), array( '', $default ), true ) ) { $is_customized = true; } } et_pb_responsive_options()->generate_responsive_css( $width_values, $selector_values, $css_prop, $function_name, $additional_css ); // Set module width and max-width into moduleSettings. These properties are // rendered as inline style for constructing sticky behaviour thus it needs // special adjustment to work correctly. if ( ! $et_fb_processing_shortcode_object && $this->is_sticky_module ) { et_()->array_set( self::$sticky_elements, self::get_module_order_class( $this->slug ) . '.styles.' . $css_prop, $width_values ); } } elseif ( '' !== $default && '100%' !== $default && ! in_array( $default, array( 'auto', 'none' ), true ) ) { $is_customized = true; } // Hover and/or Sticky styles. if ( '' !== $width_hover || '' !== $width_sticky ) { // Apply 100% max-width if there is only hover max-width set so that transition works. $selector = isset( $width_options_css['main'] ) ? $width_options_css['main'] : '%%order_class%%'; $is_customized = true; if ( '' === $width ) { $hover_width = $desktop_default ? $desktop_default : '100%'; $hover_base = array( 'selector' => $selector, 'declaration' => esc_html( "$css_prop: $hover_width;" ), ); self::set_style( $function_name, $hover_base ); } // Hover style. $additional_css = $this->get_max_width_additional_css(); if ( '' !== $width_hover ) { $selector_hover = $hover->add_hover_to_order_class( $selector ); $hover_style = array( 'selector' => $selector_hover, 'declaration' => esc_html( "$css_prop: {$width_hover}{$additional_css};" ), ); self::set_style( $function_name, $hover_style ); } // Sticky style should be printed if module is INSIDE sticky module. if ( $sticky->is_inside_sticky_module() && '' !== $width_sticky ) { $sticky_child_selector = $sticky->add_sticky_to_selectors( $selector, false ); $sticky_child_style = array( 'selector' => $sticky_child_selector, 'declaration' => esc_html( "$css_prop: {$width_sticky}{$additional_css};" ), ); self::set_style( $function_name, $sticky_child_style ); } // Sticky style on sticky module: pass it to frontend as variables. Sticky style // should not be rendered on page because it will be dynamically calculated // and inserted as inline style. if ( ! $et_fb_processing_shortcode_object && $this->is_sticky_module && '' !== $width_sticky ) { et_()->array_set( self::$sticky_elements, $order_class . '.stickyStyles.' . $css_prop, $width_sticky ); // Adjustment for max-width. if ( 'max-width' === $css_prop ) { // Get sticky style width; fallback to desktop width if empty. $sticky_style_width = et_()->array_get( self::$sticky_elements, $order_class . '.stickyStyles.width', et_()->array_get( self::$sticky_elements, $order_class . '.styles.width', '' ) ); // Force define width style using default value if max-width sticky // style is used to handle various possible scenarios. if ( '' !== $width_sticky && ! $sticky_style_width ) { et_()->array_set( self::$sticky_elements, $order_class . '.styles.width', et_()->array_get( $this->get_default_props(), 'width', '' ) ); } } } } } // Module Alignment. if ( self::$_->array_get( $field, 'use_module_alignment', true ) ) { $module_alignment_styles = array( 'left' => 'margin-left: 0px !important; margin-right: auto !important;', 'center' => 'margin-left: auto !important; margin-right: auto !important;', 'right' => 'margin-left: auto !important; margin-right: 0px !important;', ); $slug = et_builder_add_prefix( $prefix, 'module_alignment' ); $module_alignment = $this->prop( $slug, '' ); if ( $is_customized && isset( $module_alignment_styles[ $module_alignment ] ) ) { if ( 'et_pb_contact_field' === $function_name ) { $default_selector = self::$_->array_get( $field, 'css.main', 'p%%order_class%%' ); } else { $default_selector = self::$_->array_get( $field, 'css.main', '%%order_class%%.et_pb_module' ); } $selector = self::$_->array_get( $field, 'css.module_alignment', $default_selector ); $el_style = array( 'selector' => $selector, 'declaration' => $module_alignment_styles[ $module_alignment ], 'priority' => 20, ); self::set_style( $function_name, $el_style ); } $is_module_alignment_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, $slug ); $module_alignment_tablet = $this->prop( "{$slug}_tablet", '' ); if ( $is_customized && isset( $module_alignment_styles[ $module_alignment_tablet ] ) && $is_module_alignment_responsive ) { $default_selector = self::$_->array_get( $field, 'css.main', '%%order_class%%.et_pb_module' ); $selector = self::$_->array_get( $field, 'css.module_alignment', $default_selector ); $el_style = array( 'selector' => $selector, 'declaration' => $module_alignment_styles[ $module_alignment_tablet ], 'priority' => 20, 'media_query' => self::get_media_query( 'max_width_980' ), ); self::set_style( $function_name, $el_style ); } $module_alignment_phone = $this->prop( "{$slug}_phone", '' ); if ( $is_customized && isset( $module_alignment_styles[ $module_alignment_phone ] ) && $is_module_alignment_responsive ) { $default_selector = self::$_->array_get( $field, 'css.main', '%%order_class%%.et_pb_module' ); $selector = self::$_->array_get( $field, 'css.module_alignment', $default_selector ); $el_style = array( 'selector' => $selector, 'declaration' => $module_alignment_styles[ $module_alignment_phone ], 'priority' => 20, 'media_query' => self::get_media_query( 'max_width_767' ), ); self::set_style( $function_name, $el_style ); } // Set module alignment setting for sticky element. if ( ! $et_fb_processing_shortcode_object && $this->is_sticky_module ) { // Get default value. Check for default_sticky attribute before field's default // because some module (row, row inner) have severe backward compatibility that // updating its default to match visual appearance could cause issues. $module_alignment_default = et_()->array_get( $this->fields_unprocessed, 'module_alignment.default_sticky', et_()->array_get( $this->fields_unprocessed, 'module_alignment.default', '' ) ); et_()->array_set( self::$sticky_elements, self::get_module_order_class( $this->slug ) . '.styles.module_alignment', array( 'desktop' => '' !== $module_alignment ? $module_alignment : $module_alignment_default, 'tablet' => $module_alignment_tablet, 'phone' => $module_alignment_phone, ) ); } } } } /** * Return unique identifier for sticky element. For frontend, order class is considered unique * and sufficient as identifier * * @since 4.6.0 * * @param string $render_slug Render slug. * * @return string */ public function get_sticky_id( $render_slug = '' ) { return self::get_module_order_class( $render_slug ); } /** * Process sticky element * * Append current module's sticky setting into static variable which will be exposed for js files * Sticky element UX is fully handled via javascript; Module simply exposes the configuration * for JS to initialized * * @since 4.6.0 * * @param string $render_slug Render slug. */ public function process_sticky( $render_slug ) { global $et_fb_processing_shortcode_object; // Skip if current request is visual builder page. if ( $et_fb_processing_shortcode_object ) { return; } // Check if sticky is intentionally disabled or not. if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'sticky', array() ) ) ) { return; } // Get sticky prefix. $prefix = ET_Builder_Module_Fields_Factory::get( 'Sticky' )->get_default( 'prefix' ); // Bail if sticky option is not used OR there are incompatible attrs being used. if ( ! et_pb_sticky_options()->is_sticky_module( $this->props ) ) { return; } $responsive = et_pb_responsive_options(); $position = $responsive->get_checked_property_value( $this->props, $prefix . '_position', '', true ); // Module's order class. $selector = '.' . self::get_module_order_class( $render_slug ); // Some modules (eg. button module) have their order class wrapper wrapped by additional // wrapper for alignment purpose. The selector needs to be adjusted to the most outer wrapper. if ( et_()->array_get( $this->wrapper_settings, 'order_class_wrapper', false ) ) { $selector .= '_wrapper'; } // Generate sticky element settings that will be usedby stickyElement class. // Can't directly pass attribute value because technically $prefix can be different. // Settings' array element need to be fixed then. $settings = array( 'id' => $this->get_sticky_id( $render_slug ), 'selector' => $selector, 'position' => et_core_intentionally_unescaped( $position, 'fixed_string' ), 'topOffset' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_offset_top', '', true ), 'fixed_string' ), 'bottomOffset' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_offset_bottom', '', true ), 'fixed_string' ), 'topLimit' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_limit_top', '', true ), 'fixed_string' ), 'bottomLimit' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_limit_bottom', '', true ), 'fixed_string' ), 'offsetSurrounding' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_offset_surrounding', '', true ), 'fixed_string' ), 'transition' => et_core_intentionally_unescaped( $responsive->get_checked_property_value( $this->props, $prefix . '_transition', '', true ), 'fixed_string' ), ); // Populate sticky element settings. self::$sticky_elements[ self::get_module_order_class( $render_slug ) ] = $settings; } /** * Process scroll effects options. * * @param string $function_name Module slug. */ public function process_scroll_effects( $function_name ) { global $wp_version; $advanced_fields = self::$_->array_get( $this->advanced_fields, 'scroll_effects', array( 'default' => array() ) ); if ( ! $advanced_fields ) { return; } // Accordion Module reuses the Toggle Module and its child are not marked as child on FE, so check this specific case. $is_child_element = 'child' === $this->type || 'et_pb_accordion_item' === $function_name; $options = $this->get_scroll_effects_options(); $motion = ET_Builder_Module_Helper_Motion_Motions::instance(); $responsive = et_pb_responsive_options(); $devices = array( $responsive::DESKTOP, $responsive::TABLET, $responsive::PHONE, ); // Reset saved parent effects to not apply them on subsequent modules. if ( ! $is_child_element ) { self::$parent_motion_effects = array(); } // Check if animation is enabled. $scroll_effects_enabled = $this->_features_manager->get( // Scroll effects enabled. 'scef', function() use ( $options ) { return $this->is_scroll_effects_enabled( $options ); } ); if ( ! $scroll_effects_enabled ) { return; } // Disable WordPress lazy load if it's not already disabled. The wp_lazy_loading_enabled filter is available since WordPress 5.5.0. if ( false === self::$is_wp_lazy_load_disabled && version_compare( $wp_version, '5.5.0', '>=' ) ) { self::$is_wp_lazy_load_disabled = true; add_filter( 'wp_lazy_loading_enabled', '__return_false' ); } foreach ( $options as $id => $option ) { $is_effect_enabled = 'on' === $this->prop( $id . '_enable' ); $is_inherit_parent = $is_child_element && ! empty( self::$parent_motion_effects ) && isset( self::$parent_motion_effects[ $id ] ); if ( ! $is_effect_enabled && ! $is_inherit_parent ) { continue; } $default = $option['default']; foreach ( $devices as $device ) { if ( ! $is_effect_enabled ) { $item = self::$parent_motion_effects[ $id ]; $item['id'] = '.' . self::get_module_order_class( $function_name ); $item['module_type'] = esc_html( $function_name ); } else { $field = $responsive->get_field( $id, $device ); $default_value = $responsive->get_default_value( $this->props, $field, $default ); $saved_value = $this->prop( $field, $default_value ); $value = $motion->getValue( $saved_value, $default_value ); $grid_motion = $this->prop( 'enable_grid_motion', 'off' ); $trigger_start = $this->prop( 'motion_trigger_start', 'middle' ); $trigger_end = $this->prop( 'motion_trigger_end', 'middle' ); $grid_modules = array( 'et_pb_gallery', 'et_pb_portfolio', 'et_pb_fullwidth_portfolio', 'et_pb_filterable_portfolio', 'et_pb_shop', 'et_pb_blog' ); $item = array( 'id' => '.' . self::get_module_order_class( $function_name ), 'start' => $motion->getStartLimit( $value ), 'midStart' => $motion->getStartMiddle( $value ), 'midEnd' => $motion->getEndMiddle( $value ), 'end' => $motion->getEndLimit( $value ), 'startValue' => (float) $motion->getStartValue( $value ), 'midValue' => (float) $motion->getMiddleValue( $value ), 'endValue' => (float) $motion->getEndValue( $value ), 'resolver' => $option['resolver'], 'module_type' => esc_html( $function_name ), 'trigger_start' => $trigger_start, 'trigger_end' => $trigger_end, ); $transform_class = ET_Builder_Module_Fields_Factory::get( 'Transform' ); $transform_class->set_props( $this->props + array( 'transforms_important' => true ) ); $elements = $transform_class->get_elements( $device ); // Process transforms if defined. if ( ! empty( $elements ) && ! empty( $elements['transform'] ) ) { $item['transforms'] = $elements['transform']; } if ( 'on' === $grid_motion ) { $item['grid_motion'] = $grid_motion; $item['children_count'] = in_array( $function_name, $grid_modules, true ) ? $this->prop( 'posts_number', 10 ) : 0; $item['module_index'] = self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ); } if ( $this->child_slug && 'on' === $grid_motion ) { self::$parent_motion_effects[ $id ] = $item; unset( self::$parent_motion_effects[ $id ]['id'], self::$parent_motion_effects[ $id ]['grid_motion'], self::$parent_motion_effects[ $id ]['children_count'], self::$parent_motion_effects[ $id ]['module_index'] ); $item['child_slug'] = $this->child_slug; } if ( $is_child_element ) { if ( ! empty( self::$parent_motion_effects[ $id ] ) ) { $additional_item = self::$parent_motion_effects[ $id ]; $additional_item['id'] = $item['id']; self::$_scroll_effects_fields[ $device ][] = $additional_item; } } elseif ( 'on' !== $grid_motion ) { self::$parent_motion_effects = array(); } } self::$_scroll_effects_fields[ $device ][] = $item; } } } /** * Check if scroll effects is enabled. * * @param string $options Scroll options. * * @since 4.10.0 */ public function is_scroll_effects_enabled( $options ) { foreach ( $options as $id => $option ) { if ( 'on' === $this->prop( $id . '_enable' ) ) { return true; } } return false; } /** * Process height options * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support. */ public function process_height_options( $function_name ) { if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'height' ) ) ) { return; } $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $responsive = et_pb_responsive_options(); $settings = self::$_->array_get( $this->advanced_fields, 'height', array() ); $fields = array_merge( array( '' => $settings ), self::$_->array_get( $settings, 'extra', array() ) ); foreach ( $fields as $prefix => $settings ) { $prefix = et_builder_add_prefix( $prefix, '' ); $default_selector = self::$_->array_get( $settings, 'css.main', $this->main_css_element ); $helpers = array( 'height' => et_pb_height_options( $prefix ), 'min_height' => et_pb_min_height_options( $prefix ), 'max_height' => et_pb_max_height_options( $prefix ), ); foreach ( $helpers as $key => $helper ) { if ( ! self::$_->array_get( $settings, "use_{$key}", true ) ) { continue; } $slug = $helper->get_field( $prefix ); $field = self::$_->array_get( $this->fields_unprocessed, $slug, array() ); $css_props = $this->field_to_css_prop( $key ); $selector = self::$_->array_get( $settings, "css.{$key}", $default_selector ); $responsive_is_enabled = $this->_features_manager->get( // Is height responsive enabled for $slug. // keys: hhere, hmire, hmare. "h{$slug[0]}{$slug[1]}re", function() use ( $responsive, $slug ) { return $responsive->is_enabled( $slug, $this->props ); } ); if ( $responsive_is_enabled ) { $values = array(); foreach ( $responsive->get_modes() as $mode ) { $default_field = ET_Builder_Module_Helper_ResponsiveOptions::DESKTOP === $mode ? 'default' : "default_$mode"; $default = self::$_->array_get( $field, $default_field ); $values[ $mode ] = $responsive->get_value( $slug, $this->props, $mode, $default ); } et_pb_responsive_options()->generate_responsive_css( $values, $selector, $css_props, $function_name ); } else { $default = self::$_->array_get( $field, 'default' ); $value = $helper->get_value( $this->props, $default ); if ( '' !== $value && $value !== $default ) { $el_style = array( 'selector' => $selector, 'declaration' => sprintf( '%1$s: %2$s;', $css_props, esc_attr( $value ) ), ); self::set_style( $function_name, $el_style ); } } $hover_is_enabled = $this->_features_manager->get( // Is height hover enabled for $slug. // keys: hheho, hmiho, hmaho. "h{$slug[0]}{$slug[1]}ho", function() use ( $hover, $slug ) { return $hover->is_enabled( $slug, $this->props ); } ); if ( $hover_is_enabled ) { $default = self::$_->array_get( $field, 'default' ); $value = $helper->get_value( $this->props, $default ); $hover_value = $hover->get_value( $slug, $this->props, $value ); $selector = $hover->add_hover_to_selectors( $selector ); if ( '' !== $hover_value && $hover_value !== $value ) { $el_style = array( 'selector' => $selector, 'declaration' => sprintf( '%1$s: %2$s;', $css_props, esc_attr( $hover_value ) ), ); self::set_style( $function_name, $el_style ); } } $sticky_is_enabled = $this->_features_manager->get( // Is height sticky enabled for $slug. // keys: hhest, hmist, hmast. "h{$slug[0]}{$slug[1]}st", function() use ( $sticky, $slug ) { return $sticky->is_enabled( $slug, $this->props ); } ); if ( $sticky_is_enabled ) { $default = self::$_->array_get( $field, 'default' ); $value = $helper->get_value( $this->props, $default ); $sticky_value = $sticky->get_value( $slug, $this->props, $value ); $selector = $sticky->add_sticky_to_selectors( $selector, $this->is_sticky_module ); if ( '' !== $sticky_value && $value !== $sticky_value ) { $el_style = array( 'selector' => $selector, 'declaration' => sprintf( '%1$s: %2$s;', $css_props, esc_attr( $sticky_value ) ), ); self::set_style( $function_name, $el_style ); } } } } } /** * Process overflow options * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support. */ public function process_overflow_options( $function_name ) { if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'overflow', array() ) ) ) { return; } $overflow = et_pb_overflow(); $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $selector = self::$_->array_get( $this->advanced_fields, 'overflow.css.main', $this->main_css_element ); $fields = array( 'overflow-x' => $overflow->get_field_x(), 'overflow-y' => $overflow->get_field_y(), ); $controls = ET_Builder_Module_Fields_Factory::get( 'Overflow' )->get_fields( array(), true ); // Rebuilt template if template id is returned by get_fields(). if ( self::$option_template->is_enabled() && is_string( $controls ) ) { $controls = self::$option_template->rebuild_field_template( $controls ); } foreach ( $fields as $prop => $field ) { $default_value = self::$_->array_get( $controls[ $field ], 'default', '' ); $overflow_values = et_pb_responsive_options()->get_property_values( $this->props, $field, $default_value ); et_pb_responsive_options()->generate_responsive_css( $overflow_values, $selector, $field, $function_name, '', 'overflow' ); $hover_is_enabled = $this->_features_manager->get( // Is overflow hover enabled for {$field}. // keys: oxho, oyho. "{$field[0]}{$field[9]}ho", function() use ( $hover, $field ) { return $hover->is_enabled( $field, $this->props ); } ); if ( $hover_is_enabled ) { $value = $hover->get_value( $field, $this->props, '' ); if ( '' !== $value ) { $el_style = array( 'selector' => $hover->add_hover_to_selectors( $selector ), 'declaration' => sprintf( '%1$s: %2$s;', $field, esc_attr( $value ) ), ); self::set_style( $function_name, $el_style ); } } $sticky_is_enabled = $this->_features_manager->get( // Is overflow sticky enabled for {$field}. // keys: oxst, oyst. "{$field[0]}{$field[9]}st", function() use ( $sticky, $field ) { return $sticky->is_enabled( $field, $this->props ); } ); if ( $sticky_is_enabled ) { $value = $sticky->get_value( $field, $this->props, '' ); if ( '' !== $value ) { $el_style = array( 'selector' => $sticky->add_sticky_to_selectors( $selector, $this->is_sticky_module ), 'declaration' => sprintf( '%1$s: %2$s;', $field, esc_attr( $value ) ), ); self::set_style( $function_name, $el_style ); } } } } /** * Determine if custom margin or padding is used. * * @since 4.10.0 * * @return bool */ public function custom_margin_is_used() { foreach ( $this->props as $attr => $value ) { if ( ! $value ) { continue; } $is_attr = false !== strpos( $attr, 'custom_margin' ) || false !== strpos( $attr, 'custom_padding' ); if ( $is_attr ) { return true; } } return false; } /** * Determine if font options are being used. * * @since 4.10.0 * * @param string $slugs font option slugs. * @return bool */ public function font_options_are_used( $slugs ) { $is_used = false; foreach ( $this->props as $attr => $value ) { if ( ! $value ) { continue; } foreach ( $slugs as $slug ) { $is_attr = false !== strpos( $attr, "_{$slug}" ) || false !== strpos( $attr, "{$slug}_" ); if ( $is_attr ) { return true; } } } return false; } /** * Process margin options * * @param string $function_name Function name. * * @since 4.6.0 Add sticky style support */ public function process_advanced_custom_margin_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) { return; } if ( ! self::$_->array_get( $this->advanced_fields, 'margin_padding', false ) ) { return; } $is_enabled = $this->_features_manager->get( // Custom margin padding is enabled. 'cuma', function() { return $this->custom_margin_is_used(); } ); if ( ! $is_enabled ) { return; } $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $style = ''; $style_padding = ''; $style_margin = ''; $style_mobile = array(); $style_mobile_padding = array(); $style_mobile_margin = array(); $important_options = array(); $is_important_set = isset( $this->advanced_fields['margin_padding']['css']['important'] ); $use_global_important = $is_important_set && 'all' === $this->advanced_fields['margin_padding']['css']['important']; $css = isset( $this->advanced_fields['margin_padding']['css'] ) ? $this->advanced_fields['margin_padding']['css'] : array(); $item_mappings = array( 'top' => 0, 'right' => 1, 'bottom' => 2, 'left' => 3, ); if ( $is_important_set && is_array( $this->advanced_fields['margin_padding']['css']['important'] ) ) { $important_options = $this->advanced_fields['margin_padding']['css']['important']; } $custom_margin = $this->advanced_fields['margin_padding']['use_margin'] ? $this->props['custom_margin'] : ''; $custom_padding = $this->advanced_fields['margin_padding']['use_padding'] ? $this->props['custom_padding'] : ''; $custom_margin_responsive_active = isset( $this->props['custom_margin_last_edited'] ) ? et_pb_get_responsive_status( $this->props['custom_margin_last_edited'] ) : false; $custom_margin_mobile = $custom_margin_responsive_active && $this->advanced_fields['margin_padding']['use_margin'] && ( isset( $this->props['custom_margin_tablet'] ) || isset( $this->props['custom_margin_phone'] ) ) ? array( 'tablet' => isset( $this->props['custom_margin_tablet'] ) ? $this->props['custom_margin_tablet'] : '', 'phone' => isset( $this->props['custom_margin_phone'] ) ? $this->props['custom_margin_phone'] : '', ) : ''; $custom_padding_responsive_active = isset( $this->props['custom_padding_last_edited'] ) ? et_pb_get_responsive_status( $this->props['custom_padding_last_edited'] ) : false; $custom_padding_mobile = $custom_padding_responsive_active && $this->advanced_fields['margin_padding']['use_padding'] && ( isset( $this->props['custom_padding_tablet'] ) || isset( $this->props['custom_padding_phone'] ) ) ? array( 'tablet' => isset( $this->props['custom_padding_tablet'] ) ? $this->props['custom_padding_tablet'] : '', 'phone' => isset( $this->props['custom_padding_phone'] ) ? $this->props['custom_padding_phone'] : '', ) : ''; if ( '' !== $custom_padding || ! empty( $custom_padding_mobile ) ) { $important = in_array( 'custom_padding', $important_options, true ) || $use_global_important ? true : false; $has_padding_selector = isset( $this->advanced_fields['margin_padding']['css'] ) && isset( $this->advanced_fields['margin_padding']['css']['padding'] ); $padding_styling = '' !== $custom_padding ? et_builder_get_element_style_css( $custom_padding, 'padding', $important ) : ''; if ( $has_padding_selector ) { $style_padding .= $padding_styling; } else { $style .= $padding_styling; } if ( ! empty( $custom_padding_mobile ) ) { foreach ( $custom_padding_mobile as $device => $settings ) { $padding_mobile_styling = '' !== $settings ? et_builder_get_element_style_css( $settings, 'padding', $important ) : ''; if ( $has_padding_selector ) { $style_mobile_padding[ $device ][] = $padding_mobile_styling; } else { $style_mobile[ $device ][] = $padding_mobile_styling; } } } // Selective Paddings. $selective_paddings = array_filter( array( 'top' => isset( $css['padding-top'] ) ? $css['padding-top'] : false, 'right' => isset( $css['padding-right'] ) ? $css['padding-right'] : false, 'bottom' => isset( $css['padding-bottom'] ) ? $css['padding-bottom'] : false, 'left' => isset( $css['padding-left'] ) ? $css['padding-left'] : false, ) ); // Only run the following if selective-padding selector is defined. if ( ! empty( $selective_paddings ) ) { // Loop each padding sides. Selective padding works by creating filtered custom_margin value on the fly, then pass it to existin declaration builder // Ie custom_padding = 10px|10px|10px|10px. Selective padding for padding-top works by creating 10px||| value on the fly then pass it to declaration builder. foreach ( $selective_paddings as $corner => $selective_padding_selectors ) { // Default selective padding value: empty on all sides. $selective_padding = array( '', '', '', '' ); // Get padding order key. Expected order: top|right|bottom|left. $selective_padding_key = $item_mappings[ $corner ]; // Explode custom padding value into array. $selective_padding_array = explode( '|', $custom_padding ); // Pick current padding side's value. $selective_padding_value = isset( $selective_padding_array[ $selective_padding_key ] ) ? $selective_padding_array[ $selective_padding_key ] : ''; // Set selective padding value to $selective_padding. $selective_padding[ $selective_padding_key ] = $selective_padding_value; // If selective padding for current side is found, set style for it. $selective_padding_filtered = array_filter( $selective_padding ); if ( ! empty( $selective_padding_filtered ) ) { $el_style = array( 'selector' => $selective_padding_selectors, 'declaration' => rtrim( et_builder_get_element_style_css( implode( '|', $selective_padding ), 'padding' ) ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } // Check wheter responsive padding is activated and padding has mobile value. if ( $custom_padding_responsive_active && is_array( $custom_padding_mobile ) ) { // Assume no mobile padding value first. $has_selective_padding_mobile = false; // Set default selective padding mobile. $selective_padding_mobile = array( 'tablet' => array( '', '', '', '' ), 'phone' => array( '', '', '', '' ), ); // Loop padding mobile. This results per-breakpoint padding value. foreach ( $custom_padding_mobile as $breakpoint => $custom_padding_device ) { // Explode per-breakpoint padding value into array. $custom_padding_device_array = explode( '|', $custom_padding_device ); // Get current padding side value on current breakpoint. $selective_padding_mobile_value = isset( $custom_padding_device_array[ $selective_padding_key ] ) ? $custom_padding_device_array[ $selective_padding_key ] : ''; // Set picked value into current padding side on current breakpoint. $selective_padding_mobile[ $breakpoint ][ $selective_padding_key ] = $selective_padding_mobile_value; // If the side of padding on current breakpoint has value, build CSS declaration for it mark selective padding mobile as exist. $selective_padding_mobile[ $breakpoint ] = array_filter( $selective_padding_mobile[ $breakpoint ] ); if ( ! empty( $selective_padding_mobile[ $breakpoint ] ) ) { $selective_padding_mobile[ $breakpoint ] = array( et_builder_get_element_style_css( implode( '|', $selective_padding_mobile[ $breakpoint ] ), 'padding' ) ); $has_selective_padding_mobile = true; } } // Set style for selective padding on mobile. if ( $has_selective_padding_mobile ) { $this->process_advanced_mobile_margin_options( $function_name, $selective_padding_mobile, $selective_padding_selectors ); } } } } } if ( '' !== $custom_margin || ! empty( $custom_margin_mobile ) ) { $important = in_array( 'custom_margin', $important_options, true ) || $use_global_important ? true : false; $has_margin_selector = isset( $this->advanced_fields['margin_padding']['css'] ) && isset( $this->advanced_fields['margin_padding']['css']['margin'] ); $margin_styling = '' !== $custom_margin ? et_builder_get_element_style_css( $custom_margin, 'margin', $important ) : ''; if ( $has_margin_selector || $this->is_sticky_module ) { $style_margin .= $margin_styling; } else { $style .= $margin_styling; } if ( ! empty( $custom_margin_mobile ) ) { foreach ( $custom_margin_mobile as $device => $settings ) { $margin_mobile_styling = '' !== $settings ? et_builder_get_element_style_css( $settings, 'margin', $important ) : ''; if ( $has_margin_selector || $this->is_sticky_module ) { $style_mobile_margin[ $device ][] = $margin_mobile_styling; } else { $style_mobile[ $device ][] = $margin_mobile_styling; } } } } if ( '' !== $style_padding ) { $css_element_padding = $this->advanced_fields['margin_padding']['css']['padding']; $el_style = array( 'selector' => $css_element_padding, 'declaration' => rtrim( $style_padding ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } if ( '' !== $style_margin ) { $css_element_margin = et_()->array_get( $this->advanced_fields, 'margin_padding.css.margin', $this->main_css_element ); $el_style = array( 'selector' => $css_element_margin, 'declaration' => rtrim( $style_margin ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } if ( '' !== $style ) { $css_element = ! empty( $this->advanced_fields['margin_padding']['css']['main'] ) ? $this->advanced_fields['margin_padding']['css']['main'] : $this->main_css_element; $el_style = array( 'selector' => $css_element, 'declaration' => rtrim( $style ), 'priority' => $this->_style_priority, ); self::set_style( $function_name, $el_style ); } if ( ! empty( $style_mobile_padding ) ) { $this->process_advanced_mobile_margin_options( $function_name, $style_mobile_padding, $this->advanced_fields['margin_padding']['css']['padding'] ); } if ( ! empty( $style_mobile_margin ) ) { $css_element_margin_mobile = et_()->array_get( $this->advanced_fields, 'margin_padding.css.margin', et_()->array_get( $this->advanced_fields, 'margin_padding.css.main', $this->main_css_element ) ); $this->process_advanced_mobile_margin_options( $function_name, $style_mobile_margin, $css_element_margin_mobile ); } if ( ! empty( $style_mobile ) ) { $css_element = ! empty( $this->advanced_fields['margin_padding']['css']['main'] ) ? $this->advanced_fields['margin_padding']['css']['main'] : $this->main_css_element; $this->process_advanced_mobile_margin_options( $function_name, $style_mobile, $css_element ); } // Hover styles. // Bind margin_padding hover styles to the specified `hover` selector in the field's `css` attrs section. $margin_padding_hover_selector = self::$_->array_get( $this->advanced_fields, 'margin_padding.css.hover' ); $custom_margin_hover = $hover->get_value( 'custom_margin', $this->props, '' ); if ( '' !== $custom_margin_hover && et_builder_is_hover_enabled( 'custom_margin', $this->props ) ) { $css_element_margin = self::$_->array_get( $this->advanced_fields, 'margin_padding.css.margin', $this->main_css_element ); $el_style = array( 'selector' => ! empty( $margin_padding_hover_selector ) ? $margin_padding_hover_selector : $this->add_hover_to_order_class( $css_element_margin ), 'declaration' => rtrim( et_builder_get_element_style_css( $custom_margin_hover, 'margin', true ) ), 'priority' => 20, ); self::set_style( $function_name, $el_style ); } $custom_padding_hover = $hover->get_value( 'custom_padding', $this->props, '' ); if ( '' !== $custom_padding_hover && et_builder_is_hover_enabled( 'custom_padding', $this->props ) ) { $css_element_padding = self::$_->array_get( $this->advanced_fields, 'margin_padding.css.padding', $this->main_css_element ); $el_style = array( 'selector' => ! empty( $margin_padding_hover_selector ) ? $margin_padding_hover_selector : $this->add_hover_to_order_class( $css_element_padding ), 'declaration' => rtrim( et_builder_get_element_style_css( $custom_padding_hover, 'padding', true ) ), 'priority' => 20, ); self::set_style( $function_name, $el_style ); } // Sticky styles. $custom_margin_sticky = $sticky->get_value( 'custom_margin', $this->props, '' ); if ( '' !== $custom_margin_sticky && $sticky->is_enabled( 'custom_margin', $this->props ) ) { $css_element_margin = et_()->array_get( $this->advanced_fields, 'margin_padding.css.margin', $this->main_css_element ); $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( $css_element_margin, $this->is_sticky_module ), 'declaration' => rtrim( et_builder_get_element_style_css( $custom_margin_sticky, 'margin', true ) ), 'priority' => 20, ); self::set_style( $function_name, $el_style ); } $custom_padding_sticky = $sticky->get_value( 'custom_padding', $this->props, '' ); if ( '' !== $custom_padding_sticky && $sticky->is_enabled( 'custom_padding', $this->props ) ) { $css_element_padding = et_()->array_get( $this->advanced_fields, 'margin_padding.css.padding', $this->main_css_element ); $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( $css_element_padding, $this->is_sticky_module ), 'declaration' => rtrim( et_builder_get_element_style_css( $custom_padding_sticky, 'padding', true ) ), 'priority' => 20, ); self::set_style( $function_name, $el_style ); } } /** * Process mobile margin options into CSS style. * * @param string $function_name Module slug. * @param string $style_mobile Style array. * @param string $css_element CSS element selector. */ public function process_advanced_mobile_margin_options( $function_name, $style_mobile, $css_element ) { foreach ( $style_mobile as $device => $style ) { if ( ! empty( $style ) ) { $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $current_media_css = ''; foreach ( $style as $css_code ) { $current_media_css .= $css_code; } if ( '' === $current_media_css ) { continue; } $el_style = array( 'selector' => $css_element, 'declaration' => rtrim( $current_media_css ), 'priority' => $this->_style_priority, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } } } /** * Returns setting hover value if hover is enabled. * * @param string $option Option name. * * @return mixed|null */ protected function get_hover_value( $option ) { $enabled_option = 'background_color' === $option ? 'background' : $option; $original_value = self::$_->array_get( $this->props, $option ); $hover_enabled = et_pb_hover_options()->is_enabled( $enabled_option, $this->props ); $value = et_pb_hover_options()->get_value( $option, $this->props ); return ( ! $hover_enabled || $original_value === $value ) ? null : $value; } /** * Process advanced button options. * * @since 3.23 Add support to generate responsive styles of padding and button alignment. * @since 4.6.0 Add sticky style support. * @since 4.6.0 Background rendering is refactored; it now uses et_pb_background_options(). * * @param string $function_name Module slug. */ public function process_advanced_button_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_advanced_fields ) { return; } if ( ! self::$_->array_get( $this->advanced_fields, 'button', false ) ) { return; } $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); $has_wrapper = et_()->array_get( $this->wrapper_settings, 'order_class_wrapper', false ); $is_enabled = $this->_features_manager->get( // Is button visible. 'but', function() use ( $function_name ) { foreach ( $this->advanced_fields['button'] as $option_name => $option_settings ) { $custom_button = "custom_{$option_name}"; if ( isset( $this->props[ $custom_button ] ) && 'on' === $this->props[ $custom_button ] ) { return true; } } // For button only we need either name or link. return ! empty( $this->props['button_url'] ) || ! empty( $this->props['button_text'] ) || ! empty( $this->props['button_alignment'] ); } ); if ( ! $is_enabled ) { return; } foreach ( $this->advanced_fields['button'] as $option_name => $option_settings ) { $button_custom = $this->props[ "custom_{$option_name}" ]; $button_use_icon = isset( $this->props[ "{$option_name}_use_icon" ] ) ? $this->props[ "{$option_name}_use_icon" ] : 'on'; // Button Icon. $button_icon_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_icon" ); $button_icon = isset( $button_icon_values['desktop'] ) ? $button_icon_values['desktop'] : ''; $button_icon_tablet = isset( $button_icon_values['tablet'] ) ? $button_icon_values['tablet'] : ''; $button_icon_phone = isset( $button_icon_values['phone'] ) ? $button_icon_values['phone'] : ''; $important = et_()->array_get( $option_settings, 'css.important', false ) ? ' !important' : ''; // Button Icon Placement. $button_icon_placement_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_icon_placement" ); $button_icon_placement = isset( $button_icon_placement_values['desktop'] ) ? $button_icon_placement_values['desktop'] : 'right'; $button_icon_placement_tablet = isset( $button_icon_placement_values['tablet'] ) ? $button_icon_placement_values['tablet'] : ''; $button_icon_placement_phone = isset( $button_icon_placement_values['phone'] ) ? $button_icon_placement_values['phone'] : ''; // Button Icon On Hover. $button_on_hover_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_on_hover" ); $button_on_hover = isset( $button_on_hover_values['desktop'] ) ? $button_on_hover_values['desktop'] : ''; $button_on_hover_tablet = isset( $button_on_hover_values['tablet'] ) ? $button_on_hover_values['tablet'] : ''; $button_on_hover_phone = isset( $button_on_hover_values['phone'] ) ? $button_on_hover_values['phone'] : ''; // Button Text Size. $button_text_size_hover = $this->get_hover_value( "{$option_name}_text_size" ); $button_text_size_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_text_size" ); $button_text_size = isset( $button_text_size_values['desktop'] ) ? $button_text_size_values['desktop'] : ''; $button_text_size_tablet = isset( $button_text_size_values['tablet'] ) ? $button_text_size_values['tablet'] : ''; $button_text_size_phone = isset( $button_text_size_values['phone'] ) ? $button_text_size_values['phone'] : ''; // Button Text Color. $button_text_color_hover = $this->get_hover_value( "{$option_name}_text_color" ); $button_text_color_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_text_color" ); $button_text_color = isset( $button_text_color_values['desktop'] ) ? $button_text_color_values['desktop'] : ''; $button_text_color_tablet = isset( $button_text_color_values['tablet'] ) ? $button_text_color_values['tablet'] : ''; $button_text_color_phone = isset( $button_text_color_values['phone'] ) ? $button_text_color_values['phone'] : ''; // Button Border Width. $button_border_width_hover = $this->get_hover_value( "{$option_name}_border_width" ); $button_border_width_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_border_width" ); $button_border_width = isset( $button_border_width_values['desktop'] ) ? $button_border_width_values['desktop'] : ''; $button_border_width_tablet = isset( $button_border_width_values['tablet'] ) ? $button_border_width_values['tablet'] : ''; $button_border_width_phone = isset( $button_border_width_values['phone'] ) ? $button_border_width_values['phone'] : ''; // Button Border Color. $button_border_color_hover = $this->get_hover_value( "{$option_name}_border_color" ); $button_border_color_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_border_color" ); $button_border_color = isset( $button_border_color_values['desktop'] ) ? $button_border_color_values['desktop'] : ''; $button_border_color_tablet = isset( $button_border_color_values['tablet'] ) ? $button_border_color_values['tablet'] : ''; $button_border_color_phone = isset( $button_border_color_values['phone'] ) ? $button_border_color_values['phone'] : ''; // Button Border Radius. $button_border_radius_hover = $this->get_hover_value( "{$option_name}_border_radius" ); $button_border_radius_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_border_radius" ); $button_border_radius = isset( $button_border_radius_values['desktop'] ) ? $button_border_radius_values['desktop'] : ''; $button_border_radius_tablet = isset( $button_border_radius_values['tablet'] ) ? $button_border_radius_values['tablet'] : ''; $button_border_radius_phone = isset( $button_border_radius_values['phone'] ) ? $button_border_radius_values['phone'] : ''; // Button Font. $button_font_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_font" ); $button_font = isset( $button_font_values['desktop'] ) ? $button_font_values['desktop'] : ''; $button_font_tablet = isset( $button_font_values['tablet'] ) ? $button_font_values['tablet'] : ''; $button_font_phone = isset( $button_font_values['phone'] ) ? $button_font_values['phone'] : ''; // Button Letter Spacing. $button_letter_spacing_hover = $this->get_hover_value( "{$option_name}_letter_spacing" ); $button_letter_spacing_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_letter_spacing" ); $button_letter_spacing = isset( $button_letter_spacing_values['desktop'] ) ? $button_letter_spacing_values['desktop'] : ''; $button_letter_spacing_tablet = isset( $button_letter_spacing_values['tablet'] ) ? $button_letter_spacing_values['tablet'] : ''; $button_letter_spacing_phone = isset( $button_letter_spacing_values['phone'] ) ? $button_letter_spacing_values['phone'] : ''; // Button Icon Color. $button_icon_color_hover = et_pb_hover_options()->get_value( "{$option_name}_icon_color", $this->props ); $button_icon_color_sticky = $sticky->get_value( "{$option_name}_icon_color", $this->props ); $button_icon_color_values = et_pb_responsive_options()->get_property_values( $this->props, "{$option_name}_icon_color" ); $button_icon_color = isset( $button_icon_color_values['desktop'] ) ? $button_icon_color_values['desktop'] : ''; $button_icon_color_tablet = isset( $button_icon_color_values['tablet'] ) ? $button_icon_color_values['tablet'] : ''; $button_icon_color_phone = isset( $button_icon_color_values['phone'] ) ? $button_icon_color_values['phone'] : ''; $button_icon_pseudo_selector = 'left' === $button_icon_placement ? ':before' : ':after'; // Hide button settings. $hide_icon_setting = isset( $option_settings['hide_icon'] ) ? $option_settings['hide_icon'] : false; $hide_custom_padding_setting = isset( $option_settings['hide_custom_padding'] ) ? $option_settings['hide_custom_padding'] : false; // If module hides the button icon settings, no need to render button icon. So, we need // to the button_use_icon value as 'off'. if ( $hide_icon_setting ) { $button_use_icon = 'off'; } // Specific selector needs to be explicitly defined to make button alignment works. if ( isset( $option_settings['use_alignment'] ) && $option_settings['use_alignment'] && isset( $option_settings['css'] ) && isset( $option_settings['css']['alignment'] ) ) { $button_alignment_selector = $option_settings['css']['alignment']; // Button alignment. if ( '' !== $button_alignment_selector ) { // Get button alignment responsive status. $button_alignment_responsive_active = isset( $this->props[ "{$option_name}_alignment_last_edited" ] ) ? et_pb_get_responsive_status( $this->props[ "{$option_name}_alignment_last_edited" ] ) : false; // Print styles for each devices. foreach ( array( 'desktop', 'tablet', 'phone' ) as $device ) { $is_desktop = 'desktop' === $device; $button_key = ! $is_desktop ? "{$option_name}_alignment_{$device}" : "{$option_name}_alignment"; $button_alignment = $this->props[ "{$button_key}" ]; // For RTL the left allignment is 'force_left', however, the actual value for the 'text-align' property has to be 'left'. if ( 'force_left' === $button_alignment ) { $button_alignment = 'left'; } // Ensure button alignment value is not empty. if ( empty( $button_alignment ) ) { continue; } $button_alignment_data = array( 'selector' => $button_alignment_selector, 'declaration' => esc_html( "text-align: {$button_alignment};" ), ); if ( ! $is_desktop ) { // Skip tablet/phone if responsive setting is disabled. if ( ! $button_alignment_responsive_active ) { continue; } // Set media query for tablet/phone. $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $button_alignment_data['media_query'] = self::get_media_query( $current_media_query ); } self::set_style( $function_name, $button_alignment_data ); } } } if ( 'on' === $button_custom ) { // Default. $is_default_button_text_size = $this->_is_field_default( 'button_text_size', $button_text_size ); $is_default_button_icon_placement = $this->_is_field_default( 'button_icon_placement', $button_icon_placement ); $is_default_button_on_hover = $this->_is_field_default( 'button_on_hover', $button_on_hover ); $is_default_button_icon = $this->_is_field_default( 'button_icon', $button_icon ); $is_default_hover_placement = $is_default_button_on_hover && $is_default_button_icon_placement; // Processed values. $button_text_size_processed = $is_default_button_text_size ? '20px' : et_builder_process_range_value( $button_text_size ); $button_text_size_hover_processed = null !== $button_text_size_hover && $button_text_size !== $button_text_size_hover ? et_builder_process_range_value( $button_text_size_hover ) : ''; $button_border_radius_processed = '' !== $button_border_radius && 'px' !== $button_border_radius ? et_builder_process_range_value( $button_border_radius ) : ''; $button_border_radius_hover_processed = null !== $button_border_radius_hover && 'px' !== $button_border_radius_hover && $button_border_radius_hover !== $button_border_radius ? et_builder_process_range_value( $button_border_radius_hover ) : ''; $button_use_icon = '' === $button_use_icon ? 'on' : $button_use_icon; $is_sticky_module_without_wrapper = $has_wrapper ? false : $this->is_sticky_module; $css_element = ! empty( $option_settings['css']['main'] ) ? $option_settings['css']['main'] : $this->main_css_element . ' .et_pb_button'; $css_element_processed = $css_element; $is_dbp = et_builder_has_limitation( 'use_limited_main' ); if ( $is_dbp && ! empty( $option_settings['css']['limited_main'] ) ) { $css_element_processed = $option_settings['css']['limited_main']; } elseif ( ! $is_dbp ) { // Explicitly add '.et_pb_section' to the selector so selector splitting during prefixing // does not incorrectly add third party classes before #et-boc. $css_element_processed = et_()->append_prepend_comma_separated_selectors( $css_element, 'body #page-container .et_pb_section', 'prefix' ); } if ( et_builder_has_limitation( 'force_use_global_important' ) ) { $button_border_radius_processed .= '' !== $button_border_radius_processed ? ' !important' : ''; $button_border_radius_hover_processed .= '' !== $button_border_radius_hover_processed ? ' !important' : ''; } $global_use_icon_value = et_builder_option( 'all_buttons_icon' ); $main_element_styles_padding_important = 'no' === $global_use_icon_value && 'off' !== $button_use_icon; // Check existing button custom padding on desktop before generating padding. // If current button has custom padding, we should not set default padding, // just leave it empty. $button_padding_name = 'et_pb_button' !== $function_name ? "{$option_name}_custom_padding" : 'custom_padding'; $button_padding_value = et_pb_responsive_options()->get_any_value( $this->props, $button_padding_name ); $button_padding_value = ! empty( $button_padding_value ) ? explode( '|', $button_padding_value ) : array(); $button_padding_right = self::$_->array_get( $button_padding_value, 1, '' ); $button_padding_left = self::$_->array_get( $button_padding_value, 3, '' ); $main_element_styles = sprintf( '%1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s', '' !== $button_text_color ? sprintf( 'color:%1$s !important;', $button_text_color ) : '', '' !== $button_border_width && 'px' !== $button_border_width ? sprintf( 'border-width:%1$s !important;', et_builder_process_range_value( $button_border_width ) ) : '', '' !== $button_border_color ? sprintf( 'border-color:%1$s;', $button_border_color ) : '', '' !== $button_border_radius_processed ? sprintf( 'border-radius:%1$s;', $button_border_radius_processed ) : '', '' !== $button_letter_spacing && 'px' !== $button_letter_spacing ? sprintf( 'letter-spacing:%1$s;', et_builder_process_range_value( $button_letter_spacing ) ) : '', // #5 ! $is_default_button_text_size ? sprintf( 'font-size:%1$s;', $button_text_size_processed ) : '', '' !== $button_font ? et_builder_set_element_font( $button_font, true ) : '', 'off' === $button_on_hover && empty( $button_padding_right ) ? sprintf( 'padding-right: %1$s%2$s;', 'left' === $button_icon_placement ? '0.7em' : '2em', $main_element_styles_padding_important ? ' !important' : '' ) : '', 'off' === $button_on_hover && empty( $button_padding_left ) ? sprintf( 'padding-left:%1$s%2$s;', 'left' === $button_icon_placement ? '2em' : '0.7em', $main_element_styles_padding_important ? ' !important' : '' ) : '' ); $el_style = array( 'selector' => $css_element_processed, 'declaration' => rtrim( $main_element_styles ), ); self::set_style( $function_name, $el_style ); // Check existing button custom padding on hover before generating padding on // hover. If current button has custom padding on hover, we should not set // default padding on hover, just leave it empty. $button_padding_hover_value = et_pb_hover_options()->get_value( $button_padding_name, $this->props, '' ); $button_padding_hover_value = ! empty( $button_padding_hover_value ) ? explode( '|', $button_padding_hover_value ) : array(); $button_padding_hover_right = self::$_->array_get( $button_padding_hover_value, 1, '' ); $button_padding_hover_left = self::$_->array_get( $button_padding_hover_value, 3, '' ); $on_hover_padding_right = ! empty( $button_padding_hover_right ) ? '' : sprintf( 'padding-right: %1$s%2$s;', 'left' === $button_icon_placement ? '0.7em' : '2em', $main_element_styles_padding_important ? ' !important' : '' ); $on_hover_padding_left = ! empty( $button_padding_hover_left ) ? '' : sprintf( 'padding-left: %1$s%2$s;', 'left' === $button_icon_placement ? '2em' : '0.7em', $main_element_styles_padding_important ? ' !important' : '' ); // if button has default icon position or disabled globally and not enabled in module then no padding css should be generated. $on_hover_padding = $is_default_button_icon_placement || ( 'default' === $button_use_icon && 'no' === $global_use_icon_value ) ? '' : sprintf( '%1$s%2$s', $on_hover_padding_right, $on_hover_padding_left ); // Avoid adding useless style when value equals its default. $button_letter_spacing_hover = $this->_is_field_default( $hover->get_hover_field( 'button_letter_spacing' ), $button_letter_spacing_hover ) ? '' : $button_letter_spacing_hover; // Render hover and sticky style. $modes = array( 'hover', 'sticky' ); $modes_properties = array( 'color' => 'text_color', 'border-color' => 'border_color', 'border-radius' => 'border_radius', 'letter-spacing' => 'letter_spacing', 'on-hover' => 'on_hover', // hover only. 'font-size' => 'text_size', 'border-width' => 'border_width', ); foreach ( $modes as $mode ) { $helper = et_builder_get_helper( $mode ); $mode_styles_declaration = ''; foreach ( $modes_properties as $mode_attr => $mode_property ) { $mode_property_value = $helper->get_value( $option_name . '_' . $mode_property, $this->props, '' ); // Further evaluation and process; some property needs it. switch ( $mode_property ) { case 'letter_spacing': if ( 'px' === $mode_property_value ) { $mode_property_value = ''; } elseif ( '' !== $mode_property_value ) { $mode_property_value = et_builder_process_range_value( $mode_property_value ); } break; case 'on_hover': if ( 'hover' === $mode ) { $mode_property_value = 'off' === $button_on_hover || $hide_custom_padding_setting ? '' : $on_hover_padding; } else { $mode_property_value = ''; } break; } // Concatenate the style. if ( '' !== $mode_property_value ) { if ( 'on_hover' === $mode_property ) { $mode_styles_declaration .= $mode_property_value; } else { $mode_styles_declaration .= sprintf( '%1$s:%2$s !important;', esc_attr( $mode_attr ), esc_attr( $mode_property_value ) ); } } } // Trim unwanted whitespace from the output. $mode_styles_declaration = rtrim( $mode_styles_declaration ); if ( '' !== $mode_styles_declaration ) { switch ( $mode ) { case 'sticky': $mode_selector = $helper->add_sticky_to_order_class( $css_element_processed, $is_sticky_module_without_wrapper ); break; case 'hover': $mode_selector = $helper->add_hover_to_selectors( $css_element_processed ); break; default: $mode_selector = false; break; } if ( ! $mode_selector ) { continue; } $el_style = array( 'selector' => $mode_selector, 'declaration' => $mode_styles_declaration, ); self::set_style( $function_name, $el_style ); } } $main_element_styles_after_hover = ''; $main_element_styles_after_sticky = ''; if ( 'off' === $button_use_icon ) { $main_element_styles_after = 'display:none !important;'; $before_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':before', 'suffix', false ); $after_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':after', 'suffix', false ); $selector = sprintf( '%1$s, %2$s', $before_selector, $after_selector ); $no_icon_styles = ''; // Check button custom padding. Prepend option name to get the correct padding. $custom_padding = self::$_->array_get( $this->props, 'custom_padding', '' ); if ( 'et_pb_button' !== $function_name ) { $custom_padding = self::$_->array_get( $this->props, "{$option_name}_custom_padding", '' ); } if ( empty( $custom_padding ) ) { $no_icon_styles .= 'padding: 0.3em 1em !important;'; } else { $padding_array = explode( '|', $custom_padding ); if ( empty( $padding_array[1] ) ) { $no_icon_styles .= 'padding-right: 1em !important;'; } if ( empty( $padding_array[3] ) ) { $no_icon_styles .= 'padding-left: 1em !important;'; } } // No need to print custom padding if custom padding setting is disabled. if ( ! empty( $no_icon_styles ) && ! $hide_custom_padding_setting ) { $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $el_style = array( 'selector' => sprintf( '%1$s, %2$s', $css_element_processed, $hover_selector ), 'declaration' => rtrim( $no_icon_styles ), ); self::set_style( $function_name, $el_style ); } } else { $button_icon_code = '' !== $button_icon ? str_replace( ';', '', str_replace( '&#x', '', html_entity_decode( et_pb_process_font_icon( $button_icon ) ) ) ) : ''; $main_element_styles_after = sprintf( '%1$s %2$s %3$s %4$s %5$s %6$s %7$s', '' !== $button_icon_color ? sprintf( 'color:%1$s;', $button_icon_color ) : '', '' !== $button_icon_code ? 'line-height: inherit;' : '', '' !== $button_icon_code ? 'font-size: inherit !important;' : '', $is_default_hover_placement ? '' : sprintf( 'opacity:%1$s;', 'off' !== $button_on_hover ? '0' : '1' ), 'off' !== $button_on_hover && '' !== $button_icon_code ? sprintf( 'margin-left: %1$s; %2$s: auto;', 'left' === $button_icon_placement ? '-1.3em' : '-1em', 'left' === $button_icon_placement ? 'right' : 'left' ) : '', 'off' === $button_on_hover ? sprintf( 'margin-left: %1$s; %2$s:auto;', 'left' === $button_icon_placement ? '-1.3em' : '.3em', 'left' === $button_icon_placement ? 'right' : 'left' ) : '', ( ! $is_default_button_icon_placement && in_array( $button_use_icon, array( 'default', 'on' ), true ) ? 'display: inline-block;' : '' ) ); if ( ! empty( $button_icon_color_hover ) && $button_icon_color_hover !== $button_icon_color ) { $main_element_styles_after_hover = sprintf( 'color: %1$s', esc_html( $button_icon_color_hover ) ); } if ( ! empty( $button_icon_color_sticky ) && $button_icon_color_sticky !== $button_icon_color ) { $main_element_styles_after_sticky = sprintf( 'color: %1$s', esc_html( $button_icon_color_sticky ) ); } // Reverse icon position. if ( 'left' === $button_icon_placement ) { $button_icon_left_content = '' !== $button_icon_code ? 'content: attr(data-icon);' : ''; $after_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':after', 'suffix', false ); $el_style = array( 'selector' => $after_selector, 'declaration' => 'display: none;', ); self::set_style( $function_name, $el_style ); if ( et_builder_has_limitation( 'use_additional_limiting_styles' ) ) { $prefixed_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, '.et_pb_row', 'prefix', true ); $prefixed_hover_selector = et_()->append_prepend_comma_separated_selectors( $prefixed_selector, ':hover', 'suffix', false ); $el_style = array( 'selector' => $prefixed_hover_selector, 'declaration' => 'padding-right: 1em; padding-left: 2em;', ); self::set_style( $function_name, $el_style ); } $button_icon_left_content .= ' font-family: ' . et_pb_get_icon_font_family( $button_icon ) . ' !important;'; $button_icon_left_content .= ' font-weight: ' . et_pb_get_icon_font_weight( $button_icon ) . ' !important;'; $before_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':before', 'suffix', false ); $el_style = array( 'selector' => $before_selector, 'declaration' => $button_icon_left_content, ); self::set_style( $function_name, $el_style ); } // if button has default icon/hover/placement and disabled globally or not enabled in module then no :after:hover css should be generated. if ( ! ( $is_default_button_icon && $is_default_hover_placement ) && ( 'default' !== $button_use_icon || 'no' !== $global_use_icon_value ) ) { $hover_after_styles = sprintf( '%1$s %2$s %3$s', '' !== $button_icon_code ? sprintf( 'margin-left:%1$s;', '35' !== $button_icon_code ? '.3em' : '0' ) : '', '' !== $button_icon_code ? sprintf( '%1$s: auto; margin-left: %2$s;', 'left' === $button_icon_placement ? 'right' : 'left', 'left' === $button_icon_placement ? '-1.3em' : '.3em' ) : '', 'off' !== $button_on_hover ? 'opacity: 1;' : '' ); $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $hover_btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $hover_selector, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $hover_btn_icon_pseudo_selector, 'declaration' => rtrim( $hover_after_styles ), ); self::set_style( $function_name, $el_style ); } if ( '' === $button_icon && ! $is_default_button_text_size ) { $default_icons_size = '1.6em'; $custom_icon_size = $button_text_size_processed; $btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $btn_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $default_icons_size ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => 'body.et_button_custom_icon #page-container ' . $css_element . $button_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $custom_icon_size ), ); self::set_style( $function_name, $el_style ); } if ( '' === $button_icon && '' !== $button_text_size_hover_processed ) { $default_icons_size = '1.6em'; $custom_icon_size = $button_text_size_hover_processed; $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $hover_btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $hover_selector, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $hover_btn_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $default_icons_size ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => 'body.et_button_custom_icon #page-container ' . $css_element . ':hover' . $button_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $custom_icon_size ), ); self::set_style( $function_name, $el_style ); } $selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_icon_pseudo_selector, 'suffix', false ); } if ( $button_icon ) { $main_element_styles_after .= ' font-family: ' . et_pb_get_icon_font_family( $button_icon ) . ' !important;'; $main_element_styles_after .= ' font-weight: ' . et_pb_get_icon_font_weight( $button_icon ) . ' !important;'; } $el_style = array( 'selector' => $selector, 'declaration' => rtrim( $main_element_styles_after ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => et_pb_hover_options()->add_hover_to_selectors( $selector ), 'declaration' => rtrim( $main_element_styles_after_hover ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( $selector, $is_sticky_module_without_wrapper ), 'declaration' => rtrim( $main_element_styles_after_sticky ), ); self::set_style( $function_name, $el_style ); // Responsive Button Styles. $prev_icon = $button_icon; foreach ( array( 'tablet', 'phone' ) as $device ) { $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $current_text_size = 'tablet' === $device ? $button_text_size_tablet : $button_text_size_phone; $current_text_size = '' !== $current_text_size ? et_builder_process_range_value( $current_text_size ) : ''; $current_text_color = 'tablet' === $device ? $button_text_color_tablet : $button_text_color_phone; $current_border_width = 'tablet' === $device ? $button_border_width_tablet : $button_border_width_phone; $current_border_width = '' !== $current_border_width ? et_builder_process_range_value( $current_border_width ) : ''; $current_border_color = 'tablet' === $device ? $button_border_color_tablet : $button_border_color_phone; $current_border_radius = 'tablet' === $device ? $button_border_radius_tablet : $button_border_radius_phone; $current_border_radius = '' !== $current_border_radius ? et_builder_process_range_value( $current_border_radius ) : ''; $current_letter_spacing = 'tablet' === $device ? $button_letter_spacing_tablet : $button_letter_spacing_phone; $current_letter_spacing = '' !== $current_letter_spacing ? et_builder_process_range_value( $current_letter_spacing ) : ''; $current_font = 'tablet' === $device ? $button_font_tablet : $button_font_phone; $current_icon_color = 'tablet' === $device ? $button_icon_color_tablet : $button_icon_color_phone; // The attributes below should inherit larger device. $current_icon = et_pb_responsive_options()->get_property_value( $this->props, "{$option_name}_icon", $button_icon, $device, true ); $current_icon_placement = et_pb_responsive_options()->get_property_value( $this->props, "{$option_name}_icon_placement", $button_icon_placement, $device, true ); $current_on_hover = et_pb_responsive_options()->get_property_value( $this->props, "{$option_name}_on_hover", $button_on_hover, $device, true ); $is_default_hover_placement = '' === $current_on_hover && '' === $current_icon_placement; $button_icon_pseudo_selector = 'left' === $current_icon_placement ? ':before' : ':after'; // Force to have important tag. if ( et_builder_has_limitation( 'force_use_global_important' ) ) { $current_border_radius .= '' !== $current_border_radius ? ' !important' : ''; } // Get right and left custom padding value. The reset padding should not // be applied if current button has custom padding defined. $current_padding_name = et_pb_responsive_options()->get_field_name( $button_padding_name, $device ); $current_padding_value = et_pb_responsive_options()->get_any_value( $this->props, $current_padding_name, '', true ); $current_padding_value = ! empty( $current_padding_value ) ? explode( '|', $current_padding_value ) : array(); $current_padding_default = et_pb_responsive_options()->get_default_value( $this->props, $current_padding_name ); $current_padding_default = ! empty( $current_padding_default ) ? explode( '|', $current_padding_default ) : array(); $current_padding_right = self::$_->array_get( $current_padding_value, 1, '' ); $current_padding_left = self::$_->array_get( $current_padding_value, 3, '' ); $current_padding_default_right = self::$_->array_get( $current_padding_default, 1, '' ); $current_padding_default_left = self::$_->array_get( $current_padding_default, 3, '' ); // Reset responsive padding right. Only reset padding if current device // doesn't have value and the previous device has value to be reset. $responsive_padding_right = ''; $responsive_hover_padding_right = ''; if ( empty( $current_padding_right ) && ! empty( $current_padding_default_right ) ) { // Default padding for normal and hover. $responsive_padding_right = '1em'; // If padding hover right deosn't exist, add default padding for hover. if ( empty( $button_padding_hover_right ) ) { $responsive_hover_padding_right = 'left' === $current_icon_placement ? '2em' : '0.7em'; } // If icon on hover is disabled, set padding value like hover state // and remove padding for hover because it's same. if ( 'off' === $current_on_hover ) { $responsive_padding_right = 'left' === $current_icon_placement ? '2em' : '0.7em'; $responsive_hover_padding_right = ''; } } // Reset responsive padding left. Only reset padding if current device // doesn't have value and the previous device has value to be reset. $responsive_padding_left = ''; $responsive_hover_padding_left = ''; if ( empty( $current_padding_left ) && ! empty( $current_padding_default_left ) ) { // Default padding for normal and hover. $responsive_padding_left = '1em'; // If padding hover left deosn't exist, add default padding for hover. if ( empty( $button_padding_hover_left ) ) { $responsive_hover_padding_left = 'left' === $current_icon_placement ? '2em' : '0.7em'; } // If icon on hover is disabled, set padding value like hover state // and remove padding for hover because it's same. if ( 'off' === $current_on_hover ) { $responsive_padding_left = 'left' === $current_icon_placement ? '0.7em' : '2em'; $responsive_hover_padding_left = ''; } } // Remove responsive on hover padding left & right. if ( '' === $current_icon_placement || ( 'default' === $button_use_icon && 'no' === $global_use_icon_value ) || $hide_custom_padding_setting ) { $responsive_hover_padding_left = ''; $responsive_hover_padding_right = ''; } // Responsive button declaration. $responsive_button_declaration = trim( sprintf( '%1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s', '' !== $current_text_size ? sprintf( 'font-size:%1$s !important;', $current_text_size ) : '', '' !== $current_letter_spacing ? sprintf( 'letter-spacing:%1$s;', $current_letter_spacing ) : '', '' !== $current_text_color ? sprintf( 'color:%1$s !important;', $current_text_color ) : '', '' !== $current_border_width ? sprintf( 'border-width:%1$s !important;', $current_border_width ) : '', '' !== $current_border_color ? sprintf( 'border-color:%1$s;', $current_border_color ) : '', // #5 '' !== $current_border_radius ? sprintf( 'border-radius:%1$s;', $current_border_radius ) : '', '' !== $current_font ? et_builder_set_element_font( $current_font, true ) : '', '' !== $responsive_padding_right ? sprintf( 'padding-right: %1$s%2$s;', $responsive_padding_right, $main_element_styles_padding_important ? ' !important' : '' ) : '', '' !== $responsive_padding_left ? sprintf( 'padding-left: %1$s%2$s;', $responsive_padding_left, $main_element_styles_padding_important ? ' !important' : '' ) : '' ) ); if ( ! empty( $responsive_button_declaration ) ) { $el_style = array( 'selector' => $css_element_processed, 'declaration' => $responsive_button_declaration, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // Responsive button hover declaration. $responsive_button_hover_declaration = trim( sprintf( '%1$s %2$s', '' !== $responsive_hover_padding_right ? sprintf( 'padding-right: %1$s%2$s;', $responsive_hover_padding_right, $main_element_styles_padding_important ? ' !important' : '' ) : '', '' !== $responsive_hover_padding_left ? sprintf( 'padding-left: %1$s%2$s;', $responsive_hover_padding_left, $main_element_styles_padding_important ? ' !important' : '' ) : '' ) ); // Responsive button hover styles. if ( ! empty( $responsive_button_hover_declaration ) ) { $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $el_style = array( 'selector' => $hover_selector, 'declaration' => $responsive_button_hover_declaration, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // Responsive button after styles. if ( 'off' !== $button_use_icon ) { // Button Icon Code. $current_icon_code = '' !== $current_icon ? str_replace( ';', '', str_replace( '&#x', '', html_entity_decode( et_pb_process_font_icon( $current_icon ) ) ) ) : ''; // 1. Set button color, line-height, font-size, and icon placement. $responsive_button_after_declaration = trim( sprintf( '%1$s %2$s %3$s %4$s %5$s %6$s %7$s', '' !== $current_icon_color ? sprintf( 'color:%1$s;', $current_icon_color ) : '', '' !== $current_icon_code ? 'line-height: inherit;' : '', '' !== $current_icon_code ? 'font-size: inherit !important;' : '', 'off' !== $current_on_hover && '' !== $current_icon_code ? sprintf( 'margin-left: %1$s; %2$s: auto;', 'left' === $current_icon_placement ? '-1.3em' : '-1em', 'left' === $current_icon_placement ? 'right' : 'left' ) : '', 'off' === $current_on_hover ? sprintf( 'margin-left: %1$s; %2$s: auto;', 'left' === $current_icon_placement ? '-1.3em' : '.3em', 'left' === $current_icon_placement ? 'right' : 'left' ) : '', // #5 '' !== $current_icon_placement && in_array( $button_use_icon, array( 'default', 'on' ), true ) ? 'display: inline-block;' : '', 'off' !== $current_on_hover ? 'opacity: 0;' : 'opacity: 1;' ) ); if ( ! empty( $responsive_button_after_declaration ) ) { $btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $btn_icon_pseudo_selector, 'declaration' => $responsive_button_after_declaration, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // 2. DONE - Set custom icon and icon placement. if ( '' !== $current_icon_code ) { if ( 'tablet' === $device ) { $button_icon_suffix = $prev_icon !== $current_icon ? "-{$device}" : ''; } elseif ( ! empty( $prev_device ) ) { if ( $prev_icon === $button_icon && $prev_icon === $current_icon ) { $button_icon_suffix = ''; } else { $button_icon_suffix = $prev_icon !== $current_icon ? "-{$device}" : "-{$prev_device }"; } } $button_icon_content = "content: attr(data-icon{$button_icon_suffix});"; $button_side_hide = ':before'; $button_side_display = ':after'; // Reverse icon position. if ( 'left' === $current_icon_placement ) { $button_side_hide = ':after'; $button_side_display = ':before'; } $btn_side_hide_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_side_hide, 'suffix', false ); $el_style = array( 'selector' => $btn_side_hide_selector, 'declaration' => 'display: none;', 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); $btn_side_display_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_side_display, 'suffix', false ); $button_icon_content .= ' font-family: ' . et_pb_get_icon_font_family( $current_icon ) . ' !important;'; $button_icon_content .= ' font-weight: ' . et_pb_get_icon_font_weight( $current_icon ) . ' !important;'; $el_style = array( 'selector' => $btn_side_display_selector, 'declaration' => $button_icon_content, 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // 3. If button has default icon/hover/placement and disabled globally or not enabled in module then // no :after:hover css should be generated. if ( ! ( '' === $current_icon && $is_default_hover_placement ) && ( 'default' !== $button_use_icon || 'no' !== $global_use_icon_value ) ) { $hover_after_styles = sprintf( '%1$s %2$s %3$s', '' !== $current_icon_code ? sprintf( 'margin-left:%1$s;', '35' !== $current_icon_code ? '.3em' : '0' ) : '', '' !== $current_icon_code ? sprintf( '%1$s: auto; margin-left: %2$s;', 'left' === $current_icon_placement ? 'right' : 'left', 'left' === $current_icon_placement ? '-1.3em' : '.3em' ) : '', 'off' !== $current_on_hover ? 'opacity: 1;' : '' ); $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $hover_btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $hover_selector, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $hover_btn_icon_pseudo_selector, 'declaration' => rtrim( $hover_after_styles ), 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // Set button icon font size for default. if ( '' === $current_icon && '' !== $current_text_size ) { $default_icons_size = '1.6em'; $custom_icon_size = $current_text_size; $btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $btn_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $default_icons_size ), 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => 'body.et_button_custom_icon #page-container ' . $css_element . $button_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $custom_icon_size ), 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } // Set button icon font size on hover for default. if ( '' === $current_icon && '' !== $button_icon && '' !== $button_text_size_hover_processed ) { $default_icons_size = '1.6em'; $custom_icon_size = $button_text_size_hover_processed; $hover_selector = et_()->append_prepend_comma_separated_selectors( $css_element_processed, ':hover', 'suffix', false ); $hover_btn_icon_pseudo_selector = et_()->append_prepend_comma_separated_selectors( $hover_selector, $button_icon_pseudo_selector, 'suffix', false ); $el_style = array( 'selector' => $hover_btn_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $default_icons_size ), 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); $el_style = array( 'selector' => 'body.et_button_custom_icon #page-container ' . $css_element . ':hover' . $button_icon_pseudo_selector, 'declaration' => sprintf( 'font-size:%1$s;', $custom_icon_size ), 'media_query' => self::get_media_query( $current_media_query ), ); self::set_style( $function_name, $el_style ); } } // Set flag. $prev_icon = $current_icon; $prev_device = $device; } // Button background base prop name. $base_prop_name = "{$option_name}_bg"; // Render button background style. Button background uses different prop name for // activating gradient and activating hover + sticky mode thuse some aliases needs // to be defined. et_pb_background_options()->get_background_style( array( 'base_prop_name' => $base_prop_name, 'props' => $this->props, 'selector' => $css_element_processed, 'selector_sticky' => $sticky->add_sticky_to_order_class( $css_element_processed, $is_sticky_module_without_wrapper ), 'function_name' => $function_name, 'important' => et_()->array_get( $option_settings, 'css.important', false ) ? ' !important' : '', 'use_background_video' => false, 'use_background_pattern' => false, 'use_background_mask' => false, 'prop_name_aliases' => array( "use_{$base_prop_name}_color_gradient" => "{$base_prop_name}_use_color_gradient", "{$base_prop_name}" => "{$base_prop_name}_color", ), ) ); } } } /** * Process form field options into correct CSS styles. * * Fields will be processed here (name, mode, custom selector): * * - Background Color -> Hover -> Form field selector * - Background Focus Color -> Hover -> Form field focus selector * - Text Color -> Hover -> Form field & placeholder selector * - Text Focus Color -> Hover -> Form field & placeholder focus selector * * @since 3.23 * @since 4.6.0 Add sticky style support * * @param string $function_name Module slug. */ public function process_advanced_form_field_options( $function_name ) { // Disable if module doesn't set advanced_fields property and has no VB support. if ( ! $this->has_advanced_fields ) { return; } // Ensure form field exist on advanced fields. if ( ! self::$_->array_get( $this->advanced_fields, 'form_field', false ) ) { return; } // Helpers. $sticky = et_pb_sticky_options(); // Fetch every single form field instances. foreach ( $this->advanced_fields['form_field'] as $option_name => $option_settings ) { // 1.a. Build main element selector. $element_selector = ! empty( $option_settings['css']['main'] ) ? $option_settings['css']['main'] : "{$this->main_css_element} .input"; if ( et_builder_has_limitation( 'use_limited_main' ) && ! empty( $option_settings['css']['limited_main'] ) ) { $element_selector = $option_settings['css']['limited_main']; } // 1.b. Build pseudo element selector. $element_hover_selector = ! empty( $option_settings['css']['hover'] ) ? $option_settings['css']['hover'] : "{$element_selector}:hover"; $element_focus_selector = ! empty( $option_settings['css']['focus'] ) ? $option_settings['css']['focus'] : "{$element_selector}:focus"; $element_focus_hover_selector = ! empty( $option_settings['css']['focus_hover'] ) ? $option_settings['css']['focus_hover'] : "{$element_selector}:focus:hover"; $element_sticky_selector = $sticky->add_sticky_to_order_class( $element_selector, $this->is_sticky_module ); $element_focus_sticky_selector = $sticky->add_sticky_to_order_class( $element_focus_selector, $this->is_sticky_module ); // 1.c. Build custom form field selector. $bg_color_selector = ! empty( $option_settings['css']['background_color'] ) ? $option_settings['css']['background_color'] : $element_selector; $alternating_bg_color_selector = ! empty( $option_settings['css']['alternating_background_color'] ) ? $option_settings['css']['alternating_background_color'] : $element_selector; $bg_color_hover_selector = ! empty( $option_settings['css']['background_color_hover'] ) ? $option_settings['css']['background_color_hover'] : $element_hover_selector; $alternating_bg_color_hover_selector = ! empty( $option_settings['css']['alternating_background_color_hover'] ) ? $option_settings['css']['alternating_background_color_hover'] : $element_hover_selector; $bg_color_focus_selector = ! empty( $option_settings['css']['focus_background_color'] ) ? $option_settings['css']['focus_background_color'] : $element_focus_selector; $bg_color_focus_hover_selector = ! empty( $option_settings['css']['focus_background_color_hover'] ) ? $option_settings['css']['focus_background_color_hover'] : $element_focus_hover_selector; $text_color_selector = ! empty( $option_settings['css']['form_text_color'] ) ? $option_settings['css']['form_text_color'] : $element_selector; $text_color_hover_selector = ! empty( $option_settings['css']['form_text_color_hover'] ) ? $option_settings['css']['form_text_color_hover'] : $element_hover_selector; $text_color_focus_selector = ! empty( $option_settings['css']['focus_text_color'] ) ? $option_settings['css']['focus_text_color'] : $element_focus_selector; $text_color_focus_hover_selector = ! empty( $option_settings['css']['focus_text_color_hover'] ) ? $option_settings['css']['focus_text_color_hover'] : $element_focus_hover_selector; $placeholder_option = self::$_->array_get( $option_settings, 'placeholder', true ); $base_selector = false !== strpos( $element_selector, ',' ) ? "{$this->main_css_element} .input" : $element_selector; $placeholder_selector = ! empty( $option_settings['css']['placeholder'] ) ? $option_settings['css']['placeholder'] : "{$base_selector}::placeholder, {$base_selector}::-webkit-input-placeholder, {$base_selector}::-moz-placeholder, {$base_selector}::-ms-input-placeholder"; $placeholder_hover_selector = ! empty( $option_settings['css']['placeholder_hover'] ) ? $option_settings['css']['placeholder_hover'] : "{$base_selector}:hover::placeholder, {$base_selector}:hover::-webkit-input-placeholder, {$base_selector}:hover::-moz-placeholder, {$base_selector}:hover::-ms-input-placeholder"; $placeholder_focus_selector = ! empty( $option_settings['css']['placeholder_focus'] ) ? $option_settings['css']['placeholder_focus'] : "{$base_selector}:focus::placeholder, {$base_selector}:focus::-webkit-input-placeholder, {$base_selector}:focus::-moz-placeholder, {$base_selector}:focus::-ms-input-placeholder"; $placeholder_focus_hover_selector = ! empty( $option_settings['css']['placeholder_focus_hover'] ) ? $option_settings['css']['placeholder_focus_hover'] : "{$base_selector}:focus:hover::placeholder, {$base_selector}:focus:hover::-webkit-input-placeholder, {$base_selector}:focus:hover::-moz-placeholder, {$base_selector}:focus:hover::-ms-input-placeholder"; $placeholder_sticky_selector = $sticky->add_sticky_to_order_class( $placeholder_selector, $this->is_sticky_module ); $placeholder_focus_sticky_selector = $sticky->add_sticky_to_order_class( $placeholder_focus_selector, $this->is_sticky_module ); // 2. Default important status. $force_global_important = et_builder_has_limitation( 'force_use_global_important' ); $important_list = isset( $option_settings['css']['important'] ) ? $option_settings['css']['important'] : array(); // 3.a.1. Field Background Color. $is_field_bg_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$option_name}_background_color" ); $field_bg_color_values = array( 'desktop' => esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_background_color" ) ), 'tablet' => $is_field_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_background_color_tablet" ) ) : '', 'phone' => $is_field_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_background_color_phone" ) ) : '', ); $field_bg_color_important = $force_global_important || in_array( 'background_color', $important_list, true ) ? ' !important;' : ''; et_pb_responsive_options()->generate_responsive_css( $field_bg_color_values, $bg_color_selector, 'background-color', $function_name, $field_bg_color_important, 'color' ); // 3.a.1. Field Alternating Background Color. $is_field_alternating_bg_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$option_name}_alternating_background_color" ); $field_alternating_bg_color_values = array( 'desktop' => esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_alternating_background_color" ) ), 'tablet' => $is_field_alternating_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_alternating_background_color_tablet" ) ) : '', 'phone' => $is_field_alternating_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_alternating_background_color_phone" ) ) : '', ); $field_alternating_bg_color_important = $force_global_important || in_array( 'alternating_background_color', $important_list, true ) ? ' !important;' : ''; et_pb_responsive_options()->generate_responsive_css( $field_alternating_bg_color_values, $alternating_bg_color_selector, 'background-color', $function_name, $field_alternating_bg_color_important, 'color' ); // 3.a.2. Field Background Hover Color. $field_bg_color_hover = $this->get_hover_value( "{$option_name}_background_color" ); if ( ! empty( $field_bg_color_hover ) ) { $el_style = array( 'selector' => $bg_color_hover_selector, 'declaration' => sprintf( 'background-color:%1$s%2$s;', $field_bg_color_hover, $field_bg_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.a.2. Field Background Hover Color. $field_alternating_bg_color_hover = $this->get_hover_value( "{$option_name}_alternating_background_color" ); if ( ! empty( $field_alternating_bg_color_hover ) ) { $el_style = array( 'selector' => $alternating_bg_color_hover_selector, 'declaration' => sprintf( 'background-color:%1$s%2$s;', $field_alternating_bg_color_hover, $field_alternating_bg_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.a.3. Field Background Sticky Color $field_bg_color_sticky = $sticky->get_value( "{$option_name}_background_color", $this->props ); if ( ! empty( $field_bg_color_sticky ) ) { $el_style = array( 'selector' => $element_sticky_selector, 'declaration' => sprintf( 'background-color:%1$s%2$s;', $field_bg_color_sticky, $field_bg_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.b.1. Field Focus Background Color. $is_field_focus_bg_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$option_name}_focus_background_color" ); $field_focus_bg_color_values = array( 'desktop' => esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_background_color" ) ), 'tablet' => $is_field_focus_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_background_color_tablet" ) ) : '', 'phone' => $is_field_focus_bg_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_background_color_phone" ) ) : '', ); $field_focus_bg_color_important = $force_global_important || in_array( 'focus_background_color', $important_list, true ) ? ' !important;' : ''; et_pb_responsive_options()->generate_responsive_css( $field_focus_bg_color_values, $bg_color_focus_selector, 'background-color', $function_name, $field_focus_bg_color_important, 'color' ); // 3.b.2. Field Focus Background Hover Color. $field_focus_bg_color_hover = $this->get_hover_value( "{$option_name}_focus_background_color" ); if ( ! empty( $field_focus_bg_color_hover ) ) { $el_style = array( 'selector' => $bg_color_focus_hover_selector, 'declaration' => sprintf( 'background-color:%1$s%2$s;', $field_focus_bg_color_hover, $field_focus_bg_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.b.3. Field Focus Background Sticky Color $field_focus_bg_color_sticky = $sticky->get_value( "{$option_name}_focus_background_color", $this->props ); if ( ! empty( $field_focus_bg_color_sticky ) ) { $el_style = array( 'selector' => $element_focus_sticky_selector, 'declaration' => sprintf( 'background-color:%1$s%2$s;', $field_focus_bg_color_sticky, $field_focus_bg_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.c.1. Field Text Color. $is_field_text_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$option_name}_text_color" ); $field_text_color_values = array( 'desktop' => esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_text_color" ) ), 'tablet' => $is_field_text_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_text_color_tablet" ) ) : '', 'phone' => $is_field_text_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_text_color_phone" ) ) : '', ); $field_text_color_important = in_array( 'form_text_color', $important_list, true ) ? ' !important;' : ''; $text_color_selector = $placeholder_option ? "{$text_color_selector}, {$placeholder_selector}" : $text_color_selector; et_pb_responsive_options()->generate_responsive_css( $field_text_color_values, $text_color_selector, 'color', $function_name, $field_text_color_important, 'color' ); // 3.c.2. Field Text Hover Color. $field_text_color_hover = $this->get_hover_value( "{$option_name}_text_color" ); if ( ! empty( $field_text_color_hover ) ) { $text_color_hover_selector = $placeholder_option ? "{$text_color_hover_selector}, {$placeholder_hover_selector}" : $text_color_hover_selector; $el_style = array( 'selector' => $text_color_hover_selector, 'declaration' => sprintf( 'color:%1$s%2$s;', $field_text_color_hover, $field_text_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.c.3. Field Text Sticky Color $field_text_color_sticky = $sticky->get_value( "{$option_name}_text_color", $this->props ); if ( ! empty( $field_text_color_sticky ) ) { $text_color_sticky_selector = $placeholder_option ? "{$element_sticky_selector}, {$placeholder_sticky_selector}" : $element_sticky_selector; $el_style = array( 'selector' => $text_color_sticky_selector, 'declaration' => sprintf( 'color:%1$s%2$s;', $field_text_color_sticky, $field_text_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.d.1. Field Focus Text Color. $is_field_focus_text_color_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$option_name}_focus_text_color" ); $field_focus_text_color_values = array( 'desktop' => esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_text_color" ) ), 'tablet' => $is_field_focus_text_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_text_color_tablet" ) ) : '', 'phone' => $is_field_focus_text_color_responsive ? esc_attr( et_pb_responsive_options()->get_any_value( $this->props, "{$option_name}_focus_text_color_phone" ) ) : '', ); $text_color_focus_selector = $placeholder_option ? "{$text_color_focus_selector}, {$placeholder_focus_selector}" : $text_color_focus_selector; $field_focus_text_color_important = in_array( 'form_text_color', $important_list, true ) ? ' !important;' : ''; et_pb_responsive_options()->generate_responsive_css( $field_focus_text_color_values, $text_color_focus_selector, 'color', $function_name, $field_focus_text_color_important, 'color' ); // 3.d.2. Field Focus Text Hover Color. $field_focus_text_color_hover = $this->get_hover_value( "{$option_name}_focus_text_color" ); if ( ! empty( $field_focus_text_color_hover ) ) { $text_color_focus_hover_selector = $placeholder_option ? "{$text_color_focus_hover_selector}, {$placeholder_focus_hover_selector}" : $text_color_focus_hover_selector; $el_style = array( 'selector' => $text_color_focus_hover_selector, 'declaration' => sprintf( 'color:%1$s%2$s;', $field_focus_text_color_hover, $field_focus_text_color_important ), ); self::set_style( $function_name, $el_style ); } // 3.d.3. Field Focus Text Sticky Color $field_focus_text_color_sticky = $sticky->get_value( "{$option_name}_focus_text_color", $this->props ); if ( ! empty( $field_focus_text_color_sticky ) ) { $text_color_focus_sticky_selector = $placeholder_option ? "{$element_focus_sticky_selector}, {$placeholder_focus_sticky_selector}" : $element_focus_sticky_selector; $el_style = array( 'selector' => $text_color_focus_sticky_selector, 'declaration' => sprintf( 'color:%1$s%2$s;', $field_focus_text_color_sticky, $field_focus_text_color_important ), ); self::set_style( $function_name, $el_style ); } } } /** * Apply free form CSS. * * @param string $function_name Module slug. * @param string $css_string CSS string. * * @return void */ public function apply_free_form_css( $function_name, $css_string ) { if ( '' === $css_string ) { return; } $final_css_string = $css_string; $selectors = '/selector|\.selector|#selector/'; $order_class_name = self::get_module_order_class( $function_name ); if ( preg_match( $selectors, $css_string ) ) { $order_class_name = apply_filters( 'et_builder_free_form_css_selectors', ".{$order_class_name}" ); $final_css_string = preg_replace( $selectors, $order_class_name, $css_string ); } if ( '' !== $final_css_string ) { // New lines are saved as || in CSS Custom settings, remove them. $final_css_string = preg_replace( '/(\|\|)/i', '', $final_css_string ); self::_set_free_form_style( $final_css_string ); do_action( 'et_builder_after_free_form_css_processed', $final_css_string, self::setup_advanced_styles_manager() ); } } /** * Process custom css fields into CSS style. * * @param string $function_name Module slug. * * @return bool */ public function process_custom_css_fields( $function_name ) { if ( empty( $this->custom_css_fields ) ) { return false; } // Helpers. $responsive = et_pb_responsive_options(); $hover = et_pb_hover_options(); $sticky = et_pb_sticky_options(); foreach ( $this->custom_css_fields as $slug => $option ) { $css = $this->props[ "custom_css_{$slug}" ]; $order_class = isset( $this->main_css_element ) && count( explode( ' ', $this->main_css_element ) ) === 1 ? $selector = $this->main_css_element : '%%order_class%%'; $selector = ! empty( $option['selector'] ) ? $option['selector'] : ''; if ( false === strpos( $selector, '%%order_class%%' ) ) { if ( ! ( isset( $option['no_space_before_selector'] ) && $option['no_space_before_selector'] ) && '' !== $selector ) { $selector = " {$selector}"; } $selector = "{$order_class}{$selector}"; } if ( $responsive->is_responsive_enabled( $this->props, "custom_css_{$slug}" ) ) { $responsive_values = $responsive->get_property_values( $this->props, "custom_css_{$slug}" ); // Desktop mode custom CSS. if ( '' !== $css ) { $el_style = array( 'selector' => $selector, 'declaration' => trim( $css ), 'media_query' => empty( $responsive_values['tablet'] ) ? null : self::get_media_query( 'min_width_981' ), ); self::set_style( $function_name, $el_style ); } // Tablet mode custom CSS. $tablet_css = $responsive->get_tablet_value( "custom_css_{$slug}", $this->props ); if ( ! empty( $tablet_css ) ) { $el_style = array( 'selector' => $selector, 'declaration' => trim( $tablet_css ), 'media_query' => empty( $responsive_values['phone'] ) ? self::get_media_query( 'max_width_980' ) : self::get_media_query( '768_980' ), ); self::set_style( $function_name, $el_style ); } // Phone mode custom CSS. $phone_css = $responsive->get_phone_value( "custom_css_{$slug}", $this->props ); if ( ! empty( $phone_css ) ) { $el_style = array( 'selector' => $selector, 'declaration' => trim( $phone_css ), 'media_query' => self::get_media_query( 'max_width_767' ), ); self::set_style( $function_name, $el_style ); } } else { // Non responsive mode custom CSS. if ( '' !== $css ) { if ( 'free_form' === $slug ) { $this->apply_free_form_css( $function_name, $css ); } else { $el_style = array( 'selector' => $selector, 'declaration' => trim( $css ), ); self::set_style( $function_name, $el_style ); } } } // Hover mode custom CSS. if ( $hover->is_enabled( "custom_css_{$slug}", $this->props ) ) { $hover_css = $hover->get_value( "custom_css_{$slug}", $this->props ); if ( ! empty( $hover_css ) ) { $el_style = array( 'selector' => $hover->add_hover_to_selectors( $selector ), 'declaration' => trim( $hover_css ), ); self::set_style( $function_name, $el_style ); } } // Sticky mode custom CSS. if ( $sticky->is_enabled( "custom_css_{$slug}", $this->props ) ) { $sticky_css = $sticky->get_value( "custom_css_{$slug}", $this->props ); if ( ! empty( $sticky_css ) ) { $el_style = array( 'selector' => $sticky->add_sticky_to_order_class( $selector, $this->is_sticky_module ), 'declaration' => trim( $sticky_css ), ); self::set_style( $function_name, $el_style ); } } } } /** * Process box shadow CSS styles. * * @since 3.23 Add responsive support. Pass device attributes and make sure no duplicate styles * are rendered. * @since 4.6.0 Add sticky style support. * * @param string $function_name Module slug. */ public function process_box_shadow( $function_name ) { /** * Get box shadow configuration. * * @var ET_Builder_Module_Field_BoxShadow $box_shadow */ $box_shadow = ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); $advanced_fields = self::$_->array_get( $this->advanced_fields, 'box_shadow', array( 'default' => array() ) ); $has_wrapper = et_()->array_get( $this->wrapper_settings, 'order_class_wrapper', false ); $is_sticky_module_without_wrapper = $has_wrapper ? false : $this->is_sticky_module; if ( ! $advanced_fields ) { return ''; } $is_enabled = $this->_features_manager->get( // Box shadow is enabled. 'bosh', function() use ( $box_shadow, $advanced_fields ) { if ( $box_shadow->is_used( $this->props ) ) { return true; } if ( $box_shadow->has_inset( $this->props, $advanced_fields, self::$_ ) ) { return true; } return false; } ); if ( ! $is_enabled ) { return; } // A module can have multiple advanced box shadow fields (i.e. default + button's box shadow) which are // generated by advanced button fields. foreach ( $advanced_fields as $option_name => $option_settings ) { // Enable module to explicitly disable box shadow fields (box shadow is automatically) // added to all module by default. if ( false === $option_settings ) { continue; } // Prepare attribute for getting box shadow's css declaration. $declaration_args = array( 'suffix' => 'default' === $option_name ? '' : "_{$option_name}", 'important' => self::$_->array_get( $option_settings, 'css.important', false ), ); // Enable module to conditionally print box shadow styling if particular attribute(s) have specific value. // This works in 'OR' logic. Once an attribute doesn't match the value, this box shadow styling is skipped. $show_if = self::$_->array_get( $option_settings, 'css.show_if', array() ); if ( ! empty( $show_if ) ) { $show_if_skip = false; foreach ( $show_if as $show_if_attr_name => $show_if_attr_value ) { $attr_value = self::$_->array_get( $this->props, $show_if_attr_name, '' ); // Skip printing this box shadow value once one of the attribute value doesn't // match with given value. if ( $attr_value !== $show_if_attr_value ) { $show_if_skip = true; break; } } if ( $show_if_skip ) { continue; } } // Enable module to conditionally print box shadow styling if particular attribute(s) doesn't have // specific value. This works on 'OR' logic. Once an attribute matches the supplied value, this // box shadow styling is skipped. $show_if_not = self::$_->array_get( $option_settings, 'css.show_if_not', array() ); if ( ! empty( $show_if_not ) ) { $show_if_not_skip = false; foreach ( $show_if_not as $show_if_not_attr_name => $show_if_not_attr_value ) { $attr_value = self::$_->array_get( $this->props, $show_if_not_attr_name, '' ); // Skip printing this box value once this attribute value matches the given value. if ( $attr_value === $show_if_not_attr_value ) { $show_if_not_skip = true; break; } } if ( $show_if_not_skip ) { continue; } } $overlay = self::$_->array_get( $option_settings, 'css.overlay', false ); $has_video_bg = ! empty( $atts['background_video_mp4'] ) || ! empty( $atts['background_video_webm'] ); $inset = $box_shadow->is_inset( $box_shadow->get_value( $this->props, $declaration_args ) ); $inset_hover = $box_shadow->is_inset( $box_shadow->get_value( $this->props, array_merge( $declaration_args, array( 'hover' => true ) ) ) ); $selector = self::$_->array_get( $option_settings, 'css.main', '%%order_class%%' ); // Default box shadow affects module while other affects group element it belongs to (ie image, button, etc). $hover_selector = 'default' === $option_name ? $this->add_hover_to_order_class( $selector ) : $this->add_hover_to_selectors( $selector ); // Custom box shadow hover selector. $custom_hover = self::$_->array_get( $option_settings, 'css.hover', '' ); if ( '' !== $custom_hover ) { $hover_selector = $custom_hover; } // Render box shadow styles for esponsive settings. $prev_declaration = ''; foreach ( et_pb_responsive_options()->get_modes() as $device ) { // Add device argument. $device_declaration_args = array_merge( $declaration_args, array( 'device' => $device ) ); // Get box-shadow styles. if ( ( $inset && 'inset' === $overlay ) || 'always' === $overlay || $has_video_bg ) { $box_shadow_style = $box_shadow->get_overlay_style( $function_name, $selector, $this->props, $device_declaration_args ); } else { $box_shadow_style = $box_shadow->get_style( $selector, $this->props, $device_declaration_args ); } // Compare current device declaration and previous declaration to avoid // duplicate rendered styles. Or don't render if current declaration is // empty string. $declaration = isset( $box_shadow_style['declaration'] ) ? $box_shadow_style['declaration'] : ''; if ( $prev_declaration === $declaration || empty( $declaration ) ) { continue; } $prev_declaration = $declaration; // Set media query for tablet and phone. if ( 'desktop' !== $device ) { $breakpoint = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $media_query = self::get_media_query( $breakpoint ); $box_shadow_style['media_query'] = $media_query; } self::set_style( $function_name, $box_shadow_style ); } if ( ( $inset_hover && 'inset' === $overlay ) || 'always' === $overlay || $has_video_bg ) { self::set_style( $function_name, $box_shadow->get_overlay_style( $function_name, $hover_selector, $this->props, array_merge( $declaration_args, array( 'hover' => true ) ) ) ); } else { self::set_style( $function_name, $box_shadow->get_style( $hover_selector, $this->props, array_merge( $declaration_args, array( 'hover' => true ) ) ) ); } // Sticky style. $sticky = et_pb_sticky_options(); if ( $this->is_sticky_module || $sticky->is_inside_sticky_module() ) { $inset_sticky = $box_shadow->is_inset( $box_shadow->get_value( $this->props, array_merge( $declaration_args, array( 'sticky' => true ) ) ) ); $sticky_selector = 'default' === $option_name ? $sticky->add_sticky_to_order_class( $selector, $is_sticky_module_without_wrapper ) : $sticky->add_sticky_to_selectors( $selector, $is_sticky_module_without_wrapper ); // Custom box shadow sticky selector. $custom_sticky = self::$_->array_get( $option_settings, 'css.sticky', '' ); if ( '' !== $custom_sticky ) { $sticky_selector = $custom_sticky; } if ( ( $inset_sticky && 'inset' === $overlay ) || 'always' === $overlay || $has_video_bg ) { self::set_style( $function_name, $box_shadow->get_overlay_style( $function_name, $sticky_selector, $this->props, array_merge( $declaration_args, array( 'sticky' => true ) ) ) ); } else { self::set_style( $function_name, $box_shadow->get_style( $sticky_selector, $this->props, array_merge( $declaration_args, array( 'sticky' => true ) ) ) ); } } } } /** * Make Advanced Fields and Custom CSS Fields filterable. */ public function make_options_filterable() { if ( isset( $this->advanced_fields ) ) { $this->advanced_fields = apply_filters( "{$this->slug}_advanced_fields", $this->advanced_fields, $this->slug, $this->main_css_element ); } if ( isset( $this->custom_css_fields ) ) { $this->custom_css_fields = apply_filters( "{$this->slug}_custom_css_fields", $this->custom_css_fields, $this->slug, $this->main_css_element ); } } /** * Disables wptexturize on the passed shortcode.. * * @param array $shortcodes An array of shortcode names. * * @return array */ public function disable_wptexturize( $shortcodes ) { $shortcodes[] = 'et_pb_code'; $shortcodes[] = 'et_pb_fullwidth_code'; return $shortcodes; } /** * Callback :: fix_wptexturized_scripts. * * @param array $matches Found matches. * * @return string|string[] */ public function fix_wptexturized_script( $matches ) { return str_replace( '&', '&', $matches[0] ); } /** * Fix wptexturize ampersand bug. * * @param string $content Content. * * @return string|string[]|null */ public function fix_wptexturized_scripts( $content ) { return preg_replace_callback( '/<script.*?>(.*?)<\/script>/mis', array( $this, 'fix_wptexturized_script' ), $content ); } /** * Callback :: Sort fields within tabs by priority. * * @param string $a Comparision field member. * @param string $b Comparision field string. * * @return int|lt */ public static function compare_by_priority( $a, $b ) { $a_priority = ! empty( $a['priority'] ) ? (int) $a['priority'] : self::DEFAULT_PRIORITY; $b_priority = ! empty( $b['priority'] ) ? (int) $b['priority'] : self::DEFAULT_PRIORITY; if ( isset( $a['_order_number'], $b['_order_number'] ) && ( $a_priority === $b_priority ) ) { return $a['_order_number'] - $b['_order_number']; } return $a_priority - $b_priority; } /** * Reorder toggles based on the priority with respect to manually ordered items with no priority * * @param array $toggles_array Toggles to reorder. * * @return array */ public static function et_pb_order_toggles_by_priority( $toggles_array ) { if ( empty( $toggles_array ) ) { return array(); } $high_priority_toggles = array(); $low_priority_toggles = array(); $manually_ordered_toggles = array(); // fill 3 arrays based on priority. foreach ( $toggles_array as $toggle_id => $toggle_data ) { if ( isset( $toggle_data['priority'] ) ) { if ( $toggle_data['priority'] < 10 ) { $high_priority_toggles[ $toggle_id ] = $toggle_data; } else { $low_priority_toggles[ $toggle_id ] = $toggle_data; } } else { // keep the original order of options without priority defined. $manually_ordered_toggles[ $toggle_id ] = $toggle_data; } } // order high and low priority toggles. uasort( $high_priority_toggles, array( 'ET_Builder_Element', 'compare_by_priority' ) ); uasort( $low_priority_toggles, array( 'ET_Builder_Element', 'compare_by_priority' ) ); // merge 3 arrays to get the correct order of toggles. return array_merge( $high_priority_toggles, $manually_ordered_toggles, $low_priority_toggles ); } /** * Callback :: Sort modules alphabetically by name. * * @param string $a Comparision member string. * @param string $b Comparision member string. * * @return int|lt */ public static function compare_by_name( $a, $b ) { return strcasecmp( $a->name, $b->name ); } /** * Get total modules count. * * @param string $post_type Post type. * * @return int */ public static function get_modules_count( $post_type ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $overall_count = count( $parent_modules ) + count( $child_modules ); return $overall_count; } /** * Get modules js array to use in backbone template. * * @param string $post_type Current post type. * * @return string */ public static function get_modules_js_array( $post_type ) { $modules = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { /** * Sort modules alphabetically by name. */ $sorted_modules = $parent_modules; uasort( $sorted_modules, array( 'ET_Builder_Element', 'compare_by_name' ) ); foreach ( $sorted_modules as $module ) { /** * Replace single and double quotes with %% and || respectively * to avoid js conflicts */ $module_name = str_replace( array( '"', '"', '"', '"' ), '%%', $module->name ); $module_name = str_replace( array( "'", ''', ''' ), '||', $module_name ); $modules[] = sprintf( '{ "title" : "%1$s", "label" : "%2$s"%3$s}', esc_js( $module_name ), esc_js( $module->slug ), ( isset( $module->fullwidth ) && $module->fullwidth ? ', "fullwidth_only" : "on"' : '' ) ); } } return '[' . implode( ',', $modules ) . ']'; } /** * Get all modules array. * * @param string $post_type Post type. * @param bool $include_child Whether to include childs. * * @return array */ public static function get_modules_array( $post_type = '', $include_child = false ) { $modules = array(); $module_icons = self::get_module_icons(); if ( ! empty( $post_type ) ) { $parent_modules = self::get_parent_modules( $post_type ); if ( $include_child ) { $parent_modules = array_merge( $parent_modules, self::get_child_modules( $post_type ) ); } if ( ! empty( $parent_modules ) ) { $sorted_modules = $parent_modules; } } else { $parent_modules = self::get_parent_modules(); if ( $include_child ) { $parent_modules = array_merge( $parent_modules, self::get_child_modules() ); } if ( ! empty( $parent_modules ) ) { $all_modules = array(); foreach ( $parent_modules as $post_type => $post_type_modules ) { foreach ( $post_type_modules as $module_slug => $module ) { $all_modules[ $module_slug ] = $module; } } $sorted_modules = $all_modules; } } if ( ! empty( $sorted_modules ) ) { /** * Sort modules alphabetically by name. */ uasort( $sorted_modules, array( 'ET_Builder_Element', 'compare_by_name' ) ); foreach ( $sorted_modules as $module ) { /** * Replace single and double quotes with %% and || respectively * to avoid js conflicts */ $module_name = str_replace( '"', '%%', $module->name ); $module_name = str_replace( "'", '||', $module_name ); $module_name_plural = str_replace( '"', '%%', empty( $module->plural ) ? $module->name : $module->plural ); $module_name_plural = str_replace( "'", '||', $module_name_plural ); $_module = array( 'title' => esc_attr( $module_name ), 'plural' => esc_attr( $module_name_plural ), 'label' => esc_attr( $module->slug ), 'is_parent' => 'child' === $module->type ? 'off' : 'on', 'is_official_module' => $module->_is_official_module, 'use_unique_id' => $module->_use_unique_id, 'vb_support' => isset( $module->vb_support ) ? $module->vb_support : 'off', 'folder_name' => isset( $module->folder_name ) ? $module->folder_name : '', ); if ( isset( $module->fullwidth ) && $module->fullwidth ) { $_module['fullwidth_only'] = 'on'; } // Get module icon character (font-icon). $icon = self::$_->array_get( $module_icons, "{$module->slug}.icon" ); if ( $icon ) { $_module['icon'] = $icon; } // Get module icon svg from fetched svg content. $icon_svg = self::$_->array_get( $module_icons, "{$module->slug}.icon_svg" ); if ( $icon_svg ) { $_module['icon_svg'] = $icon_svg; } $modules[] = $_module; } } return $modules; } /** * Get modules that does not support VB. * * @return array */ public static function get_fb_unsupported_modules() { $parent_modules = self::get_parent_modules(); $unsupported_modules_array = array(); foreach ( $parent_modules as $post_type => $post_type_modules ) { foreach ( $post_type_modules as $module_slug => $module ) { if ( ! isset( $module->vb_support ) || 'off' === $module->vb_support ) { $unsupported_modules_array[] = $module_slug; } } } return array_unique( $unsupported_modules_array ); } /** * Get list of modules that has rich content option * * @since 3.18 * * @return array */ public static function get_has_content_modules() { return self::$has_content_modules; } /** * Returns a regex pattern that includes all parent module slugs. * * @since 3.1 Renamed from `get_parent_shortcodes()` to `get_parent_slugs_regex()` * @since 1.0 * * @param string $post_type Post type. * * @return string */ public static function get_parent_slugs_regex( $post_type = 'page' ) { $slugs = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { foreach ( $parent_modules as $module ) { $slugs[] = $module->slug; } } return implode( '|', $slugs ); } /** * Returns a regex pattern that includes all child module slugs. * * @since 3.1 Renamed from `get_child_shortcodes()` to `get_child_slugs_regex()` * @since 1.0 * * @param string $post_type Post type. * * @return string */ public static function get_child_slugs_regex( $post_type = 'page' ) { $slugs = array(); $child_modules = self::get_child_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $slug => $module ) { if ( ! empty( $slug ) ) { $slugs[] = $slug; } } } return implode( '|', $slugs ); } /** * Get child module slugs. * * @param string $post_type Post type. * * @return array */ public static function get_child_slugs( $post_type ) { $child_slugs = array(); $child_modules = self::get_parent_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $module ) { if ( ! empty( $module->child_slug ) ) { $child_slugs[ $module->slug ] = $module->child_slug; } } } return $child_slugs; } /** * Get row content module slugs. e.x et_pb_code * * @param string $post_type Post type. * * @return string */ public static function get_raw_content_slugs( $post_type ) { $shortcodes = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { foreach ( $parent_modules as $module ) { if ( isset( $module->use_raw_content ) && $module->use_raw_content ) { $shortcodes[] = $module->slug; } } } $child_modules = self::get_child_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $module ) { if ( isset( $module->use_raw_content ) && $module->use_raw_content ) { $shortcodes[] = $module->slug; } } } return implode( '|', $shortcodes ); } /** * Get the portion of templates for specified slugs. * * @param string $post_type Post type. * @param array $slugs_array Module slugs. * * @return array|string|void */ public static function get_modules_templates( $post_type, $slugs_array ) { $all_modules = self::get_parent_and_child_modules( $post_type ); $templates_array = array(); if ( empty( $slugs_array ) ) { return; } foreach ( $slugs_array as $slug ) { if ( ! isset( $all_modules[ $slug ] ) ) { return ''; } $module = $all_modules[ $slug ]; $templates_array[] = array( 'slug' => $slug, 'template' => $module->build_microtemplate(), ); } if ( ET_BUILDER_OPTIMIZE_TEMPLATES ) { $templates_array = array( 'templates' => $templates_array, 'unique' => self::$_unique_bb_keys_values, ); } return $templates_array; } /** * Output modules backbone templates. * * @param string $post_type Post type. * @param int $start_from Unused arg. * @param int $amount Unused arg. * * @return array */ public static function output_templates( $post_type = '', $start_from = 0, $amount = 999 ) { $all_modules = self::get_parent_and_child_modules( $post_type ); $modules_names = array_keys( $all_modules ); $output = array(); $output['templates'] = array(); if ( ! empty( $all_modules ) ) { for ( $i = 0; $i < ET_BUILDER_AJAX_TEMPLATES_AMOUNT; $i++ ) { if ( isset( $modules_names[ $i ] ) ) { $module = $all_modules[ $modules_names[ $i ] ]; $output['templates'][ $module->slug ] = self::optimize_bb_chunk( $module->build_microtemplate() ); } else { break; } } } if ( ET_BUILDER_OPTIMIZE_TEMPLATES ) { $output['unique'] = self::$_unique_bb_keys_values; } return $output; } /** * Get structure module slugs. * * @return array */ public static function get_structure_module_slugs() { if ( ! empty( self::$structure_module_slugs ) ) { return self::$structure_module_slugs; } $structure_modules = self::get_structure_modules(); self::$structure_module_slugs = array(); foreach ( $structure_modules as $structural_module ) { self::$structure_module_slugs[] = $structural_module->slug; } /** * Filters structural module slugs. * * @since 4.10.0 * * @param array $structure_module_slugs. */ return apply_filters( 'et_builder_get_structural_module_slugs', self::$structure_module_slugs ); } /** * Get structure modules. * * @return array */ public static function get_structure_modules() { if ( ! empty( self::$structure_modules ) ) { return self::$structure_modules; } $parent_modules = self::get_parent_modules( 'et_pb_layout' ); self::$structure_modules = array(); foreach ( $parent_modules as $parent_module ) { if ( isset( $parent_module->is_structure_element ) && $parent_module->is_structure_element ) { $parent_module->plural = empty( $parent_module->plural ) ? $parent_module->name : $parent_module->plural; self::$structure_modules[] = $parent_module; } } return self::$structure_modules; } /** * Get a filtered list of modules. * * @since 3.10 * * @param string $post_type Leave empty for any. * @param string $type 'parent' or 'child'. Leave empty for any. * * @return ET_Builder_Element[] */ public static function get_modules( $post_type = '', $type = '' ) { $modules = array(); foreach ( self::$modules as $slug => $module ) { if ( '' !== $post_type && ! in_array( $post_type, $module->post_types, true ) ) { continue; } if ( '' !== $type && ! $module->type !== $type ) { continue; } $modules[ $slug ] = $module; } return $modules; } /** * Get modules by fallback post type for disabled post type. * * @param string $type Module type. * * @return ET_Builder_Element[]|mixed */ public static function get_custom_post_type_fallback_modules( $type = 'parent' ) { $modules = 'child' === $type ? self::$child_modules : self::$parent_modules; // Most of the time, page module is expected to be used as disabled post type fallback. if ( isset( $modules['page'] ) ) { return $modules['page']; } // Post module is also expected to be used. if ( isset( $modules['post'] ) ) { return $modules['post']; } // If Divi Builder is disabled for all post types use layout modules as fallback. if ( isset( $modules['et_pb_layout'] ) ) { return $modules['et_pb_layout']; } // If all else fail, use all modules. return self::get_modules(); } /** * Get all parent modules. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_parent_modules( $post_type = '' ) { if ( ! empty( $post_type ) ) { // We get all modules when post type is not enabled so that posts that have // had their post type support disabled still load all necessary modules. $parent_modules = ! empty( self::$parent_modules[ $post_type ] ) ? self::$parent_modules[ $post_type ] : self::get_custom_post_type_fallback_modules( 'parent' ); } else { $parent_modules = self::$parent_modules; } return apply_filters( 'et_builder_get_parent_modules', $parent_modules, $post_type ); } /** * Get all child modules. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_child_modules( $post_type = '' ) { if ( ! empty( $post_type ) ) { // We get all modules when post type is not enabled so that posts that have // had their post type support disabled still load all necessary modules. $child_modules = ! empty( self::$child_modules[ $post_type ] ) ? self::$child_modules[ $post_type ] : self::get_custom_post_type_fallback_modules( 'child' ); } else { $child_modules = self::$child_modules; } return apply_filters( 'et_builder_get_child_modules', $child_modules, $post_type ); } /** * Get woocommerce modules. * * @return mixed|void */ public static function get_woocommerce_modules() { return apply_filters( 'et_builder_get_woocommerce_modules', self::$woocommerce_modules ); } /** * Get registered module icons * * @since 3.1 * * @return array */ public static function get_module_icons() { /** * Filters Module Icons displayed in Add Module modals. * * @param array $module_icons Array of all registered module icons. */ $module_icons = apply_filters( 'et_builder_module_icons', self::$module_icons ); // Loop module icons. foreach ( $module_icons as $key => $icons ) { if ( isset( $icons['icon_path'] ) ) { // Get svg content based on given svg's path. $icon_svg = et_()->WPFS()->exists( $icons['icon_path'] ) ? et_()->WPFS()->get_contents( $icons['icon_path'] ) : false; if ( $icon_svg ) { $module_icons[ $key ]['icon_svg'] = $icon_svg; // Remove icon path attribute since it's no longer used. unset( $module_icons[ $key ]['icon_path'] ); } } } return $module_icons; } /** * Get combined array of child and parent modules for provided post_type * * @param string $post_type Post type. * * @since 3.1 * * @return array */ public static function get_parent_and_child_modules( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); return array_merge( $parent_modules, $child_modules ); } /** * Get a module instance for provided post type by its slug. * * @since 3.10 * * @param string $slug Module slug. * @param string $post_type Current post type. * * @return ET_Builder_Element|null */ public static function get_module( $slug, $post_type = 'post' ) { $modules = self::get_parent_and_child_modules( $post_type ); return self::$_->array_get( $modules, $slug ); } /** * Outputs list of all module help videos array * * @since 3.1 * * @return array */ public static function get_help_videos() { return self::$module_help_videos; } /** * Get list of modules with support of featured image as background. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_featured_image_background_modules( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $featured_image_background_modules = array(); foreach ( $parent_modules as $slug => $parent_module ) { if ( ! empty( $parent_module->featured_image_background ) ) { $featured_image_background_modules[] = $slug; } } /** * Filters list of modules with support of featured image as background. * * @since 3.1 * * @param array[] $featured_image_background_modules List of modules with support of featured image as background. */ return apply_filters( 'et_pb_featured_image_background_modules', $featured_image_background_modules ); } /** * Get field group toggles. * * @param string $post_type Post type. * * @return array */ public static function get_toggles( $post_type ) { static $toggles_array = array(); if ( $toggles_array ) { return $toggles_array; } $modules = self::get_parent_and_child_modules( $post_type ); $custom_modules = array(); foreach ( $modules as $module_slug => $module ) { if ( ! $module->_is_official_module ) { $custom_modules[ $module_slug ] = $module; } foreach ( $module->settings_modal_toggles as $tab_slug => &$tab_data ) { if ( ! isset( $tab_data['toggles'] ) ) { continue; } $tab_data['toggles'] = self::et_pb_order_toggles_by_priority( $tab_data['toggles'] ); } $toggles_array[ $module_slug ] = $module->settings_modal_toggles; } if ( $custom_modules ) { // Add missing toggle definitions for any existing toggles used in custom modules. foreach ( $custom_modules as $module_slug => $module ) { foreach ( $module->get_complete_fields() as $field_name => $field_info ) { $tab_slug = self::$_->array_get( $field_info, 'tab_slug' ); $tab_slug = empty( $tab_slug ) ? 'general' : $tab_slug; $toggle_slug = self::$_->array_get( $field_info, 'toggle_slug' ); if ( ! $toggle_slug || isset( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { continue; } // Find existing definition. foreach ( $toggles_array as $_module_slug => $tabs ) { foreach ( $tabs as $tab => $toggles ) { if ( isset( $toggles['toggles'][ $toggle_slug ] ) ) { self::$_->array_set( $toggles_array, "{$module_slug}.{$tab_slug}.toggles.{$toggle_slug}", $toggles['toggles'][ $toggle_slug ] ); $toggles_array[ $module_slug ][ $tab_slug ]['toggles'] = self::et_pb_order_toggles_by_priority( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'] ); break 2; } } } // Add missing unregistered toggles to the list. if ( ! isset( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { if ( ! isset( $toggles_array[ $module_slug ][ $tab_slug ] ) ) { $toggles_array[ $module_slug ][ $tab_slug ] = array( 'toggles' => array( $toggle_slug ) ); } else { $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][] = $toggle_slug; } } } } } return $toggles_array; } /** * Get setting modal tabs. * * @param string $post_type Current post type. * * @return array */ public static function get_tabs( $post_type = '' ) { $official_tabs = array( 'general' => '', 'advanced' => '', 'custom_css' => '', ); $tabs_array = array(); $modules = self::get_parent_and_child_modules( $post_type ); foreach ( $modules as $module_slug => $module ) { if ( '' === $post_type ) { foreach ( $module as $_module_slug => $_module ) { // Backward compatibility with custom tabs registered via `et_builder_main_tabs` filter. $bb_custom_tabs = array_diff_key( $_module->get_main_tabs(), $official_tabs ); $bb_custom_tabs_formatted = array(); // Prepare properly formatted array of tabs data. foreach ( $bb_custom_tabs as $tab_id => $tab_name ) { $bb_custom_tabs_formatted[ $tab_id ] = array( 'name' => $tab_name ); } // Add BB custom tabs to all modules. $tabs_array[ $_module_slug ] = $bb_custom_tabs_formatted; if ( ! isset( $_module->settings_modal_tabs ) ) { continue; } $tabs_array[ $_module_slug ] = array_merge( $tabs_array[ $_module_slug ], $_module->settings_modal_tabs ); } } else { // Backward compatibility with custom tabs registered via `et_builder_main_tabs` filter. $bb_custom_tabs = array_diff_key( $module->get_main_tabs(), $official_tabs ); $bb_custom_tabs_formatted = array(); // Prepare properly formatted array of tabs data. foreach ( $bb_custom_tabs as $tab_id => $tab_name ) { $bb_custom_tabs_formatted[ $tab_id ] = array( 'name' => $tab_name ); } // Add BB custom tabs to all modules. $tabs_array[ $module_slug ] = $bb_custom_tabs_formatted; if ( ! isset( $module->settings_modal_tabs ) ) { continue; } $tabs_array[ $module_slug ] = array_merge( $tabs_array[ $module_slug ], $module->settings_modal_tabs ); } } return $tabs_array; } /** * Get permission options categories. * * @return array */ public static function get_options_categories() { $options_categories = array( 'edit_colors' => array( 'name' => esc_html__( 'Edit Colors', 'et_builder' ), ), 'edit_content' => array( 'name' => esc_html__( 'Edit Content', 'et_builder' ), ), 'edit_fonts' => array( 'name' => esc_html__( 'Edit Fonts', 'et_builder' ), ), 'edit_buttons' => array( 'name' => esc_html__( 'Edit Buttons', 'et_builder' ), ), 'edit_layout' => array( 'name' => esc_html__( 'Edit Layout', 'et_builder' ), ), 'edit_borders' => array( 'name' => esc_html__( 'Edit Borders', 'et_builder' ), ), 'edit_configuration' => array( 'name' => esc_html__( 'Edit Configuration', 'et_builder' ), ), ); $options_categories = array_merge( $options_categories, self::get_custom_options_categories() ); return $options_categories; } /** * Get custom permission option categories. * * @param string $post_type Post type. * * @return array */ public static function get_custom_options_categories( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $custom_options_categories = array(); $_modules = array_merge_recursive( $parent_modules, $child_modules ); foreach ( $_modules as $_module_slug => $_module ) { if ( '' === $post_type ) { foreach ( $_module as $__module_slug => $__module ) { if ( ! isset( $__module->options_categories ) ) { continue; } $custom_options_categories = array_merge( $custom_options_categories, $__module->options_categories ); } } else { if ( ! isset( $_module->options_categories ) ) { continue; } $custom_options_categories = array_merge( $custom_options_categories, $_module->options_categories ); } } return $custom_options_categories; } /** * Get all fields. * * @param string $post_type Post type. * * @return array */ public static function get_all_fields( $post_type = '' ) { $_modules = self::get_parent_and_child_modules( $post_type ); $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // skip modules without fb support. if ( ! isset( $_module->vb_support ) || 'off' === $_module->vb_support ) { continue; } $_module->set_fields(); $_module->_add_additional_fields(); $_module->_add_custom_css_fields(); $_module->_maybe_add_defaults(); $_module->_finalize_all_fields(); foreach ( $_module->fields_unprocessed as $field_key => $field ) { // do not add the fields with 'skip' type. These fields used for rendering shortcode on Front End only. if ( isset( $field['type'] ) && 'skip' === $field['type'] ) { continue; } $field['name'] = $field_key; $module_fields[ $_module_slug ][ $field_key ] = $field; } } return $module_fields; } /** * Get general fields of modules. * * @param string $post_type Post type. * @param string $mode Modules Mode - Parent, Child and All. * @param string $module_type Module Slug. * * @return array|mixed */ public static function get_general_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $is_option_template = self::$option_template->is_option_template_field( $field_key ); // Do not process field template. if ( ! $is_option_template && ( isset( $field['tab_slug'] ) && 'general' !== $field['tab_slug'] ) ) { continue; } // Skip if current option template isn't eligible for `advanced` tab. if ( $is_option_template && ! self::$option_template->is_template_inside_tab( 'general', $field ) ) { continue; } $module_fields[ $_module_slug ][ $field_key ] = $field; } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get setting fields from custom tabs. * * @param string $post_type Post type. * @param string $mode Modules Mode - Parent, Child and All. * @param string $module_type Module Slug. * * @return array|mixed */ public static function get_settings_modal_tabs_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $this_tab_slug = isset( $field['tab_slug'] ) ? $field['tab_slug'] : false; if ( ! $this_tab_slug || in_array( $this_tab_slug, array( 'general', 'advanced', 'custom_css' ), true ) ) { continue; } $field['name'] = $field_key; $module_fields[ $_module_slug ][ $this_tab_slug ][ $field_key ] = $field; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get child module titles. * * @param string $post_type Post type. * * @return array */ public static function get_child_module_titles( $post_type ) { $child_modules = self::get_child_modules( $post_type ); $child_modules_titles = array(); $child_modules_titles_fields = array( 'advanced_setting_title_text', 'child_title_fallback_var', 'child_title_var' ); foreach ( $child_modules as $_module_slug => $_module ) { foreach ( $child_modules_titles_fields as $single_field ) { if ( isset( $_module->$single_field ) ) { $child_modules_titles[ $_module_slug ][ $single_field ] = $_module->$single_field; } } } return $child_modules_titles; } /** * Get advanced fields. * * @param string $post_type Post type. * @param string $mode Whether modules are parent, child or all. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_advanced_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $is_option_template = self::$option_template->is_option_template_field( $field_key ); // Do not process field template. if ( ! $is_option_template && ( ! isset( $field['tab_slug'] ) || 'advanced' !== $field['tab_slug'] ) ) { continue; } // Skip if current option template isn't eligible for `advanced` tab. if ( $is_option_template && ! self::$option_template->is_template_inside_tab( 'advanced', $field ) ) { continue; } if ( isset( $field['default'] ) ) { $module_fields[ $_module_slug ]['advanced_defaults'][ $field_key ] = $field['default']; } $module_fields[ $_module_slug ][ $field_key ] = $field; } if ( ! empty( $_module->advanced_fields ) ) { $module_fields[ $_module_slug ]['advanced_common'] = $_module->advanced_fields; if ( isset( $_module->advanced_fields['border']['border_styles'] ) ) { $module_fields[ $_module_slug ]['border_styles'] = array_merge( $module_fields[ $_module_slug ]['border_styles'], $_module->advanced_fields['border']['border_styles'] ); } if ( isset( $_module->advanced_fields['border']['border_radii'] ) ) { $module_fileds[ $_module_slug ]['border_radii'] = array_merge( $module_fields[ $_module_slug ]['border_radii'], $_module->advanced_fields['border']['border_radii'] ); } } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get custom css fields. * * @param string $post_type Post type. * @param string $mode Whether modules are parent, child or all. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_custom_css_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); $custom_css_unwanted_types = array( 'custom_css', 'column_settings_css', 'column_settings_css_fields', 'column_settings_custom_css' ); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $module_fields[ $_module_slug ] = $_module->custom_css_fields; foreach ( $module_fields[ $_module_slug ] as &$item ) { $item['hover'] = self::$_->array_get( $item, 'hover', 'tabs' ); $item['mobile_options'] = self::$_->array_get( $item, 'mobile_options', true ); $item['sticky'] = self::$_->array_get( $item, 'sticky', true ); } // Automatically added module ID and module class fields to setting modal's CSS tab. if ( ! empty( $_module->fields_unprocessed ) ) { foreach ( $_module->fields_unprocessed as $field_unprocessed_key => $field_unprocessed ) { $has_tab_slug = isset( $field_unprocessed['tab_slug'] ); $is_css_field = $has_tab_slug && 'custom_css' === $field_unprocessed['tab_slug']; $has_type = isset( $field_unprocessed['type'] ); $is_unwanted_css_field = $has_type && in_array( $field_unprocessed['type'], $custom_css_unwanted_types, true ); $is_template_inside_css_tab = is_string( $field_unprocessed ) && self::$option_template->is_template_inside_tab( 'custom_css', $field_unprocessed ); // Option template's template that might be rendered in custom_css tab. if ( ( $is_css_field && ! $is_unwanted_css_field ) || $is_template_inside_css_tab ) { $module_fields[ $_module_slug ][ $field_unprocessed_key ] = $field_unprocessed; } } } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get modules i10n. * * @param string $post_type Post type. * @param string $mode Whether it is parent, child or all module. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_modules_i10n( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $fields[ $_module_slug ] = array( 'addNew' => $_module->add_new_child_text(), ); } if ( 'all' !== $module_type ) { return $fields[ $module_type ]; } return $fields; } /** * Get CSS fields transition for module. * * @param string $post_type Post type. * @param string $mode Whether it is parent, child or all module. * @param string $module_type Module slug. * * @return array */ public static function get_modules_transitions( $post_type = '', $mode = 'all', $module_type = 'all' ) { /** * List of `ET_Builder_Element` instances. * * @var ET_Builder_Element[] $_modules */ $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $fields = array(); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $fields[ $_module_slug ] = $_module->get_transition_fields_css_props(); } return $fields; } /** * Get module items configs. * * @param string $post_type Current post type. * * @return array */ public static function get_module_items_configs( $post_type ) { $modules = self::get_parent_and_child_modules( $post_type ); $configs = array(); foreach ( $modules as $slug => $module ) { if ( isset( $module->module_items_config ) ) { $configs[ $slug ] = $module->module_items_config; } } return $configs; } /** * Get combined array of parent and child modules fields. * * @param string $post_type Post type. * @param string $module Parent module slug. * * @return array|bool */ public static function get_module_fields( $post_type, $module ) { $_modules = self::get_parent_and_child_modules( $post_type ); if ( ! empty( $_modules[ $module ] ) ) { return $_modules[ $module ]->fields_unprocessed; } return false; } /** * Get all fields of parent module. * * @param string $post_type Post type. * @param string $module Module slug. * * @return bool */ public static function get_parent_module_fields( $post_type, $module ) { if ( ! empty( self::$parent_modules[ $post_type ][ $module ] ) ) { return self::$parent_modules[ $post_type ][ $module ]->get_complete_fields(); } return false; } /** * Get all child module fields. * * @param string $post_type Post type. * @param string $module Module slug. * * @return bool */ public static function get_child_module_fields( $post_type, $module ) { if ( ! empty( self::$child_modules[ $post_type ][ $module ] ) ) { return self::$child_modules[ $post_type ][ $module ]->get_complete_fields(); } return false; } /** * Get parent module field. * * @param string $post_type Post type. * @param string $module Module slug. * @param string $field Field slug. * * @return bool|mixed */ public static function get_parent_module_field( $post_type, $module, $field ) { $fields = self::get_parent_module_fields( $post_type, $module ); if ( ! empty( $fields[ $field ] ) ) { return $fields[ $field ]; } return false; } /** * Return font icon fields of all modules. * * @param string $post_type Current post type. * * @return array */ public static function get_font_icon_fields( $post_type = '' ) { $_modules = self::get_parent_and_child_modules( $post_type ); $module_fields = array(); foreach ( $_modules as $module_name => $module ) { foreach ( $module->fields_unprocessed as $module_field_name => $module_field ) { if ( isset( $module_field['type'] ) && 'select_icon' === $module_field['type'] ) { $module_fields[ $module_name ][ $module_field_name ] = true; } } } return $module_fields; } /** * Retrieves credits of custom modules for VB * * @param string $post_type Current post type. * * @return array of credits info by module slug */ public static function get_custom_modules_credits( $post_type = '' ) { $result = array(); $modules = self::get_parent_and_child_modules( $post_type ); /** * Loop over the all modules to gather module credits. * * @var $module_slug string * @var $module ET_Builder_Module */ foreach ( $modules as $module_slug => $module ) { // Include custom module credits for displaying them within VB. if ( $module->_is_official_module ) { continue; } else { if ( isset( $module->module_credits ) && is_array( $module->module_credits ) ) { $result[ $module_slug ] = $module->module_credits; } } } return $result; } /** * Return media query key value pairs. * * @param bool $for_js Whether media queries is for js ETBuilderBackend.et_builder_css_media_queries variable. * * @return array|mixed|void */ public static function get_media_quries( $for_js = false ) { $media_queries = array( 'min_width_1405' => '@media only screen and ( min-width: 1405px )', '1100_1405' => '@media only screen and ( min-width: 1100px ) and ( max-width: 1405px)', '981_1405' => '@media only screen and ( min-width: 981px ) and ( max-width: 1405px)', '981_1100' => '@media only screen and ( min-width: 981px ) and ( max-width: 1100px )', 'min_width_981' => '@media only screen and ( min-width: 981px )', 'max_width_980' => '@media only screen and ( max-width: 980px )', '768_980' => '@media only screen and ( min-width: 768px ) and ( max-width: 980px )', 'min_width_768' => '@media only screen and ( min-width: 768px )', 'max_width_767' => '@media only screen and ( max-width: 767px )', 'max_width_479' => '@media only screen and ( max-width: 479px )', ); $media_queries['mobile'] = $media_queries['max_width_767']; $media_queries = apply_filters( 'et_builder_media_queries', $media_queries ); if ( 'for_js' === $for_js ) { $processed_queries = array(); foreach ( $media_queries as $key => $value ) { $processed_queries[] = array( $key, $value ); } } else { $processed_queries = $media_queries; } return $processed_queries; } /** * Set media queries key value pairs. */ public static function set_media_queries() { self::$media_queries = self::get_media_quries(); } /** * Return media query from the media query name. * E.g For max_width_767 media query name, this function return "@media only screen and ( max-width: 767px )". * * @param string $name Media query name e.g max_width_767, max_width_980. * * @return bool|mixed */ public static function get_media_query( $name ) { if ( ! isset( self::$media_queries[ $name ] ) ) { return false; } return self::$media_queries[ $name ]; } /** * Get style key. * * @return int|string */ public static function get_style_key() { if ( self::is_theme_builder_layout() || self::is_wp_editor_template() ) { return self::get_layout_id(); } // Use a generic key in all other cases. // For example, injector plugins that repeat a layout in a loop // need to group that CSS under the same key. return 'post'; } /** * Return style array from {@see self::$internal_modules_styles} or {@see self::$styles}. * * @param bool $internal Whether to return style from internal modules styles. * @param int $key Style key. * * @return array|mixed */ public static function get_style_array( $internal = false, $key = 0 ) { $styles = $internal ? self::$internal_modules_styles : self::$styles; if ( 0 === $key ) { $key = self::get_style_key(); } return isset( $styles[ $key ] ) ? $styles[ $key ] : array(); } /** * Return style string from {@see self::$_free_form_styles}. * * @return string */ public static function get_free_form_styles() { return self::$_free_form_styles; } /** * Intended to be used for unit testing * * @intendedForTesting */ public static function reset_styles() { self::$internal_modules_styles = array(); self::$styles = array(); self::$media_queries = array(); } /** * Get styles of the current page. * * @see set_advanced_styles() * * @param bool $internal Whether or not module's internal style. * @param int $key Style key. * * @return string */ public static function get_style( $internal = false, $key = 0, $critical = false ) { // use appropriate array depending on which styles we need. $styles_array = self::get_style_array( $internal, $key ); $free_form_styles_output = self::get_free_form_styles(); if ( empty( $styles_array ) && empty( $free_form_styles_output ) ) { return ''; } global $et_user_fonts_queue; $output = ''; if ( ! empty( $et_user_fonts_queue ) ) { $output .= et_builder_enqueue_user_fonts( $et_user_fonts_queue ); } $styles_by_media_queries = $styles_array; $styles_count = (int) count( $styles_by_media_queries ); $media_queries_order = array_merge( array( 'general' ), array_values( self::$media_queries ) ); // make sure styles in the array ordered by media query correctly from bigger to smaller screensize. $styles_by_media_queries_sorted = array_merge( array_flip( $media_queries_order ), $styles_by_media_queries ); foreach ( $styles_by_media_queries_sorted as $media_query => $styles ) { // skip wrong values which were added during the array sorting. if ( ! is_array( $styles ) ) { continue; } $media_query_output = ''; $wrap_into_media_query = 'general' !== $media_query; // sort styles by priority. et_()->uasort( $styles, array( 'ET_Builder_Element', 'compare_by_priority' ) ); // merge styles with identical declarations. $merged_declarations = []; foreach ( $styles as $selector => $settings ) { if ( false === $critical && isset( $settings['critical'] ) ) { continue; } elseif ( true === $critical && empty( $settings['critical'] ) ) { continue; } $this_declaration = md5( $settings['declaration'] ); // we want to skip combining anything with psuedo selectors or keyframes. if ( false !== strpos( $selector, ':-' ) || false !== strpos( $selector, '@keyframes' ) ) { // set unique key so that it cant be matched. $unique_key = $this_declaration . '-' . uniqid(); $merged_declarations[ $unique_key ] = [ 'declaration' => $settings['declaration'], 'selector' => $selector, ]; if ( ! empty( $settings['priority'] ) ) { $merged_declarations[ $unique_key ]['priority'] = $settings['priority']; } continue; } if ( empty( $merged_declarations[ $this_declaration ] ) ) { $merged_declarations[ $this_declaration ] = [ 'selector' => '', 'priority' => '', ]; } $new_selector = ! empty( $merged_declarations[ $this_declaration ]['selector'] ) ? $merged_declarations[ $this_declaration ]['selector'] . ', ' . $selector : $selector; $merged_declarations[ $this_declaration ] = [ 'declaration' => $settings['declaration'], 'selector' => $new_selector, ]; if ( ! empty( $settings['priority'] ) ) { $merged_declarations[ $this_declaration ]['priority'] = $settings['priority']; } } // get each rule in a media query. foreach ( $merged_declarations as $settings ) { $media_query_output .= sprintf( '%3$s%4$s%1$s { %2$s }', $settings['selector'], $settings['declaration'], "\n", ( $wrap_into_media_query ? "\t" : '' ) ); } // All css rules that don't use media queries are assigned to the "general" key. // Wrap all non-general settings into media query. if ( $wrap_into_media_query && '' !== $media_query_output ) { $media_query_output = sprintf( '%3$s%3$s%1$s {%2$s%3$s}', $media_query, $media_query_output, "\n" ); } $output .= $media_query_output; } if ( isset( $free_form_styles_output ) && '' !== $free_form_styles_output ) { $output .= wp_strip_all_tags( $free_form_styles_output ); if ( $internal ) { // Output is already set, we can reset the free form styles to avoid duplicate output. self::_set_free_form_style( '', $reset = true ); } } return $output; } /** * Generate video background markup for columns. * * @param array $args Background values. * @param array $conditional_tags Conditional tags. * @param array $current_page Current page info. * * @return bool|mixed */ public static function get_column_video_background( $args = array(), $conditional_tags = array(), $current_page = array() ) { if ( empty( $args ) ) { return false; } $formatted_args = array(); foreach ( $args as $key => $value ) { $key_length = strlen( $key ); $formatted_args[ substr( $key, 0, ( $key_length - 2 ) ) ] = $value; } return self::get_video_background( $formatted_args, $conditional_tags, $current_page ); } /** * Generate video background markup. * * @since 3.23 Add support for responsive settings. * * @param array $args Background values. * @param array $conditional_tags Conditional tags. * @param array $current_page Current page info. * @return mixed Mixed background content generated as video markup. */ public static function get_video_background( $args = array(), $conditional_tags = array(), $current_page = array() ) { $base_name = isset( $args['computed_variables'] ) && isset( $args['computed_variables']['base_name'] ) ? $args['computed_variables']['base_name'] : 'background'; $device = isset( $args['computed_variables'] ) && isset( $args['computed_variables']['device'] ) ? $args['computed_variables']['device'] : 'desktop'; $suffix = ! empty( $device ) && 'desktop' !== $device ? "_{$device}" : ''; $defaults = array( "{$base_name}_video_mp4{$suffix}" => '', "{$base_name}_video_webm{$suffix}" => '', "{$base_name}_video_width{$suffix}" => '', "{$base_name}_video_height{$suffix}" => '', ); $args = wp_parse_args( $args, $defaults ); if ( '' === $args[ "{$base_name}_video_mp4{$suffix}" ] && '' === $args[ "{$base_name}_video_webm{$suffix}" ] ) { return false; } return do_shortcode( sprintf( ' <video loop="loop" autoplay playsinline muted %3$s%4$s> %1$s %2$s </video>', ( '' !== $args[ "{$base_name}_video_mp4{$suffix}" ] ? sprintf( '<source type="video/mp4" src="%s" />', esc_url( $args[ "{$base_name}_video_mp4{$suffix}" ] ) ) : '' ), ( '' !== $args[ "{$base_name}_video_webm{$suffix}" ] ? sprintf( '<source type="video/webm" src="%s" />', esc_url( $args[ "{$base_name}_video_webm{$suffix}" ] ) ) : '' ), ( '' !== $args[ "{$base_name}_video_width{$suffix}" ] ? sprintf( ' width="%s"', esc_attr( intval( $args[ "{$base_name}_video_width{$suffix}" ] ) ) ) : '' ), ( '' !== $args[ "{$base_name}_video_height{$suffix}" ] ? sprintf( ' height="%s"', esc_attr( intval( $args[ "{$base_name}_video_height{$suffix}" ] ) ) ) : '' ) ) ); } /** * Clean the styles array {@see self::$internal_modules_styles}. * * @param bool $need_internal_styles Set the flag to make sure new styles will be saved to the correct place. */ public static function clean_internal_modules_styles( $need_internal_styles = true ) { // clean the styles array. self::$internal_modules_styles[ self::get_style_key() ] = array(); // set the flag to make sure new styles will be saved to the correct place. self::$prepare_internal_styles = $need_internal_styles; // generate unique number to make sure module classes will be unique if shortcode is generated via ajax. self::$internal_modules_counter = wp_rand( 10000, 99999 ); } /** * Set the field dependencies based on the `show_if` or `show_if_not` key from the * field. * * @param string $slug The module's slug. ie `et_pb_section`. * @param string $field_id The field id. id `background_color`. * @param array $field_info Associative array of the field's data. */ protected static function set_field_dependencies( $slug, $field_id, $field_info ) { // bail if the field_info is not an array. if ( ! is_array( $field_info ) || ! self::$_->array_get( $field_info, 'bb_support', true ) ) { return; } // otherwise we keep going. foreach ( array( 'show_if', 'show_if_not' ) as $dependency_type ) { if ( ! isset( $field_info[ $dependency_type ] ) ) { continue; } if ( ! self::$data_utils->is_assoc_array( $field_info[ $dependency_type ] ) ) { continue; } foreach ( $field_info[ $dependency_type ] as $dependency => $value ) { // dependency -> dependent (eg. et_pb_signup.provider.affects.first_name_field.show_if: mailchimp). $address = self::$_->esc_array( array( $slug, $dependency, 'affects', $field_id, $dependency_type ), 'esc_attr' ); self::$data_utils->array_set( self::$field_dependencies, $address, self::$_->esc_array( $value, 'esc_attr' ) ); // dependent -> dependency (eg. et_pb_signup.first_name_field.show_if.provider: mailchimp). $address = self::$_->esc_array( array( $slug, $field_id, $dependency_type, $dependency ), 'esc_attr' ); self::$data_utils->array_set( self::$field_dependencies, $address, self::$_->esc_array( $value, 'esc_attr' ) ); } } } /** * Get all modules fields dependencies. * * @param string $post_type Post type. * * @return array */ public static function get_field_dependencies( $post_type ) { if ( self::$field_dependencies ) { return self::$field_dependencies; } $all_modules = self::get_parent_and_child_modules( $post_type ); foreach ( $all_modules as $module_slug => $module ) { // Get all the fields. $all_fields = $module->sort_fields( $module->_get_fields() ); foreach ( $all_fields as $field_id => $field_info ) { if ( isset( $field_info['type'] ) && 'composite' === $field_info['type'] ) { foreach ( $field_info['composite_structure'] as $field ) { foreach ( $field['controls'] as $control => $data ) { self::set_field_dependencies( $module_slug, $control, $data ); } } } self::set_field_dependencies( $module_slug, $field_id, $field_info ); } } return self::$field_dependencies; } /** * Set module style. * * @param string $function_name Module slug. * @param array $style Style array. */ public static function set_style( $function_name, $style ) { $selectors = is_array( $style['selector'] ) ? $style['selector'] : array( $style['selector'] ); foreach ( $selectors as $item ) { foreach ( self::$_->sanitize_css_placeholders( $item ) as $selector ) { $selector = apply_filters( "{$function_name}_css_selector", $selector ); $selector = apply_filters( 'all_module_css_selector', $selector, $function_name ); self::_set_style( $function_name, array_merge( $style, array( 'selector' => $selector ) ) ); } } } /** * Check if the style processor allowed to be executed. * Currently, we only use a custom processor from the method inside `ET_Builder_Module_Helper_Style_Processor`, * * NOTE: If there are more processors introduced, this needs to be updated * * @since 4.6.0 * * @param array $processor Style processor. * * @return bool */ protected static function _is_style_processor_allowed( $processor ) { $allow_list = array( 'ET_Builder_Module_Helper_Style_Processor', ); return in_array( et_()->array_get( $processor, '0' ), $allow_list, true ); } /** * Generate responsive + hover + sticky style using the same configuration at once * { * * @type string $mode * @type string $render_slug * @type string $base_attr_name * @type array $attrs * @type string $css_property * @type string $selector * @type bool $is_sticky_module * @type bool|array $important Allowed value is boolean or array of mode, e.g ['sticky', 'hover']. * @type string $additional_css * @type int $priority * @type bool $responsive * @type bool $hover * @type string $hover_selector * @type string $hover_pseudo_selector_location * @type bool $sticky * @type string $sticky_pseudo_selector_location * @type string $utility_arg * } * * NOTE: If there are more mode besides sticky and hover introduced, this needs to be updated. * * @since 4.6.0 * * @param array $args Function arguments. * * @return void */ public function generate_styles( $args = array() ) { $defaults = array( 'mode' => 'sticky', 'render_slug' => '', 'base_attr_name' => '', 'attrs' => $this->props, 'css_property' => '', 'selector' => '%%order_class%%', 'is_sticky_module' => $this->is_sticky_module, 'important' => false, 'additional_css' => '', 'type' => '', 'priority' => '', 'responsive' => true, 'hover' => true, 'hover_selector' => '', 'hover_pseudo_selector_location' => 'order_class', 'sticky' => true, 'sticky_pseudo_selector_location' => 'order_class', 'processor' => false, 'responsive_processor' => false, 'hover_processor' => false, 'sticky_processor' => false, 'processor_declaration_format' => '', 'utility_arg' => '', ); $args = wp_parse_args( $args, $defaults ); $attrs = $args['attrs']; $base_attr_name = $args['base_attr_name']; $selector = $args['selector']; // Responsive Options. if ( $args['responsive'] ) { $responsive = et_pb_responsive_options(); $responsive_values = $responsive->get_property_values( $attrs, $base_attr_name ); $responsive_processor = $args['responsive_processor']; // Custom processor fallback, if there's any. if ( ! $responsive_processor && $args['processor'] ) { $responsive_processor = $args['processor']; } if ( $responsive_processor && self::_is_style_processor_allowed( $responsive_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $responsive_processor, $selector, $responsive_values, $args, 'responsive' ); } else { // Append important tag to responsive's additional css. $responsive_additional_css = '; ' . $args['additional_css']; $responsive_important = is_array( $args['important'] ) ? in_array( 'responsive', $args['important'], true ) : $args['important']; if ( $responsive_important ) { $responsive_additional_css = ' !important;' . $args['additional_css']; } // Responsive Options. $responsive->generate_responsive_css( $responsive_values, $selector, $args['css_property'], $args['render_slug'], $responsive_additional_css, $args['type'], $args['priority'] ); } } // Hover Option. if ( $args['hover'] ) { $hover = et_pb_hover_options(); $hover_value = $hover->get_value( $base_attr_name, $attrs ); $hover_processor = $args['hover_processor']; $hover_important = is_array( $args['important'] ) ? in_array( 'hover', $args['important'], true ) : $args['important']; // Custom processor fallback, if there's any. if ( ! $hover_processor && $args['processor'] ) { $hover_processor = $args['processor']; } // Generate hover selector. if ( '' !== $args['hover_selector'] ) { $hover_selector = $args['hover_selector']; } elseif ( 'order_class' === $args['hover_pseudo_selector_location'] ) { $hover_selector = $hover->add_hover_to_order_class( $selector ); } else { $hover_selector = $hover->add_hover_to_selectors( $selector ); } if ( $hover_processor && self::_is_style_processor_allowed( $hover_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $hover_processor, $hover_selector, $hover_value, $args, 'hover' ); } elseif ( ! empty( $hover_value ) ) { $declaration = $this->generate_declaration( $args['css_property'], $hover_value, $hover_important, $args['additional_css'] ); $el_style = array( 'selector' => $hover_selector, 'declaration' => $declaration, ); self::set_style( $args['render_slug'], $el_style ); } } // Sticky Option. if ( $args['sticky'] ) { $sticky = et_pb_sticky_options(); $sticky_value = $sticky->get_value( $base_attr_name, $attrs ); $sticky_processor = $args['sticky_processor']; $sticky_important = is_array( $args['important'] ) ? in_array( 'sticky', $args['important'], true ) : $args['important']; // Custom processor fallback, if there's any. if ( ! $sticky_processor && $args['processor'] ) { $sticky_processor = $args['processor']; } // If generate_styles() is called multiple times, check for it once then pass // it down as param to skip sticky module check on this method level. $is_sticky_module = null === $args['is_sticky_module'] ? $sticky->is_sticky_module( $attrs ) : $args['is_sticky_module']; // Generate sticky selector. if ( 'order_class' === $args['sticky_pseudo_selector_location'] ) { $sticky_selector = $sticky->add_sticky_to_order_class( $selector, $is_sticky_module ); } else { $sticky_selector = $sticky->add_sticky_to_selectors( $selector, $is_sticky_module ); } if ( $sticky_processor && self::_is_style_processor_allowed( $sticky_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $sticky_processor, $sticky_selector, $sticky_value, $args, 'sticky' ); } elseif ( ! empty( $sticky_value ) ) { $sticky_declaration = self::generate_declaration( $args['css_property'], $sticky_value, $sticky_important, $args['additional_css'] ); $el_style = array( 'selector' => $sticky_selector, 'declaration' => $sticky_declaration, ); self::set_style( $args['render_slug'], $el_style ); } } } /** * Generate CSS declaration. * * @since 4.6.0 * * @param array|string $css_property CSS Property. * @param string $value Value. * @param bool $important Use important tag. * @param string $additional_css Additional CSS. * * @return string */ public function generate_declaration( $css_property, $value = '', $important = false, $additional_css = '' ) { $important_tag = $important ? ' !important' : ''; $declaration = ''; // Assign value to one or more properties. if ( is_string( $css_property ) ) { $declaration = sprintf( '%1$s: %2$s%3$s;%4$s', esc_attr( $css_property ), esc_attr( $value ), esc_attr( $important_tag ), esc_attr( $additional_css ) ); } elseif ( is_array( $css_property ) ) { foreach ( $css_property as $property ) { $declaration .= sprintf( '%1$s: %2$s%3$s;%4$s', esc_attr( $property ), esc_attr( $value ), esc_attr( $important_tag ), esc_attr( $additional_css ) ); } } return $declaration; } /** * Applies the responsive and hover style for a specified option * * @since 3.25.3 * * @param string $option Setting option. * @param string $selector CSS Selector. * @param string $css_prop CSS property. */ public function generate_responsive_hover_style( $option, $selector, $css_prop ) { $responsive = et_pb_responsive_options(); $hover = et_pb_hover_options(); $values = $responsive->get_property_values( $this->props, $option ); $hover_value = $hover->get_value( $option, $this->props ); $responsive->generate_responsive_css( $values, $selector, $css_prop, $this->slug, '', 'color' ); if ( $hover_value ) { self::set_style( $this->slug, array( 'selector' => $hover->add_hover_to_selectors( $selector ), 'declaration' => "{$css_prop}: $hover_value;", ) ); } } /** * Set module style. * * @param string $function_name Module slug. * @param array $style Style array. */ protected static function _set_style( $function_name, $style ) { static $builder_post_types = null; static $allowed_post_types = null; $declaration = isset( $style['declaration'] ) && ! is_null( $style['declaration'] ) ? rtrim( $style['declaration'] ) : ''; if ( empty( $declaration ) ) { // Do not add empty declarations. return; } if ( null === $builder_post_types ) { $builder_post_types = et_builder_get_builder_post_types(); /** * Filters the Builder Post Types. * * @since 4.10.0 * * @param array $builder_post_types Builder Post Types. */ $allowed_post_types = apply_filters( 'et_builder_set_style_allowed_post_types', $builder_post_types ); } if ( $builder_post_types !== $allowed_post_types ) { $matches = array_intersect( $allowed_post_types, array_keys( self::$_module_slugs_by_post_type ) ); $allowed = false; foreach ( $matches as $post_type ) { if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) { continue; } if ( in_array( $function_name, self::$_module_slugs_by_post_type[ $post_type ], true ) ) { $allowed = true; break; } } if ( ! $allowed ) { return; } } global $et_pb_rendering_column_content; // do not process all the styles if FB enabled. Only those for modules without fb support and styles for the internal modules from Blog/Slider. $main_query_post = ET_Post_Stack::get_main_post(); $main_query_post_id = null !== $main_query_post ? $main_query_post->ID : 0; $editing_current_post = et_fb_is_enabled() && self::get_layout_id() === $main_query_post_id; if ( $editing_current_post && ! in_array( $function_name, self::get_fb_unsupported_modules(), true ) && ! $et_pb_rendering_column_content ) { return; } $order_class_name = self::get_module_order_class( $function_name ); $selector = str_replace( '%%order_class%%', ".{$order_class_name}", $style['selector'] ); $selector = str_replace( '%order_class%', ".{$order_class_name}", $selector ); // %%parent_class%% only works if child module's slug is `parent_slug` + _item suffix. If child module slug // use different slug structure, %%parent_class%% should not be used if ( false !== strpos( $selector, '%%parent_class%%' ) ) { $parent_class = str_replace( '_item', '', $function_name ); $selector = str_replace( '%%parent_class%%', ".{$parent_class}", $selector ); } $selector = et_builder_maybe_wrap_css_selectors( $selector, false, ".{$order_class_name}" ); $selector = wp_strip_all_tags( apply_filters( 'et_pb_set_style_selector', $selector, $function_name ) ); // New lines are saved as || in CSS Custom settings, remove them. $declaration = preg_replace( '/(\|\|)/i', '', $declaration ); $media_query = isset( $style['media_query'] ) ? $style['media_query'] : 'general'; $internal = $et_pb_rendering_column_content && self::$prepare_internal_styles; $style_key = self::get_style_key(); $styles = self::get_style_array( $internal ); // prepare styles for internal content. Used in Blog/Slider modules if they contain Divi modules. if ( isset( $styles[ $media_query ][ $selector ]['declaration'] ) ) { $styles[ $media_query ][ $selector ]['declaration'] = sprintf( '%1$s %2$s', $styles[ $media_query ][ $selector ]['declaration'], $declaration ); } else { $styles[ $media_query ][ $selector ]['declaration'] = $declaration; } if ( isset( $style['priority'] ) ) { $styles[ $media_query ][ $selector ]['priority'] = (int) $style['priority']; } $style = $styles[ $media_query ][ $selector ]; /** * Filters the Builder CSS * * @since 4.10.0 * * @param array $style Style Declaration. * @param string $selector Style Selector. */ $style = apply_filters( 'et_builder_set_style', $style, $selector ); $styles[ $media_query ][ $selector ] = $style; if ( $internal ) { self::$internal_modules_styles[ $style_key ] = $styles; } else { self::$styles[ $style_key ] = $styles; } } /** * Set free form module style. * * @param string $style Style string. */ protected static function _set_free_form_style( $style, $reset = false ) { if ( $reset ) { self::$_free_form_styles = ''; return; } if ( '' !== $style ) { self::$_free_form_styles .= $style; } } /** * Return module order class. * * @param string $function_name Module slug. * * @return bool|string */ public static function get_module_order_class( $function_name ) { global $et_pb_rendering_column_content, $et_pb_predefined_module_index; // determine whether we need to get the internal module class or regular. $get_inner_module_class = $et_pb_rendering_column_content; if ( $get_inner_module_class ) { if ( self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ) === -1 ) { return false; } } else { if ( self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ) === -1 ) { return false; } } if ( isset( $et_pb_predefined_module_index ) && $et_pb_predefined_module_index ) { $shortcode_order_num = $et_pb_predefined_module_index; } else { $shortcode_order_num = $get_inner_module_class ? self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ) : self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ); } $theme_builder_suffix = self::_get_theme_builder_order_class_suffix(); $wp_editor_suffix = self::_get_wp_editor_order_class_suffix(); // TB should be prioritized over WP Editor. Need to check WP Template editor suffix. if ( empty( $theme_builder_suffix ) && ! empty( $wp_editor_suffix ) ) { $theme_builder_suffix = $wp_editor_suffix; } $order_class_name = sprintf( '%1$s_%2$s%3$s', $function_name, $shortcode_order_num, $theme_builder_suffix ); return $order_class_name; } /** * Set module order class. * * @param string $function_name Module slug. */ public static function set_order_class( $function_name ) { global $et_pb_rendering_column_content; // determine whether we need to update the internal module class or regular. $process_inner_module_class = $et_pb_rendering_column_content; if ( $process_inner_module_class ) { $current_inner_index = self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ); self::_set_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ), $current_inner_index > -1 ? $current_inner_index + 1 : self::$internal_modules_counter ); } else { self::_set_index( array( self::INDEX_MODULE_ORDER, $function_name ), self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ) + 1 ); } } /** * Add a modal order class e.g et_pb_section_0, et_pb_section_1. * * @param string $module_class Module class e.g et_pb_section_. * @param string $function_name Module slug. * * @return string */ public static function add_module_order_class( $module_class, $function_name ) { $order_class_name = self::get_module_order_class( $function_name ); return "{$module_class} {$order_class_name}"; } /** * Generate video background markup. * * @since 3.23 Add support for responsive settings. * * @param array $args Background values. * @param string $base_name Background base name. * @return string Video background string value. */ public function video_background( $args = array(), $base_name = 'background' ) { $attr_prefix = "{$base_name}_"; $custom_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $module_attrs = $this->props; // Default background class for each devices. $background_video_class = ''; $background_video_class_tablet = 'et_pb_section_video_bg_tablet'; $background_video_class_phone = 'et_pb_section_video_bg_phone'; $background_video_class_hover = 'et_pb_section_video_bg_hover'; // Hover and Responsive Status. $is_background_hover = et_pb_hover_options()->is_enabled( $base_name, $this->props ); $is_background_mobile = et_pb_responsive_options()->is_responsive_enabled( $this->props, $base_name ); if ( ! empty( $args ) ) { $background_video = self::get_video_background( $args ); $background_video_tablet = ''; $background_video_phone = ''; $background_video_hover = ''; $pause_outside_viewport = self::$_->array_get( $args, "{$attr_prefix}video_pause_outside_viewport", 'off' ); $allow_player_pause = self::$_->array_get( $args, "{$custom_prefix}allow_player_pause", 'off' ); } else { $background_videos = array(); // Desktop. $default_args = array( "{$attr_prefix}video_mp4" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4" ), "{$attr_prefix}video_webm" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm" ), "{$attr_prefix}video_width" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width", '', true ), "{$attr_prefix}video_height" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height", '', true ), 'computed_variables' => array( 'base_name' => $base_name, ), ); // Collecting background videos. $background_videos['desktop'] = self::get_video_background( $default_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'desktop', $base_name, $this->fields_unprocessed ); $background_video = self::$_->array_get( $background_video_status, 'video', '' ); $background_display = self::$_->array_get( $background_video_status, 'display', '' ); // Hover. $background_video_hover = ''; $background_display_hover = ''; if ( $is_background_hover ) { $hover_args = array( "{$attr_prefix}video_mp4__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4__hover" ), "{$attr_prefix}video_webm__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm__hover" ), "{$attr_prefix}video_width__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width__hover", '', true ), "{$attr_prefix}video_height__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height__hover", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => '_hover', ), ); // Collecting background videos. $background_videos['hover'] = self::get_video_background( $hover_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status_hover = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'hover', $base_name, $this->fields_unprocessed ); $background_video_hover = self::$_->array_get( $background_video_status_hover, 'video', '' ); $background_display_hover = self::$_->array_get( $background_video_status_hover, 'display', '' ); } // Tablet and Phone. $background_video_tablet = ''; $background_display_tablet = ''; $background_video_phone = ''; $background_display_phone = ''; if ( $is_background_mobile ) { $tablet_args = array( "{$attr_prefix}video_mp4_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4_tablet" ), "{$attr_prefix}video_webm_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm_tablet" ), "{$attr_prefix}video_width_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width_tablet", '', true ), "{$attr_prefix}video_height_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height_tablet", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'tablet', ), ); $phone_args = array( "{$attr_prefix}video_mp4_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4_phone" ), "{$attr_prefix}video_webm_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm_phone" ), "{$attr_prefix}video_width_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width_phone", '', true ), "{$attr_prefix}video_height_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height_phone", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'phone', ), ); // Collecting background videos. $background_videos['tablet'] = self::get_video_background( $tablet_args ); $background_videos['phone'] = self::get_video_background( $phone_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status_tablet = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'tablet', $base_name, $this->fields_unprocessed ); $background_video_tablet = self::$_->array_get( $background_video_status_tablet, 'video', '' ); $background_display_tablet = self::$_->array_get( $background_video_status_tablet, 'display', '' ); $background_video_status_phone = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'phone', $base_name, $this->fields_unprocessed ); $background_video_phone = self::$_->array_get( $background_video_status_phone, 'video', '' ); $background_display_phone = self::$_->array_get( $background_video_status_phone, 'display', '' ); } // Set background video and class. Inherit is used to avoid rendering the same video. if ( '' !== $background_display_phone ) { if ( 'hide' === $background_display_phone ) { $background_video_class = 'et_pb_section_video_bg_desktop_tablet'; $background_video_class_tablet = 'et_pb_section_video_bg_tablet_only'; } elseif ( 'inherit' === $background_display_phone ) { $background_video_phone = ''; } } if ( '' !== $background_display_tablet ) { if ( 'hide' === $background_display_tablet ) { $background_video_class = 'et_pb_section_video_bg_desktop_only'; } elseif ( 'inherit' === $background_display_tablet ) { $background_video_tablet = ''; } } if ( '' !== $background_display_hover ) { if ( 'inherit' === $background_display_hover ) { $background_video_class .= ' et_pb_section_video_bg_hover_inherit'; $background_video_hover = ''; } } } $video_background = ''; // Desktop. if ( $background_video ) { // Video on desktop properties. $pause_outside_viewport = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport", '' ); $allow_player_pause = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause", 'off' ); $video_background .= sprintf( '<span class="et_pb_section_video_bg %2$s %3$s%4$s"> %1$s </span>', $background_video, $background_video_class, ( 'on' === $allow_player_pause ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Hover. if ( $is_background_hover ) { if ( $background_video_hover ) { // Video on hover properties. $pause_outside_viewport_hover = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport__hover", '' ); $allow_player_pause_hover = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause__hover", 'off' ); $video_background .= sprintf( '<span class="et_pb_section_video_bg %2$s %3$s%4$s"> %1$s </span>', $background_video_hover, $background_video_class_hover, ( 'on' === $allow_player_pause_hover ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_hover ? ' et_pb_video_play_outside_viewport' : '' ) ); } $this->add_classname( 'et_pb_section_video_on_hover' ); } // Tablet. if ( $background_video_tablet && $is_background_mobile ) { // Video on tablet properties. $pause_outside_viewport_tablet = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport_tablet", '' ); $allow_player_pause_tablet = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause_tablet", 'off' ); $video_background .= sprintf( '<span class="et_pb_section_video_bg %2$s %3$s%4$s"> %1$s </span>', $background_video_tablet, $background_video_class_tablet, ( 'on' === $allow_player_pause_tablet ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_tablet ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Phone. if ( $background_video_phone && $is_background_mobile ) { // Video on phone properties. $pause_outside_viewport_phone = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport_phone", '' ); $allow_player_pause_phone = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause_phone", 'off' ); $video_background .= sprintf( '<span class="et_pb_section_video_bg %2$s %3$s%4$s"> %1$s </span>', $background_video_phone, $background_video_class_phone, ( 'on' === $allow_player_pause_phone ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_phone ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Added classname for module wrapper. if ( '' !== $video_background ) { wp_enqueue_style( 'wp-mediaelement' ); wp_enqueue_script( 'wp-mediaelement' ); $this->add_classname( array( 'et_pb_section_video', 'et_pb_preload' ) ); } return $video_background; } /** * Generate parallax image background markup. * * @param string $base_name Background base name. * @param array $props Props (optional). * * @since 4.15.0 Added $props property. * * @return string */ public function get_parallax_image_background( $base_name = 'background', $props = array() ) { $props = empty( $props ) ? $this->props : $props; $attr_prefix = "{$base_name}_"; $custom_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $parallax_processed = array(); $parallax_background = ''; $hover_suffix = et_pb_hover_options()->get_suffix(); $sticky_suffix = et_pb_sticky_options()->get_suffix(); $preview_modes = array( $hover_suffix, $sticky_suffix, '_phone', '_tablet', '' ); // Featured Image as Background. $featured_image = ''; $featured_placement = ''; $featured_image_src = ''; if ( $this->featured_image_background ) { $featured_image = self::$_->array_get( $props, 'featured_image', '' ); $featured_placement = self::$_->array_get( $props, 'featured_placement', '' ); $featured_image_src_obj = wp_get_attachment_image_src( get_post_thumbnail_id( self::_get_main_post_id() ), 'full' ); $featured_image_src = isset( $featured_image_src_obj[0] ) ? $featured_image_src_obj[0] : ''; } // Parallax Gradient. $background_options = et_pb_background_options(); $is_gradient_on = false; foreach ( $preview_modes as $suffix ) { $is_hover = $hover_suffix === $suffix; $is_sticky = $sticky_suffix === $suffix; // A. Bail early if hover or responsive settings disabled on mobile/hover. if ( '' !== $suffix ) { // Ensure responsive settings is enabled on mobile. if ( ! $is_hover && ! $is_sticky && ! et_pb_responsive_options()->is_responsive_enabled( $props, $base_name ) ) { continue; } // Ensure hover settings is enabled. if ( $is_hover && ! et_pb_hover_options()->is_enabled( $base_name, $props ) ) { continue; } // Ensure sticky setting is enabled. if ( $is_sticky && ! et_pb_sticky_options()->is_enabled( $base_name, $props ) ) { continue; } } // Prepare preview mode. $mode = '' !== $suffix ? str_replace( '_', '', $suffix ) : 'desktop'; $mode = $is_hover ? 'hover' : $mode; $mode = $is_sticky ? 'sticky' : $mode; // B.1. Get inherited background value. $use_gradient_options = et_pb_responsive_options()->get_inheritance_background_value( $props, "use_{$base_name}_color_gradient", $mode, $base_name, $this->fields_unprocessed ); $background_image = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$attr_prefix}image", $mode, $base_name, $this->fields_unprocessed ); $parallax = et_pb_responsive_options()->get_any_value( $props, "parallax{$suffix}", '', true ); $parallax_method = et_pb_responsive_options()->get_any_value( $props, "parallax_method{$suffix}", '', true ); $gradient_overlays_image = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_color_gradient_overlays_image{$suffix}", '', true ); $background_gradient_blend = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_blend{$suffix}", 'normal', true ); // B.2. Set default value for parallax and parallax method on hover when they are empty. if ( $is_hover || $is_sticky ) { $parallax = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax", $props, $parallax ); $parallax_method = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax_method", $props, $parallax_method ); $gradient_overlays_image = et_pb_hover_options()->get_raw_value( "{$base_name}_color_gradient_overlays_image", $props, $gradient_overlays_image ); $background_gradient_blend = et_pb_hover_options()->get_raw_value( "{$base_name}_blend", $props, $background_gradient_blend ); $gradient_properties_desktop = $background_options->get_gradient_properties( $props, $base_name, '' ); $gradient_properties = $background_options->get_gradient_mode_properties( $mode, $props, $base_name, $gradient_properties_desktop ); } else { $gradient_properties = $background_options->get_gradient_properties( $props, $base_name, $suffix ); } $background_gradient_style = $background_options->get_gradient_style( $gradient_properties ); if ( 'on' === $use_gradient_options && 'on' === $gradient_overlays_image && 'on' === $parallax ) { $is_gradient_on = '' !== $background_gradient_style; } // B.3. Override background image with featured image if needed. if ( 'on' === $featured_image && 'background' === $featured_placement && '' !== $featured_image_src ) { $background_image = $featured_image_src; } // C.1. Parallax BG Class to inform if other modes exist. $parallax_classname = array(); $parallax_gradient_classname = array(); if ( ( '_tablet' === $suffix || '' === $suffix ) && in_array( '_phone', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_phone_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_phone_exist'; } if ( '' === $suffix && in_array( '_tablet', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_tablet_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_tablet_exist'; } if ( in_array( $hover_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_hover_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_hover_exist'; } if ( in_array( $sticky_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_sticky_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_sticky_exist'; } // C.2. Set up parallax class and wrapper. if ( '' !== $background_image && 'on' === $parallax ) { $parallax_classname[] = 'et_parallax_bg'; $parallax_gradient_classname[] = 'et_parallax_gradient'; if ( 'off' === $parallax_method ) { $parallax_classname[] = 'et_pb_parallax_css'; $parallax_gradient_classname[] = 'et_pb_parallax_css'; if ( isset( $props['inner_shadow'] ) && 'off' !== $props['inner_shadow'] ) { $parallax_classname[] = 'et_pb_inner_shadow'; } } // Parallax BG Class with suffix. if ( '' !== $suffix ) { $parallax_classname[] = "et_parallax_bg{$suffix}"; $parallax_gradient_classname[] = "et_parallax_gradient{$suffix}"; } $background_gradient_image = sprintf( 'background-image: %1$s;', esc_html( $background_gradient_style ) ); $background_gradient_blend = 'normal' !== $background_gradient_blend ? sprintf( 'mix-blend-mode: %1$s;', esc_html( $background_gradient_blend ) ) : ''; $parallax_gradient = sprintf( '<span class="%1$s" style="%2$s%3$s" ></span>', esc_attr( implode( ' ', $parallax_gradient_classname ) ), // #1 et_core_esc_previously( $background_gradient_image ), // #2 et_core_esc_previously( $background_gradient_blend ) // #3 ); $parallax_background .= sprintf( '<span class="et_parallax_bg_wrap"><span class="%1$s" style="background-image: url(%2$s);" ></span>%3$s</span>', esc_attr( implode( ' ', $parallax_classname ) ), esc_url( $background_image ), $is_gradient_on ? et_core_esc_previously( $parallax_gradient ) : '' ); // Cleanup. $background_gradient_image = null; $background_gradient_blend = null; $parallax_gradient = null; // set `.et_parallax_bg_wrap` border-radius. et_set_parallax_bg_wrap_border_radius( $props, $this->slug, $this->main_css_element ); } // C.3. Hover parallax class. if ( '' !== $background_image && $is_hover ) { $this->add_classname( 'et_pb_section_parallax_hover' ); } // C.4. Sticky parallax class. if ( '' !== $background_image && $is_sticky ) { $this->add_classname( 'et_pb_section_parallax_sticky' ); } array_push( $parallax_processed, $suffix ); } // Added classname for module wrapper. if ( '' !== $parallax_background ) { $this->add_classname( 'et_pb_section_parallax' ); } return $parallax_background; } /** * Check if filters are used in a module. * * @since 4.10.0 * @return bool Use of filter. */ public function are_filters_used() { $filter_keys = array( 'filter_hue_rotate' => '0deg', 'filter_saturate' => '100%', 'filter_brightness' => '100%', 'filter_contrast' => '100%', 'filter_invert' => '0%', 'filter_sepia' => '0%', 'filter_opacity' => '100%', 'filter_blur' => '0px', 'mix_blend_mode' => 'normal', ); foreach ( $this->props as $attr => $value ) { if ( ! $value ) { continue; } foreach ( array_keys( $filter_keys ) as $filter_key ) { $is_attr = false !== strpos( $attr, $filter_key ); $default_value = $filter_keys[ $filter_key ]; if ( $is_attr && $value !== $default_value ) { return true; } } } return false; } /** * Generate CSS Filters * Check our shortcode arguments for CSS `filter` properties. If found, set the style rules for this block. (This * function reads options set by the 'Filters' and 'Image Filters' builder menu fields.) * * @since 3.23 Add responsive setting styling processing here. * @since 4.6.0 Add sticky style support. * * @param string $function_name Builder module's function name (keeps the CSS rules straight). * @param string $prefix Optional string prepended to the field name (i.e., `filter_saturate` -> `child_filter_saturate`). * @param mixed $selectors Array or string containing all target DOM element(s), ID(s), and/or class(es). * * @return string Any additional CSS classes (added if filters were applied). */ public function generate_css_filters( $function_name = '', $prefix = '', $selectors = array( '%%order_class%%' ) ) { if ( '' === $function_name ) { ET_Core_Logger::error( '$function_name is required.' ); return; } // If `$selectors` is a string, convert to an array before we continue. $selectors_prepared = $selectors; if ( ! is_array( $selectors ) ) { $selectors_prepared = explode( ',', et_core_intentionally_unescaped( $selectors, 'fixed_string' ) ); } $responsive_selectors = $selectors_prepared; $additional_classes = ''; // If we don't have a target selector, get out now. if ( ! $selectors_prepared ) { return $additional_classes; } $hover_suffix = et_pb_hover_options()->get_suffix(); $sticky = et_pb_sticky_options(); $sticky_suffix = $sticky->get_suffix(); $field_suffixes = array( '', 'tablet', 'phone', $hover_suffix, $sticky_suffix ); $filters_default = array(); $filters_default_fb = array(); $filters_hover = array(); $hover_selectors = array(); $filter_keys = array( 'hue_rotate', 'saturate', 'brightness', 'contrast', 'invert', 'sepia', 'opacity', 'blur', ); foreach ( $field_suffixes as $suffix ) { $sticky_mode = $sticky_suffix === $suffix; if ( $hover_suffix === $suffix ) { $selectors_prepared = array_map( array( $this, 'add_hover_to_selectors' ), $selectors_prepared ); } if ( $sticky_mode ) { $selectors_prepared = $sticky->add_sticky_to_selectors( $selectors, $this->is_sticky_module, false ); } // Mobile parameters. Update suffix and add media query argument for styles declaration. $device_suffix = ''; $media_query = array(); $is_mobile = in_array( $suffix, array( 'tablet', 'phone' ), true ); if ( $is_mobile ) { $breakpoint = 'tablet' === $suffix ? 'max_width_980' : 'max_width_767'; $media_query = array( 'media_query' => self::get_media_query( $breakpoint ) ); // For mobile, we need to reset $suffix and use $devie_suffix instead. Later on with // empty suffix, the filter will only return desktop value and will be used as default // and will be merged with filter mobile values. $device_suffix = "_{$suffix}"; $suffix = ''; } // Some web browser glitches with filters and blend modes can be improved this way // see https://bugs.chromium.org/p/chromium/issues/detail?id=157218 for more info. $backface_visibility = 'backface-visibility:hidden;'; $backface_visibility_added = array(); $additional_classes = ''; // Blend Mode. $mix_blend_mode = self::$data_utils->array_get( $this->props, "{$prefix}mix_blend_mode", '' ); // Filters. $filter = array(); $filter_names = array(); // Assign filter values and names. foreach ( $filter_keys as $filter_key ) { $filter_name = "{$prefix}filter_{$filter_key}"; $filter_names[] = $filter_name; $filter[ $filter_key ] = self::$data_utils->array_get( $this->props, "{$filter_name}{$suffix}", '' ); } $is_any_filter_responsive = et_pb_responsive_options()->is_any_responsive_enabled( $this->props, $filter_names ); $is_any_filter_hover_enabled = et_pb_hover_options()->is_any_hover_enabled( $this->props, $filter_names ); // For mobile, it should return any value exist if current device value is empty. if ( $is_mobile ) { // Blend Mode. $is_blend_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}mix_blend_mode" ); $mix_blend_mode = $is_blend_responsive ? et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}mix_blend_mode{$suffix}", '', true ) : ''; // Filters. $filters_mobile = array(); foreach ( $filter as $filter_key => $filter_value ) { if ( ! et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}filter_{$filter_key}" ) ) { continue; } $filters_mobile[ $filter_key ] = et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}filter_{$filter_key}{$device_suffix}", '', true ); } // If any responsive settings active on filter settings, set desktop value as default. if ( $is_any_filter_responsive ) { $filters_mobile = array_merge( $filter, $filters_mobile ); } // Replace current filter values with mobile filter values. $filter = $filters_mobile; } // Remove any filters with null or default values. $filter = array_filter( $filter, 'strlen' ); // Optional: CSS `mix-blend-mode` rule. $mix_blend_mode_default = ET_Global_Settings::get_value( 'all_mix_blend_mode', 'default' ); if ( $mix_blend_mode && $mix_blend_mode !== $mix_blend_mode_default ) { if ( ! $sticky_mode ) { foreach ( $selectors_prepared as $selector ) { $el_style = array_merge( array( 'selector' => $selector, 'declaration' => sprintf( 'mix-blend-mode: %1$s;', esc_html( $mix_blend_mode ) ) . $backface_visibility, ), $media_query ); self::set_style( $function_name, $el_style ); $backface_visibility_added[] = $selector; } } $additional_classes .= ' et_pb_css_mix_blend_mode'; } elseif ( 'et_pb_column' === $function_name ) { // Columns need to pass through. $additional_classes .= ' et_pb_css_mix_blend_mode_passthrough'; } // Optional: CSS `filter` rule. if ( empty( $filter ) ) { // Since we added responsive settings, the process should not be stopped here. // It should continue until tablet and phone are checked completely. Replace // return with continue. continue; } $css_value = array(); $css_value_fb_hover = array(); foreach ( $filter as $label => $value ) { // Check against our default settings, and only append the rule if it differs // (only for default state since hover and mobile might be equal to default, // ie. no filter on hover only). if ( ET_Global_Settings::get_value( 'all_filter_' . $label, 'default' ) === $value && $hover_suffix !== $suffix && ! $sticky_mode && ! $is_mobile && ! ( $is_any_filter_responsive && $is_any_filter_hover_enabled ) ) { continue; } // Don't apply hover filter if it is not enabled. if ( $hover_suffix === $suffix && ! et_pb_hover_options()->is_enabled( "{$prefix}filter_{$label}{$suffix}", $this->props ) ) { continue; } // Don't apply sticky filter if it is not enabled. if ( $sticky_mode && ! $sticky->is_enabled( "{$prefix}filter_{$label}{$suffix}", $this->props ) ) { continue; } $value = et_sanitize_input_unit( $value, false, 'deg' ); $label_css_format = str_replace( '_', '-', $label ); // Construct string of all CSS Filter values. $css_value[ $label ] = esc_html( "{$label_css_format}({$value})" ); // Construct Visual Builder hover rules. if ( ! in_array( $label, array( 'opacity', 'blur' ), true ) ) { // Skip those, because they mess with VB controls. $css_value_fb_hover[ $label ] = esc_html( "{$label_css_format}({$value})" ); } } // Append our new CSS rules. if ( $css_value ) { // Store the default (non-hover) filters. if ( '' === $suffix ) { $filters_default = $css_value; } // Merge the hover filters onto the default filters so that filters that // have no hover option set are not removed from the CSS declaration. if ( $hover_suffix === $suffix ) { $css_value = array_merge( $filters_default, $css_value ); $filters_hover = $css_value; } foreach ( $selectors_prepared as $selector ) { $backface_visibility_declaration = in_array( $selector, $backface_visibility_added, true ) ? '' : $backface_visibility; // Allow custom child filter target hover selector. if ( $hover_suffix === $suffix ) { if ( 'child_' === $prefix ) { $selector = self::$_->array_get( $this->advanced_fields, 'filters.child_filters_target.css.hover', $selector ); } $hover_selectors[] = $selector; } $el_style = array_merge( array( 'selector' => $selector, 'declaration' => sprintf( 'filter: %1$s;', implode( ' ', $css_value ) ) . $backface_visibility_declaration, ), $media_query ); self::set_style( $function_name, $el_style ); } // Add css for hover styles in sticky state. if ( $sticky_mode && ! empty( $filters_hover ) && ! empty( $hover_selectors ) ) { $sticky_hover_css_value = implode( ' ', $filters_hover ); foreach ( $hover_selectors as $hover_selector ) { $sticky_hover_selector = $sticky->add_sticky_to_order_class( $hover_selector, $this->is_sticky_module ); $el_style = array_merge( array( 'selector' => $sticky_hover_selector, 'declaration' => sprintf( 'filter: %1$s;', $sticky_hover_css_value ) . $backface_visibility_declaration, ), $media_query ); self::set_style( $function_name, $el_style ); } } $additional_classes .= ' et_pb_css_filters'; } // If we have VB hover-friendly CSS rules, we'll gather those and append them here. if ( $css_value_fb_hover ) { // Store the default (non-hover) filters. if ( '' === $suffix ) { $filters_default_fb = $css_value_fb_hover; } // Merge the hover filters onto the default filters so that filters that // have no hover option set are not removed from the CSS declaration. if ( $hover_suffix === $suffix ) { $css_value_fb_hover = array_merge( $filters_default_fb, $css_value_fb_hover ); } if ( ! $sticky_mode ) { foreach ( $selectors_prepared as $selector ) { $selector_hover = str_replace( '%%order_class%%', 'html:not(.et_fb_edit_enabled) #et-fb-app %%order_class%%:hover', $selector ); $el_style = array( 'selector' => $selector_hover, 'declaration' => esc_html( sprintf( 'filter: %1$s;', implode( ' ', $css_value_fb_hover ) ) ), ); self::set_style( $function_name, $el_style ); } } $additional_classes .= ' et_pb_css_filters_hover'; } } return $additional_classes; } /** * Convert classes array to a string. Also removes any duplicate classes * * @param array $classes A list of CSS classnames. * * @return array */ public function stringify_css_filter_classes( $classes ) { // Remove repeating classes. $classes = array_unique( $classes ); // Transform classes to a string. $classes = ' ' . implode( ' ', $classes ); return $classes; } /** * Adds a suffix at the end of the selector * E.g: add_suffix_to_selectors(':hover', '%%order_class%%% .image') >>> '%%order_class%%% .image:hover' * * @param string $suffix e.g ':hover'. * @param string $selector CSS selector. * * @return string */ public function add_suffix_to_selectors( $suffix, $selector ) { $selectors = explode( ',', $selector ); $selectors = array_map( 'trim', $selectors ); foreach ( $selectors as &$selector ) { $selector .= $suffix; } return implode( ', ', $selectors ); } /** * Adds `:hover` in selector at the end of the selector * E.g: add_hover_to_selectors('%%order_class%%% .image') >>> '%%order_class%%% .image:hover' * * @param string $selector CSS selector. * * @return string * * @deprecated Use et_pb_hover_options()->add_hover_to_selectors( $selector ); */ public function add_hover_to_selectors( $selector ) { return et_pb_hover_options()->add_hover_to_selectors( $selector ); } /** * Adds `:hover` in selector at the end of the selector if $add_hover is true * otherwise returns the original selector * * @param string $selector CSS selector. * @param bool $add_hover Whether to add hover on selector. * * @return string */ protected function _maybe_add_hover_to_selectors( $selector, $add_hover = false ) { return $add_hover ? et_pb_hover_options()->add_hover_to_selectors( $selector ) : $selector; } /** * Adds `:hover` in selector after `%%order_class%%` * E.g: add_hover_to_order_class('%%order_class%%% .image') >>> '%%order_class%%%:hover .image' * * @param string $selector CSS selector. * * @return string * * @deprecated Use et_pb_hover_options()->add_hover_to_order_class( $selector ); */ public function add_hover_to_order_class( $selector ) { return et_pb_hover_options()->add_hover_to_order_class( $selector ); } /** * Adds `:hover` to order class only if is specified, in other cse returns original selector * otherwise returns the original selector * * @param string $selector CSS selector. * @param bool $add_hover Whether to add hover on selector. * * @return string */ protected function _maybe_add_hover_to_order_class( $selector, $add_hover = false ) { return $add_hover ? et_pb_hover_options()->add_hover_to_order_class( $selector ) : $selector; } /** * Convert smart quotes and & entity to their applicable characters * * @param string $text Input text. * * @return string */ public static function convert_smart_quotes_and_amp( $text ) { $smart_quotes = array( '“', '”', '″', '‘', '’', ''', '&', ); $replacements = array( '"', '"', '"', ''', ''', ''', '&', ); if ( 'fr_FR' === get_locale() ) { $french_smart_quotes = array( ' »', '″>', ); $french_replacements = array( '"', '">', ); $smart_quotes = array_merge( $smart_quotes, $french_smart_quotes ); $replacements = array_merge( $replacements, $french_replacements ); } $text = str_replace( $smart_quotes, $replacements, $text ); return $text; } /** * Process multiple checkbox field value. * * @param array $value_map Checkbox value map. * @param string $value Checkbox value. * * @return string */ public function process_multiple_checkboxes_field_value( $value_map, $value ) { $result = array(); $index = 0; foreach ( explode( '|', $value ) as $checkbox_value ) { if ( 'on' === $checkbox_value ) { $result[] = $value_map[ $index ]; } $index++; } return implode( '|', $result ); } /** * Adds one or more CSS classes to the module on the frontend. * * @since 3.1 * * @param string|array $to_add classname(s) to be added. * @param number|bool $position position of added classname (0-based). Some class need to be placed * at exact position. i.e. .et_pb_column_{$type} on column inner. */ public function add_classname( $to_add, $position = false ) { if ( empty( $to_add ) ) { return; } $classname = is_array( $to_add ) ? $to_add : array( $to_add ); if ( is_numeric( $position ) ) { array_splice( $this->classname, intval( $position ), 0, $classname ); } else { $this->classname = array_merge( $this->classname, $classname ); } } /** * Removes one ore more CSS classes to the module on the frontend * * @since 3.1 * * @param string|array $to_remove classname(s) to be removed. */ public function remove_classname( $to_remove ) { $this->classname = array_filter( $this->classname ); if ( is_string( $to_remove ) && '' !== $to_remove ) { $this->classname = array_diff( $this->classname, array( $to_remove ) ); } elseif ( is_array( $to_remove ) ) { $to_remove = array_filter( $to_remove ); $this->classname = array_diff( $this->classname, $to_remove ); } } /** * Outputs module class * * @param string $function_name Module slug. * * @since 3.1 * * @return string escaped class */ public function module_classname( $function_name = '' ) { if ( ! in_array( $function_name, self::$uses_module_classname, true ) ) { // Add module slug to array of modules where `module_classname()` used. self::$uses_module_classname[] = $function_name; } $module_name = str_replace( 'et_pb_', '', $this->slug ); /** * Filters module classes. * * @since 3.1 * * @param array $classname Array of classnames. * @param int $render_count Number of times render function has been executed */ $classname = (array) array_unique( apply_filters( "et_builder_{$module_name}_classes", $this->classname, $this->render_count() ) ); return implode( ' ', array_map( 'esc_attr', $classname ) ); } /** * Outputs module id * * @since 3.1 * * @param bool $include_attribute wrap module id with id attribute name or not (to be used directly on module div). * * @return string module id / module id wrapped by id attribute */ public function module_id( $include_attribute = true ) { $module_id = esc_attr( $this->props['module_id'] ); $output = $include_attribute ? sprintf( ' id="%1$s"', $module_id ) : $module_id; return '' !== $module_id ? $output : ''; } /** * Helper method for rendering button markup which works compatible with advanced options' button * * @since 3.1 * * @param array $args button settings. * * @return string rendered button HTML */ public function render_button( $args = array() ) { // Prepare arguments. $defaults = array( 'button_id' => '', 'button_classname' => array(), 'button_custom' => '', 'button_rel' => '', 'button_text' => '', 'button_text_escaped' => false, 'button_url' => '', 'custom_icon' => '', 'custom_icon_tablet' => '', 'custom_icon_phone' => '', 'display_button' => true, 'has_wrapper' => true, 'url_new_window' => '', 'multi_view_data' => '', ); $args = wp_parse_args( $args, $defaults ); // Do not proceed if display_button argument is false. if ( ! $args['display_button'] ) { return ''; } $button_text = $args['button_text_escaped'] ? $args['button_text'] : esc_html( $args['button_text'] ); // Do not proceed if button_text argument is empty and not having multi view value. if ( '' === $button_text && ! $args['multi_view_data'] ) { return ''; } // Button classname. $button_classname = array( 'et_pb_button' ); // Add multi view CSS hidden helper class when button text is empty on desktop mode. if ( '' === $button_text && $args['multi_view_data'] ) { $button_classname[] = 'et_multi_view_hidden'; } if ( ! empty( $args['button_classname'] ) ) { $button_classname = array_merge( $button_classname, $args['button_classname'] ); } // Custom icon data attribute. $use_data_icon = '' !== $args['custom_icon'] && 'on' === $args['button_custom']; if ( $use_data_icon && et_pb_maybe_extended_icon( $args['custom_icon'] ) ) { $args['custom_icon'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon'] ) ); } $data_icon = $use_data_icon ? sprintf( ' data-icon="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon'] ) ) ) : ''; $use_data_icon_tablet = '' !== $args['custom_icon_tablet'] && 'on' === $args['button_custom']; if ( $use_data_icon_tablet && et_pb_maybe_extended_icon( $args['custom_icon_tablet'] ) ) { $args['custom_icon_tablet'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon_tablet'] ) ); } $data_icon_tablet = $use_data_icon_tablet ? sprintf( ' data-icon-tablet="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon_tablet'] ) ) ) : ''; $use_data_icon_phone = '' !== $args['custom_icon_phone'] && 'on' === $args['button_custom']; if ( $use_data_icon_phone && et_pb_maybe_extended_icon( $args['custom_icon_phone'] ) ) { $args['custom_icon_phone'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon_phone'] ) ); } $data_icon_phone = $use_data_icon_phone ? sprintf( ' data-icon-phone="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon_phone'] ) ) ) : ''; // Render button. return sprintf( '%7$s<a%9$s class="%5$s" href="%1$s"%3$s%4$s%6$s%10$s%11$s%12$s>%2$s</a>%8$s', esc_url( $args['button_url'] ), et_core_esc_previously( $button_text ), ( 'on' === $args['url_new_window'] ? ' target="_blank"' : '' ), et_core_esc_previously( $data_icon ), esc_attr( implode( ' ', array_unique( $button_classname ) ) ), // #5 et_core_esc_previously( $this->get_rel_attributes( $args['button_rel'] ) ), $args['has_wrapper'] ? '<div class="et_pb_button_wrapper">' : '', $args['has_wrapper'] ? '</div>' : '', '' !== $args['button_id'] ? sprintf( ' id="%1$s"', esc_attr( $args['button_id'] ) ) : '', et_core_esc_previously( $data_icon_tablet ), // #10 et_core_esc_previously( $data_icon_phone ), et_core_esc_previously( $args['multi_view_data'] ) ); } /** * Determine builder module is saving cache. * * @return mixed|void */ public static function is_saving_cache() { return apply_filters( 'et_builder_modules_is_saving_cache', false ); } /** * Get array of attributes which have dynamic content enabled. * * @since 3.17.2 * * @param mixed[] $attrs Module attributes. * * @return string[] */ protected function _get_enabled_dynamic_attributes( $attrs ) { $enabled_dynamic_attributes = isset( $attrs['_dynamic_attributes'] ) ? $attrs['_dynamic_attributes'] : ''; $enabled_dynamic_attributes = array_filter( explode( ',', $enabled_dynamic_attributes ) ); return $enabled_dynamic_attributes; } /** * Check if an attribute value is dynamic or not. * * @since 3.17.2 * * @param string $attribute Attribute name. * @param string $value Attribute value. * @param array $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return bool */ protected function _is_dynamic_value( $attribute, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $attribute, $enabled_dynamic_attributes, true ) ) { return false; } return et_builder_parse_dynamic_content( $value )->is_dynamic(); } /** * Re-encode legacy dynamic content values in an attrs array. * * @since 3.20.2 * * @param string[] $attrs Module attributes. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string[] */ protected function _encode_legacy_dynamic_content( $attrs, $enabled_dynamic_attributes ) { if ( is_array( $attrs ) ) { foreach ( $attrs as $field => $value ) { $attrs[ $field ] = $this->_encode_legacy_dynamic_content_value( $field, $value, $enabled_dynamic_attributes ); } } return $attrs; } /** * Re-encode legacy dynamic content value. * * @since 3.20.2 * * @param string $field Attribute name. * @param string $value Attribute value. * @param array $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string */ protected function _encode_legacy_dynamic_content_value( $field, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $json = et_builder_clean_dynamic_content( $value ); if ( preg_match( '/^@ET-DC@(.*?)@$/', $json ) ) { return $value; } return $this->_resolve_value_from_json( $field, $json, $enabled_dynamic_attributes ); } /** * Resolve a value, be it static or dynamic to a static one. * * @since 3.17.2 * * @param integer $post_id Current post id. * @param string $field Content key. * @param string $value Content value. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * @param boolean $serialize Whether value is serializable. * * @return string */ protected function _resolve_value( $post_id, $field, $value, $enabled_dynamic_attributes, $serialize ) { global $wp_query; if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $builder_value = et_builder_parse_dynamic_content( $value ); if ( $serialize ) { return $builder_value->serialize(); } $is_blog_query = isset( $wp_query->et_pb_blog_query ) && $wp_query->et_pb_blog_query; if ( ! $is_blog_query && ! $wp_query->is_singular() ) { return $builder_value->resolve( null ); } return $builder_value->resolve( $post_id ); } /** * Resolve a value from the legacy JSON format of dynamic content. * This is essentially a migration but is implemented separately * as it needs to parse every field of every module and do it * before actual migrations are ran. * * @since 3.20.2 * * @param string $field Field content. * @param string $value Json value. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string */ protected function _resolve_value_from_json( $field, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $json = et_builder_clean_dynamic_content( $value ); // Replace encoded quotes. $json = str_replace( array( '“', '”', '″', '%22' ), '"', $json ); // Strip <p></p> artifacts from wpautop in before/after settings. Example: // {"dynamic":true,"content":"post_title","settings":{"before":"</p> // <h1>","after":"</h1> // <p>"}} // This is a rough solution implemented due to time constraints. $json = preg_replace( '~ ("(?:before|after)":") # $1 = Anchor to the before/after settings. (?: # Match cases where the value starts with the offending tag. <\/?p> # The root of all evil. [\r\n]+ # Whitespace follows the tag. )* (?: # Match cases where the value ends with the offending tag. ([^"]*) # $2 = The preceeding value. [\r\n]+ # Whitespace preceedes the tag. <\/?p> # The root of all evil. )* ~xi', '$1$2', $json ); // Remove line-breaks which break the json strings. $json = preg_replace( '/\r|\n/', '', $json ); $json_value = et_builder_parse_dynamic_content_json( $json ); if ( null === $json_value ) { return $value; } return $json_value->serialize(); } /** * Escape an attribute's value. * * @since 3.17.2 * * @param string $attribute Attribute name. * @param string $html 'limited', 'full', 'none'. * @param string $predefined_value Predifined value need to escape. * * @return string */ protected function _esc_attr( $attribute, $html = 'none', $predefined_value = null ) { $html = in_array( $html, array( 'limited', 'full' ), true ) ? $html : 'none'; $raw = isset( $this->attrs_unprocessed[ $attribute ] ) ? $this->attrs_unprocessed[ $attribute ] : ''; $formatted = isset( $this->props[ $attribute ] ) ? $this->props[ $attribute ] : ''; $dynamic_attributes = $this->_get_enabled_dynamic_attributes( $this->props ); // More often than not content is not an attribute so we need to handle that special case. if ( 'content' === $attribute && ! isset( $this->attrs_unprocessed[ $attribute ] ) ) { $raw = $this->content_unprocessed; $formatted = $this->content; } if ( ! is_null( $predefined_value ) ) { $formatted = $predefined_value; } if ( ! $this->_is_dynamic_value( $attribute, $raw, $dynamic_attributes ) ) { if ( 'full' === $html ) { return $formatted; } return esc_html( $formatted ); } if ( 'limited' === $html ) { return wp_kses( $formatted, array( 'strong' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), 'em' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), 'i' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), ) ); } // Dynamic content values are escaped when they are resolved so we do not want to // double-escape them when using them in the frontend, for example. return et_core_esc_previously( $formatted ); } /** * Get the current TB layout ID if we are rendering one or the current post ID instead. * * @since 4.0 * @since 4.14.8 Get WP Template ID if we are rendering Divi Builder block in template. * * @return integer */ public static function get_layout_id() { // TB Layout ID. $layout_id = self::get_theme_builder_layout_id(); if ( $layout_id ) { return $layout_id; } // WP Template ID. $template_id = self::get_wp_editor_template_id(); if ( $template_id ) { return $template_id; } // Post ID by default. return self::get_current_post_id_reverse(); } /** * Get the current theme builder layout. * Returns 'default' if no layout has been started. * * @since 4.0 * * @return string */ public static function get_theme_builder_layout_type() { $count = count( self::$theme_builder_layout ); if ( $count > 0 ) { return self::$theme_builder_layout[ $count - 1 ]['type']; } return 'default'; } /** * Check if a module is rendered as normal post content or theme builder layout. * * @since 4.0 * * @return bool */ public static function is_theme_builder_layout() { return 'default' !== self::get_theme_builder_layout_type(); } /** * Get the current theme builder layout id. * Returns 0 if no layout has been started. * * @since 4.0 * * @return integer */ public static function get_theme_builder_layout_id() { $count = count( self::$theme_builder_layout ); if ( $count > 0 ) { return self::$theme_builder_layout[ $count - 1 ]['id']; } return 0; } /** * Begin a theme builder layout. * * @since 4.0 * * @param integer $layout_id Layout post id. * * @return void */ public static function begin_theme_builder_layout( $layout_id ) { $type = get_post_type( $layout_id ); if ( ! et_theme_builder_is_layout_post_type( $type ) ) { $type = 'default'; } self::$theme_builder_layout[] = array( 'id' => (int) $layout_id, 'type' => $type, ); } /** * End the current theme builder layout. * * @since 4.0 * * @return void */ public static function end_theme_builder_layout() { array_pop( self::$theme_builder_layout ); } /** * Get the order class suffix for the current theme builder layout, if any. * * @since 4.0 * * @return string */ protected static function _get_theme_builder_order_class_suffix() { $layout_type = self::get_theme_builder_layout_type(); $type_map = array( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE => '_tb_header', ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE => '_tb_body', ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE => '_tb_footer', ); if ( empty( $layout_type ) || ! isset( $type_map[ $layout_type ] ) ) { return ''; } return $type_map[ $layout_type ]; } /** * Begin Divi Builder block output on WP Editor template. * * As identifier od Divi Builder block render template location and the template ID. * Introduced to handle Divi Layout block render on WP Template outside Post Content. * WP Editor templates: * - wp_template * - wp_template_part * * @since 4.14.8 * * @param array $template_id Template post ID. * * @return void */ public static function begin_wp_editor_template( $template_id ) { $type = get_post_type( $template_id ); if ( ! et_builder_is_wp_editor_template_post_type( $type ) ) { $type = 'default'; } self::$wp_editor_template[] = array( 'id' => (int) $template_id, 'type' => $type, ); } /** * End Divi Builder block output on WP Editor template. * * @since 4.14.8 * * @return void */ public static function end_wp_editor_template() { array_pop( self::$wp_editor_template ); } /** * Whether a module is rendered in WP Editor template or not. * * @since 4.14.8 * * @return bool WP Editor template status. */ public static function is_wp_editor_template() { return 'default' !== self::get_wp_editor_template_type(); } /** * Get the current WP Editor template id. * * Returns 0 if no template has been started. * * @since 4.14.8 * * @return integer Template post ID (wp_id). */ public static function get_wp_editor_template_id() { $count = count( self::$wp_editor_template ); $id = 0; if ( $count > 0 ) { $id = et_()->array_get( self::$wp_editor_template, array( $count - 1, 'id' ), 0 ); } // Just want to be safe to not return any unexpected result. return is_int( $id ) ? $id : 0; } /** * Get the current WP Editor template type. * * Returns 'default' if no template has been started. * * @since 4.14.8 * * @param boolean $is_id_needed Whether template ID is needed or not. * * @return string Template type. */ public static function get_wp_editor_template_type( $is_id_needed = false ) { $count = count( self::$wp_editor_template ); $type = ''; if ( $count > 0 ) { $type = et_()->array_get( self::$wp_editor_template, array( $count - 1, 'type' ) ); // Page may have more than one template parts. So, the wp_id is needed in certain // situation as unique identifier. if ( $is_id_needed && ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE === $type ) { $id = self::get_wp_editor_template_id(); $type .= "-{$id}"; } } // Just want to be safe to not return any unexpected result. return ! empty( $type ) && is_string( $type ) ? $type : 'default'; } /** * Get the order class suffix for the current WP Editor template, if any. * * @since 4.14.8 * * @return string Order class suffix. */ protected static function _get_wp_editor_order_class_suffix() { $template_type = self::get_wp_editor_template_type(); $type_map = array( ET_WP_EDITOR_TEMPLATE_POST_TYPE => '_wp_template', ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE => '_wp_template_part', ); if ( ! isset( $type_map[ $template_type ] ) ) { return ''; } $suffix = $type_map[ $template_type ]; // Page may have more than one template parts. So, the wp_id is needed identifier. if ( ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE === $template_type ) { $id = self::get_wp_editor_template_id(); $suffix .= "-{$id}"; } return $suffix; } /** * Convert field name into css property name. * * @param string $field Field name. * * @return string|string[] */ protected function field_to_css_prop( $field ) { return str_replace( '_', '-', $field ); } /** * Initialize Modules Cache. * * @since 3.24 */ public static function init_cache() { $cache = self::get_cache_filename(); if ( $cache && et_()->WPFS()->is_readable( $cache ) ) { // Load cache. $result = @unserialize( et_()->WPFS()->get_contents( $cache ) ); if ( false !== $result ) { if ( count( $result ) < 3 ) { // Old cache format detected, delete everything. et_fb_delete_builder_assets(); if ( ! file_exists( $cache ) ) { // If cache has been successfully deleted, then init again. self::init_cache(); } return; } list ( self::$_cache, self::$_fields_unprocessed ) = $result; // Define option template variable instead of using list to avoid error that might // happen when option template file exists (theme is updated) and frontend is // accessed while static module field data hasn't been updated. $cached_option_template_data = et_()->array_get( $result, '2', array() ); $cached_option_template = et_()->array_get( $result, '3', array() ); $cached_option_template_tab_slug_maps = et_()->array_get( $result, '4', array() ); // init_cache() is called really early. $template property might not be available yet. if ( null === self::$option_template ) { self::$option_template = et_pb_option_template(); } // Set option template data from static cache if exist. if ( is_array( $cached_option_template_data ) && ! empty( $cached_option_template_data ) ) { self::$option_template->set_data( $cached_option_template_data ); } // Set option template from static cache if exist. if ( is_array( $cached_option_template ) && ! empty( $cached_option_template ) ) { self::$option_template->set_templates( $cached_option_template ); } // Set option template tab slug maps from static cache if exist. if ( is_array( $cached_option_template_tab_slug_maps ) && ! empty( $cached_option_template_tab_slug_maps ) ) { self::$option_template->set_tab_slug_map( $cached_option_template_tab_slug_maps ); } // Box Shadow sets WP hooks internally so we gotta load it anyway -> #blame_george. ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); } else { // Cache couldn't be unserialized, delete the file so it will be regenerated. @unlink( $cache ); } } elseif ( $cache ) { // Only save cache when a builder page is being rendered, needed because some data // (e.g. mail provider defaults) is only generated in this case, hence saving while rendering // a FE page or during AJAX call would result in cache missing data. self::$_cache = array(); add_filter( 'et_builder_modules_is_saving_cache', '__return_true' ); add_filter( 'et_builder_should_load_all_module_data', '__return_true' ); add_action( 'et_builder_ready', array( 'ET_Builder_Element', 'save_cache' ) ); } } /** * Get Modules cache file name. * * @param mixed $post_type When set to `false`, autodetect. * * @since 3.24 * * @return bool|mixed|string */ public static function get_cache_filename( $post_type = false ) { global $post, $et_builder_post_type; $ajax_use_cache = apply_filters( 'et_builder_ajax_use_cache', false ); if ( false === $post_type ) { if ( is_a( $post, 'WP_POST' ) ) { $post_type = $post->post_type; } elseif ( $ajax_use_cache ) { // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified in the ajax request. $post_type = et_()->array_get( $_POST, 'et_post_type', 'page' ); } elseif ( is_admin() && ! wp_doing_ajax() ) { $post_type = 'page'; $et_builder_post_type = $post_type; } if ( false === $post_type ) { return false; } } $post_type = apply_filters( 'et_builder_cache_post_type', $post_type, 'modules' ); $post_type = trim( sanitize_file_name( $post_type ), '.' ); // Per language Cache due to fields data being localized. // Use user custom locale only if admin or VB/BFB. $lang = is_admin() || et_fb_is_enabled() ? get_user_locale() : get_locale(); $lang = trim( sanitize_file_name( $lang ), '.' ); $prefix = 'modules'; $cache = sprintf( '%s/%s', ET_Core_PageResource::get_cache_directory(), $lang ); $files = glob( sprintf( '%s/%s-%s-*.data', $cache, $prefix, $post_type ) ); $exists = is_array( $files ) && $files; if ( $exists ) { return $files[0]; } elseif ( $ajax_use_cache ) { // Allowlisted AJAX requests aren't allowed to generate cache, only to use it. return false; } wp_mkdir_p( $cache ); // Create uniq filename. $uniq = str_replace( '.', '', (string) microtime( true ) ); $file = sprintf( '%s/%s-%s-%s.data', $cache, $prefix, $post_type, $uniq ); return wp_is_writable( dirname( $file ) ) ? $file : false; } /** * Get Module cache file name's id. * * @since 3.28 * * @param mixed $post_type When set to `false`, autodetect. * * @return bool|string */ public static function get_cache_filename_id( $post_type = false ) { $filename = self::get_cache_filename( $post_type ); if ( ! is_string( $filename ) ) { return false; } preg_match( '/(?<=-)[0-9]*(?=.data)/', $filename, $matches ); return isset( $matches[0] ) ? $matches[0] : false; } /** * Save the builder module caache. */ public static function save_cache() { remove_filter( 'et_builder_modules_is_saving_cache', '__return_true' ); $cache = self::get_cache_filename(); if ( $cache ) { et_()->WPFS()->put_contents( $cache, serialize( array( self::$_cache, self::$_fields_unprocessed, self::$option_template->all(), self::$option_template->templates(), self::$option_template->get_tab_slug_map(), '3.0', ) ) ); } } /** * Render image element HTML * * @since 3.27.1 * * @param string $image_props Image data props key or actual image URL. * @param array $image_attrs_raw List of extra image attributes. * @param bool $echo Whether to print the image output or return it. * @param bool $disable_responsive Whether to enable the responsive image or not. * * @return string The images's HTML output. Empty string on failure. */ protected function render_image( $image_props, $image_attrs_raw = array(), $echo = true, $disable_responsive = false ) { // Bail early when the $image_props arg passed is empty. if ( ! $image_props ) { return ''; } $img_src = $image_props && is_string( $image_props ) ? self::$_->array_get( $this->props, $image_props, $image_props ) : $image_props; if ( ! $img_src ) { return ''; } if ( ! count( $image_attrs_raw ) ) { $html = sprintf( '<img src="%1$s" />', esc_url( $img_src ) ); return et_image_add_srcset_and_sizes( $html, $echo ); } $image_attrs = array(); $is_disable_responsive = $disable_responsive || ! et_is_responsive_images_enabled(); foreach ( $image_attrs_raw as $name => $value ) { // Skip src attributes key. if ( 'src' === $name ) { continue; } // Skip srcset & sizes attributes when setting is off. if ( $is_disable_responsive && in_array( $name, array( 'srcset', 'sizes' ), true ) ) { continue; } // Skip if attributes value is empty. if ( ! strlen( $value ) ) { continue; } // Format as JSON if the value is array or object. if ( is_array( $value ) || is_object( $value ) ) { $value = wp_json_encode( $value ); } // Trim extra space from attributes value. $value = trim( $value ); // Standalone attributes that act as Booleans (Numerical indexed array keys such as required, disabled, multiple). if ( is_numeric( $name ) ) { $value = et_core_esc_attr( $value, $value ); if ( ! is_wp_error( $value ) ) { $image_attrs[ $value ] = et_core_esc_previously( $value ); } } else { $value = et_core_esc_attr( $name, $value ); if ( ! is_wp_error( $value ) ) { $image_attrs[ $name ] = esc_attr( $name ) . '="' . et_core_esc_previously( $value ) . '"'; } } } $html = sprintf( '<img src="%1$s" %2$s />', esc_url( $img_src ), et_core_esc_previously( implode( ' ', $image_attrs ) ) ); if ( ! $is_disable_responsive && ! isset( $image_attrs['srcset'] ) && ! isset( $image_attrs['sizes'] ) ) { $html = et_image_add_srcset_and_sizes( $html, false ); } if ( ! $echo ) { return $html; } echo et_core_intentionally_unescaped( $html, 'html' ); } /** * Get advanced field settings exposed for layout block preview * * @since 4.3.2 * * @return array */ public static function get_layout_block_assistive_settings() { return self::$layout_block_assistive_settings; } /** * Get whether the provided element content contains at least one of the * specified modules based on their slugs. * * @since 4.3.3 * * @param string $content Element content. * @param string[] $module_slugs Module slug to search. * * @return bool */ protected static function contains( $content, $module_slugs ) { foreach ( $module_slugs as $slug ) { if ( false !== strpos( $content, '[' . $slug ) ) { return true; } } return false; } /** * Public access provider for self::contains(). * * @since 4.17.4 * * @param string $content Element content. * @param string[] $module_slugs Module slug to search. * * @return bool */ public static function module_contains( $content, $module_slugs ) { return self::contains( $content, $module_slugs ); } /** * Generate background field setting properties by template * * NOTE: Unless the `priority` property is used, the order that settings are listed is the order * that they'll appear in the settings modal. * * @param string $field_template Field template slug. * @param array $overrides Field properties to override. * @param array $unsets Field properties to unset. * * @return array * * @since 4.8.0 * */ public static function background_field_template( $field_template, $overrides = array(), $unsets = array() ) { static $cache = null; if ( is_null( $cache ) ) { $cache = array(); $tabs = array( // Tab: color, priority start with 100. 'color' => array( 'priority' => 100, 'data' => array( 'enable_color' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color' => array( 'type' => 'color-alpha', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'use_color' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: gradient, priority start with 200. 'gradient' => array( 'priority' => 200, 'data' => array( 'use_color_gradient' => array( 'priority' => 201, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'color_gradient_repeat' => array( 'priority' => 205, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_type' => array( 'priority' => 203, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_direction' => array( 'priority' => 204, 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'show_if' => array( 'color_gradient_type' => array( 'conic', 'linear', ), ), ), 'color_gradient_direction_radial' => array( 'priority' => 204, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'show_if' => array( 'color_gradient_type' => array( 'radial', 'circular', 'elliptical', ), ), ), 'color_gradient_stops' => array( 'priority' => 202, 'type' => 'gradient-stops', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_unit' => array( 'priority' => 206, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_overlays_image' => array( 'priority' => 299, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_start' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_start_position' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'option_category' => 'configuration', ), // Deprecated. 'color_gradient_end' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_end_position' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: image, priority start with 300. 'image' => array( 'priority' => 300, 'data' => array( 'enable_image' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'parallax' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'parallax_method' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'size' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image_width' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image_height' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'position' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'horizontal_offset' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'vertical_offset' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'repeat' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'blend' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: video, priority start with 400. 'video' => array( 'priority' => 400, 'data' => array( 'enable_video_mp4' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_mp4' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'enable_video_webm' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_webm' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'video_width' => array( 'type' => 'text', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_height' => array( 'type' => 'text', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'allow_player_pause' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_pause_outside_viewport' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_computed' => array( 'type' => 'computed', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: pattern, priority start with 500. 'pattern' => array( 'priority' => 500, 'data' => et_ph_pattern_field_templates(), ), // Tab: mask, priority start with 500. 'mask' => array( 'priority' => 600, 'data' => et_ph_mask_field_templates(), ), ); foreach ( $tabs as $background_tab_slug => $tab ) { $priority = $tab['priority']; foreach ( $tab['data'] as $template_slug => $template_data ) { $template_priority = isset( $template_data['priority'] ) && is_numeric( $template_data['priority'] ) ? $template_data['priority'] : $priority; if ( $template_priority < $tab['priority'] ) { $template_priority += $tab['priority']; } $cache[ $template_slug ] = array_merge( $template_data, array( 'field_template' => $template_slug, 'background_tab' => $background_tab_slug, 'priority' => $template_priority, 'tab_slug' => 'general', 'toggle_slug' => 'background', ) ); $priority += 10; } } } $template = isset( $cache[ $field_template ] ) ? $cache[ $field_template ] : array(); if ( $overrides && is_array( $overrides ) ) { $template = array_merge( $template, $overrides ); } if ( $unsets && is_array( $unsets ) ) { foreach ( $unsets as $unset ) { unset( $template[ $unset ] ); } } return array_merge( $template, array( 'field_template' => $field_template, ) ); } /** * Generate markup for background pattern. * * @since 4.15.0 * * @param array $args Props. * @param string $base_name Background base name. * * @return string Background pattern markup. */ public function background_pattern( $args = array(), $base_name = 'background' ) { $props = ! empty( $args ) ? (array) $args : $this->props; $modes = array( 'desktop', 'hover', 'sticky', 'tablet', 'phone' ); $is_active = false; foreach ( $modes as $mode ) { // Checks whether pattern style is enabled and has value. $style_name = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$base_name}_pattern_style", $mode, 'background', $this->fields_unprocessed ); if ( ! empty( $style_name ) ) { // We need to render pattern element. $is_active = true; break; } } $output = ''; if ( $is_active ) { $class_name = array( 'et_pb_background_pattern', ); $output = sprintf( '<span class="%1$s"></span>', implode( ' ', array_map( 'esc_attr', $class_name ) ) ); } return $output; } /** * Generate markup for background mask. * * @since 4.15.0 * * @param array $args Props. * @param string $base_name Background base name. * * @return string Background mask markup. */ public function background_mask( $args = array(), $base_name = 'background' ) { $props = ! empty( $args ) ? (array) $args : $this->props; $modes = array( 'desktop', 'hover', 'sticky', 'tablet', 'phone' ); $is_active = false; foreach ( $modes as $mode ) { // Checks whether mask style is enabled and has value. $style_name = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$base_name}_mask_style", $mode, 'background', $this->fields_unprocessed ); if ( ! empty( $style_name ) ) { // We need to render mask element. $is_active = true; break; } } $output = ''; if ( $is_active ) { $class_name = array( 'et_pb_background_mask', ); $output = sprintf( '<span class="%1$s"></span>', implode( ' ', array_map( 'esc_attr', $class_name ) ) ); } return $output; } /** * Get components before & after module. * * This method is introduced to handle additional components added on WooCommerce * product summary that should be moved to any suitable modules on builder preview. * * @since 4.14.5 * * @param string $module_slug Module slug. * @param array $module_data Module data passed. * * @return array Components before & after module. */ public static function get_component_before_after_module( $module_slug, $module_data ) { // Default processed components for the return request. Need to use `__` to be set // as the attribute name. $processed_components = array( 'has_components' => false, '__before_component' => '', '__after_component' => '', ); /** * Filters module's before & after components for current builder page. * * @since 4.14.5 * * @param array $processed_components Default of module's before & after components. * @param string $module_slug Module slug. * @param array $module_data Module data passed from the request. */ $components = apply_filters( "{$module_slug}_fb_before_after_components", $processed_components, $module_slug, $module_data ); // Skip if there is no before & after components. $has_components = et_()->array_get( $components, 'has_components' ); if ( true !== $has_components ) { return $processed_components; } $processed_components = array( 'has_components' => true, '__before_component' => et_()->array_get( $components, '__before_component' ), '__after_component' => et_()->array_get( $components, '__after_component' ), ); return $processed_components; } /** * ================================================================================================================ * -------------------------->>> Class-level (static) deprecations begin here! <<<--------------------------------- * ================================================================================================================ */ // phpcs:disable -- Deprecated functions. /** * @deprecated See {@see self::get_parent_slugs_regex()} */ public static function get_parent_shortcodes( $post_type ) { $method = __METHOD__; $replacement = __CLASS__ . '::get_parent_slugs_regex()'; et_error( "You're Doing It Wrong! {$method} is deprecated. Use {$replacement} instead." ); return self::get_parent_slugs_regex( $post_type ); } /** * @deprecated See {@see self::get_child_slugs_regex()} */ public static function get_child_shortcodes( $post_type ) { $method = __METHOD__; $replacement = __CLASS__ . '::get_child_slugs_regex()'; et_error( "You're Doing It Wrong! {$method} is deprecated. Use {$replacement} instead." ); return self::get_child_slugs_regex( $post_type ); } /** * Deprecated. * * @deprecated * * @param string $post_type * @param string $mode * * @return array */ public static function get_defaults( $post_type = '', $mode = 'all' ) { et_error( "You're Doing It Wrong! " . __METHOD__ . ' is deprecated and should not be used.' ); return array(); } /** * Deprecated. * * @deprecated * * @param string $post_type * @param string $mode * * @return array */ public static function get_fields_defaults( $post_type = '', $mode = 'all' ) { et_error( "You're Doing It Wrong! " . __METHOD__ . ' is deprecated and should not be used.' ); return array(); } /** * @deprecated */ public static function get_slugs_with_children( $post_type ) { $parent_modules = self::get_parent_modules( $post_type ); $slugs = array(); foreach ( $parent_modules as $module ) { if ( ! empty( $module->child_slug ) ) { $slugs[] = sprintf( '"%1$s":"%2$s"', esc_js( $module->slug ), esc_js( $module->child_slug ) ); } } return '{' . implode( ',', $slugs ) . '}'; } // phpcs:enable /** * ================================================================================================================ * ------------------------------->>> Non-static deprecations begin here! <<<-------------------------------------- * ================================================================================================================ */ /** * Determine if current request is VB Data Request by checking $_POST['action'] value * * @deprecated {@see et_builder_is_loading_vb_data()} * * @since 4.0.7 Deprecated. * * @return bool */ protected function is_loading_vb_data() { return et_builder_is_loading_data(); } /** * Determine if current request is BB Data Request by checking $_POST['action'] value * * @deprecated {@see et_builder_is_loading_bb_data()} * * @since 4.0.7 Deprecated. * * @return bool */ protected function is_loading_bb_data() { return et_builder_is_loading_data( 'bb' ); } /* NOTE: Adding a new method? New methods should be placed BEFORE deprecated methods. */ } do_action( 'et_pagebuilder_module_init' ); /** * Base class for module. * * Class ET_Builder_Module */ class ET_Builder_Module extends ET_Builder_Element { /** * Whether element is structure element. * * @var bool */ public $is_structure_element = false; /** * Name of the folder under which the module should fall into.. * * @var string */ public $folder_name; } /** * Base class for structure elements. * * Class ET_Builder_Structure_Element */ class ET_Builder_Structure_Element extends ET_Builder_Element { /** * Whether element is structure element. * * @var bool */ public $is_structure_element = true; /** * BB :: Wrap setting option in parent div. * * @param string $option_output Setting options markup. * @param array $field Setting field. * @param string $name Setting field name e.g background_color. * * @return string|string[] */ public function wrap_settings_option( $option_output, $field, $name = '' ) { // Option template convert array field into string id; return early to prevent error. if ( is_string( $field ) ) { return ''; } $field_type = ! empty( $field['type'] ) ? $field['type'] : ''; switch ( $field_type ) { case 'column_settings_background': $output = $this->generate_columns_settings_background(); $field['hover'] = 'tabs'; break; case 'column_settings_padding': $output = $this->generate_columns_settings_padding(); break; case 'column_settings_css_fields': $output = $this->generate_columns_settings_css_fields(); break; case 'column_settings_css': $output = $this->generate_columns_settings_css(); break; case 'column-structure': // column structure option is not supported in BB. return ''; break; default: $depends = false; $new_depends = isset( $field['show_if'] ) || isset( $field['show_if_not'] ); if ( ! $new_depends && ( isset( $field['depends_show_if'] ) || isset( $field['depends_show_if_not'] ) ) ) { $depends = true; if ( isset( $field['depends_show_if_not'] ) ) { $depends_show_if_not = is_array( $field['depends_show_if_not'] ) ? implode( ',', $field['depends_show_if_not'] ) : $field['depends_show_if_not']; $depends_attr = sprintf( ' data-depends_show_if_not="%s"', esc_attr( $depends_show_if_not ) ); } else { $depends_attr = sprintf( ' data-depends_show_if="%s"', esc_attr( $field['depends_show_if'] ) ); } } // Overriding background color's attribute, turning it into appropriate background attributes. if ( isset( $field['type'] ) && isset( $field['name'] ) && in_array( $field['name'], array( 'background_color' ), true ) ) { $field['type'] = 'background'; // Appending background class. if ( isset( $field['option_class'] ) ) { $field['option_class'] .= ' et-pb-option--background'; } else { $field['option_class'] = 'et-pb-option--background'; } // Removing depends default variable which hides background color for unified background field UI. $depends = false; if ( isset( $field['depends_show_if'] ) ) { unset( $field['depends_show_if'] ); } } $output = sprintf( '%6$s<div class="et-pb-option et-pb-option--%11$s%1$s%2$s%3$s%8$s%9$s%10$s%13$s"%4$s data-option_name="%12$s">%5$s</div>%7$s', ( ! empty( $field['type'] ) && 'tiny_mce' === $field['type'] ? ' et-pb-option-main-content' : '' ), $depends || $new_depends ? ' et-pb-depends' : '', ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? ' et_pb_hidden' : '' ), ( $depends ? $depends_attr : '' ), "\n\t\t\t\t" . $option_output . "\n\t\t\t", "\t", "\n\n\t\t", ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? esc_attr( sprintf( ' et-pb-option-%1$s', $field['name'] ) ) : '' ), ( ! empty( $field['option_class'] ) ? ' ' . $field['option_class'] : '' ), isset( $field['specialty_only'] ) && 'yes' === $field['specialty_only'] ? ' et-pb-specialty-only-option' : '', isset( $field['type'] ) ? esc_attr( $field['type'] ) : '', esc_attr( $field['name'] ), $new_depends ? ' et-pb-new-depends' : '' ); break; } if ( ! empty( $field['hover'] ) ) { if ( 'tabs' === $field['hover'] ) { $name = ( 'columns_background' === $name ) ? 'background_color_<%= counter %>' : $name; $this->last_hover_tab_field = $name; } $hover = $this->last_hover_tab_field; if ( $hover ) { $begin = '<div class="et-pb-option '; $pos = strpos( $output, $begin ); if ( $pos >= 0 ) { $output = substr_replace( $output, "<div data-depends_hover=\"$hover\" class=\"et-pb-option-standard et-pb-option ", $pos, strlen( $begin ) ); } } } return self::get_unique_bb_key( $output ); } /** * BB :: Generate custom css values for column padding settings. * * @return string */ public function generate_column_vars_css() { $output = ''; for ( $i = 1; $i < 4; $i++ ) { $output .= sprintf( 'case %1$s : current_module_id_value = typeof et_pb_module_id_%1$s !== \'undefined\' ? et_pb_module_id_%1$s : \'\', current_module_class_value = typeof et_pb_module_class_%1$s !== \'undefined\' ? et_pb_module_class_%1$s : \'\', current_custom_css_before_value = typeof et_pb_custom_css_before_%1$s !== \'undefined\' ? et_pb_custom_css_before_%1$s : \'\', current_custom_css_main_value = typeof et_pb_custom_css_main_%1$s !== \'undefined\' ? et_pb_custom_css_main_%1$s : \'\', current_custom_css_after_value = typeof et_pb_custom_css_after_%1$s !== \'undefined\' ? et_pb_custom_css_after_%1$s : \'\'; break; ', esc_attr( $i ) ); } return $output; } /** * BB :: Generate background values for column padding settings. * * @return string */ public function generate_column_vars_bg() { $output = ''; for ( $i = 1; $i < 4; $i++ ) { $output .= sprintf( 'case %1$s : current_value_bg = typeof et_pb_background_color_%1$s !== \'undefined\' ? et_pb_background_color_%1$s : \'\', current_value_bg_img = typeof et_pb_bg_img_%1$s !== \'undefined\' ? et_pb_bg_img_%1$s : \'\'; current_background_size_cover = typeof et_pb_background_size_%1$s !== \'undefined\' && et_pb_background_size_%1$s === \'cover\' ? \' selected="selected"\' : \'\'; current_background_size_contain = typeof et_pb_background_size_%1$s !== \'undefined\' && et_pb_background_size_%1$s === \'contain\' ? \' selected="selected"\' : \'\'; current_background_size_initial = typeof et_pb_background_size_%1$s !== \'undefined\' && et_pb_background_size_%1$s === \'initial\' ? \' selected="selected"\' : \'\'; current_background_position_topleft = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'top_left\' ? \' selected="selected"\' : \'\'; current_background_position_topcenter = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'top_center\' ? \' selected="selected"\' : \'\'; current_background_position_topright = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'top_right\' ? \' selected="selected"\' : \'\'; current_background_position_centerleft = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'center_left\' ? \' selected="selected"\' : \'\'; current_background_position_center = typeof et_pb_background_position_%1$s === \'undefined\' || et_pb_background_position_%1$s === \'center\' ? \' selected="selected"\' : \'\'; current_background_position_centerright = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'center_right\' ? \' selected="selected"\' : \'\'; current_background_position_bottomleft = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'bottom_left\' ? \' selected="selected"\' : \'\'; current_background_position_bottomcenter = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'bottom_center\' ? \' selected="selected"\' : \'\'; current_background_position_bottomright = typeof et_pb_background_position_%1$s !== \'undefined\' && et_pb_background_position_%1$s === \'bottom_right\' ? \' selected="selected"\' : \'\'; current_background_repeat_repeat = typeof et_pb_background_repeat_%1$s === \'undefined\' || et_pb_background_repeat_%1$s === \'repeat\' ? \' selected="selected"\' : \'\'; current_background_repeat_repeatx = typeof et_pb_background_repeat_%1$s !== \'undefined\' && et_pb_background_repeat_%1$s === \'repeat-x\' ? \' selected="selected"\' : \'\'; current_background_repeat_repeaty = typeof et_pb_background_repeat_%1$s !== \'undefined\' && et_pb_background_repeat_%1$s === \'repeat-y\' ? \' selected="selected"\' : \'\'; current_background_repeat_space = typeof et_pb_background_repeat_%1$s !== \'undefined\' && et_pb_background_repeat_%1$s === \'space\' ? \' selected="selected"\' : \'\'; current_background_repeat_round = typeof et_pb_background_repeat_%1$s !== \'undefined\' && et_pb_background_repeat_%1$s === \'round\' ? \' selected="selected"\' : \'\'; current_background_repeat_norepeat = typeof et_pb_background_repeat_%1$s !== \'undefined\' && et_pb_background_repeat_%1$s === \'no-repeat\' ? \' selected="selected"\' : \'\'; current_background_blend_normal = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'normal\' ? \' selected="selected"\' : \'\'; current_background_blend_multiply = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'multiply\' ? \' selected="selected"\' : \'\'; current_background_blend_screen = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'screen\' ? \' selected="selected"\' : \'\'; current_background_blend_overlay = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'overlay\' ? \' selected="selected"\' : \'\'; current_background_blend_darken = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'darken\' ? \' selected="selected"\' : \'\'; current_background_blend_lighten = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'lighten\' ? \' selected="selected"\' : \'\'; current_background_blend_colordodge = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'color-dodge\' ? \' selected="selected"\' : \'\'; current_background_blend_colorburn = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'color-burn\' ? \' selected="selected"\' : \'\'; current_background_blend_hardlight = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'hard-light\' ? \' selected="selected"\' : \'\'; current_background_blend_softlight = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'soft-light\' ? \' selected="selected"\' : \'\'; current_background_blend_difference = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'difference\' ? \' selected="selected"\' : \'\'; current_background_blend_exclusion = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'hue\' ? \' selected="selected"\' : \'\'; current_background_blend_hue = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'saturation\' ? \' selected="selected"\' : \'\'; current_background_blend_saturation = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'color\' ? \' selected="selected"\' : \'\'; current_background_blend_color = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'normal\' ? \' selected="selected"\' : \'\'; current_background_blend_luminosity = typeof et_pb_background_blend_%1$s !== \'undefined\' && et_pb_background_blend_%1$s === \'luminosity\' ? \' selected="selected"\' : \'\'; current_use_background_color_gradient = typeof et_pb_use_background_color_gradient_%1$s !== \'undefined\' && \'on\' === et_pb_use_background_color_gradient_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_repeat = typeof et_pb_background_color_gradient_repeat_%1$s !== \'undefined\' && \'on\' === et_pb_background_color_gradient_repeat_%1$s ? \' selected="selected"\' : \'%6$s\'; current_background_color_gradient_unit = typeof et_pb_background_color_gradient_unit_%1$s !== \'undefined\' ? et_pb_background_color_gradient_unit_%1$s : \'%7$s\'; current_background_color_gradient_type = typeof et_pb_background_color_gradient_type_%1$s !== \'undefined\' && \'radial\' === et_pb_background_color_gradient_type_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction = typeof et_pb_background_color_gradient_direction_%1$s !== \'undefined\' ? et_pb_background_color_gradient_direction_%1$s : \'%4$s\'; current_background_color_gradient_direction_radial_center = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'center\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_top_left = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'top left\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_top = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'top\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_top_right = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'top right\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_right = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'right\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_bottom_right = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'bottom right\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_bottom = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'bottom\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_bottom_left = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'bottom left\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_direction_radial_left = typeof et_pb_background_color_gradient_direction_radial_%1$s !== \'undefined\' && \'left\' === et_pb_background_color_gradient_direction_radial_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_stops = typeof et_pb_background_color_gradient_stops_%1$s !== \'undefined\' ? et_pb_background_color_gradient_stops_%1$s : \'%5$s\'; current_background_color_gradient_overlays_image = typeof et_pb_background_color_gradient_overlays_image_%1$s !== \'undefined\' && \'on\' === et_pb_background_color_gradient_overlays_image_%1$s ? \' selected="selected"\' : \'\'; current_background_color_gradient_start = typeof et_pb_background_color_gradient_start_%1$s !== \'undefined\' ? et_pb_background_color_gradient_start_%1$s : \'%2$s\'; current_background_color_gradient_end = typeof et_pb_background_color_gradient_end_%1$s !== \'undefined\' ? et_pb_background_color_gradient_end_%1$s : \'%3$s\'; current_background_video_mp4 = typeof et_pb_background_video_mp4_%1$s !== \'undefined\' ? et_pb_background_video_mp4_%1$s : \'\'; current_background_video_webm = typeof et_pb_background_video_webm_%1$s !== \'undefined\' ? et_pb_background_video_webm_%1$s : \'\'; current_background_video_width = typeof et_pb_background_video_width_%1$s !== \'undefined\' ? et_pb_background_video_width_%1$s : \'\'; current_background_video_height = typeof et_pb_background_video_height_%1$s !== \'undefined\' ? et_pb_background_video_height_%1$s : \'\'; current_allow_played_pause = typeof et_pb_allow_player_pause_%1$s !== \'undefined\' && \'on\' === et_pb_allow_player_pause_%1$s ? \' selected="selected"\' : \'\'; current_background_video_pause_outside_viewport = typeof et_pb_background_video_pause_outside_viewport_%1$s !== \'undefined\' && \'off\' === et_pb_background_video_pause_outside_viewport_%1$s ? \' selected="selected"\' : \'\'; current_value_parallax = typeof et_pb_parallax_%1$s !== \'undefined\' && \'on\' === et_pb_parallax_%1$s ? \' selected="selected"\' : \'\'; current_value_parallax_method = typeof et_pb_parallax_method_%1$s !== \'undefined\' && \'on\' !== et_pb_parallax_method_%1$s ? \' selected="selected"\' : \'\'; break; ', esc_attr( $i ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_start' ) ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_end' ) ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_direction' ) ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_stops' ) ), // #5 esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_repeat' ) ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_unit' ) ) ); } return $output; } /** * BB :: Generate Padding values for column padding settings. * * @return string */ public function generate_column_vars_padding() { $output = ''; for ( $i = 1; $i < 4; $i++ ) { $output .= sprintf( 'case %1$s : current_value_pt = typeof et_pb_padding_top_%1$s !== \'undefined\' ? et_pb_padding_top_%1$s : \'\', current_value_pr = typeof et_pb_padding_right_%1$s !== \'undefined\' ? et_pb_padding_right_%1$s : \'\', current_value_pb = typeof et_pb_padding_bottom_%1$s !== \'undefined\' ? et_pb_padding_bottom_%1$s : \'\', current_value_pl = typeof et_pb_padding_left_%1$s !== \'undefined\' ? et_pb_padding_left_%1$s : \'\', current_value_padding_tablet = typeof et_pb_padding_%1$s_tablet !== \'undefined\' ? et_pb_padding_%1$s_tablet : \'\', current_value_padding_phone = typeof et_pb_padding_%1$s_phone !== \'undefined\' ? et_pb_padding_%1$s_phone : \'\', last_edited_padding_field = typeof et_pb_padding_%1$s_last_edited !== \'undefined\' ? et_pb_padding_%1$s_last_edited : \'\', has_tablet_padding = typeof et_pb_padding_%1$s_tablet !== \'undefined\' ? \'yes\' : \'no\', has_phone_padding = typeof et_pb_padding_%1$s_phone !== \'undefined\' ? \'yes\' : \'no\'; break; ', esc_attr( $i ) ); } return $output; } /** * BB :: Generate Background settings for columns. * * @return string */ public function generate_columns_settings_background() { $output = sprintf( '<%% var columns = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter = 1; _.each( columns, function ( column_type ) { var current_value_bg, current_value_bg_img, current_value_parallax, current_value_parallax_method, current_background_size_cover, current_background_size_contain, current_background_size_initial, current_background_position_topleft, current_background_position_topcenter, current_background_position_topright, current_background_position_centerleft, current_background_position_center, current_background_position_centerright, current_background_position_bottomleft, current_background_position_bottomcenter, current_background_position_bottomright, current_background_repeat_repeat, current_background_repeat_repeatx, current_background_repeat_repeaty, current_background_repeat_space, current_background_repeat_round, current_background_repeat_norepeat, current_background_blend_normal, current_background_blend_multiply, current_background_blend_screen, current_background_blend_overlay, current_background_blend_darken, current_background_blend_lighten, current_background_blend_colordodge, current_background_blend_colorburn, current_background_blend_hardlight, current_background_blend_softlight, current_background_blend_difference, current_background_blend_exclusion, current_background_blend_hue, current_background_blend_saturation, current_background_blend_color, current_background_blend_luminosity, current_use_background_color_gradient, current_background_color_gradient_repeat, current_background_color_gradient_type, current_background_color_gradient_direction, current_background_color_gradient_direction_radial_center, current_background_color_gradient_direction_radial_top_left, current_background_color_gradient_direction_radial_top, current_background_color_gradient_direction_radial_top_right, current_background_color_gradient_direction_radial_right, current_background_color_gradient_direction_radial_bottom_right, current_background_color_gradient_direction_radial_bottom, current_background_color_gradient_direction_radial_bottom_left, current_background_color_gradient_direction_radial_left, current_background_color_gradient_stops, current_background_color_gradient_overlays_image, current_background_color_gradient_start, current_background_color_gradient_start_position, current_background_color_gradient_end, current_background_color_gradient_end_position, current_background_video_mp4, current_background_video_webm, current_background_video_width, current_background_video_height, current_allow_played_pause, current_background_video_pause_outside_viewport; switch ( counter ) { %1$s } %%>', $this->generate_column_vars_bg() ); $tab_navs = sprintf( '<ul class="et_pb_background-tab-navs"> <li> <a href="#" class="et_pb_background-tab-nav et_pb_background-tab-nav--color" data-tab="color" title="%1$s"> %5$s </a> </li><li> <a href="#" class="et_pb_background-tab-nav et_pb_background-tab-nav--gradient" data-tab="gradient" title="%2$s"> %6$s </a> </li><li> <a href="#" class="et_pb_background-tab-nav et_pb_background-tab-nav--image" data-tab="image" title="%3$s"> %7$s </a> </li><li> <a href="#" class="et_pb_background-tab-nav et_pb_background-tab-nav--video" data-tab="video" title="%4$s"> %8$s </a> </li> </ul>', et_builder_i18n( 'Color' ), esc_html__( 'Gradient', 'et_builder' ), et_builder_i18n( 'Image' ), esc_html__( 'Video', 'et_builder' ), $this->get_icon( 'background-color' ), $this->get_icon( 'background-gradient' ), $this->get_icon( 'background-image' ), $this->get_icon( 'background-video' ) ); $tab_color = sprintf( '<div class="et_pb_background-tab et_pb_background-tab--color" data-tab="color"> <div class="et_pb_background-option et_pb_background-option--background_color et-pb-option et-pb-option--background_color et-pb-option--has-preview"> <label for="et_pb_background_color">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--color-alpha"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> %2$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--edit"> %3$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> %4$s </button> </div> <input id="et_pb_background_color_<%%= counter %%>" class="et-pb-color-picker-hex et-pb-color-picker-hex-alpha et-pb-color-picker-hex-has-preview" type="text" data-alpha="true" placeholder="%5$s" data-selected-value="" value="<%%= current_value_bg %%>"> </div> </div> </div>', esc_html__( 'Background Color', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), esc_html__( 'Hex Value', 'et_builder' ) ); $tab_gradient_options = ''; // Gradient Preview (with Add/Swap/Delete buttons). $tab_gradient_options .= sprintf( ' <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> %1$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--swap"> %2$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> %3$s </button> </div>', $this->get_icon( 'add' ), $this->get_icon( 'swap' ), $this->get_icon( 'delete' ) ); // Use Background Color Gradient. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--use_background_color_gradient et_pb_background-template--use_color_gradient et-pb-option et-pb-option--use_background_color_gradient"> <label for="et_pb_use_background_color_gradient_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%2$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%3$s</span> </div> <select name="et_pb_use_background_color_gradient_<%%= counter %%>" id="et_pb_use_background_color_gradient_<%%= counter %%>" class="et-pb-main-setting regular-text et-pb-affects" data-affects="background_color_gradient_start_<%%= counter %%>, background_color_gradient_end_<%%= counter %%>, background_color_gradient_type_<%%= counter %%>, background_color_gradient_stops_<%%= counter %%>, background_color_gradient_repeat_<%%= counter %%>, background_color_gradient_overlays_image_<%%= counter %%>" data-default="off"> <option value="off">%3$s</option> <option value="on" <%%= current_use_background_color_gradient %%>>%2$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Background Gradient', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Gradient Type. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_type et_pb_background-template--color_gradient_type et-pb-option et-pb-option--background_color_gradient_type" data-depends_show_if="on"> <label for="et_pb_background_color_gradient_type_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--select"> <select name="et_pb_background_color_gradient_type_<%%= counter %%>" id="et_pb_background_color_gradient_type_<%%= counter %%>" class="et-pb-main-setting et-pb-affects" data-affects="background_color_gradient_direction_<%%= counter %%>, background_color_gradient_direction_radial_<%%= counter %%>" data-default="linear"> <option value="linear">%2$s</option> <option value="circular">%3$s</option> <option value="elliptical" <%%= current_background_color_gradient_type %%>>%4$s</option> <option value="conic">%5$s</option> </select> <span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Gradient Type', 'et_builder' ), et_builder_i18n( 'Linear' ), et_builder_i18n( 'Circular' ), et_builder_i18n( 'Elliptical' ), et_builder_i18n( 'Conical' ) ); // Gradient Direction. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_direction et_pb_background-template--color_gradient_direction et-pb-option et-pb-option--background_color_gradient_direction"> <label for="et_pb_background_color_gradient_direction_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--range"> <input type="range" class="et-pb-main-setting et-pb-range et-pb-fixed-range" data-default="180" value="<%%= current_background_color_gradient_direction %%>" min="0" max="360" step="1"> <input id="et_pb_background_color_gradient_direction_<%%= counter %%>" type="text" class="regular-text et-pb-validate-unit et-pb-range-input" value="<%%= current_background_color_gradient_direction %%>" data-default="180deg"> <span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Gradient Direction', 'et_builder' ) ); // Gradient Position. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_direction_radial et_pb_background-template--color_gradient_direction_radial et-pb-option et-pb-option--background_color_gradient_direction_radial"> <label for="et_pb_background_color_gradient_direction_radial_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--select"> <select name="et_pb_background_color_gradient_direction_radial_<%%= counter %%>" id="et_pb_background_color_gradient_direction_radial_<%%= counter %%>" class="et-pb-main-setting" data-default="center"> <option value="center" <%%= current_background_color_gradient_direction_radial_center %%>>%2$s</option> <option value="top left" <%%= current_background_color_gradient_direction_radial_top_left %%>>%3$s</option> <option value="top" <%%= current_background_color_gradient_direction_radial_top %%>>%4$s</option> <option value="top right" <%%= current_background_color_gradient_direction_radial_top_right %%>>%5$s</option> <option value="right" <%%= current_background_color_gradient_direction_radial_right %%>>%6$s</option> <option value="bottom right" <%%= current_background_color_gradient_direction_radial_bottom_right %%>>%7$s</option> <option value="bottom" <%%= current_background_color_gradient_direction_radial_bottom %%>>%8$s</option> <option value="bottom left" <%%= current_background_color_gradient_direction_radial_bottom_left %%>>%9$s</option> <option value="left" <%%= current_background_color_gradient_direction_radial_left %%>>%10$s</option> </select> <span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Gradient Position', 'et_builder' ), et_builder_i18n( 'Center' ), et_builder_i18n( 'Top Left' ), et_builder_i18n( 'Top' ), et_builder_i18n( 'Top Right' ), // #5 et_builder_i18n( 'Right' ), et_builder_i18n( 'Bottom Right' ), et_builder_i18n( 'Bottom' ), et_builder_i18n( 'Bottom Left' ), et_builder_i18n( 'Left' ) // #10 ); // Gradient Stops. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_stops et_pb_background-template--color_gradient_stops et-pb-option et-pb-option--background_color_gradient_stops" data-depends_show_if="on"> <label for="et_pb_background_color_gradient_stops_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--text"> <input id="et_pb_background_color_gradient_stops_<%%= counter %%>" type="text" class="regular-text et-pb-main-setting" value="<%%= current_background_color_gradient_stops %%>" placeholder="%2$s"> <span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Gradient Stops', 'et_builder' ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_stops' ) ) ); // Repeat Gradient toggle. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_repeat et_pb_background-template--use_color_gradient et-pb-option et-pb-option--background_color_gradient_repeat"> <label for="et_pb_background_color_gradient_repeat_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%2$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%3$s</span> </div> <select name="et_pb_background_color_gradient_repeat_<%%= counter %%>" id="et_pb_background_color_gradient_repeat_<%%= counter %%>" class="et-pb-main-setting regular-text" data-depends_show_if="on" data-default="off"> <option value="off">%3$s</option> <option value="on" <%%= current_background_color_gradient_repeat %%>>%2$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Repeat Gradient', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Gradient Unit list. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_unit et_pb_background-template--color_gradient_unit et-pb-option et-pb-option--background_color_gradient_unit" data-depends_show_if="on"> <label for="et_pb_background_color_gradient_unit_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--select"> <select name="et_pb_background_color_gradient_unit_<%%= counter %%>" id="et_pb_background_color_gradient_unit_<%%= counter %%>" class="et-pb-main-setting" data-default="linear"> <option value="%%" <%%= current_background_color_gradient_unit %%>>%2$s</option> <option value="px">%3$s</option> <option value="em">%4$s</option> <option value="rem">%5$s</option> <option value="ex">%6$s</option> <option value="ch">%7$s</option> <option value="pc">%8$s</option> <option value="pt">%9$s</option> <option value="cm">%10$s</option> <option value="mm">%11$s</option> <option value="in">%12$s</option> <option value="vh">%13$s</option> <option value="vw">%14$s</option> <option value="vmim">%15$s</option> <option value="vmax">%16$s</option> </select> <span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Gradient Unit', 'et_builder' ), et_builder_i18n( 'Percent' ), et_builder_i18n( 'Pixels' ), et_builder_i18n( 'Font Size (em)' ), et_builder_i18n( 'Root-level Font Size (rem)' ), // #5 et_builder_i18n( 'X-Height (ex)' ), et_builder_i18n( 'Zero-width (ch)' ), et_builder_i18n( 'Picas (pc)' ), et_builder_i18n( 'Points (pt)' ), et_builder_i18n( 'Centimeters (cm)' ), // #10 et_builder_i18n( 'Millimeters (mm)' ), et_builder_i18n( 'Inches (in)' ), et_builder_i18n( 'Viewport Height (vh)' ), et_builder_i18n( 'Viewport Width (vw)' ), et_builder_i18n( 'Viewport Minimum (vmin)' ), // #15 et_builder_i18n( 'Viewport Maximum (vmax)' ) ); // Gradient/Image Overlay. $tab_gradient_options .= sprintf( ' <div class="et_pb_background-option et_pb_background-option--background_color_gradient_overlays_image et_pb_background-template--use_color_gradient et-pb-option et-pb-option--background_color_gradient_overlays_image"> <label for="et_pb_background_color_gradient_overlays_image_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%2$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%3$s</span> </div> <select name="et_pb_background_color_gradient_overlays_image_<%%= counter %%>" id="et_pb_background_color_gradient_overlays_image_<%%= counter %%>" class="et-pb-main-setting regular-text" data-depends_show_if="on" data-default="off"> <option value="off">%3$s</option> <option value="on" <%%= current_background_color_gradient_overlays_image %%>>%2$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div>', esc_html__( 'Place Gradient Above Background Image', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Options for the Gradient tab. $tab_gradient = sprintf( '<div class="et_pb_background-tab et_pb_background-tab--gradient" data-tab="gradient">%1$s</div>', $tab_gradient_options ); $select_background_size = sprintf( '<select name="et_pb_background_size_<%%= counter %%>" id="et_pb_background_size_<%%= counter %%>" class="et-pb-main-setting" data-default="cover"> <option value="cover"<%%= current_background_size_cover %%>>%1$s</option> <option value="contain"<%%= current_background_size_contain %%>>%2$s</option> <option value="initial"<%%= current_background_size_initial %%>>%3$s</option> </select>', esc_html__( 'Cover', 'et_builder' ), esc_html__( 'Fit', 'et_builder' ), esc_html__( 'Actual Size', 'et_builder' ) ); $select_background_position = sprintf( '<select name="et_pb_background_position_<%%= counter %%>" id="et_pb_background_position_<%%= counter %%>" class="et-pb-main-setting" data-default="center"> <option value="top_left"<%%= current_background_position_topleft %%>>%1$s</option> <option value="top_center"<%%= current_background_position_topcenter %%>>%2$s</option> <option value="top_right"<%%= current_background_position_topright %%>>%3$s</option> <option value="center_left"<%%= current_background_position_centerleft %%>>%4$s</option> <option value="center"<%%= current_background_position_center %%>>%5$s</option> <option value="center_right"<%%= current_background_position_centerright %%>>%6$s</option> <option value="bottom_left"<%%= current_background_position_bottomleft %%>>%7$s</option> <option value="bottom_center"<%%= current_background_position_bottomcenter %%>>%8$s</option> <option value="bottom_right"<%%= current_background_position_bottomright %%>>%9$s</option> </select>', et_builder_i18n( 'Top Left' ), et_builder_i18n( 'Top Center' ), et_builder_i18n( 'Top Right' ), et_builder_i18n( 'Center Left' ), et_builder_i18n( 'Center' ), et_builder_i18n( 'Center Right' ), et_builder_i18n( 'Bottom Left' ), et_builder_i18n( 'Bottom Center' ), et_builder_i18n( 'Bottom Right' ) ); $select_background_repeat = sprintf( '<select name="et_pb_background_repeat_<%%= counter %%>" id="et_pb_background_repeat_<%%= counter %%>" class="et-pb-main-setting" data-default="repeat"> <option value="no-repeat"<%%= current_background_repeat_norepeat %%>>%1$s</option> <option value="repeat"<%%= current_background_repeat_repeat %%>>%2$s</option> <option value="repeat-x"<%%= current_background_repeat_repeatx %%>>%3$s</option> <option value="repeat-y"<%%= current_background_repeat_repeaty %%>>%4$s</option> <option value="space"<%%= current_background_repeat_space %%>>%5$s</option> <option value="round"<%%= current_background_repeat_round %%>>%6$s</option> </select>', esc_html__( 'No Repeat', 'et_builder' ), esc_html__( 'Repeat', 'et_builder' ), esc_html__( 'Repeat X (horizontal)', 'et_builder' ), esc_html__( 'Repeat Y (vertical)', 'et_builder' ), et_builder_i18n( 'Space' ), esc_html__( 'Round', 'et_builder' ) ); $select_background_blend = sprintf( '<select name="et_pb_background_blend_<%%= counter %%>" id="et_pb_background_blend_<%%= counter %%>" class="et-pb-main-setting" data-default="normal"> <option value="normal"<%%= current_background_blend_normal %%>>%1$s</option> <option value="multiply"<%%= current_background_blend_multiply %%>>%2$s</option> <option value="screen"<%%= current_background_blend_screen %%>>%3$s</option> <option value="overlay"<%%= current_background_blend_overlay %%>>%4$s</option> <option value="darken"<%%= current_background_blend_darken %%>>%5$s</option> <option value="lighten"<%%= current_background_blend_lighten %%>>%6$s</option> <option value="color-dodge"<%%= current_background_blend_colordodge %%>>%7$s</option> <option value="color-burn"<%%= current_background_blend_colorburn %%>>%8$s</option> <option value="hard-light"<%%= current_background_blend_hardlight %%>>%9$s</option> <option value="soft-light"<%%= current_background_blend_softlight %%>>%10$s</option> <option value="difference"<%%= current_background_blend_difference %%>>%11$s</option> <option value="exclusion"<%%= current_background_blend_exclusion %%>>%12$s</option> <option value="hue"<%%= current_background_blend_hue %%>>%13$s</option> <option value="saturation"<%%= current_background_blend_saturation %%>>%14$s</option> <option value="color"<%%= current_background_blend_color %%>>%15$s</option> <option value="luminosity"<%%= current_background_blend_luminosity %%>>%16$s</option> </select>', et_builder_i18n( 'Normal' ), et_builder_i18n( 'Multiply' ), et_builder_i18n( 'Screen' ), et_builder_i18n( 'Overlay' ), et_builder_i18n( 'Darken' ), et_builder_i18n( 'Lighten' ), et_builder_i18n( 'Color Dodge' ), et_builder_i18n( 'Color Burn' ), et_builder_i18n( 'Hard Light' ), et_builder_i18n( 'Soft Light' ), et_builder_i18n( 'Difference' ), et_builder_i18n( 'Exclusion' ), et_builder_i18n( 'Hue' ), et_builder_i18n( 'Saturation' ), et_builder_i18n( 'Color' ), et_builder_i18n( 'Luminosity' ) ); $tab_image = sprintf( '<div class="et_pb_background-tab et_pb_background-tab--image" data-tab="image"> <div class="et_pb_background-option et_pb_background-option--background_image et-pb-option et-pb-option--background_image et-pb-option--has-preview"> <label for="et_pb_bg_img_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--upload"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> %2$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--edit"> %3$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> %4$s </button> </div> <input id="et_pb_bg_img_<%%= counter %%>" type="text" class="et-pb-main-setting regular-text et-pb-upload-field" value="<%%= current_value_bg_img %%>"> <input type="button" class="button button-upload et-pb-upload-button" value="%5$s" data-choose="%6$s" data-update="%7$s" data-type="image"> <span class="et-pb-reset-setting" style="display: none;"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--parallax et_pb_background-template--parallax et-pb-option et-pb-option--parallax"> <label for="et_pb_parallax_<%%= counter %%>">%8$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%9$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%10$s</span> </div> <select name="et_pb_parallax_<%%= counter %%>" id="et_pb_parallax_<%%= counter %%>" class="et-pb-main-setting regular-text" data-default="off"> <option value="off">%10$s</option> <option value="on" <%%= current_value_parallax %%>>%9$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--parallax_method et_pb_background-template--parallax_method et-pb-option et-pb-option--parallax_method"> <label for="et_pb_parallax_method_<%%= counter %%>">%11$s: </label> <div class="et-pb-option-container et-pb-option-container--select"> <select name="et_pb_parallax_method_<%%= counter %%>" id="et_pb_parallax_method_<%%= counter %%>" class="et-pb-main-setting" data-default="on"> <option value="on">%12$s</option> <option value="off" <%%= current_value_parallax_method %%>>%13$s</option> </select> <span class="et-pb-reset-setting" style="display: none;"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--background_size et_pb_background-template--size et-pb-option et-pb-option--background_size" data-option_name="background_size"> <label for="et_pb_background_size">%14$s:</label> <div class="et-pb-option-container et-pb-option-container--select"> %15$s </div> </div> <div class="et_pb_background-option et_pb_background-option--background_position et_pb_background-template--position et-pb-option et-pb-option--background_position" data-option_name="background_position"> <label for="et_pb_background_position">%16$s:</label> <div class="et-pb-option-container et-pb-option-container--select"> %17$s </div> </div> <div class="et_pb_background-option et_pb_background-option--background_repeat et_pb_background-template--repeat et-pb-option et-pb-option--background_repeat" data-option_name="background_repeat"> <label for="et_pb_background_repeat">%18$s:</label> <div class="et-pb-option-container et-pb-option-container--select"> %19$s </div> </div> <div class="et_pb_background-option et_pb_background-option--background_blend et_pb_background-template--blend et-pb-option et-pb-option--background_blend" data-option_name="background_blend"> <label for="et_pb_background_blend">%20$s: </label> <div class="et-pb-option-container et-pb-option-container--select"> %21$s </div> </div> </div>', esc_html__( 'Background Image', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), et_builder_i18n( 'Upload an image' ), // #5 esc_html__( 'Choose a Background Image', 'et_builder' ), esc_html__( 'Set As Background', 'et_builder' ), esc_html__( 'Use Parallax Effect', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ), // #10 esc_html__( 'Parallax Method', 'et_builder' ), esc_html__( 'True Parallax', 'et_builder' ), esc_html__( 'CSS', 'et_builder' ), esc_html__( 'Background Image Size', 'et_builder' ), $select_background_size, // #15 esc_html__( 'Background Image Position', 'et_builder' ), $select_background_position, esc_html__( 'Background Image Repeat', 'et_builder' ), $select_background_repeat, esc_html__( 'Background Image Blend', 'et_builder' ), // #20 $select_background_blend ); $tab_video = sprintf( '<div class="et_pb_background-tab et_pb_background-tab--video" data-tab="video"> <div class="et_pb_background-option et_pb_background-option--background_video_mp4 et_pb_background-template--video_mp4 et-pb-option et-pb-option--background_video_mp4 et-pb-option--has-preview"> <label for="et_pb_background_video_mp4_<%%= counter %%>">%1$s: </label> <div class="et-pb-option-container et-pb-option-container--upload"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> %2$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--edit"> %3$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> %4$s </button> </div> <input id="et_pb_background_video_mp4_<%%= counter %%>" type="text" class="et-pb-main-setting regular-text et-pb-upload-field" value="<%%= current_background_video_mp4 %%>"> <input type="button" class="button button-upload et-pb-upload-button" value="%5$s" data-choose="%6$s" data-update="%7$s" data-type="video"> <span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--background_video_webm et_pb_background-template--video_webm et-pb-option et-pb-option--background_video_webm et-pb-option--has-preview"> <label for="et_pb_background_video_webm_<%%= counter %%>">%8$s: </label> <div class="et-pb-option-container et-pb-option-container--upload"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> %2$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--edit"> %3$s </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> %4$s </button> </div> <input id="et_pb_background_video_webm_<%%= counter %%>" type="text" class="et-pb-main-setting regular-text et-pb-upload-field" value="<%%= current_background_video_webm %%>"> <input type="button" class="button button-upload et-pb-upload-button" value="%5$s" data-choose="%9$s" data-update="%7$s" data-type="video"> <span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--background_video_width et_pb_background-template--video_width et-pb-option et-pb-option--background_video_width"> <label for="et_pb_background_video_width_<%%= counter %%>">%10$s: </label> <div class="et-pb-option-container et-pb-option-container--text"> <input id="et_pb_background_video_width_<%%= counter %%>" type="text" class="regular-text et-pb-main-setting" value="<%%= current_background_video_width %%>"> <span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--background_video_height et_pb_background-template--video_height et-pb-option et-pb-option--background_video_height"> <label for="et_pb_background_video_height_<%%= counter %%>">%11$s: </label> <div class="et-pb-option-container et-pb-option-container--text"> <input id="et_pb_background_video_height_<%%= counter %%>" type="text" class="regular-text et-pb-main-setting" value="<%%= current_background_video_height %%>"> <span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--allow_player_pause et_pb_background-template--allow_player_pause et-pb-option et-pb-option--allow_player_pause"> <label for="et_pb_allow_player_pause_<%%= counter %%>">%12$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%13$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%14$s</span> </div> <select name="et_pb_allow_player_pause_<%%= counter %%>" id="et_pb_allow_player_pause_<%%= counter %%>" class="et-pb-main-setting regular-text" data-default="off"> <option value="off">%14$s</option> <option value="on" <%%= current_allow_played_pause %%>>%13$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div> <div class="et_pb_background-option et_pb_background-option--background_video_pause_outside_viewport et_pb_background-template--background_video_pause_outside_viewport et-pb-option et-pb-option--background_video_pause_outside_viewport"> <label for="et_pb_background_video_pause_outside_viewport_<%%= counter %%>">%15$s: </label> <div class="et-pb-option-container et-pb-option-container--yes_no_button"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%13$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%14$s</span> </div> <select name="et_pb_background_video_pause_outside_viewport_<%%= counter %%>" id="et_pb_background_video_pause_outside_viewport_<%%= counter %%>" class="et-pb-main-setting regular-text" data-default="on"> <option value="on">%13$s</option> <option value="off" <%%= current_background_video_pause_outside_viewport %%>>%14$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div> </div> </div>', esc_html__( 'Background Video MP4', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), esc_html__( 'Upload a video', 'et_builder' ), // #5 esc_html__( 'Choose a Background Video MP4 File', 'et_builder' ), esc_html__( 'Set As Background Video', 'et_builder' ), esc_html__( 'Background Video Webm', 'et_builder' ), esc_html__( 'Choose a Background Video WEBM File', 'et_builder' ), esc_html__( 'Background Video Width', 'et_builder' ), // #10 esc_html__( 'Background Video Height', 'et_builder' ), esc_html__( 'Pause Video When Another Video Plays', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ), esc_html__( 'Pause Video While Not In View', 'et_builder' ) // #15 ); $output .= sprintf( '<div class="et_pb_subtoggle_section"> <div class="et-pb-option-toggle-content"> <div class="et-pb-option et-pb-option--background" data-option_name="background_color_<%%= counter %%>"> <label for="et_pb_background"> %1$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter + " " %%> <%% } %%> %2$s: </label> <div class="et-pb-option-container et-pb-option-container-inner et-pb-option-container--background" data-column-index="<%%= counter %%>" data-base_name="background"> %3$s %4$s %5$s %6$s %7$s </div> </div> </div> </div> <%% counter++; }); %%>', esc_html__( 'Column', 'et_builder' ), et_builder_i18n( 'Background' ), $tab_navs, $tab_color, $tab_gradient, // #5 $tab_image, $tab_video ); return $output; } /** * BB :: Generate Padding settings for columns. * * @return string */ public function generate_columns_settings_padding() { $output = sprintf( '<%% var columns = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter = 1; _.each( columns, function ( column_type ) { var current_value_pt, current_value_pr, current_value_pb, current_value_pl, current_value_padding_tablet, current_value_padding_phone, has_tablet_padding, has_phone_padding; switch ( counter ) { %1$s } %%>', $this->generate_column_vars_padding() ); $output .= sprintf( '<div class="et_pb_subtoggle_section"> <div class="et-pb-option-toggle-content"> <div class="et-pb-option"> <label for="et_pb_padding_<%%= counter %%>"> %1$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter + " " %%> <%% } %%> %2$s: </label> <div class="et-pb-option-container"> %7$s <div class="et_margin_padding"> <label> %3$s <input type="text" class="medium-text et_custom_margin et_custom_margin_top et-pb-validate-unit et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active" id="et_pb_padding_top_<%%= counter %%>" name="et_pb_padding_top_<%%= counter %%>" value="<%%= current_value_pt %%>" data-device="desktop"> <input type="text" class="medium-text et_custom_margin et_custom_margin_top et_pb_setting_mobile et_pb_setting_mobile_tablet" data-device="tablet"> <input type="text" class="medium-text et_custom_margin et_custom_margin_top et_pb_setting_mobile et_pb_setting_mobile_phone" data-device="phone"> </label> <label> %4$s <input type="text" class="medium-text et_custom_margin et_custom_margin_right et-pb-validate-unit et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active" id="et_pb_padding_right_<%%= counter %%>" name="et_pb_padding_right_<%%= counter %%>" value="<%%= current_value_pr %%>" data-device="desktop"> <input type="text" class="medium-text et_custom_margin et_custom_margin_right et_pb_setting_mobile et_pb_setting_mobile_tablet" data-device="tablet"> <input type="text" class="medium-text et_custom_margin et_custom_margin_right et_pb_setting_mobile et_pb_setting_mobile_phone" data-device="phone"> </label> <label> %5$s <input type="text" class="medium-text et_custom_margin et_custom_margin_bottom et-pb-validate-unit et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active" id="et_pb_padding_bottom_<%%= counter %%>" name="et_pb_padding_bottom_<%%= counter %%>" value="<%%= current_value_pb %%>" data-device="desktop"> <input type="text" class="medium-text et_custom_margin et_custom_margin_bottom et_pb_setting_mobile et_pb_setting_mobile_tablet" data-device="tablet"> <input type="text" class="medium-text et_custom_margin et_custom_margin_bottom et_pb_setting_mobile et_pb_setting_mobile_phone" data-device="phone"> </label> <label> %6$s <input type="text" class="medium-text et_custom_margin et_custom_margin_left et-pb-validate-unit et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active" id="et_pb_padding_left_<%%= counter %%>" name="et_pb_padding_left_<%%= counter %%>" value="<%%= current_value_pl %%>" data-device="desktop"> <input type="text" class="medium-text et_custom_margin et_custom_margin_left et_pb_setting_mobile et_pb_setting_mobile_tablet" data-device="tablet"> <input type="text" class="medium-text et_custom_margin et_custom_margin_left et_pb_setting_mobile et_pb_setting_mobile_phone" data-device="phone"> </label> <input type="hidden" class="et_custom_margin_main et_pb_setting_mobile et_pb_setting_mobile_desktop et-pb-main-setting et_pb_setting_mobile_active" value="<%%= \'\' === current_value_pt && \'\' === current_value_pr && \'\' === current_value_pb && \'\' === current_value_pl ? \'\' : current_value_pt + \'|\' + current_value_pr + \'|\' + current_value_pb + \'|\' + current_value_pl %%>" data-device="desktop"> <input type="hidden" class="et_custom_margin_main et_pb_setting_mobile et_pb_setting_mobile_tablet et-pb-main-setting" id="et_pb_padding_<%%= counter %%>_tablet" name="et_pb_padding_<%%= counter %%>_tablet" value="<%%= current_value_padding_tablet %%>" data-device="tablet" data-has_saved_value="<%%= has_tablet_padding %%>"> <input type="hidden" class="et_custom_margin_main et_pb_setting_mobile et_pb_setting_mobile_phone et-pb-main-setting" id="et_pb_padding_<%%= counter %%>_phone" name="et_pb_padding_<%%= counter %%>_phone" value="<%%= current_value_padding_phone %%>" data-device="phone" data-has_saved_value="<%%= has_phone_padding %%>"> <input id="et_pb_padding_<%%= counter %%>_last_edited" type="hidden" class="et_pb_mobile_last_edited_field" value="<%%= last_edited_padding_field %%>"> </div> <span class="et-pb-mobile-settings-toggle"></span> <span class="et-pb-reset-setting"></span> </div> </div> </div> </div> <%% counter++; }); %%>', esc_html__( 'Column', 'et_builder' ), esc_html__( 'Padding', 'et_builder' ), et_builder_i18n( 'Top' ), et_builder_i18n( 'Right' ), et_builder_i18n( 'Bottom' ), // #5 et_builder_i18n( 'Left' ), et_pb_generate_mobile_settings_tabs() // #7 ); return $output; } /** * BB :: Generate "Custom CSS" settings for columns. * * @return string */ public function generate_columns_settings_css() { $output = sprintf( '<%% var columns_css = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter_css = 1; _.each( columns_css, function ( column_type ) { var current_module_id_value, current_module_class_value, current_custom_css_before_value, current_custom_css_main_value, current_custom_css_after_value; switch ( counter_css ) { %1$s } %%> <div class="et_pb_subtoggle_section"> <div class="et-pb-option-toggle-content"> <div class="et-pb-option et-pb-option--custom_css"> <label for="et_pb_custom_css_before_<%%= counter_css %%>"> %2$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter_css + " " %%> <%% } %%> %3$s:<span>.et_pb_column_<%%= \'row_inner\' === module_type ? \'inner_\' : \'\' %%><%%= typeof columns_order !== \'undefined\' && typeof columns_order[counter_css-1] !== \'undefined\' ? columns_order[counter_css-1] : \'\' %%>:before</span> </label> <div class="et-pb-option-container et-pb-custom-css-option"> <textarea id="et_pb_custom_css_before_<%%= counter_css %%>" class="et-pb-main-setting large-text coderegular-text" rows="4" cols="50"><%%= current_custom_css_before_value.replace( /\|\|/g, "\n" ) %%></textarea> </div> </div> <div class="et-pb-option et-pb-option--custom_css"> <label for="et_pb_custom_css_main_<%%= counter_css %%>"> %2$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter_css + " " %%> <%% } %%> %4$s:<span>.et_pb_column_<%%= \'row_inner\' === module_type ? \'inner_\' : \'\' %%><%%= typeof columns_order !== \'undefined\' && typeof columns_order[counter_css-1] !== \'undefined\' ? columns_order[counter_css-1] : \'\' %%></span> </label> <div class="et-pb-option-container et-pb-custom-css-option"> <textarea id="et_pb_custom_css_main_<%%= counter_css %%>" class="et-pb-main-setting large-text coderegular-text" rows="4" cols="50"><%%= current_custom_css_main_value.replace( /\|\|/g, "\n" ) %%></textarea> </div> </div> <div class="et-pb-option et-pb-option--custom_css"> <label for="et_pb_custom_css_after_<%%= counter_css %%>"> %2$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter_css + " " %%> <%% } %%> %5$s:<span>.et_pb_column_<%%= \'row_inner\' === module_type ? \'inner_\' : \'\' %%><%%= typeof columns_order !== \'undefined\' && typeof columns_order[counter_css-1] !== \'undefined\' ? columns_order[counter_css-1] : \'\' %%>:after</span> </label> <div class="et-pb-option-container et-pb-custom-css-option"> <textarea id="et_pb_custom_css_after_<%%= counter_css %%>" class="et-pb-main-setting large-text coderegular-text" rows="4" cols="50"><%%= current_custom_css_after_value.replace( /\|\|/g, "\n" ) %%></textarea> </div> </div> </div> </div> <%% counter_css++; }); %%>', $this->generate_column_vars_css(), esc_html__( 'Column', 'et_builder' ), et_builder_i18n( 'Before' ), et_builder_i18n( 'Main Element' ), et_builder_i18n( 'After' ) ); return $output; } /** * BB :: Generate "CSS ID & Classes" settings for columns. * * @return string */ public function generate_columns_settings_css_fields() { $output = sprintf( '<%% var columns_css = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter_css = 1; _.each( columns_css, function ( column_type ) { var current_module_id_value, current_module_class_value; switch ( counter_css ) { %1$s } %%> <div class="et_pb_subtoggle_section"> <div class="et-pb-option-toggle-content"> <div class="et-pb-option et_pb_custom_css_regular"> <label for="et_pb_module_id_<%%= counter_css %%>"> %2$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter_css + " " %%> <%% } %%> %3$s: </label> <div class="et-pb-option-container"> <input id="et_pb_module_id_<%%= counter_css %%>" type="text" class="regular-text et_pb_custom_css_regular et-pb-main-setting" value="<%%= current_module_id_value %%>"> </div> </div> <div class="et-pb-option et_pb_custom_css_regular"> <label for="et_pb_module_class_<%%= counter_css %%>"> %2$s <%% if ( "4_4" !== column_type ) { %%> <%%= counter_css + " " %%> <%% } %%> %4$s: </label> <div class="et-pb-option-container"> <input id="et_pb_module_class_<%%= counter_css %%>" type="text" class="regular-text et_pb_custom_css_regular et-pb-main-setting" value="<%%= current_module_class_value %%>"> </div> </div> </div> </div> <%% counter_css++; }); %%>', $this->generate_column_vars_css(), esc_html__( 'Column', 'et_builder' ), esc_html__( 'CSS ID', 'et_builder' ), esc_html__( 'CSS Class', 'et_builder' ) ); return $output; } } PK X�\E���~ ~ builder/comments_template.phpnu �[��� <?php if ( post_password_required() ) : ?> <p class="nocomments container"><?php esc_html_e( 'This post is password protected. Enter the password to view comments.', 'et_builder' ); ?></p> <?php return; endif; global $et_comments_header_level, $et_comments_form_title_level; $et_comments_header_level_processed = isset( $et_comments_header_level ) && '' !== $et_comments_header_level ? et_pb_process_header_level( $et_comments_header_level, 'h1' ) : 'h1'; ?> <?php if ( empty( $comments_by_type ) ) { $comments_by_type = separate_comments( $comments ); } ?> <section id="comment-wrap"> <<?php echo et_core_intentionally_unescaped( $et_comments_header_level_processed, 'fixed_string' ); ?> id="comments" class="page_title"><?php comments_number( esc_html__( '0 Comments', 'et_builder' ), esc_html__( '1 Comment', 'et_builder' ), '% ' . esc_html__( 'Comments', 'et_builder' ) ); ?></<?php echo et_core_intentionally_unescaped( $et_comments_header_level_processed, 'fixed_string' ); ?>> <?php if ( have_comments() ) : ?> <?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? ?> <div class="comment_navigation_top clearfix"> <div class="nav-previous"><?php previous_comments_link( et_get_safe_localization( __( '<span class="meta-nav">←</span> Older Comments', 'et_builder' ) ) ); ?></div> <div class="nav-next"><?php next_comments_link( et_get_safe_localization( __( 'Newer Comments <span class="meta-nav">→</span>', 'et_builder' ) ) ); ?></div> </div> <?php endif; // check for comment navigation ?> <?php if ( ! empty( $comments_by_type['comment'] ) ) : ?> <ol class="commentlist clearfix"> <?php wp_list_comments( array( 'type' => 'comment', 'callback' => 'et_custom_comments_display', ) ); ?> </ol> <?php endif; ?> <?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? ?> <div class="comment_navigation_bottom clearfix"> <div class="nav-previous"><?php previous_comments_link( et_get_safe_localization( __( '<span class="meta-nav">←</span> Older Comments', 'et_builder' ) ) ); ?></div> <div class="nav-next"><?php next_comments_link( et_get_safe_localization( __( 'Newer Comments <span class="meta-nav">→</span>', 'et_builder' ) ) ); ?></div> </div> <?php endif; // check for comment navigation ?> <?php if ( ! empty( $comments_by_type['pings'] ) ) : ?> <div id="trackbacks"> <h3 id="trackbacks-title"><?php esc_html_e( 'Trackbacks/Pingbacks', 'et_builder' ); ?></h3> <ol class="pinglist"> <?php wp_list_comments( 'type=pings&callback=et_list_pings' ); ?> </ol> </div> <?php endif; ?> <?php else : // this is displayed if there are no comments so far ?> <div id="comment-section" class="nocomments"> <?php if ( 'open' === $post->comment_status ) : ?> <?php else : // comments are closed ?> <?php endif; ?> </div> <?php endif; ?> <?php if ( 'open' === $post->comment_status ) : ?> <?php // Comment submit content title level. $et_comments_form_title_level_processed = isset( $et_comments_form_title_level ) && '' !== $et_comments_form_title_level ? et_pb_process_header_level( $et_comments_form_title_level, 'h3' ) : 'h3'; $et_comments_form_title_level_escaped = et_core_intentionally_unescaped( $et_comments_form_title_level_processed, 'fixed_string' ); comment_form( array( 'label_submit' => esc_attr__( 'Submit Comment', 'et_builder' ), 'title_reply' => '<span>' . esc_attr__( 'Submit a Comment', 'et_builder' ) . '</span>', 'title_reply_to' => esc_attr__( 'Leave a Reply to %s', 'et_builder' ), 'title_reply_before' => '<' . $et_comments_form_title_level_escaped . ' id="reply-title" class="comment-reply-title">', 'title_reply_after' => '</' . $et_comments_form_title_level_escaped . '>', 'class_submit' => 'submit et_pb_button', 'comment_notes_after' => '', 'id_submit' => 'et_pb_submit', ) ); ?> <?php else : ?> <?php endif; // if you delete this the sky will fall on your head ?> </section> PK X�\^9� � . builder/class-et-builder-theme-compat-base.phpnu �[��� <?php /** * Theme compat base class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Base class for plugin compatibility file. * * @since 4.10.0 */ class ET_Builder_Theme_Compat_Base { /** * Theme name. * * @access private * @var array */ public $theme_id; /** * Get theme dir path based on theme_id. * * @return sting */ public function get_theme_dir_path() { return get_stylesheet_directory(); } /** * Get theme data. * * @return object */ public function get_theme_data() { return wp_get_theme(); } } PK X�\���� � builder/deprecations.phpnu �[��� <?php /** * Deprecated symbols mapped to replacements when applicable, organized by type. * * @since 3.1 * * @return array[] { * Deprecations * * @type string[] $functions { * Deprecated Functions * * @type mixed $deprecated_symbol Replacement symbol or default value * ... * } * @type array[] $classes { * Deprecations By Class * * @type array[] $class_name { * Deprecated Symbols * * @type string[] $symbol_type { * * @type mixed $deprecated_symbol Replacement symbol or default value * ... * } * ... * } * ... * } * } */ return array( 'functions' => array(), 'classes' => array( '\ET_Builder_Module_Blurb' => array( 'method_args' => array( '_add_option_toggles' => array( 'general', array() ), '_shortcode_callback' => array( array(), null, 'et_pb_blurb' ), '_shortcode_passthru_callback' => array( array(), null, 'et_pb_blurb' ), 'additional_shortcode_callback' => array( array(), null, 'et_pb_blurb' ), 'shortcode_callback' => array( array(), null, 'et_pb_blurb' ), ), 'methods' => array( '_add_option_toggles' => '_add_settings_modal_toggles', '_get_current_shortcode_address' => 'generate_element_address', '_shortcode_callback' => '_render', '_shortcode_passthru_callback' => 'render_as_builder_data', 'additional_shortcode_callback' => 'additional_render', 'get_shortcode_fields' => 'get_default_props', 'pre_shortcode_content' => 'before_render', 'shortcode_atts' => '', 'shortcode_atts_to_data_atts' => 'props_to_html_data_attrs', 'shortcode_callback' => 'render', 'shortcode_output' => 'output', ), 'properties' => array( 'advanced_options' => 'advanced_fields', 'custom_css_options' => 'custom_css_fields', 'options_toggles' => 'settings_modal_toggles', 'shortcode_atts' => 'props', 'shortcode_content' => 'content', 'allowlisted_fields' => array(), 'no_shortcode_callback' => 'no_render', ), ), ), ); PK X�\ یǤ� �� builder/functions.phpnu �[��� <?php /** * Core builder functions. * * @package Builder * @since 1.0 */ if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) ) { // Note, this will be updated automatically during grunt release task. define( 'ET_BUILDER_PRODUCT_VERSION', '4.27.4' ); } if ( ! defined( 'ET_BUILDER_VERSION' ) ) { define( 'ET_BUILDER_VERSION', 0.7 ); } if ( ! defined( 'ET_BUILDER_FORCE_CACHE_PURGE' ) ) { define( 'ET_BUILDER_FORCE_CACHE_PURGE', false ); } if ( ! defined( 'ET_BUILDER_DIR_RESOLVED_PATH' ) ) { define( 'ET_BUILDER_DIR_RESOLVED_PATH', dirname( __FILE__ ) ); } // When set to true, the builder will use the new loading method. if ( ! defined( 'ET_BUILDER_CACHE_ASSETS' ) ) { define( 'ET_BUILDER_CACHE_ASSETS', ! isset( $_REQUEST['nocache'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- CSRF ok. } if ( ! defined( 'ET_BUILDER_CACHE_MODULES' ) ) { define( 'ET_BUILDER_CACHE_MODULES', apply_filters( 'et_builder_cache_modules', true ) ); } if ( ! defined( 'ET_BUILDER_JSON_ENCODE_OPTIONS' ) ) { define( 'ET_BUILDER_JSON_ENCODE_OPTIONS', 0 ); } if ( ! defined( 'ET_BUILDER_KEEP_OLDEST_CACHED_ASSETS' ) ) { define( 'ET_BUILDER_KEEP_OLDEST_CACHED_ASSETS', false ); } if ( ! defined( 'ET_BUILDER_PURGE_OLD_CACHED_ASSETS' ) ) { define( 'ET_BUILDER_PURGE_OLD_CACHED_ASSETS', true ); } if ( defined( 'ET_BUILDER_DEFINITION_SORT' ) && ET_BUILDER_DEFINITION_SORT ) { /** * You don't want to know and this isn't the function you're looking for. * Still reading ? Aight, this is only used to debug definitions. * * @param array $definitions Definitions. */ function et_builder_definition_sort( &$definitions ) { if ( ! is_array( $definitions ) ) { return; } $fields = array_keys( $definitions ); $order = array( 'label', 'description', 'option_category', 'type', 'data_type', 'upload_button_text', ); foreach ( $fields as $field ) { $definition =& $definitions[ $field ]; if ( is_array( $definition ) ) { foreach ( $order as $key ) { if ( isset( $definition[ $key ] ) ) { $value = $definition[ $key ]; unset( $definition[ $key ] ); $definition[ $key ] = $value; } } et_builder_definition_sort( $definition ); } } } } $et_fonts_queue = array(); /** * Exclude predefined layouts from import. * * @param array $posts The imported posts. * * @return array */ function et_remove_predefined_layouts_from_import( $posts ) { $processed_posts = $posts; if ( isset( $posts ) && is_array( $posts ) ) { $processed_posts = array(); foreach ( $posts as $post ) { if ( isset( $post['postmeta'] ) && is_array( $post['postmeta'] ) ) { foreach ( $post['postmeta'] as $meta ) { if ( '_et_pb_predefined_layout' === $meta['key'] && 'on' === $meta['value'] ) { continue 2; } } } $processed_posts[] = $post; } } return $processed_posts; } add_filter( 'wp_import_posts', 'et_remove_predefined_layouts_from_import', 5 ); /** * Output all module fields JSON dump. * For dev use only. * * @return void */ // phpcs:disable Squiz.PHP.CommentedOutCode -- This is debug function. // function et_dev_output_all_fields() { // die( json_encode( ET_Builder_Element::get_all_fields('page') ) ); // } // add_action('wp', 'et_dev_output_all_fields', 100);. // phpcs:enable /** * Set the layout_type taxonomy to "layout" for layouts imported from old version of Divi. * * @param array $posts Imported posts. * * @return array */ function et_update_old_layouts_taxonomy( $posts ) { $processed_posts = $posts; if ( isset( $posts ) && is_array( $posts ) ) { $processed_posts = array(); foreach ( $posts as $post ) { $update_built_for_post_type = false; if ( 'et_pb_layout' === $post['post_type'] ) { if ( ! isset( $post['terms'] ) ) { $post['terms'][] = array( 'name' => 'layout', 'slug' => 'layout', 'domain' => 'layout_type', ); $post['terms'][] = array( 'name' => 'not_global', 'slug' => 'not_global', 'domain' => 'scope', ); } $update_built_for_post_type = true; // check whether _et_pb_built_for_post_type custom field exists. if ( ! empty( $post['postmeta'] ) ) { foreach ( $post['postmeta'] as $index => $value ) { if ( '_et_pb_built_for_post_type' === $value['key'] ) { $update_built_for_post_type = false; } } } } // set _et_pb_built_for_post_type value to 'page' if not exists. if ( $update_built_for_post_type ) { $post['postmeta'][] = array( 'key' => '_et_pb_built_for_post_type', 'value' => 'page', ); } $processed_posts[] = $post; } } return $processed_posts; } add_filter( 'wp_import_posts', 'et_update_old_layouts_taxonomy', 10 ); if ( ! function_exists( 'et_pb_add_layout_filters' ) ) : /** * Add custom filters for posts in the Divi Library. */ function et_pb_add_layout_filters() { // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( isset( $_GET['post_type'] ) && 'et_pb_layout' === $_GET['post_type'] ) { $layout_categories = get_terms( 'layout_category' ); $filter_category = array(); $filter_category[''] = esc_html__( 'All Categories', 'et_builder' ); if ( is_array( $layout_categories ) && ! empty( $layout_categories ) ) { foreach ( $layout_categories as $category ) { $filter_category[ $category->slug ] = $category->name; } } $layout_packs = get_terms( 'layout_pack' ); $filter_pack = array(); $filter_pack[''] = esc_html_x( 'All Packs', 'Layout Packs', 'et_builder' ); if ( is_array( $layout_packs ) ) { foreach ( $layout_packs as $pack ) { $filter_pack[ $pack->slug ] = $pack->name; } } $filter_layout_type = array( '' => esc_html__( 'All Types', 'et_builder' ), 'module' => esc_html__( 'Modules', 'et_builder' ), 'row' => esc_html__( 'Rows', 'et_builder' ), 'section' => esc_html__( 'Sections', 'et_builder' ), 'layout' => esc_html__( 'Layouts', 'et_builder' ), ); $filter_scope = array( '' => esc_html__( 'All Scopes', 'et_builder' ), 'global' => esc_html__( 'Global', 'et_builder' ), 'not_global' => esc_html__( 'Not Global', 'et_builder' ), ); ?> <select name="layout_type"> <?php $selected = isset( $_GET['layout_type'] ) ? sanitize_text_field( $_GET['layout_type'] ) : ''; foreach ( $filter_layout_type as $value => $label ) { printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $value ), selected( $value, $selected ), esc_html( $label ) ); } ?> </select> <select name="scope"> <?php $selected = isset( $_GET['scope'] ) ? sanitize_text_field( $_GET['scope'] ) : ''; foreach ( $filter_scope as $value => $label ) { printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $value ), selected( $value, $selected ), esc_html( $label ) ); } ?> </select> <select name="layout_category"> <?php $selected = isset( $_GET['layout_category'] ) ? sanitize_text_field( $_GET['layout_category'] ) : ''; foreach ( $filter_category as $value => $label ) { printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $value ), selected( $value, $selected ), esc_html( $label ) ); } ?> </select> <select name="layout_pack"> <?php $selected = isset( $_GET['layout_pack'] ) ? sanitize_text_field( $_GET['layout_pack'] ) : ''; foreach ( $filter_pack as $value => $label ) { printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $value ), selected( $value, $selected ), esc_html( $label ) ); } ?> </select> <?php // phpcs:enable } } endif; add_action( 'restrict_manage_posts', 'et_pb_add_layout_filters' ); if ( ! function_exists( 'et_pb_load_export_section' ) ) : /** * Add "Export Divi Layouts" button to the Divi Library page. */ function et_pb_load_export_section() { $current_screen = get_current_screen(); if ( 'edit-et_pb_layout' === $current_screen->id ) { // display wp error screen if library is disabled for current user. if ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'add_library' ) || ! et_pb_is_allowed( 'save_library' ) ) { wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) ); } add_action( 'all_admin_notices', 'et_pb_export_layouts_interface' ); } } endif; add_action( 'load-edit.php', 'et_pb_load_export_section' ); if ( ! function_exists( 'et_pb_edit_library_categories' ) ) : /** * Enqueue script on Library Categories editing screen. */ function et_pb_edit_library_categories() { $current_screen = get_current_screen(); if ( 'edit-layout_category' === $current_screen->id || 'edit-layout_pack' === $current_screen->id ) { // display wp error screen if library is disabled for current user. if ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'add_library' ) || ! et_pb_is_allowed( 'save_library' ) ) { wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) ); } wp_enqueue_script( 'builder-library-category', ET_BUILDER_URI . '/scripts/library_category.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); } } endif; add_action( 'load-edit-tags.php', 'et_pb_edit_library_categories' ); /** * Check whether the library editor page should be displayed or not. */ function et_pb_check_library_permissions() { $current_screen = get_current_screen(); if ( 'et_pb_layout' === $current_screen->id && ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'save_library' ) ) ) { // display wp error screen if library is disabled for current user. wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) ); } } add_action( 'load-post.php', 'et_pb_check_library_permissions' ); if ( ! function_exists( 'exclude_premade_layouts_library' ) ) : /** * Exclude premade layouts from the list of all templates in the library. * * @param WP_Query $query Query. */ function exclude_premade_layouts_library( $query ) { // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. global $pagenow; $current_post_type = get_query_var( 'post_type' ); if ( is_admin() && 'edit.php' === $pagenow && $current_post_type && 'et_pb_layout' === $current_post_type ) { $meta_query = array( array( 'key' => '_et_pb_predefined_layout', 'value' => 'on', 'compare' => 'NOT EXISTS', ), ); $used_built_for_post_types = ET_Builder_Library::built_for_post_types( 'all' ); if ( isset( $_GET['built_for'] ) && count( $used_built_for_post_types ) > 1 ) { $built_for_post_type = sanitize_text_field( $_GET['built_for'] ); // get array of all standard post types if built_for is one of them. $built_for_post_type_processed = in_array( $built_for_post_type, ET_Builder_Library::built_for_post_types(), true ) ? ET_Builder_Library::built_for_post_types() : $built_for_post_type; if ( in_array( $built_for_post_type, $used_built_for_post_types, true ) ) { $meta_query[] = array( 'key' => '_et_pb_built_for_post_type', 'value' => $built_for_post_type_processed, 'compare' => 'IN', ); } } $query->set( 'meta_query', $meta_query ); //phpcs:enable } return $query; } endif; add_action( 'pre_get_posts', 'exclude_premade_layouts_library' ); if ( ! function_exists( 'exclude_premade_layouts_library_count' ) ) : /** * Post count for "mine" in post table relies to fixed value set by WP_Posts_List_Table->user_posts_count * Thus, exclude_premade_layouts_library() action doesn't automatically exclude premade layout and * it has to be late filtered via this exclude_premade_layouts_library_count(). * * @see WP_Posts_List_Table->user_posts_count to see how mine post value is retrieved. * * @param array $views All views in post list table. * @return array */ function exclude_premade_layouts_library_count( $views ) { if ( isset( $views['mine'] ) ) { $current_user_id = get_current_user_id(); if ( isset( $_GET['author'] ) && ( $_GET['author'] === $current_user_id ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. $class = 'current'; // Reuse current $wp_query global. global $wp_query; $mine_posts_count = $wp_query->found_posts; } else { $class = ''; // Use WP_Query instead of plain MySQL SELECT because the custom field filtering uses // GROUP BY which needs FOUND_ROWS() and this has been automatically handled by WP_Query. $query = new WP_Query( array( 'post_type' => 'et_pb_layout', 'author' => $current_user_id, 'meta_query' => array( 'key' => '_et_pb_predefined_layout', 'value' => 'on', 'compare' => 'NOT EXISTS', ), ) ); $mine_posts_count = $query->found_posts; } $url = add_query_arg( array( 'post_type' => 'et_pb_layout', 'author' => $current_user_id, ), 'edit.php' ); $views['mine'] = sprintf( '<a href="%1$s" class="%2$s">%3$s <span class="count">(%4$s)</span></a>', esc_url( $url ), esc_attr( $class ), esc_html__( 'Mine', 'et_builder' ), esc_html( intval( $mine_posts_count ) ) ); } return $views; } endif; add_filter( 'views_edit-et_pb_layout', 'exclude_premade_layouts_library_count' ); if ( ! function_exists( 'et_pb_get_standard_post_types' ) ) : /** * Returns the standard '_et_pb_built_for_post_type' post types. * * @deprecated {@see ET_Builder_Post_Type_Layout::get_built_for_post_types()} * * @since 3.1 Deprecated. * @since 1.8 * * @return string[] */ function et_pb_get_standard_post_types() { return ET_Builder_Library::built_for_post_types(); } endif; if ( ! function_exists( 'et_pb_get_used_built_for_post_types' ) ) : /** * Returns all current '_et_pb_built_for_post_type' post types. * * @deprecated {@see ET_Builder_Post_Type_Layout::get_built_for_post_types()} * * @since 3.1 Deprecated. * @since 1.8 * * @return string[] */ function et_pb_get_used_built_for_post_types() { return ET_Builder_Library::built_for_post_types( 'all' ); } endif; if ( ! function_exists( 'et_pb_get_font_icon_symbols' ) ) : /** * Return fon icon symbols. */ function et_pb_get_font_icon_symbols() { $symbols = array( '&#x21;', '&#x22;', '&#x23;', '&#x24;', '&#x25;', '&#x26;', '&#x27;', '&#x28;', '&#x29;', '&#x2a;', '&#x2b;', '&#x2c;', '&#x2d;', '&#x2e;', '&#x2f;', '&#x30;', '&#x31;', '&#x32;', '&#x33;', '&#x34;', '&#x35;', '&#x36;', '&#x37;', '&#x38;', '&#x39;', '&#x3a;', '&#x3b;', '&#x3c;', '&#x3d;', '&#x3e;', '&#x3f;', '&#x40;', '&#x41;', '&#x42;', '&#x43;', '&#x44;', '&#x45;', '&#x46;', '&#x47;', '&#x48;', '&#x49;', '&#x4a;', '&#x4b;', '&#x4c;', '&#x4d;', '&#x4e;', '&#x4f;', '&#x50;', '&#x51;', '&#x52;', '&#x53;', '&#x54;', '&#x55;', '&#x56;', '&#x57;', '&#x58;', '&#x59;', '&#x5a;', '&#x5b;', '&#x5c;', '&#x5d;', '&#x5e;', '&#x5f;', '&#x60;', '&#x61;', '&#x62;', '&#x63;', '&#x64;', '&#x65;', '&#x66;', '&#x67;', '&#x68;', '&#x69;', '&#x6a;', '&#x6b;', '&#x6c;', '&#x6d;', '&#x6e;', '&#x6f;', '&#x70;', '&#x71;', '&#x72;', '&#x73;', '&#x74;', '&#x75;', '&#x76;', '&#x77;', '&#x78;', '&#x79;', '&#x7a;', '&#x7b;', '&#x7c;', '&#x7d;', '&#x7e;', '&#xe000;', '&#xe001;', '&#xe002;', '&#xe003;', '&#xe004;', '&#xe005;', '&#xe006;', '&#xe007;', '&#xe009;', '&#xe00a;', '&#xe00b;', '&#xe00c;', '&#xe00d;', '&#xe00e;', '&#xe00f;', '&#xe010;', '&#xe011;', '&#xe012;', '&#xe013;', '&#xe014;', '&#xe015;', '&#xe016;', '&#xe017;', '&#xe018;', '&#xe019;', '&#xe01a;', '&#xe01b;', '&#xe01c;', '&#xe01d;', '&#xe01e;', '&#xe01f;', '&#xe020;', '&#xe021;', '&#xe022;', '&#xe023;', '&#xe024;', '&#xe025;', '&#xe026;', '&#xe027;', '&#xe028;', '&#xe029;', '&#xe02a;', '&#xe02b;', '&#xe02c;', '&#xe02d;', '&#xe02e;', '&#xe02f;', '&#xe030;', '&#xe103;', '&#xe0ee;', '&#xe0ef;', '&#xe0e8;', '&#xe0ea;', '&#xe101;', '&#xe107;', '&#xe108;', '&#xe102;', '&#xe106;', '&#xe0eb;', '&#xe010;', '&#xe105;', '&#xe0ed;', '&#xe100;', '&#xe104;', '&#xe0e9;', '&#xe109;', '&#xe0ec;', '&#xe0fe;', '&#xe0f6;', '&#xe0fb;', '&#xe0e2;', '&#xe0e3;', '&#xe0f5;', '&#xe0e1;', '&#xe0ff;', '&#xe031;', '&#xe032;', '&#xe033;', '&#xe034;', '&#xe035;', '&#xe036;', '&#xe037;', '&#xe038;', '&#xe039;', '&#xe03a;', '&#xe03b;', '&#xe03c;', '&#xe03d;', '&#xe03e;', '&#xe03f;', '&#xe040;', '&#xe041;', '&#xe042;', '&#xe043;', '&#xe044;', '&#xe045;', '&#xe046;', '&#xe047;', '&#xe048;', '&#xe049;', '&#xe04a;', '&#xe04b;', '&#xe04c;', '&#xe04d;', '&#xe04e;', '&#xe04f;', '&#xe050;', '&#xe051;', '&#xe052;', '&#xe053;', '&#xe054;', '&#xe055;', '&#xe056;', '&#xe057;', '&#xe058;', '&#xe059;', '&#xe05a;', '&#xe05b;', '&#xe05c;', '&#xe05d;', '&#xe05e;', '&#xe05f;', '&#xe060;', '&#xe061;', '&#xe062;', '&#xe063;', '&#xe064;', '&#xe065;', '&#xe066;', '&#xe067;', '&#xe068;', '&#xe069;', '&#xe06a;', '&#xe06b;', '&#xe06c;', '&#xe06d;', '&#xe06e;', '&#xe06f;', '&#xe070;', '&#xe071;', '&#xe072;', '&#xe073;', '&#xe074;', '&#xe075;', '&#xe076;', '&#xe077;', '&#xe078;', '&#xe079;', '&#xe07a;', '&#xe07b;', '&#xe07c;', '&#xe07d;', '&#xe07e;', '&#xe07f;', '&#xe080;', '&#xe081;', '&#xe082;', '&#xe083;', '&#xe084;', '&#xe085;', '&#xe086;', '&#xe087;', '&#xe088;', '&#xe089;', '&#xe08a;', '&#xe08b;', '&#xe08c;', '&#xe08d;', '&#xe08e;', '&#xe08f;', '&#xe090;', '&#xe091;', '&#xe092;', '&#xe0f8;', '&#xe0fa;', '&#xe0e7;', '&#xe0fd;', '&#xe0e4;', '&#xe0e5;', '&#xe0f7;', '&#xe0e0;', '&#xe0fc;', '&#xe0f9;', '&#xe0dd;', '&#xe0f1;', '&#xe0dc;', '&#xe0f3;', '&#xe0d8;', '&#xe0db;', '&#xe0f0;', '&#xe0df;', '&#xe0f2;', '&#xe0f4;', '&#xe0d9;', '&#xe0da;', '&#xe0de;', '&#xe0e6;', '&#xe093;', '&#xe094;', '&#xe095;', '&#xe096;', '&#xe097;', '&#xe098;', '&#xe099;', '&#xe09a;', '&#xe09b;', '&#xe09c;', '&#xe09d;', '&#xe09e;', '&#xe09f;', '&#xe0a0;', '&#xe0a1;', '&#xe0a2;', '&#xe0a3;', '&#xe0a4;', '&#xe0a5;', '&#xe0a6;', '&#xe0a7;', '&#xe0a8;', '&#xe0a9;', '&#xe0aa;', '&#xe0ab;', '&#xe0ac;', '&#xe0ad;', '&#xe0ae;', '&#xe0af;', '&#xe0b0;', '&#xe0b1;', '&#xe0b2;', '&#xe0b3;', '&#xe0b4;', '&#xe0b5;', '&#xe0b6;', '&#xe0b7;', '&#xe0b8;', '&#xe0b9;', '&#xe0ba;', '&#xe0bb;', '&#xe0bc;', '&#xe0bd;', '&#xe0be;', '&#xe0bf;', '&#xe0c0;', '&#xe0c1;', '&#xe0c2;', '&#xe0c3;', '&#xe0c4;', '&#xe0c5;', '&#xe0c6;', '&#xe0c7;', '&#xe0c8;', '&#xe0c9;', '&#xe0ca;', '&#xe0cb;', '&#xe0cc;', '&#xe0cd;', '&#xe0ce;', '&#xe0cf;', '&#xe0d0;', '&#xe0d1;', '&#xe0d2;', '&#xe0d3;', '&#xe0d4;', '&#xe0d5;', '&#xe0d6;', '&#xe0d7;', '&#xe600;', '&#xe601;', '&#xe602;', '&#xe603;', '&#xe604;', '&#xe605;', '&#xe606;', '&#xe607;', '&#xe608;', '&#xe609;', '&#xe60a;', '&#xe60b;', '&#xe60c;', '&#xe60d;', '&#xe60e;', '&#xe60f;', '&#xe610;', '&#xe611;', '&#xe612;', '&#xe008;' ); $symbols = apply_filters( 'et_pb_font_icon_symbols', $symbols ); return $symbols; } endif; if ( ! function_exists( 'et_pb_get_font_icon_list' ) ) : /** * Font icon list. */ function et_pb_get_font_icon_list() { $output = is_customize_preview() ? et_pb_get_font_icon_list_items() : '<%= window.et_builder.font_icon_list_template() %>'; $output = sprintf( '<ul class="et_font_icon">%1$s</ul>', et_core_esc_previously( $output ) ); return $output; } endif; if ( ! function_exists( 'et_pb_get_svg_icons_list' ) ) : /** * Return svg icons list. */ function et_pb_get_svg_icons_list() { $all_icons = array( 'add' => '<g> <path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd"></path> </g>', 'delete' => '<g> <path d="M19 9h-3V8a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v1H9a1 1 0 1 0 0 2h10a1 1 0 0 0 .004-2H19zM9 20c.021.543.457.979 1 1h8c.55-.004.996-.45 1-1v-7H9v7zm2.02-4.985h2v4h-2v-4zm4 0h2v4h-2v-4z" fillRule="evenodd"></path> </g>', 'setting' => '<g> <path d="M20.426 13.088l-1.383-.362a.874.874 0 0 1-.589-.514l-.043-.107a.871.871 0 0 1 .053-.779l.721-1.234a.766.766 0 0 0-.116-.917 6.682 6.682 0 0 0-.252-.253.768.768 0 0 0-.917-.116l-1.234.722a.877.877 0 0 1-.779.053l-.107-.044a.87.87 0 0 1-.513-.587l-.362-1.383a.767.767 0 0 0-.73-.567h-.358a.768.768 0 0 0-.73.567l-.362 1.383a.878.878 0 0 1-.513.589l-.107.044a.875.875 0 0 1-.778-.054l-1.234-.722a.769.769 0 0 0-.918.117c-.086.082-.17.166-.253.253a.766.766 0 0 0-.115.916l.721 1.234a.87.87 0 0 1 .053.779l-.043.106a.874.874 0 0 1-.589.514l-1.382.362a.766.766 0 0 0-.567.731v.357a.766.766 0 0 0 .567.731l1.383.362c.266.07.483.26.588.513l.043.107a.87.87 0 0 1-.053.779l-.721 1.233a.767.767 0 0 0 .115.917c.083.087.167.171.253.253a.77.77 0 0 0 .918.116l1.234-.721a.87.87 0 0 1 .779-.054l.107.044a.878.878 0 0 1 .513.589l.362 1.383a.77.77 0 0 0 .731.567h.356a.766.766 0 0 0 .73-.567l.362-1.383a.878.878 0 0 1 .515-.589l.107-.044a.875.875 0 0 1 .778.054l1.234.721c.297.17.672.123.917-.117.087-.082.171-.166.253-.253a.766.766 0 0 0 .116-.917l-.721-1.234a.874.874 0 0 1-.054-.779l.044-.107a.88.88 0 0 1 .589-.513l1.383-.362a.77.77 0 0 0 .567-.731v-.357a.772.772 0 0 0-.569-.724v-.005zm-6.43 3.9a2.986 2.986 0 1 1 2.985-2.986 3 3 0 0 1-2.985 2.987v-.001z" fillRule="evenodd"></path> </g>', 'background-color' => '<g> <path d="M19.4 14.6c0 0-1.5 3.1-1.5 4.4 0 0.9 0.7 1.6 1.5 1.6 0.8 0 1.5-0.7 1.5-1.6C20.9 17.6 19.4 14.6 19.4 14.6zM19.3 12.8l-4.8-4.8c-0.2-0.2-0.4-0.3-0.6-0.3 -0.3 0-0.5 0.1-0.7 0.3l-1.6 1.6L9.8 7.8c-0.4-0.4-1-0.4-1.4 0C8 8.1 8 8.8 8.4 9.1l1.8 1.8 -2.8 2.8c-0.4 0.4-0.4 1-0.1 1.4l4.6 4.6c0.2 0.2 0.4 0.3 0.6 0.3 0.3 0 0.5-0.1 0.7-0.3l6.1-6.1C19.5 13.4 19.5 13.1 19.3 12.8zM15.6 14.6c-1.7 1.7-4.5 1.7-6.2 0l2.1-2.1 1 1c0.4 0.4 1 0.4 1.4 0 0.4-0.4 0.4-1 0-1.4l-1-1 0.9-0.9 3.1 3.1L15.6 14.6z" fillRule="evenodd"></path> </g>', 'background-image' => '<g> <path d="M22.9 7.5c-0.1-0.3-0.5-0.6-0.8-0.6H5.9c-0.4 0-0.7 0.2-0.8 0.6C5.1 7.6 5 7.7 5 7.9v12.2c0 0.1 0 0.2 0.1 0.4 0.1 0.3 0.5 0.5 0.8 0.6h16.2c0.4 0 0.7-0.2 0.8-0.6 0-0.1 0.1-0.2 0.1-0.4V7.9C23 7.7 23 7.6 22.9 7.5zM21 18.9H7v-10h14V18.9z" fillRule="evenodd"></path> <circle cx="10.5" cy="12.4" r="1.5"></circle> <polygon points="15 16.9 13 13.9 11 16.9 "></polygon> <polygon points="17 10.9 15 16.9 19 16.9 "></polygon> </g>', 'background-gradient' => '<g> <path d="M22.9 7.5c-0.1-0.3-0.5-0.6-0.8-0.6H5.9c-0.4 0-0.7 0.2-0.8 0.6C5.1 7.6 5 7.7 5 7.9v12.2c0 0.1 0 0.2 0.1 0.4 0.1 0.3 0.5 0.5 0.8 0.6h16.2c0.4 0 0.7-0.2 0.8-0.6 0-0.1 0.1-0.2 0.1-0.4V7.9C23 7.7 23 7.6 22.9 7.5zM21 18.9L7 8.9h14V18.9z" fillRule="evenodd"></path> </g>', 'background-video' => '<g> <path d="M22.9 7.5c-0.1-0.3-0.5-0.6-0.8-0.6H5.9c-0.4 0-0.7 0.2-0.8 0.6C5.1 7.6 5 7.7 5 7.9v12.2c0 0.1 0 0.2 0.1 0.4 0.1 0.3 0.5 0.5 0.8 0.6h16.2c0.4 0 0.7-0.2 0.8-0.6 0-0.1 0.1-0.2 0.1-0.4V7.9C23 7.7 23 7.6 22.9 7.5zM21 18.9H7v-10h14V18.9z" fillRule="evenodd"></path> <polygon points="13 10.9 13 16.9 17 13.9 "></polygon> </g>', 'swap' => '<g> <path d="M19 12h-3V9c0-0.5-0.5-1-1-1H8C7.5 8 7 8.5 7 9v7c0 0.5 0.5 1 1 1h3v3c0 0.5 0.5 1 1 1h7c0.5 0 1-0.5 1-1v-7C20 12.5 19.5 12 19 12zM18 19h-5v-2h2c0.5 0 1-0.5 1-1v-2h2V19z" fillRule="evenodd"></path> </g>', 'none' => '<g> <path d="M14 24c5.5 0 10-4.5 10-10S19.5 4 14 4 4 8.5 4 14s4.5 10 10 10zm0-17.5c4.1 0 7.5 3.4 7.5 7.5 0 1.5-.5 2.9-1.2 4.1L9.9 7.7c1.2-.7 2.6-1.2 4.1-1.2zM7.7 9.9l10.4 10.4c-1.2.8-2.6 1.2-4.1 1.2-4.1 0-7.5-3.4-7.5-7.5 0-1.5.5-2.9 1.2-4.1z"></path> </g>', 'animation-none' => '<g> <path d="M14 24c5.5 0 10-4.5 10-10S19.5 4 14 4 4 8.5 4 14s4.5 10 10 10zm0-17.5c4.1 0 7.5 3.4 7.5 7.5 0 1.5-.5 2.9-1.2 4.1L9.9 7.7c1.2-.7 2.6-1.2 4.1-1.2zM7.7 9.9l10.4 10.4c-1.2.8-2.6 1.2-4.1 1.2-4.1 0-7.5-3.4-7.5-7.5 0-1.5.5-2.9 1.2-4.1z"></path> </g>', 'animation-fade' => '<g> <circle cx="8.5" cy="19.5" r="1.5"></circle> <circle cx="8.5" cy="14.5" r="1.5"></circle> <circle cx="5" cy="12" r="1"></circle> <circle cx="5" cy="17" r="1"></circle> <circle cx="8.5" cy="9.5" r="1.5"></circle> <path d="M15.7 4c-.4 0-.8.1-1.2.3-.6.3-.5.7-1.5.7-1.1 0-2 .9-2 2s.9 2 2 2c.3 0 .5.2.5.5s-.2.5-.5.5c-1.1 0-2 .9-2 2s.9 2 2 2c.3 0 .5.2.5.5s-.2.5-.5.5c-1.1 0-2 .9-2 2s.9 2 2 2c.3 0 .5.2.5.5s-.2.5-.5.5c-1.1 0-2 .9-2 2s.9 2 2 2c1 0 .9.4 1.4.7.4.2.8.3 1.2.3 4.3-.4 8.3-5.3 8.3-10.5s-4-10-8.2-10.5z"></path> </g>', 'animation-slide' => '<g> <path d="M22 4h-5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h5c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM10 14c0 .6.4 1 1 1h.6L10 16.6c-.4.4-.4 1 0 1.4.4.4 1 .4 1.4 0l3.3-3.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7L11.4 10c-.4-.4-1-.4-1.4 0-.4.4-.4 1 0 1.4l1.6 1.6H11c-.6 0-1 .4-1 1z"></path> <circle cx="7" cy="14" r="1.5"></circle> <circle cx="3" cy="14" r="1"></circle> </g>', 'animation-bounce' => '<g> <circle cx="21.5" cy="8.5" r="3.5"></circle> <circle cx="16" cy="12" r="1.7"></circle> <circle cx="13" cy="15" r="1.2"></circle> <circle cx="11" cy="18" r="1"></circle> <circle cx="9" cy="22" r="1"></circle> <circle cx="7" cy="19" r="1"></circle> <circle cx="4" cy="17" r="1"></circle> </g>', 'animation-zoom' => '<g> <path d="M23.7 4.3c-.1-.1-.2-.2-.3-.2-.1-.1-.3-.1-.4-.1h-5c-.6 0-1 .4-1 1s.4 1 1 1h2.6l-3.1 3.1c-.2-.1-.3-.1-.5-.1h-6c-.2 0-.3 0-.5.1L7.4 6H10c.6 0 1-.4 1-1s-.4-1-1-1H5c-.1 0-.3 0-.4.1-.2.1-.4.3-.5.5-.1.1-.1.3-.1.4v5c0 .6.4 1 1 1s1-.4 1-1V7.4l3.1 3.1c-.1.2-.1.3-.1.5v6c0 .2 0 .3.1.5L6 20.6V18c0-.6-.4-1-1-1s-1 .4-1 1v5c0 .1 0 .3.1.4.1.2.3.4.5.5.1.1.3.1.4.1h5c.6 0 1-.4 1-1s-.4-1-1-1H7.4l3.1-3.1c.2 0 .3.1.5.1h6c.2 0 .3 0 .5-.1l3.1 3.1H18c-.6 0-1 .4-1 1s.4 1 1 1h5c.1 0 .3 0 .4-.1.2-.1.4-.3.5-.5.1-.1.1-.3.1-.4v-5c0-.6-.4-1-1-1s-1 .4-1 1v2.6l-3.1-3.1c0-.2.1-.3.1-.5v-6c0-.2 0-.3-.1-.5L22 7.4V10c0 .6.4 1 1 1s1-.4 1-1V5c0-.1 0-.3-.1-.4 0-.1-.1-.2-.2-.3z"></path> </g>', 'animation-flip' => '<g> <path d="M22 2.4l-7 2.9V7h-2v-.8L7.6 8.7c-.4.2-.6.5-.6.9v8.7c0 .4.2.7.6.9l5.4 2.5V21h2v1.7l7 2.9c.5.2 1-.2 1-.7V3.1c0-.5-.5-.9-1-.7zM15 19h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zM13 2h2v2.5h-2zM13 23.5h2V26h-2z"></path> </g>', 'animation-fold' => '<g> <path d="M24 7h-4V3.4c0-.8-.6-1.4-1.3-1.4-.2 0-.5.1-.7.2l-6.5 3.9c-.9.6-1.5 1.6-1.5 2.6V23c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm-6 10.5c0 .2-.1.4-.3.5L12 21.5V8.7c0-.4.2-.7.5-.9L18 4.5v13zM6 7h2v2H6zM6 23h2v2H6zM2.6 7.1c-.1 0-.1.1-.2.1v.1l-.1.1-.1.1c-.1.1-.2.3-.2.5v1h2V7H3c-.1 0-.2 0-.4.1zM2 23v1c0 .4.3.8.7.9.1.1.2.1.3.1h1v-2H2zM2 11h2v2H2zM2 19h2v2H2zM2 15h2v2H2z"></path> </g>', 'animation-roll' => '<g> <path d="M18.8 5c-5.3-2.7-11.8.2-14 5.6-1.1 2.8-1 6 .2 8.8.4 1 3.9 6.5 5 3.6.5-1.2-1.3-2.2-1.9-3-.8-1.2-1.4-2.5-1.6-3.9-.4-2.7.5-5.5 2.4-7.4 4-4 11.6-2.5 12.6 3.4.4 2.7-.9 5.5-3.4 6.6-2.6 1.1-6 0-6.8-2.8-.7-2.4 1.2-5.7 4-4.8 1.1.3 2 1.5 1.5 2.7-.3.7-1.7 1.2-1.6.1 0-.3.2-.4.2-.8-.1-.4-.5-.6-.9-.6-1.1.1-1.6 1.6-1.3 2.5.3 1.2 1.5 1.9 2.7 1.9 2.9 0 4.2-3.4 3.1-5.7-1.2-2.6-4.6-3.4-7-2.2-2.6 1.3-3.8 4.4-3.1 7.2 1.6 5.9 9.3 6.8 13.1 2.5 3.8-4.2 1.9-11.1-3.2-13.7z"></path> </g>', 'border-link' => '<g> <path d="M14.71 17.71a3 3 0 0 1-2.12-.88l-.71-.71a1 1 0 0 1 1.41-1.41l.71.71a1 1 0 0 0 1.41 0l5-4.95a1 1 0 0 0 0-1.41l-1.46-1.42a1 1 0 0 0-1.41 0L16.1 9.07a1 1 0 0 1-1.41-1.41l1.43-1.43a3.07 3.07 0 0 1 4.24 0l1.41 1.41a3 3 0 0 1 0 4.24l-5 4.95a3 3 0 0 1-2.06.88z"></path> <path d="M9.76 22.66a3 3 0 0 1-2.12-.88l-1.42-1.42a3 3 0 0 1 0-4.24l5-4.95a3.07 3.07 0 0 1 4.24 0l.71.71a1 1 0 0 1-1.41 1.41l-.76-.7a1 1 0 0 0-1.41 0l-5 4.95a1 1 0 0 0 0 1.41L9 20.36a1 1 0 0 0 1.41 0L11.82 19a1 1 0 0 1 1.41 1.41l-1.36 1.36a3 3 0 0 1-2.11.89z"></path> </g>', 'border-all' => '<g> <path d="M22 5H6a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm-2 15H8V8h12z"></path> </g>', 'border-top' => '<g> <path d="M17 21h2v2h-2zM5 9h2v2H5zM21 17h2v2h-2zM21 9h2v2h-2zM21 13h2v2h-2zM21 23h1a1 1 0 0 0 1-1v-1h-2zM5 17h2v2H5zM5 13h2v2H5zM13 21h2v2h-2zM9 21h2v2H9zM5 21v1a1 1 0 0 0 1 1h1v-2zM22 5H6a1 1 0 0 0-1 1v2h18V6a1 1 0 0 0-1-1z"></path> </g>', 'border-right' => '<g> <path d="M13 5h2v2h-2zM5 9h2v2H5zM9 5h2v2H9zM7 5H6a1 1 0 0 0-1 1v1h2zM5 13h2v2H5zM13 21h2v2h-2zM5 17h2v2H5zM9 21h2v2H9zM17 5h2v2h-2zM5 21v1a1 1 0 0 0 1 1h1v-2zM22 5h-2v18h2a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zM17 21h2v2h-2z"></path> </g>', 'border-bottom' => '<g> <path d="M9 5h2v2H9zM7 20H5v2a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-2H7zM17 5h2v2h-2zM5 13h2v2H5zM5 9h2v2H5zM13 5h2v2h-2zM5 17h2v2H5zM21 9h2v2h-2zM21 17h2v2h-2zM22 5h-1v2h2V6a1 1 0 0 0-1-1zM21 13h2v2h-2zM7 5H6a1 1 0 0 0-1 1v1h2z"></path> </g>', 'border-left' => '<g> <path d="M22 5h-1v2h2V6a1 1 0 0 0-1-1zM9 21h2v2H9zM21 17h2v2h-2zM13 21h2v2h-2zM21 13h2v2h-2zM9 5h2v2H9zM17 21h2v2h-2zM17 5h2v2h-2zM21 9h2v2h-2zM8 7V5H6a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h2V7zM21 23h1a1 1 0 0 0 1-1v-1h-2zM13 5h2v2h-2z"></path> </g>', 'lock' => '<g> <path d="M20 12C19.9 11.7 19.3 11 19 11L18 11C18 8.1 16.2 6 14 6 11.8 6 10 8.1 10 11L9 11C8.6 11 8.1 11.6 8 12L8 13 8 19 8 20C8.1 20.3 8.7 20.9 9 21L19 21C19.4 21 19.9 20.4 20 20L20 19 20 14 20 12 20 12ZM14 8C15.1 8 16 9.4 16 11.1L12 11.1C12 9.4 12.9 8 14 8L14 8ZM18 19L10 19 10 13 18 13 18 19 18 19Z" fillRule="evenodd"></path> <path d="M14 18C14.6 18 15 17.6 15 17L15 15C15 14.4 14.6 14 14 14 13.4 14 13 14.4 13 15L13 15 13 17C13 17.6 13.4 18 14 18L14 18Z" fillRule="evenodd"></path> </g>', ); return $all_icons; } endif; if ( ! function_exists( 'et_pb_get_font_icon_list_items' ) ) : /** * Return Font icon list items. */ function et_pb_get_font_icon_list_items() { $output = ''; $symbols = et_pb_get_font_icon_symbols(); foreach ( $symbols as $symbol ) { $output .= sprintf( '<li data-icon=\'%1$s\'></li>', esc_attr( $symbol ) ); } return $output; } endif; if ( ! function_exists( 'et_pb_font_icon_list' ) ) : /** * Display font icon list. */ function et_pb_font_icon_list() { echo et_core_esc_previously( et_pb_get_font_icon_list() ); } endif; if ( ! function_exists( 'et_pb_get_font_down_icon_symbols' ) ) : /** * Return icon to display for the scroll down button. */ function et_pb_get_font_down_icon_symbols() { $symbols = array( '&#x22;', '&#x33;', '&#x37;', '&#x3b;', '&#x3f;', '&#x43;', '&#x47;', '&#xe03a;', '&#xe044;', '&#xe048;', '&#xe04c;' ); return $symbols; } endif; if ( ! function_exists( 'et_pb_get_font_down_icon_list' ) ) : /** * Return font down icon list. */ function et_pb_get_font_down_icon_list() { $output = is_customize_preview() ? et_pb_get_font_down_icon_list_items() : '<%= window.et_builder.font_down_icon_list_template() %>'; $output = sprintf( '<ul class="et_font_icon">%1$s</ul>', et_core_esc_previously( $output ) ); return $output; } endif; if ( ! function_exists( 'et_pb_get_font_down_icon_list_items' ) ) : /** * Return font down icon list items. */ function et_pb_get_font_down_icon_list_items() { $output = ''; $symbols = et_pb_get_font_down_icon_symbols(); foreach ( $symbols as $symbol ) { $output .= sprintf( '<li data-icon="%1$s"></li>', esc_attr( $symbol ) ); } return $output; } endif; if ( ! function_exists( 'et_pb_font_down_icon_list' ) ) : /** * Return icon to display for the scroll down button. */ function et_pb_font_down_icon_list() { echo et_core_esc_previously( et_pb_get_font_down_icon_list() ); } endif; if ( ! function_exists( 'et_pb_process_font_icon' ) ) : /** * Processes font icon value for use on front-end * * @param string $font_icon Font Icon ( exact value or in %%index_number%% format ). * @param string $symbols_function Optional. Name of the function that gets an array of font icon values. * et_pb_get_font_icon_symbols function is used by default. * @return string $font_icon Font Icon value */ function et_pb_process_font_icon( $font_icon, $symbols_function = 'default' ) { // Do it if $font_icon is an extended icon. if ( et_pb_maybe_extended_icon( $font_icon ) ) { return et_pb_get_extended_font_icon_value( $font_icon ); } // the exact font icon value is saved. if ( 1 !== preg_match( '/^%%/', trim( $font_icon ) ) ) { return $font_icon; } // the font icon value is saved in the following format: %%index_number%%. $icon_index = (int) str_replace( '%', '', $font_icon ); $icon_symbols = 'default' === $symbols_function ? et_pb_get_font_icon_symbols() : call_user_func( $symbols_function ); $font_icon = isset( $icon_symbols[ $icon_index ] ) ? $icon_symbols[ $icon_index ] : ''; return $font_icon; } endif; if ( ! function_exists( 'et_builder_accent_color' ) ) : /** * Return an accent color. * * @param string $default_color Default color. */ function et_builder_accent_color( $default_color = '#7EBEC5' ) { // Accent color option exists in Divi theme only. Use default color in plugin. $accent_color = ! et_is_builder_plugin_active() ? et_get_option( 'accent_color', $default_color ) : $default_color; return apply_filters( 'et_builder_accent_color', $accent_color ); } endif; if ( ! function_exists( 'et_pb_process_header_level' ) ) : /** * Process header level. * * @param string $new_level Header level. * @param string $default Default header level. * * @return string */ function et_pb_process_header_level( $new_level, $default ) { $valid_header_levels = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); // return the new header level if exists in the list of valid header levels. if ( in_array( $new_level, $valid_header_levels, true ) ) { return $new_level; } // return default if defined. Fallback to h2 otherwise. return isset( $default ) ? $default : 'h2'; } endif; if ( ! function_exists( 'et_pb_get_alignment' ) ) { /** * Return an alignment value from alignment key. * * @param string $key Alignment key. * * @return string */ function et_pb_get_alignment( $key ) { if ( is_rtl() && 'left' === $key ) { $key = 'right'; } switch ( $key ) { case 'force_left': return 'left'; case 'justified': return 'justify'; default: return $key; } } } if ( ! function_exists( 'et_builder_get_text_orientation_options' ) ) : /** * Return text orientation options to use in dropdown. * * @param array $exclude_options Options to be excluded. * @param array $include_options Options to be included. */ function et_builder_get_text_orientation_options( $exclude_options = array(), $include_options = array() ) { $text_orientation_options = array( 'left' => et_builder_i18n( 'Left' ), 'center' => et_builder_i18n( 'Center' ), 'right' => et_builder_i18n( 'Right' ), 'justified' => et_builder_i18n( 'Justified' ), ); if ( is_rtl() ) { $text_orientation_options = array( 'right' => et_builder_i18n( 'Right' ), 'center' => et_builder_i18n( 'Center' ), 'force_left' => et_builder_i18n( 'Left' ), ); } // Exclude some options if needed. if ( ! empty( $exclude_options ) ) { foreach ( $exclude_options as $exclude ) { unset( $text_orientation_options[ $exclude ] ); } } // Include some options if needed. if ( ! empty( $include_options ) ) { $text_orientation_options = wp_parse_args( $include_options, $text_orientation_options ); } return apply_filters( 'et_builder_text_orientation_options', $text_orientation_options ); } endif; if ( ! function_exists( 'et_builder_get_gallery_settings' ) ) : /** * Return gallery button. */ function et_builder_get_gallery_settings() { $output = sprintf( '<input type="button" class="button button-upload et-pb-gallery-button" value="%1$s" />', esc_attr__( 'Update Gallery', 'et_builder' ) ); return $output; } endif; if ( ! function_exists( 'et_builder_get_nav_menus_options' ) ) : /** * Return navigation menus options. */ function et_builder_get_nav_menus_options() { $nav_menus_options = array( 'none' => esc_html__( 'Select a menu', 'et_builder' ) ); $nav_menus = wp_get_nav_menus( array( 'orderby' => 'name' ) ); foreach ( (array) $nav_menus as $_nav_menu ) { $nav_menus_options[ $_nav_menu->term_id ] = $_nav_menu->name; } return apply_filters( 'et_builder_nav_menus_options', $nav_menus_options ); } endif; if ( ! function_exists( 'et_builder_generate_center_map_setting' ) ) : /** * Return center map div. */ function et_builder_generate_center_map_setting() { return '<div id="et_pb_map_center_map" class="et-pb-map et_pb_map_center_map"></div>'; } endif; if ( ! function_exists( 'et_builder_generate_pin_zoom_level_input' ) ) : /** * Return map's pin zoom level input element. */ function et_builder_generate_pin_zoom_level_input() { return '<input class="et_pb_zoom_level" type="hidden" value="18" />'; } endif; /** * Define conditional tags needed for component's backend parser. This is used for FB's public facing update * mechanism to pass conditional tag to admin-ajax.php for component which relies to backend parsing. Backend * uses this conditional tags' key as well for sanitization * * @return array */ function et_fb_conditional_tag_params() { global $post; $post_type = isset( $post->post_type ) ? $post->post_type : false; $conditional_tags = array( 'is_limited_mode' => et_builder_is_limited_mode(), 'is_bfb' => et_builder_bfb_enabled(), 'is_bfb_activated' => et_builder_bfb_activated(), 'is_tb' => et_builder_tb_enabled(), 'is_front_page' => is_front_page(), 'is_home_page' => is_home() || is_front_page(), 'is_search' => is_search(), 'is_single' => is_single(), 'is_singular' => is_singular(), 'is_singular_project' => is_singular( 'project' ), 'is_rtl' => is_rtl(), 'is_no_rtl' => 'on' === et_get_option( 'divi_disable_translations', 'off' ), 'et_is_builder_plugin_active' => et_is_builder_plugin_active(), 'is_user_logged_in' => is_user_logged_in(), 'et_is_ab_testing_active' => et_is_ab_testing_active() ? 'yes' : 'no', 'is_wrapped_styles' => et_builder_has_limitation( 'use_wrapped_styles' ), 'is_gutenberg' => et_core_is_gutenberg_active(), 'is_custom_post_type' => et_builder_is_post_type_custom( $post_type ), 'is_layout_post_type' => et_theme_builder_is_layout_post_type( $post_type ), 'is_rich_editor' => 'true' === apply_filters( 'user_can_richedit', get_user_option( 'rich_editing' ) ) ? 'yes' : 'no', // Pass falsey as empty string so it remains falsey when conditionalTags is fetched and // passed string as AJAX payload (on AJAX string, false bool becomes 'false' string). 'is_layout_block' => ET_GB_Block_Layout::is_layout_block_preview() ? true : '', ); return apply_filters( 'et_fb_conditional_tag_params', $conditional_tags ); } if ( ! function_exists( 'et_builder_page_creation_options' ) ) : /** * Get Page Creation flow options * * @since 4.2 * * @return array */ function et_builder_page_creation_options() { return array( 'build_from_scratch' => array( 'className' => 'accent-blue', 'imgSrc' => 'scratch.png', 'imgSrcHover' => 'scratch.gif', 'titleText' => esc_html__( 'Build From Scratch', 'et_builder' ), 'descriptionText' => esc_html__( 'Build your page from the ground up. Don’t worry, you can access our premade layouts at any time.', 'et_builder' ), 'buttonText' => esc_html__( 'Start Building', 'et_builder' ), 'permission' => array( 'add_module' ), 'setting' => array( 'value_index' => 1, ), ), 'use_existing_content' => array( 'className' => 'accent-blue', 'imgSrc' => 'existing.png', 'imgSrcHover' => 'existing.gif', 'titleText' => esc_html__( 'Use Existing Content', 'et_builder' ), 'descriptionText' => esc_html__( 'Use the Divi Builder while retaining your existing page content.', 'et_builder' ), 'buttonText' => esc_html__( 'Start Building', 'et_builder' ), 'permission' => array( 'edit_module' ), 'setting' => false, ), 'choose_premade_layout' => array( 'className' => 'accent-purple', 'imgSrc' => 'premade.png', 'imgSrcHover' => 'premade.gif', 'titleText' => esc_html__( 'Choose a premade Layout', 'et_builder' ), 'descriptionText' => esc_html__( 'Choose from hundreds of premade layouts, start from any of your saved layouts, or clone an existing page.', 'et_builder' ), 'buttonText' => esc_html__( 'Browse Layouts', 'et_builder' ), 'permission' => array( 'load_layout', 'divi_library' ), 'setting' => array( 'label' => esc_html__( 'Load Premade Layout', 'et_builder' ), 'value_index' => 2, ), ), 'build_with_ai' => array( 'className' => 'accent-dark-blue', 'imgSrc' => 'layout-insert-build-with-ai.svg', 'imgSrcHover' => 'layout-insert-build-with-ai.svg', 'titleText' => esc_html__( 'Build With AI', 'et_builder' ), 'bannerText' => esc_html__( 'Brand New', 'et_builder' ), 'descriptionText' => esc_html__( 'Simply describe your page content, sit back, relax, and let Divi AI build your page with the click of a button.', 'et_builder' ), 'buttonText' => esc_html__( 'Generate Layout', 'et_builder' ), 'permission' => array( 'divi_ai' ), 'setting' => array( 'value_index' => 3, ), ), ); } endif; if ( ! function_exists( 'et_builder_page_creation_settings' ) ) : /** * Get Page Creation flow setting options * * @since 4.2 * * @param bool $value_as_index Flag to set the options value as numeric index. * * @return array */ function et_builder_page_creation_settings( $value_as_index = false ) { $default_label = esc_html__( 'Give Me A Choice', 'et_builder' ); if ( $value_as_index ) { $settings = array( $default_label, ); } else { $settings = array( 'default' => $default_label, ); } foreach ( et_builder_page_creation_options() as $key => $option ) { if ( ! et_()->array_get( $option, 'setting' ) ) { continue; } if ( isset( $option['permission'] ) ) { $capabilities = is_array( $option['permission'] ) ? $option['permission'] : explode( ',', $option['permission'] ); $allowed = array_filter( $capabilities, 'et_pb_is_allowed' ); if ( ! $allowed || count( $capabilities ) !== count( $allowed ) ) { continue; } } $value = $value_as_index ? $option['setting']['value_index'] : $key; $label = et_()->array_get( $option, 'setting.label', $option['titleText'] ); $settings[ $value ] = $label; } return $settings; } endif; /** * Return an app preferences. * * @return mixed|void */ function et_fb_app_preferences_settings() { $app_preferences = array( 'settings_bar_location' => array( 'type' => 'string', 'default' => 'bottom', 'options' => array( 'top-left', 'top', 'top-right', 'right', 'bottom-right', 'bottom', 'bottom-left', 'left', ), ), 'builder_animation' => array( 'type' => 'bool', 'default' => true, ), 'builder_display_modal_settings' => array( 'type' => 'bool', 'default' => false, ), 'builder_enable_dummy_content' => array( 'type' => 'bool', 'default' => true, ), 'builder_enable_visual_theme_builder' => array( 'type' => 'bool', 'default' => true, ), 'event_mode' => array( 'type' => 'string', 'default' => 'hover', 'options' => array( 'hover' => esc_html__( 'Hover Mode', 'et_builder' ), 'click' => esc_html__( 'Click Mode', 'et_builder' ), 'grid' => esc_html__( 'Grid Mode', 'et_builder' ), ), ), 'view_mode' => array( 'type' => 'string', 'default' => et_builder_bfb_enabled() ? 'wireframe' : 'desktop', 'options' => array( 'desktop' => esc_html__( 'Desktop View', 'et_builder' ), 'tablet' => esc_html__( 'Tablet View', 'et_builder' ), 'phone' => esc_html__( 'Phone View', 'et_builder' ), 'wireframe' => esc_html__( 'Wireframe View', 'et_builder' ), ), ), 'hide_disabled_modules' => array( 'type' => 'bool', 'default' => false, ), 'history_intervals' => array( 'type' => 'int', 'default' => 1, 'options' => array( '1' => esc_html__( 'After Every Action', 'et_builder' ), '10' => esc_html__( 'After Every 10th Action', 'et_builder' ), '20' => esc_html__( 'After Every 20th Action', 'et_builder' ), '30' => esc_html__( 'After Every 30th Action', 'et_builder' ), '40' => esc_html__( 'After Every 40th Action', 'et_builder' ), ), ), 'page_creation_flow' => array( 'type' => 'string', 'default' => 'default', 'options' => et_builder_page_creation_settings(), ), 'quick_actions_always_start_with' => array( 'type' => 'string', 'default' => 'nothing', ), 'quick_actions_show_recent_queries' => array( 'type' => 'string', 'default' => 'off', ), 'quick_actions_recent_queries' => array( 'type' => 'string', 'default' => '', 'max_length' => 100, ), 'quick_actions_recent_category' => array( 'type' => 'string', 'default' => '', 'max_length' => 100, ), 'modal_preference' => array( 'type' => 'string', 'default' => 'default', 'options' => array( 'default' => esc_html__( 'Last Used Position', 'et_builder' ), 'minimum' => esc_html__( 'Floating Minimum Size', 'et_builder' ), 'fullscreen' => esc_html__( 'Fullscreen', 'et_builder' ), 'left' => esc_html__( 'Fixed Left Sidebar', 'et_builder' ), 'right' => esc_html__( 'Fixed Right Sidebar', 'et_builder' ), 'bottom' => esc_html__( 'Fixed Bottom Panel', 'et_builder' ), // TODO, disabled until further notice (Issue #3930 & #5859) // 'top' => esc_html__( 'Fixed Top Panel', 'et_builder' ),. ), ), 'modal_snap_location' => array( 'type' => 'string', 'default' => '', ), 'modal_snap' => array( 'type' => 'bool', 'default' => false, ), 'modal_fullscreen' => array( 'type' => 'bool', 'default' => false, ), 'modal_dimension_width' => array( 'type' => 'int', 'default' => 400, ), 'modal_dimension_height' => array( 'type' => 'int', 'default' => 400, ), 'modal_position_x' => array( 'type' => 'int', 'default' => 30, ), 'modal_position_y' => array( 'type' => 'int', 'default' => 50, ), 'toolbar_click' => array( 'type' => 'bool', 'default' => false, ), 'toolbar_desktop' => array( 'type' => 'bool', 'default' => true, ), 'toolbar_grid' => array( 'type' => 'bool', 'default' => false, ), 'toolbar_hover' => array( 'type' => 'bool', 'default' => false, ), 'toolbar_phone' => array( 'type' => 'bool', 'default' => true, ), 'toolbar_tablet' => array( 'type' => 'bool', 'default' => true, ), 'toolbar_wireframe' => array( 'type' => 'bool', 'default' => true, ), 'toolbar_zoom' => array( 'type' => 'bool', 'default' => true, ), 'lv_modal_dimension_height' => array( 'type' => 'int', 'default' => 0, ), 'lv_modal_dimension_width' => array( 'type' => 'int', 'default' => 0, ), 'lv_modal_position_x' => array( 'type' => 'int', 'default' => 0, ), 'lv_modal_position_y' => array( 'type' => 'int', 'default' => 0, ), // Re: "width/height": responsive dimensions presume portrait orientation. 'responsive_tablet_width' => array( 'type' => 'int', 'default' => 768, ), 'responsive_tablet_height' => array( 'type' => 'int', 'default' => 0, ), 'responsive_phone_width' => array( 'type' => 'int', 'default' => 400, ), 'responsive_phone_height' => array( 'type' => 'int', 'default' => 0, ), 'responsive_minimum_width' => array( 'type' => 'int', 'default' => 320, ), 'responsive_maximum_width' => array( 'type' => 'int', 'default' => 980, ), ); return apply_filters( 'et_fb_app_preferences_defaults', $app_preferences ); } /** * Return the preferences list which should not be synced between Visual Builder and Backend Visual Builder. * * @return mixed|void */ function et_fb_unsynced_preferences() { /** * Filters the preferences list which should not be synced between Visual Builder and Backend Visual Builder. * * @since 3.18 * * @param array */ return apply_filters( 'et_fb_app_preferences_unsynced', array( 'view_mode', 'toolbar_click', 'toolbar_desktop', 'toolbar_grid', 'toolbar_hover', 'toolbar_phone', 'toolbar_tablet', 'toolbar_wireframe', 'toolbar_zoom', 'modal_preference' ) ); } /** * Return app preferences. * * @return mixed|void */ function et_fb_app_preferences() { $app_preferences = et_fb_app_preferences_settings(); if ( et_is_builder_plugin_active() ) { // Since Divi Builder Plugin is always 'limited', need to use a different // condition to prefix the options when BFB is used. $limited_prefix = et_builder_bfb_enabled() ? 'limited_' : ''; } else { $limited_prefix = et_builder_is_limited_mode() ? 'limited_' : ''; } foreach ( $app_preferences as $preference_key => $preference ) { $option_name = 'et_fb_pref_' . $preference_key; // Some preferences should not be synced between VB and Limited VB. if ( in_array( $preference_key, et_fb_unsynced_preferences(), true ) ) { $option_name = 'et_fb_pref_' . $limited_prefix . $preference_key; } $option_value = et_get_option( $option_name, $preference['default'], '', true ); // If options available, verify returned value against valid options. Return default if fails. if ( isset( $preference['options'] ) ) { $options = $preference['options']; $valid_options = isset( $options[0] ) ? $options : array_keys( $options ); // phpcs:ignore WordPress.PHP.StrictInArray -- $valid_options array has strings and numbers values. if ( ! in_array( (string) $option_value, $valid_options ) ) { $option_value = $preference['default']; } } // Exceptional preference. Snap left is not supported in Limited mode, so replace it with default. if ( '' !== $limited_prefix && 'modal_snap_location' === $preference_key && 'left' === $option_value ) { $option_value = $preference['default']; } $app_preferences[ $preference_key ]['value'] = $option_value; } return apply_filters( 'et_fb_app_preferences', $app_preferences ); } /** * Woocommerce Components for visual builder * * @since 4.0.1 * * @return array */ function et_fb_current_page_woocommerce_components() { $is_product_cpt = 'product' === get_post_type(); $is_tb = et_builder_tb_enabled(); $cpt_has_wc_components = $is_product_cpt || $is_tb; $has_wc_components = et_is_woocommerce_plugin_active() && $cpt_has_wc_components; if ( $has_wc_components && $is_tb ) { // Set upsells ID for upsell module in TB. ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder::set_tb_upsells_ids(); // Force set product's class to ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder in TB. add_filter( 'woocommerce_product_class', 'et_theme_builder_wc_product_class' ); // Set product categories and tags in TB. add_filter( 'get_the_terms', 'et_theme_builder_wc_terms', 10, 3 ); // Use Divi's image placeholder in TB. add_filter( 'woocommerce_single_product_image_thumbnail_html', 'et_builder_wc_placeholder_img' ); } $woocommerce_components = ! $has_wc_components ? array() : array( 'et_pb_wc_add_to_cart' => ET_Builder_Module_Woocommerce_Add_To_Cart::get_add_to_cart(), 'et_pb_wc_additional_info' => ET_Builder_Module_Woocommerce_Additional_Info::get_additional_info(), 'et_pb_wc_breadcrumb' => ET_Builder_Module_Woocommerce_Breadcrumb::get_breadcrumb(), 'et_pb_wc_cart_notice' => ET_Builder_Module_Woocommerce_Cart_Notice::get_cart_notice(), 'et_pb_wc_description' => ET_Builder_Module_Woocommerce_Description::get_description(), 'et_pb_wc_images' => ET_Builder_Module_Woocommerce_Images::get_images(), 'et_pb_wc_meta' => ET_Builder_Module_Woocommerce_Meta::get_meta(), 'et_pb_wc_price' => ET_Builder_Module_Woocommerce_Price::get_price(), 'et_pb_wc_rating' => ET_Builder_Module_Woocommerce_Rating::get_rating(), 'et_pb_wc_reviews' => ET_Builder_Module_Woocommerce_Reviews::get_reviews_html(), 'et_pb_wc_stock' => ET_Builder_Module_Woocommerce_Stock::get_stock(), 'et_pb_wc_tabs' => ET_Builder_Module_Woocommerce_Tabs::get_tabs(), 'et_pb_wc_title' => ET_Builder_Module_Woocommerce_Title::get_title(), 'et_pb_wc_related_products' => ET_Builder_Module_Woocommerce_Related_Products::get_related_products(), 'et_pb_wc_upsells' => ET_Builder_Module_Woocommerce_Upsells::get_upsells(), ); return $woocommerce_components; } /** * Before & after components for builder. * * This method should not be used for anything other than to determine whether a module * has before & after components on builder load. * * @since 4.14.5 * * @return array Components (HTML). */ function et_fb_current_page_before_after_components() { $modules_components = array(); // Bail early if current request comes from any Ajax request. if ( wp_doing_ajax() ) { return $modules_components; } // Bail early if Module Shortcode Manager class doesn't exist. if ( ! class_exists( 'ET_Builder_Module_Shortcode_Manager' ) ) { return $modules_components; } /** * Filters modules list. * * The modules list comes from Shortcode Manager only contains built-in modules * intentionally. 3rd-party modules need to include their module slug and class name * via `et_fb_fetch_before_after_modules_map` filter. * * @param array Modules list. */ $modules_map = apply_filters( 'et_fb_fetch_before_after_modules_map', ET_Builder_Module_Shortcode_Manager::get_modules_map() ); // Bail early if components map is empty. if ( empty( $modules_map ) ) { return $modules_components; } foreach ( $modules_map as $module_slug => $module_data ) { $module_class = et_()->array_get( $module_data, 'classname' ); // Skip if module class name is not found. if ( empty( $module_class ) || ! class_exists( $module_class ) ) { continue; } $module_components = $module_class::get_component_before_after_module( $module_slug, array() ); // Skip if there is no before & after components. $has_components = et_()->array_get( $module_components, 'has_components' ); if ( true !== $has_components ) { continue; } $modules_components[ $module_slug ] = $module_components; } return $modules_components; } /** * Array of WooCommerce Tabs. * * @since 4.4.2 Fixed fatal error @link https://github.com/elegantthemes/Divi/issues/19404 * @since 4.4.2 Added Custom Tabs support. * * @used-by et_fb_current_page_params() * * @return array */ function et_fb_woocommerce_tabs() { global $product, $post; $old_product = $product; $old_post = $post; $is_product = isset( $product ) && is_a( $product, 'WC_Product' ); if ( ! $is_product && et_is_woocommerce_plugin_active() ) { $product = ET_Builder_Module_Helper_Woocommerce_Modules::get_product( 'latest' ); if ( $product ) { $post = get_post( $product->get_id() ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Overriding global post is safe as original $post has been restored at the end. } else { $product = $old_product; return ET_Builder_Module_Helper_Woocommerce_Modules::get_default_tab_options(); } } // On non-product post types, the filter will cause fatal error // unless we have global $product set. $tabs = apply_filters( 'woocommerce_product_tabs', array() ); $options = array(); foreach ( $tabs as $name => $tab ) { $options[ $name ] = array( 'value' => $name, 'label' => $tab['title'], ); } // Reset global $product. $product = $old_product; $post = $old_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Restoring original global $post data. return $options; } /** * Get the category taxonomy associated with a given post type. * * @since 4.0.6 * * @param string $post_type Post type. * * @return string|bool */ function et_builder_get_category_taxonomy( $post_type ) { static $cache = array(); // Address common cases. switch ( $post_type ) { case 'page': return false; case 'post': return 'category'; case 'project': return 'project_category'; case 'product': return 'product_cat'; } if ( isset( $cache[ $post_type ] ) ) { // Use cached value. return $cache[ $post_type ]; } // Unknown post_type, guess the taxonomy. $taxonomies = get_object_taxonomies( $post_type, 'names' ); foreach ( array( 'category', 'cat' ) as $pattern ) { $matches = preg_grep( '/' . $pattern . '$/', $taxonomies ); if ( ! empty( $matches ) ) { $cache[ $post_type ] = reset( $matches ); return $cache[ $post_type ]; } } // Tough luck. $cache[ $post_type ] = false; return $cache[ $post_type ]; } /** * Retrieve a post's category terms as a list with specified format. * * @since 4.0.6 * * @param string $separator Optional. Separate items using this. * * @return string|false|WP_Error A list of terms on success, false if there are no terms, WP_Error on failure. */ function et_builder_get_the_term_list( $separator = '' ) { $id = get_the_ID(); $taxonomy = et_builder_get_category_taxonomy( get_post_type( $id ) ); return $taxonomy ? get_the_term_list( $id, $taxonomy, $before = '', $separator ) : false; } /** * Define current-page related data that are needed by frontend builder. Backend parser also uses this * to sanitize updated value for computed data * * @return array */ function et_fb_current_page_params() { global $post, $authordata, $paged; // Get current page url. $current_url = ( isset( $_SERVER['HTTP_HOST'] ) && isset( $_SERVER['REQUEST_URI'] ) ) ? ( is_ssl() ? 'https://' : 'http://' ) . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] ) : ''; // Fallback for preview. if ( empty( $authordata ) && isset( $post->post_author ) ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- A fallback to set global $authordata. $authordata = get_userdata( $post->post_author ); } // Get comment count. $comment_count = isset( $post->ID ) ? get_comments_number( $post->ID ) : 0; // WordPress' _n() only supports singular n plural, thus we do comment count to text manually. if ( 0 === $comment_count ) { $comment_count_text = __( 'No Comments', 'et_builder' ); } elseif ( 1 === $comment_count ) { $comment_count_text = __( '1 Comment', 'et_builder' ); } else { // translators: comments count. $comment_count_text = sprintf( __( '%d Comments', 'et_builder' ), $comment_count ); } // Get current page paginated data. $et_paged = is_front_page() ? get_query_var( 'page' ) : get_query_var( 'paged' ); // Get thumbnail size. $thumbnail_size = isset( $post->ID ) && 'post' === get_post_type( $post->ID ) && 'et_full_width_page' === get_post_meta( $post->ID, '_et_pb_page_layout', true ) ? 'et-pb-post-main-image-fullwidth-large' : 'large'; $post_id = isset( $post->ID ) ? $post->ID : (int) et_()->array_get( $_POST, 'current_page.id' ); // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. $exclude_woo = wp_doing_ajax() || ! et_is_woocommerce_plugin_active() || 'latest' === ET_Builder_Module_Helper_Woocommerce_Modules::get_product_default(); $default_categories = array( get_term_by( 'name', 'Uncategorized', 'category' ) ); $categories = et_pb_get_post_categories( $post_id, $default_categories ); // phpcs:ignore $block_id = et_()->array_get( $_GET, 'blockId', '' ); $current_page = array( 'url' => esc_url( $current_url ), 'permalink' => esc_url( remove_query_arg( 'et_fb', $current_url ) ), 'backendBuilderUrl' => esc_url( sprintf( admin_url( '/post.php?post=%d&action=edit' ), get_the_ID() ) ), 'id' => isset( $post->ID ) ? $post->ID : false, 'title' => esc_html( get_the_title() ), 'thumbnailUrl' => isset( $post->ID ) ? esc_url( get_the_post_thumbnail_url( $post->ID, $thumbnail_size ) ) : '', 'thumbnailId' => isset( $post->ID ) ? get_post_thumbnail_id( $post->ID ) : '', 'authorName' => esc_html( get_the_author() ), 'authorUrl' => isset( $authordata->ID ) && isset( $authordata->user_nicename ) ? esc_html( get_author_posts_url( $authordata->ID, $authordata->user_nicename ) ) : false, // translators: post author name. 'authorUrlTitle' => sprintf( esc_html__( 'Posts by %s', 'et_builder' ), get_the_author() ), 'date' => intval( get_the_time( 'U' ) ), 'categories' => $categories, 'commentsPopup' => esc_html( $comment_count_text ), 'commentsCount' => esc_html( $comment_count ), 'comments_popup_tb' => esc_html__( '12 Comments', 'et_builder' ), 'paged' => is_front_page() ? $et_paged : $paged, 'post_modified' => isset( $post->ID ) ? esc_attr( $post->post_modified ) : '', 'lang' => get_locale(), 'blockId' => ET_GB_Block_Layout::is_layout_block_preview() ? sanitize_title( et_()->array_get( $_GET, 'blockId', '' ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. 'langCode' => get_locale(), 'page_layout' => $post_id ? get_post_meta( $post_id, '_et_pb_page_layout', true ) : '', 'woocommerceComponents' => $exclude_woo ? array() : et_fb_current_page_woocommerce_components(), 'woocommerceTabs' => et_builder_tb_enabled() && et_is_woocommerce_plugin_active() ? ET_Builder_Module_Helper_Woocommerce_Modules::get_default_tab_options() : et_fb_woocommerce_tabs(), 'woocommerce' => array( 'inactive_module_notice' => esc_html__( 'WooCommerce must be active for this module to appear', 'et_builder' ), ), 'beforeAfterComponents' => et_fb_current_page_before_after_components(), ); return apply_filters( 'et_fb_current_page_params', $current_page ); } /** * Ajax Callback :: Process computed property. */ function et_pb_process_computed_property() { if ( ! isset( $_POST['et_pb_process_computed_property_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_pb_process_computed_property_nonce'] ), 'et_pb_process_computed_property_nonce' ) ) { die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } if ( ! isset( $_POST['depends_on'], $_POST['conditional_tags'], $_POST['current_page'] ) ) { // Shouldn't even be a possibility, but... // Since computing `__page` can exit here too, we need to json_encode the reponse. // This is needed in case jQuery migrate is disabled (eg via plugin) otherwise the AJAX success callback // won't be executed (because json is malformed). die( wp_json_encode( null ) ); } $utils = ET_Core_Data_Utils::instance(); // phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- Sanitization of following arrays is done at the time of accessing an array values. $depends_on = isset( $_POST['depends_on'] ) ? $_POST['depends_on'] : array(); $conditional_tags = isset( $_POST['conditional_tags'] ) ? $_POST['conditional_tags'] : array(); $current_page = isset( $_POST['current_page'] ) ? $_POST['current_page'] : array(); // phpcs:enable // allowlist keys. $conditional_tags = array_intersect_key( $conditional_tags, et_fb_conditional_tag_params() ); $current_page = array_intersect_key( $current_page, et_fb_current_page_params() ); // sanitize values. $conditional_tags = $utils->sanitize_text_fields( $conditional_tags ); $current_page = $utils->sanitize_text_fields( $current_page ); $module_slug = isset( $_POST['module_type'] ) ? sanitize_text_field( $_POST['module_type'] ) : ''; $request_type = isset( $_POST['request_type'] ) ? sanitize_text_field( $_POST['request_type'] ) : ''; if ( in_array( $request_type, array( '404', 'archive', 'home' ), true ) ) { // On non-singular page, we do not have $current_page id, so we will check if user has theme_builder capability. if ( ! et_pb_is_allowed( 'theme_builder' ) ) { die( -1 ); } } else { // For other pages, we will check if user can edit specific post. if ( ! current_user_can( 'edit_post', $current_page['id'] ) ) { die( -1 ); } } // Check if there is page id. if ( empty( $current_page['id'] ) && '404' !== $request_type ) { die( -1 ); } // $_POST['depends_on'] is a single dimensional assoc array created by jQuery.ajax data param, sanitize each key and value, they will both be strings foreach ( $depends_on as $key => $value ) { if ( et_()->includes( $value, '%' ) ) { // `sanitize_text_fields` removes octets `%[a-f0-9]{2}` and would zap icon values / `%date` // so we prefix octets with `_` to protected them and remove the prefix after sanitization. $prepared_value = preg_replace( '/%([a-f0-9]{2})/', '%_$1', $value ); $sanitized_value = preg_replace( '/%_([a-f0-9]{2})/', '%$1', sanitize_text_field( $prepared_value ) ); } else { $sanitized_value = sanitize_text_field( $value ); } $depends_on[ sanitize_text_field( $key ) ] = $sanitized_value; } // Since VB performance, it is introduced single ajax request for several property // in that case, computed_property posted data can be as an array // hence we get the raw post data value, then sanitize it afterward either as array or string. // @phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- Will be sanitized conditionally as string or array afterward. $computed_property = isset( $_POST['computed_property'] ) ? $_POST['computed_property'] : ''; $computed_property = is_array( $computed_property ) ? array_map( 'sanitize_text_field', $computed_property ) : sanitize_text_field( $computed_property ); // get all fields for module. $fields = ET_Builder_Element::get_module_fields( $request_type, $module_slug ); // make sure only valid fields are being passed through. $depends_on = array_intersect_key( $depends_on, $fields ); if ( is_array( $computed_property ) ) { $results = array(); foreach ( $computed_property as $property ) { if ( ! isset( $fields[ $property ], $fields[ $property ]['computed_callback'] ) ) { continue; } $callback = $fields[ $property ]['computed_callback']; if ( is_callable( $callback ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- The callback is hard-coded in module fields configuration. $results[ $property ] = call_user_func( $callback, $depends_on, $conditional_tags, $current_page ); } } if ( empty( $results ) ) { die( -1 ); } die( wp_json_encode( $results ) ); } // computed property field. $field = $fields[ $computed_property ]; $callback = $field['computed_callback']; if ( is_callable( $callback ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- The callback is hard-coded in module fields configuration. die( wp_json_encode( call_user_func( $callback, $depends_on, $conditional_tags, $current_page ) ) ); } else { die( -1 ); } } add_action( 'wp_ajax_et_pb_process_computed_property', 'et_pb_process_computed_property' ); /** * Fetch before or after components. * * @since 4.14.5 * * @return string Components outputs. */ function et_fb_fetch_before_after_components() { // Bail early if the nonce is incorrect or current user can't edit posts. $nonce = ! empty( $_POST['et_fb_fetch_before_after_components_nonce'] ) ? sanitize_text_field( $_POST['et_fb_fetch_before_after_components_nonce'] ) : ''; if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'et_fb_fetch_before_after_components_nonce' ) ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } // Bail early if Module Shortcode Manager class doesn't exist. if ( ! class_exists( 'ET_Builder_Module_Shortcode_Manager' ) ) { wp_send_json_error(); } /** * Filters modules list. * * The modules list comes from Shortcode Manager only contains built-in modules * intentionally. 3rd-party modules need to include their module slug and class name * via `et_fb_fetch_before_after_modules_map` filter. * * @param array Modules list. */ $modules_map = apply_filters( 'et_fb_fetch_before_after_modules_map', ET_Builder_Module_Shortcode_Manager::get_modules_map() ); // Bail early if components map is empty. if ( empty( $modules_map ) ) { return $modules_components; } $module_type = ! empty( $_POST['module_type'] ) ? sanitize_text_field( $_POST['module_type'] ) : ''; $module_class = et_()->array_get( $modules_map, array( $module_type, 'classname' ) ); // Bail early if module class name is not found. if ( empty( $module_class ) || ! class_exists( $module_class ) ) { wp_send_json_error(); } // phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- Sanitization of following arrays is done on Sanitize values section below. $conditional_tags = isset( $_POST['conditional_tags'] ) ? $_POST['conditional_tags'] : array(); $current_page = isset( $_POST['current_page'] ) ? $_POST['current_page'] : array(); $module_attrs = isset( $_POST['module_attrs'] ) ? $_POST['module_attrs'] : array(); // phpcs:enable // Allow list keys. $conditional_tags = array_intersect_key( $conditional_tags, et_fb_conditional_tag_params() ); $current_page = array_intersect_key( $current_page, et_fb_current_page_params() ); // Sanitize values. $conditional_tags = et_()->sanitize_text_fields( $conditional_tags ); $current_page = et_()->sanitize_text_fields( $current_page ); $module_attrs = et_()->sanitize_text_fields( $module_attrs ); $render_mode = isset( $_POST['render_mode'] ) ? sanitize_text_field( $_POST['render_mode'] ) : ''; $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : ''; $action = isset( $_POST['action'] ) ? sanitize_text_field( $_POST['action'] ) : ''; // Bail early if current user can't edit this post. if ( empty( $current_page['id'] ) || ! current_user_can( 'edit_post', $current_page['id'] ) ) { wp_send_json_error(); } // Setup module data. $module_data = array( 'conditional_tags' => $conditional_tags, 'current_page' => $current_page, 'module_attrs' => $module_attrs, 'render_mode' => $render_mode, 'post_type' => $post_type, 'action' => $action, ); /** * Fires before processing before & after components. * * @since 4.14.5 * * @param string $module_type Module slug. * @param array $module_data Module data passed from the request. */ do_action( 'et_fb_pre_fetch_before_after_components', $module_type, $module_data ); $output = $module_class::get_component_before_after_module( $module_type, $module_data ); wp_send_json_success( $output ); } add_action( 'wp_ajax_et_fb_fetch_before_after_components', 'et_fb_fetch_before_after_components' ); /** * Process shortcode json. * * @since 4.11.4 Added $inject_responsive_hover param. * * @param array $object Shortcodes object. * @param array $options Options. * @param string $library_item_type Library item type. * @param bool $escape_content_slashes Whether escape content slashes. * @param bool $inject_responsive_hover Flag to inject missing responsive and hover mode attributes. * * @return string */ function et_fb_process_to_shortcode( $object, $options = array(), $library_item_type = '', $escape_content_slashes = true, $inject_responsive_hover = false ) { $output = ''; $_object = array(); $default_options = array( 'force_valid_slugs' => false, 'post_type' => false, 'apply_global_presets' => false, ); $options = wp_parse_args( $options, $default_options ); $global_presets_manager = ET_Builder_Global_Presets_Settings::instance(); // do not proceed if $object is empty. if ( empty( $object ) ) { return ''; } $font_icon_fields = ! empty( $options['post_type'] ) ? ET_Builder_Element::get_font_icon_fields( $options['post_type'] ) : false; $structure_types = ET_Builder_Element::get_structure_module_slugs(); if ( in_array( $library_item_type, array( 'module', 'row' ), true ) ) { $excluded_elements = array(); switch ( $library_item_type ) { case 'module': $excluded_elements = array( 'et_pb_section', 'et_pb_row', 'et_pb_column' ); break; case 'row': $excluded_elements = array( 'et_pb_section' ); break; } foreach ( $object as $item ) { // do not proceed if $item is empty. if ( empty( $item ) ) { continue; } while ( in_array( $item['type'], $excluded_elements, true ) ) { $item = $item['content'][0]; } $_object[] = $item; } } else { $_object = $object; } if ( $options['force_valid_slugs'] ) { // we need to supply a reasonable default post type to get a simple list of slugs, // otherwise the function will return an array of arrays of slugs for every possible post_type. $slug_post_type = ! empty( $options['post_type'] ) ? $options['post_type'] : 'page'; $valid_slugs = ET_Builder_Element::get_module_slugs_by_post_type( $slug_post_type ); } foreach ( $_object as $item ) { // do not proceed if $item is empty. if ( empty( $item ) ) { continue; } $attributes = ''; $content = ''; $type = sanitize_text_field( $item['type'] ); $type = esc_attr( $type ); // if option enabled, reject invalid slugs. if ( $options['force_valid_slugs'] ) { if ( ! in_array( $type, $valid_slugs, true ) ) { continue; } } if ( ! empty( $item['raw_child_content'] ) ) { $content = stripslashes( $item['raw_child_content'] ); } if ( $options['apply_global_presets'] ) { $module_type = $global_presets_manager->maybe_convert_module_type( $type, $item['attrs'] ); $module_global_presets = $global_presets_manager->get_module_presets_settings( $module_type, $item['attrs'] ); $item['attrs'] = array_merge( $module_global_presets, $item['attrs'] ); } // Inject responsive/hover attribute value to inherit from desktop // when the responsive/hover setting is "on" but the responsive/hover attribute is not exist. // This can happen when responsive/hover mode values reset in the builder. if ( $inject_responsive_hover ) { foreach ( $item['attrs'] as $attribute => $value ) { // Inject responsive mode attribute value. if ( '_last_edited' === substr( $attribute, -12 ) && 0 === strpos( $value, 'on' ) ) { $attr_key_base = str_replace( '_last_edited', '', $attribute ); $attr_key_tablet = $attr_key_base . '_tablet'; $attr_key_phone = $attr_key_base . '_phone'; $attr_is_content = 'content' === $attr_key_base && isset( $item['content'] ) && is_string( $item['content'] ); // Inject tablet mode attribute value. if ( ! isset( $item['attrs'][ $attr_key_tablet ] ) ) { if ( $attr_is_content ) { $item['attrs'][ $attr_key_tablet ] = $item['content']; } elseif ( isset( $item['attrs'][ $attr_key_base ] ) ) { $item['attrs'][ $attr_key_tablet ] = $item['attrs'][ $attr_key_base ]; } } // Inject phone mode attribute value. if ( ! isset( $item['attrs'][ $attr_key_phone ] ) ) { if ( isset( $item['attrs'][ $attr_key_tablet ] ) ) { $item['attrs'][ $attr_key_phone ] = $item['attrs'][ $attr_key_tablet ]; } else { if ( $attr_is_content ) { $item['attrs'][ $attr_key_phone ] = $content; } elseif ( isset( $values[ $attr_key_base ] ) ) { $item['attrs'][ $attr_key_phone ] = $values[ $attr_key_base ]; } } } } // Inject hover mode attribute value. if ( '__hover_enabled' === substr( $attribute, -15 ) && 0 === strpos( $value, 'on' ) ) { $attr_key_base = str_replace( '__hover_enabled', '', $attribute ); $attr_key_hover = $attr_key_base . '__hover'; if ( ! isset( $item['attrs'][ $attr_key_hover ] ) ) { $attr_is_content = 'content' === $attr_key_base && isset( $item['content'] ) && is_string( $item['content'] ); if ( $attr_is_content ) { $item['attrs'][ $attr_key_hover ] = $item['content']; } elseif ( isset( $item['attrs'][ $attr_key_base ] ) ) { $item['attrs'][ $attr_key_hover ] = $item['attrs'][ $attr_key_base ]; } } } } } foreach ( $item['attrs'] as $attribute => $value ) { // ignore computed fields. if ( '__' === substr( $attribute, 0, 2 ) ) { continue; } // Ignore post_content_module_attrs. They are needed only during editing. if ( 'post_content_module_attrs' === $attribute ) { continue; } // Sanitize attribute. $attribute = sanitize_text_field( $attribute ); // Sanitize input properly. if ( isset( $font_icon_fields[ $item['type'] ][ $attribute ] ) ) { $value = esc_attr( $value ); } // handle content. if ( in_array( $attribute, array( 'content', 'raw_content' ), true ) ) { // do not override the content if item has raw_child_content. if ( empty( $item['raw_child_content'] ) ) { $content = $value; $content = trim( $content ); if ( ! empty( $content ) && 'content' === $attribute ) { $content = "\n\n" . $content . "\n\n"; } } } else { // Since WordPress version 5.1, any links in the content that // has "target" attribute will be automatically added // rel="noreferrer noopener" attribute. This attribute added // after the shortcode processed in et_fb_process_to_shortcode // function. This become an issue for the builder while parsing the shortcode attributes // because the double quote that wrapping the "rel" attribute value is not encoded. // So we need to manipulate "target" attribute here before storing the content by renaming // is as "data-et-target-link". Later in "et_pb_fix_shortcodes" function // we will turn it back as "target". $value = str_replace( ' target=', ' data-et-target-link=', $value ); $is_include_attr = false; if ( '' === $value && et_pb_hover_options()->get_field_base_name( $attribute ) !== $attribute && et_pb_hover_options()->is_enabled( et_pb_hover_options()->get_field_base_name( $attribute ), $item['attrs'] ) ) { $is_include_attr = true; } if ( '' === $value && et_pb_responsive_options()->get_field_base_name( $attribute ) !== $attribute && et_pb_responsive_options()->is_enabled( et_pb_responsive_options()->get_field_base_name( $attribute ), $item['attrs'] ) ) { $is_include_attr = true; } if ( '' !== $value ) { $is_include_attr = true; } if ( $is_include_attr ) { // TODO, should we check for and handle default here? probably done in FB alredy... // Make sure double quotes are encoded, before adding values to shortcode. $value = str_ireplace( '"', '%22', $value ); // Make sure single backslash is encoded, before adding values to Shortcode. if ( 'breadcrumb_separator' === $attribute ) { $value = str_ireplace( '\\', '%5c', $value ); } // Encode backslash for custom CSS-related and json attributes. $json_attributes = array( 'checkbox_options', 'radio_options', 'select_options', 'conditional_logic_rules' ); if ( 0 === strpos( $attribute, 'custom_css_' ) || in_array( $attribute, $json_attributes, true ) ) { $value = str_ireplace( '\\', '%92', $value ); } elseif ( et_builder_parse_dynamic_content( $value )->is_dynamic() ) { $value = str_replace( '\\', '%92', $value ); } // Encode backslash for custom date format attributes. $modules_and_attr_with_custom_date = array( 'et_pb_blog' => 'meta_date', 'et_pb_fullwidth_post_title' => 'date_format', 'et_pb_post_title' => 'date_format', ); if ( ! empty( $modules_and_attr_with_custom_date[ $type ] ) && $modules_and_attr_with_custom_date[ $type ] === $attribute ) { $value = str_replace( '\\', '%92', $value ); } $attributes .= ' ' . esc_attr( $attribute ) . '="' . et_core_esc_previously( $value ) . '"'; } } } $attributes = str_replace( array( '[', ']' ), array( '%91', '%93' ), $attributes ); // prefix sections with a fb_built attr flag. if ( 'et_pb_section' === $type ) { $attributes = ' fb_built="1"' . $attributes; } // build shortcode // start the opening tag. $output .= '[' . $type . $attributes; // close the opening tag, depending on self closing. if ( empty( $content ) && ! isset( $item['content'] ) && ! in_array( $type, $structure_types, true ) ) { $open_tag_only = true; $output .= ' /]'; } else { $open_tag_only = false; $output .= ']'; } // if applicable, add inner content and close tag. if ( ! $open_tag_only ) { if ( 'et_pb_section' === $type && isset( $item['attrs'] ) && isset( $item['attrs']['fullwidth'] ) && 'on' !== $item['attrs']['fullwidth'] && isset( $item['attrs']['specialty'] ) && 'on' !== $item['attrs']['specialty'] && ( ! isset( $item['content'] ) || ! is_array( $item['content'] ) ) ) { // insert empty row if saving empty Regular section to make it work correctly in BB. $output .= '[et_pb_row admin_label="Row"][/et_pb_row]'; } elseif ( isset( $item['content'] ) && is_array( $item['content'] ) ) { $output .= et_fb_process_to_shortcode( $item['content'], $options, '', $escape_content_slashes, $inject_responsive_hover ); } else { if ( ! empty( $content ) ) { if ( et_is_builder_plugin_active() && in_array( $type, ET_Builder_Element::get_has_content_modules(), true ) ) { // Wrap content in autop to avoid tagless content on FE due to content is edited on html editor and only // have one-line without newline wrap which prevent `the_content`'s wpautop filter to properly wrap it. /** * Filter whether to apply wpautop to content. * * This filter allows customization of whether the wpautop filter should be applied to * the content of a shortcode. It helps in wrapping content in <p> tags automatically when necessary. * * @since 4.11.4 * * @param bool $should_wpautop Whether to apply wpautop. Default true. * @param string $type The shortcode or module type. * @param string $content The content to be filtered. * @param array $item The current shortcode being processed. */ $should_wpautop = apply_filters( 'et_fb_should_apply_wpautop', true, $type, $content, $item ); $content = $should_wpautop ? wpautop( $content ) : $content; } $output .= $content; } else { if ( isset( $item['content'] ) ) { $_content = $item['content']; if ( $escape_content_slashes ) { $_content = str_replace( '\\', '\\\\', $_content ); } if ( et_is_builder_plugin_active() && in_array( $type, ET_Builder_Element::get_has_content_modules(), true ) ) { // Wrap content in autop to avoid tagless content on FE due to content is edited on html editor and only // have one-line without newline wrap which prevent `the_content`'s wpautop filter to properly wrap it. /** * Filter whether to apply wpautop to content. * * This filter allows customization of whether the wpautop filter should be applied to * the content of a shortcode. It helps in wrapping content in <p> tags automatically when necessary. * * @since 4.11.4 * * @param bool $should_wpautop Whether to apply wpautop. Default true. * @param string $type The shortcode or module type. * @param string $content The content to be filtered. * @param array $item The current shortcode being processed. */ $should_wpautop = apply_filters( 'et_fb_should_apply_wpautop', true, $type, $_content, $item ); $_content = $should_wpautop ? wpautop( $_content ) : $_content; } $output .= $_content; } else { $output .= ''; } } } // add the closing tag. $output .= '[/' . $type . ']'; } } return $output; } /** * Ajax Callback :: Render shortcode output. */ function et_fb_ajax_render_shortcode() { if ( ! isset( $_POST['et_pb_render_shortcode_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_pb_render_shortcode_nonce'] ), 'et_pb_render_shortcode_nonce' ) ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } $utils = ET_Core_Data_Utils::instance(); global $et_pb_predefined_module_index; $et_pb_predefined_module_index = isset( $_POST['et_fb_module_index'] ) && 'default' !== $_POST['et_fb_module_index'] ? sanitize_text_field( $_POST['et_fb_module_index'] ) : false; $options = isset( $_POST['options'] ) ? $utils->sanitize_text_fields( $_POST['options'] ) : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- sanitize_text_fields sanitize the options. // enforce valid module slugs only // shortcode slugs need to be allowlisted so as to prevent malicious shortcodes from being generated and run through do_shortcode(). $options['force_valid_slugs'] = true; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['object'] will not be stored in db. $object = isset( $_POST['object'] ) ? $_POST['object'] : array(); // convert shortcode array to shortcode string. $shortcode = et_fb_process_to_shortcode( $object, $options ); // take shortcode string and ensure it's properly sanitized for the purposes of this function. $shortcode = et_pb_enforce_builder_shortcode( $shortcode ); $output = do_shortcode( $shortcode ); $styles = ET_Builder_Element::get_style(); if ( ! empty( $styles ) ) { $output .= sprintf( '<style type="text/css" class="et-builder-advanced-style"> %1$s </style>', $styles ); } wp_send_json_success( $output ); } add_action( 'wp_ajax_et_fb_ajax_render_shortcode', 'et_fb_ajax_render_shortcode' ); /** * Determine current user can save the post. * * @param int $post_id Post id. * @param string $status Post status. * * @return bool */ function et_fb_current_user_can_save( $post_id, $status = '' ) { if ( 'page' === get_post_type( $post_id ) ) { if ( ! current_user_can( 'edit_pages' ) ) { return false; } if ( ! current_user_can( 'publish_pages' ) && 'publish' === $status ) { return false; } if ( ! current_user_can( 'edit_published_pages' ) && 'publish' === get_post_status( $post_id ) ) { return false; } if ( ! current_user_can( 'edit_others_pages' ) && ! current_user_can( 'edit_page', $post_id ) ) { return false; } } else { if ( ! current_user_can( 'edit_posts' ) ) { return false; } if ( ! current_user_can( 'publish_posts' ) && 'publish' === $status ) { return false; } if ( ! current_user_can( 'edit_published_posts' ) && 'publish' === get_post_status( $post_id ) ) { return false; } if ( ! current_user_can( 'edit_others_posts' ) && ! current_user_can( 'edit_post', $post_id ) ) { return false; } } // If this is a theme builder layout post type, check divi roles for that capability. if ( in_array( get_post_type( $post_id ), et_theme_builder_get_layout_post_types(), true ) && ! et_pb_is_allowed( 'theme_builder' ) ) { return false; } return true; } /** * Ajax Callback :: Drop backup/autosave depending on exit type. */ function et_fb_ajax_drop_autosave() { if ( ! isset( $_POST['et_fb_drop_autosave_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_drop_autosave_nonce'] ), 'et_fb_drop_autosave_nonce' ) ) { wp_send_json_error(); } $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0; if ( ! et_fb_current_user_can_save( $post_id ) ) { wp_send_json_error(); } $post_author = get_current_user_id(); $autosave = wp_get_post_autosave( $post_id, $post_author ); $autosave_deleted = false; // delete builder settings autosave. delete_post_meta( $post_id, "_et_builder_settings_autosave_{$post_author}" ); if ( ! empty( $autosave ) ) { wp_delete_post_revision( $autosave->ID ); $autosave = wp_get_post_autosave( $post_id, $post_author ); if ( empty( $autosave ) ) { $autosave_deleted = true; } } else { $autosave_deleted = true; } if ( $autosave_deleted ) { wp_send_json_success(); } else { wp_send_json_error(); } } add_action( 'wp_ajax_et_fb_ajax_drop_autosave', 'et_fb_ajax_drop_autosave' ); /** * Ajax Callback :: Save layout. */ function et_fb_ajax_save() { if ( ! isset( $_POST['et_fb_save_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_save_nonce'] ), 'et_fb_save_nonce' ) ) { wp_send_json_error(); } $utils = ET_Core_Data_Utils::instance(); $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0; $options = isset( $_POST['options'] ) ? $_POST['options'] : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['options'] is an array, it's value sanitization is done at the time of accessing value. $layout_type = isset( $_POST['layout_type'] ) ? sanitize_text_field( $_POST['layout_type'] ) : ''; $is_theme_builder_layout = in_array( get_post_type( $post_id ), et_theme_builder_get_layout_post_types(), true ); // For post content check if user can save post. if ( ! et_fb_current_user_can_save( $post_id, $utils->array_get_sanitized( $options, 'status' ) ) ) { wp_send_json_error(); } $update = false; if ( ! isset( $_POST['skip_post_update'] ) ) { $is_layout_block_preview = sanitize_text_field( $utils->array_get( $_POST, 'options.conditional_tags.is_layout_block', '' ) ); $block_id = sanitize_title( $utils->array_get( $_POST, 'options.current_page.blockId', '' ) ); $shortcode_data = isset( $_POST['modules'] ) ? json_decode( stripslashes( $_POST['modules'] ), true ) : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- modules string will be sanitized at the time of saving in the db. // Cast as bool if falsey; blockId is retrieved from ajax request, and // already return empty string (falsey) if no value found. Nevertheless let's be more safe. if ( ! $block_id ) { $block_id = false; } // Cast as bool if falsey; is_layout_block_preview is retrieved from ajax request, and // already return empty string (falsey) if no value found. Nevertheless let's be more safe. if ( ! $is_layout_block_preview ) { $is_layout_block_preview = false; } $built_for_type = get_post_meta( $post_id, '_et_pb_built_for_post_type', true ); if ( ! $built_for_type && ! $is_layout_block_preview ) { update_post_meta( $post_id, '_et_pb_built_for_post_type', 'page' ); } // If Default Editor is used for Post Content, and Post Content is not edited, // handleAjaxSave will pass return_to_default_editor, // and in that case we need reactivate the default editor for the post. // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- input is inside isset() function so its value is not used if ( isset( $_POST['return_to_default_editor'] ) && rest_sanitize_boolean( $_POST['return_to_default_editor'] ) ) { update_post_meta( $post_id, '_et_pb_use_builder', 'off' ); update_post_meta( $post_id, '_et_pb_show_page_creation', 'on' ); // Get old content and if we should return to the Default Editor. $post_content = get_post_meta( $post_id, '_et_pb_old_content', true ); } else { if ( ! $is_layout_block_preview ) { update_post_meta( $post_id, '_et_pb_use_builder', 'on' ); } $post_content = et_fb_process_to_shortcode( $shortcode_data, $options, $layout_type, true, true ); } // Store a copy of the sanitized post content in case wpkses alters it since that // would cause our check at the end of this function to fail. $sanitized_content = sanitize_post_field( 'post_content', $post_content, $post_id, 'db' ); // Exit early for layout block update; builder should not actually save post content in this scenario // Update post meta and let it is being used to update layoutContent on editor. if ( $is_layout_block_preview && $block_id ) { $layout_preview_meta_key = "_et_block_layout_preview_{$block_id}"; $saved_layout = get_post_meta( $post_id, $layout_preview_meta_key, true ); // If saved layout is identical to the the layout sent via AJAX, return send json success; // this is needed because update_post_meta() returns false if the saved layout is identical // to the the one given as param. if ( ! empty( $saved_layout ) && $saved_layout === $post_content ) { wp_send_json_success( array( 'save_verification' => true, ) ); wp_die(); } $update = update_post_meta( $post_id, $layout_preview_meta_key, $post_content ); if ( $update ) { wp_send_json_success( array( 'save_verification' => true, ) ); } else { wp_send_json_error(); } wp_die(); } $update = wp_update_post( array( 'ID' => $post_id, 'post_content' => $post_content, 'post_status' => $utils->array_get_sanitized( $options, 'status' ), ) ); } // update Global modules with selective sync. if ( 'module' === $layout_type && isset( $_POST['unsyncedGlobalSettings'] ) && 'none' !== $_POST['unsyncedGlobalSettings'] ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['unsyncedGlobalSettings'] will be sanitized before storing in db. $unsynced_options = stripslashes( $_POST['unsyncedGlobalSettings'] ); update_post_meta( $post_id, '_et_pb_excluded_global_options', sanitize_text_field( $unsynced_options ) ); } // check if there is an autosave that is newer. $post_author = get_current_user_id(); // Store one autosave per author. If there is already an autosave, overwrite it. $autosave = wp_get_post_autosave( $post_id, $post_author ); if ( ! empty( $autosave ) ) { wp_delete_post_revision( $autosave->ID ); } if ( isset( $_POST['settings'] ) && is_array( $_POST['settings'] ) ) { et_builder_update_settings( $_POST['settings'], $post_id ); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['settings'] is an array, it's value sanitization is done inside function at the time of accessing value. } if ( isset( $_POST['preferences'] ) && is_array( $_POST['preferences'] ) && ! $is_theme_builder_layout ) { $app_preferences = et_fb_app_preferences_settings(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['et_builder_mode'] value used in the comparision. $limited_prefix = ! empty( $_POST['et_builder_mode'] ) && 'limited' === $_POST['et_builder_mode'] ? 'limited_' : ''; foreach ( $app_preferences as $preference_key => $preference_data ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $preference_value will be sanitized before saving in db. $preference_value = isset( $_POST['preferences'][ $preference_key ] ) && isset( $_POST['preferences'][ $preference_key ]['value'] ) ? $_POST['preferences'][ $preference_key ]['value'] : $preference_data['default']; // sanitize based on type. switch ( $preference_data['type'] ) { case 'int': $preference_value = absint( $preference_value ); break; case 'bool': $preference_value = 'true' === $preference_value ? 'true' : 'false'; break; default: $preference_value = sanitize_text_field( $preference_value ); break; } $preference_value_max_length = et_()->array_get( $preference_data, 'max_length', 0 ); if ( $preference_value && is_numeric( $preference_value_max_length ) && $preference_value_max_length > 0 ) { $preference_value = substr( $preference_value, 0, $preference_value_max_length ); } $option_name = 'et_fb_pref_' . $preference_key; if ( in_array( $preference_key, et_fb_unsynced_preferences(), true ) ) { $option_name = 'et_fb_pref_' . $limited_prefix . $preference_key; } et_update_option( $option_name, $preference_value ); } } // Clear AB Testing stats & transient data. if ( isset( $_POST['ab_testing'] ) && isset( $_POST['ab_testing']['is_clear_stats'] ) && 'true' === $_POST['ab_testing']['is_clear_stats'] && et_pb_is_allowed( 'ab_testing' ) ) { et_pb_ab_remove_stats( $post_id ); et_pb_ab_clear_cache_handler( $post_id ); } do_action( 'et_save_post', $post_id ); if ( $update ) { if ( ! empty( $_POST['et_builder_version'] ) ) { update_post_meta( $post_id, '_et_builder_version', sanitize_text_field( $_POST['et_builder_version'] ) ); } // Get saved post, verify its content against the one that is being sent. $saved_post = get_post( $update ); $saved_post_content = $saved_post->post_content; $builder_post_content = stripslashes( $sanitized_content ); // Get rendered post content only if it's needed. $return_rendered_content = sanitize_text_field( $utils->array_get( $_POST, 'options.return_rendered_content', 'false' ) ); $rendered_post_content = 'true' === $return_rendered_content ? do_shortcode( $saved_post_content ) : ''; // If `post_content` column on wp_posts table doesn't use `utf8mb4` charset, the saved post // content's emoji will be encoded which means the check of saved post_content vs // builder's post_content will be false; Thus check the charset of `post_content` column // first then encode the builder's post_content if needed // @see https://make.wordpress.org/core/2015/04/02/omg-emoji-%f0%9f%98%8e/ // @see https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/. global $wpdb; if ( 'utf8' === $wpdb->get_col_charset( $wpdb->posts, 'post_content' ) ) { $builder_post_content = wp_encode_emoji( $builder_post_content ); } $saved_verification = $saved_post_content === $builder_post_content; if ( $saved_verification ) { // Strip non-printable characters to ensure preg_match_all operation work properly. $post_content_cleaned = preg_replace( '/[\x00-\x1F\x7F]/u', '', $saved_post->post_content ); preg_match_all( '/\[et_pb_section(.*?)?\]\[et_pb_row(.*?)?\]\[et_pb_column(.*?)?\](.+?)\[\/et_pb_column\]\[\/et_pb_row\]\[\/et_pb_section\]/m', $post_content_cleaned, $matches ); if ( isset( $matches[4] ) && ! empty( $matches[4] ) ) { // Set page creation flow to off. update_post_meta( $post_id, '_et_pb_show_page_creation', 'off' ); } else { delete_post_meta( $post_id, '_et_pb_show_page_creation' ); } } /** * Hook triggered when the Post is updated. * * @param int $post_id Post ID. * * @since 3.29 */ do_action( 'et_update_post', $post_id ); wp_send_json_success( array( 'status' => get_post_status( $update ), 'save_verification' => apply_filters( 'et_fb_ajax_save_verification_result', $saved_verification ), 'rendered_content' => $rendered_post_content, ) ); } elseif ( isset( $_POST['skip_post_update'] ) ) { wp_send_json_success(); } else { wp_send_json_error(); } } add_action( 'wp_ajax_et_fb_ajax_save', 'et_fb_ajax_save' ); /** * Ajax Callback :: Convert fb object into shortcode. */ function et_fb_get_shortcode_from_fb_object() { if ( ! et_core_security_check( 'edit_posts', 'et_fb_convert_to_shortcode_nonce', 'et_fb_convert_to_shortcode_nonce', '_POST', false ) ) { wp_send_json_error(); } // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['modules'] will not be stored in db. $shortcode_data = isset( $_POST['modules'] ) ? json_decode( stripslashes( $_POST['modules'] ), true ) : array(); $layout_type = isset( $_POST['layout_type'] ) ? sanitize_text_field( $_POST['layout_type'] ) : ''; $post_content = et_fb_process_to_shortcode( $shortcode_data, array(), $layout_type ); // Get rendered post content only if it's needed. $utils = ET_Core_Data_Utils::instance(); $return_rendered_content = sanitize_text_field( $utils->array_get( $_POST, 'options.return_rendered_content', 'false' ) ); $rendered_post_content = 'true' === $return_rendered_content ? do_shortcode( $post_content ) : ''; wp_send_json_success( array( 'processed_content' => $post_content, 'rendered_content' => $rendered_post_content, ) ); } add_action( 'wp_ajax_et_fb_get_shortcode_from_fb_object', 'et_fb_get_shortcode_from_fb_object' ); /** * Ajax Callback :: Convert shortcode into HTML. */ function et_fb_get_html_from_shortcode() { if ( ! et_core_security_check( 'edit_posts', 'et_fb_shortcode_to_html_nonce', 'et_fb_shortcode_to_html_nonce', '_POST', false ) ) { wp_send_json_error(); } // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['modules'] will not be stored in db. $post_content = isset( $_POST['content'] ) ? stripslashes( $_POST['content'] ) : ''; // Get rendered post content by shortcode. $rendered_post_content = do_shortcode( $post_content ); wp_send_json_success( array( 'rendered_content' => $rendered_post_content, ) ); } add_action( 'wp_ajax_et_fb_get_html_from_shortcode', 'et_fb_get_html_from_shortcode' ); /** * Ajax Callback :: Save library modules. */ function et_fb_save_layout() { if ( ! isset( $_POST['et_fb_save_library_modules_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_save_library_modules_nonce'] ), 'et_fb_save_library_modules_nonce' ) ) { die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } if ( empty( $_POST['et_layout_name'] ) ) { die( -1 ); } $post_type = sanitize_text_field( et_()->array_get( $_POST, 'et_post_type', 'page' ) ); if ( et_theme_builder_is_layout_post_type( $post_type ) ) { // Treat TB layouts as normal posts when storing layouts from the library. $post_type = 'page'; } $args = array( 'layout_type' => isset( $_POST['et_layout_type'] ) ? sanitize_text_field( $_POST['et_layout_type'] ) : 'layout', 'layout_selected_cats' => isset( $_POST['et_layout_cats'] ) ? sanitize_text_field( $_POST['et_layout_cats'] ) : '', 'layout_selected_tags' => isset( $_POST['et_layout_tags'] ) ? sanitize_text_field( $_POST['et_layout_tags'] ) : '', 'built_for_post_type' => $post_type, 'layout_new_cat' => isset( $_POST['et_layout_new_cat'] ) ? sanitize_text_field( $_POST['et_layout_new_cat'] ) : '', 'layout_new_tag' => isset( $_POST['et_layout_new_tag'] ) ? sanitize_text_field( $_POST['et_layout_new_tag'] ) : '', 'columns_layout' => isset( $_POST['et_columns_layout'] ) ? sanitize_text_field( $_POST['et_columns_layout'] ) : '0', 'module_type' => isset( $_POST['et_module_type'] ) ? sanitize_text_field( $_POST['et_module_type'] ) : 'et_pb_unknown', 'layout_scope' => isset( $_POST['et_layout_scope'] ) ? sanitize_text_field( $_POST['et_layout_scope'] ) : 'not_global', 'module_width' => isset( $_POST['et_module_width'] ) ? sanitize_text_field( $_POST['et_module_width'] ) : 'regular', 'layout_content' => isset( $_POST['et_layout_content'] ) ? et_fb_process_to_shortcode( json_decode( stripslashes( $_POST['et_layout_content'] ), true ) ) : '', // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The `$_POST['et_layout_content']` will be sanitized before saving into db. 'layout_name' => isset( $_POST['et_layout_name'] ) ? sanitize_text_field( $_POST['et_layout_name'] ) : '', ); $new_layout_meta = et_pb_submit_layout( $args ); $updated_terms = array(); foreach ( [ 'layout_category', 'layout_tag' ] as $taxonomy ) { $raw_terms_array = apply_filters( 'et_pb_new_layout_cats_array', get_terms( $taxonomy, array( 'hide_empty' => false ) ) ); $clean_terms_array = array(); if ( is_array( $raw_terms_array ) && ! empty( $raw_terms_array ) ) { foreach ( $raw_terms_array as $term ) { $clean_terms_array[] = array( 'name' => html_entity_decode( $term->name ), 'id' => $term->term_id, 'slug' => $term->slug, ); } } $updated_terms[ $taxonomy ] = $clean_terms_array; } $data = array( 'layout_data' => json_decode( $new_layout_meta, true ), 'updated_terms' => $updated_terms, ); die( wp_json_encode( et_core_esc_previously( $data ) ) ); } add_action( 'wp_ajax_et_fb_save_layout', 'et_fb_save_layout' ); /** * Ajax Callback :: Process shortcode to exported layout object. */ function et_fb_get_cloud_item_content() { if ( ! isset( $_POST['et_fb_save_cloud_item_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_save_cloud_item_nonce'] ), 'et_fb_save_cloud_item_nonce' ) ) { die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } $layout_type = isset( $_POST['et_layout_type'] ) ? sanitize_text_field( $_POST['et_layout_type'] ) : ''; $layout_content = isset( $_POST['et_layout_content'] ) ? et_fb_process_to_shortcode( json_decode( stripslashes( $_POST['et_layout_content'] ), true ), array(), $layout_type ) : ''; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The `$_POST['et_layout_content']` will be sanitized before saving into db. $exported_shortcode = get_exported_content( $layout_content ); die( wp_json_encode( array( 'shortcode' => $exported_shortcode ) ) ); } add_action( 'wp_ajax_et_fb_get_cloud_item_content', 'et_fb_get_cloud_item_content' ); /** * Prepare shortcode for exporting. * * @since 4.17.0 * * @param string $shortcode Shortcode to process. * * @return array */ function get_exported_content( $shortcode ) { // Set faux $_POST value that is required by portability. $_POST['post'] = '1'; $_POST['content'] = $shortcode; // Remove page value if it is equal to `false`, avoiding paginated images not accidentally triggered. if ( isset( $_POST['page'] ) && false === $_POST['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. unset( $_POST['page'] ); // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. } $portability = et_core_portability_load( 'et_builder' ); // Export the content. return $portability->export( true, true ); } /** * Ajax Callback :: Process shortcode. */ function et_fb_prepare_shortcode() { if ( ! isset( $_POST['et_fb_prepare_shortcode_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_prepare_shortcode_nonce'] ), 'et_fb_prepare_shortcode_nonce' ) ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } // phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- The `$_POST['et_page_content']` and `$_POST['apply_global_presets']` will not be stored on db. $content = isset( $_POST['et_page_content'] ) ? json_decode( stripslashes( $_POST['et_page_content'] ), true ) : ''; $apply_global_presets = isset( $_POST['apply_global_presets'] ) ? wp_validate_boolean( $_POST['apply_global_presets'] ) : false; // phpcs:enable $options = array( 'apply_global_presets' => $apply_global_presets, ); $result = $content ? et_fb_process_to_shortcode( $content, $options, '', false ) : ''; die( wp_json_encode( array( 'shortcode' => $result ) ) ); } add_action( 'wp_ajax_et_fb_prepare_shortcode', 'et_fb_prepare_shortcode' ); /** * Ajax Callback :: Save library module. */ function et_fb_update_layout() { if ( ! isset( $_POST['et_fb_save_library_modules_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_save_library_modules_nonce'], 'et_fb_save_library_modules_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } $post_id = isset( $_POST['et_template_post_id'] ) ? absint( $_POST['et_template_post_id'] ) : ''; if ( ! current_user_can( 'edit_post', $post_id ) ) { die( -1 ); } // phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- Following $_POST values will be sanitized before storing in db. $post_content = isset( $_POST['et_layout_content'] ) ? json_decode( stripslashes( $_POST['et_layout_content'] ), true ) : array(); $new_content = isset( $_POST['et_layout_content'] ) ? et_fb_process_to_shortcode( $post_content ) : ''; $excluded_global_options = isset( $_POST['et_excluded_global_options'] ) ? stripslashes( $_POST['et_excluded_global_options'] ) : array(); $is_saving_global_module = isset( $_POST['et_saving_global_module'] ) ? sanitize_text_field( $_POST['et_saving_global_module'] ) : ''; // phpcs:enable if ( '' !== $post_id ) { $update = array( 'ID' => $post_id, 'post_content' => $new_content, ); $result = wp_update_post( $update ); if ( ! $result || is_wp_error( $result ) ) { wp_send_json_error(); } ET_Core_PageResource::remove_static_resources( 'all', 'all' ); // update list of unsynced options for global module. if ( 'true' === $is_saving_global_module ) { update_post_meta( $post_id, '_et_pb_excluded_global_options', sanitize_text_field( $excluded_global_options ) ); } } die(); } add_action( 'wp_ajax_et_fb_update_layout', 'et_fb_update_layout' ); /** * Ajax Callback :: Return requested attachments from media library. */ function et_fb_fetch_attachments() { et_core_security_check( 'edit_posts', 'et_fb_fetch_attachments', 'et_fb_fetch_attachments' ); $ids = ET_Core_Data_Utils::instance()->array_get( $_POST, 'ids' ); if ( empty( $ids ) ) { wp_send_json( null ); } else { $attachments = get_posts( array( 'posts_per_page' => - 1, 'include' => $ids, 'post_type' => 'attachment', ) ); foreach ( $attachments as $index => $attachment ) { $metadata = array(); foreach ( get_intermediate_image_sizes() as $size ) { $metadata[ $size ] = wp_get_attachment_image_src( $attachment->ID, $size ); } $attachments[ $index ] = array_merge( get_object_vars( $attachment ), array( 'metadata' => $metadata, ) ); } wp_send_json( $attachments ); } die(); } add_action( 'wp_ajax_et_fb_fetch_attachments', 'et_fb_fetch_attachments' ); if ( ! function_exists( 'et_fb_disable_product_tour' ) ) : /** * Saving User Specific Tour status. */ function et_fb_disable_product_tour() { do_action( 'et_fb_disable_product_tour' ); if ( ! et_core_security_check_passed( 'edit_posts' ) ) { ET_Core_Logger::debug( 'Unable to disable product tour. Security check failed!' ); return; } $user_id = (int) get_current_user_id(); $product_tour_status = et_get_option( 'product_tour_status', [] ); $all_product_settings = is_array( $product_tour_status ) ? $product_tour_status : []; $all_product_settings[ $user_id ] = 'off'; et_update_option( 'product_tour_status', $all_product_settings ); } endif; if ( ! function_exists( 'et_builder_include_categories_option' ) ) : /** * Generate output string for `include_categories` option used in backbone template. * * @param array $args Arguments to get project categories. * @param string $default_category @todo Add parameter doc. * @return string */ function et_builder_include_categories_option( $args = array(), $default_category = '' ) { $custom_items = array(); if ( ! empty( $args['custom_items'] ) ) { $custom_items = $args['custom_items']; unset( $args['custom_items'] ); } $defaults = array( 'use_terms' => true, 'term_name' => 'project_category', 'field_name' => 'et_pb_include_categories', ); $defaults = apply_filters( 'et_builder_include_categories_defaults', $defaults ); $args = wp_parse_args( $args, $defaults ); $args['field_name'] = esc_attr( $args['field_name'] ); $term_args = apply_filters( 'et_builder_include_categories_option_args', array( 'hide_empty' => false ) ); $output = "\t<% var {$args['field_name']}_temp = typeof data !== 'undefined' && typeof data.{$args['field_name']} !== 'undefined' ? data.{$args['field_name']}.split( ',' ) : ['" . esc_html( $default_category ) . "']; {$args['field_name']}_temp = typeof data === 'undefined' && typeof {$args['field_name']} !== 'undefined' ? {$args['field_name']}.split( ',' ) : {$args['field_name']}_temp; %>\n"; if ( $args['use_terms'] ) { $cats_array = get_terms( $args['term_name'], $term_args ); } else { $cats_array = get_categories( apply_filters( 'et_builder_get_categories_args', 'hide_empty=0' ) ); } $cats_array = array_merge( $custom_items, $cats_array ); if ( empty( $cats_array ) ) { $taxonomy_type = $args['use_terms'] ? $args['term_name'] : 'category'; $taxonomy = get_taxonomy( $taxonomy_type ); $labels = get_taxonomy_labels( $taxonomy ); $output = sprintf( '<p>%1$s</p>', esc_html( $labels->not_found ) ); } foreach ( $cats_array as $category ) { $contains = sprintf( "<%%= _.contains( {$args['field_name']}_temp, '%1\$s' ) ? checked='checked' : '' %%>", is_array( $category ) ? esc_html( $category['term_id'] ) : esc_html( $category->term_id ) ); $output .= sprintf( '%4$s<label><input type="checkbox" name="%5$s" value="%1$s"%3$s> %2$s</label><br/>', is_array( $category ) ? esc_html( $category['term_id'] ) : esc_html( $category->term_id ), is_array( $category ) ? esc_html( $category['name'] ) : esc_html( $category->name ), $contains, "\n\t\t\t\t\t", $args['field_name'] ); } $output = "<div id='{$args['field_name']}'>" . $output . '</div>'; return apply_filters( 'et_builder_include_categories_option_html', $output ); } endif; if ( ! function_exists( 'et_builder_include_categories_shop_option' ) ) : /** * Generate output string for `include_shop_categories` option used in backbone template. * * @param array $args arguments to get shop categories. * @return string */ function et_builder_include_categories_shop_option( $args = array() ) { if ( ! class_exists( 'WooCommerce' ) ) { return ''; } $output = "\t<% var et_pb_include_categories_shop_temp = typeof data !== 'undefined' && typeof data.et_pb_include_categories !== 'undefined' ? data.et_pb_include_categories.split( ',' ) : []; et_pb_include_categories_shop_temp = typeof data === 'undefined' && typeof et_pb_include_categories !== 'undefined' ? et_pb_include_categories.split( ',' ) : et_pb_include_categories_shop_temp; %>\n"; $product_categories = et_builder_get_shop_categories( $args ); $output .= '<div id="et_pb_include_categories">'; if ( is_array( $product_categories ) && ! empty( $product_categories ) ) { foreach ( $product_categories as $category ) { if ( is_object( $category ) && is_a( $category, 'WP_Term' ) ) { $contains = sprintf( '<%%= _.contains( et_pb_include_categories_shop_temp, "%1$s" ) ? checked="checked" : "" %%>', esc_html( $category->term_id ) ); $output .= sprintf( '%4$s<label><input type="checkbox" name="et_pb_include_categories" value="%1$s"%3$s> %2$s</label><br/>', esc_attr( $category->term_id ), esc_html( $category->name ), $contains, "\n\t\t\t\t\t" ); } } } $output .= '</div>'; return apply_filters( 'et_builder_include_categories_option_html', $output ); } endif; if ( ! function_exists( 'et_divi_get_projects' ) ) : /** * Return projects. * * @param array $args WP_Query arguments. */ function et_divi_get_projects( $args = array() ) { $default_args = array( 'post_type' => 'project', ); $args = wp_parse_args( $args, $default_args ); return new WP_Query( $args ); } endif; if ( ! function_exists( 'et_pb_extract_items' ) ) : /** * Return pricing table items html. * * @param string $content Content. */ function et_pb_extract_items( $content ) { $output = ''; $first_character = ''; $lines = array_filter( explode( "\n", str_replace( array( '<p>', '</p>', '<br />' ), "\n", $content ) ) ); foreach ( $lines as $line ) { $line = trim( $line ); if ( '–' === substr( $line, 0, 7 ) ) { $line = '-' . substr( $line, 7 ); } if ( '' === $line ) { continue; } $first_character = $line[0]; if ( in_array( $first_character, array( '-', '+' ), true ) ) { $line = trim( substr( $line, 1 ) ); } $output .= sprintf( '[et_pb_pricing_item available="%2$s"]%1$s[/et_pb_pricing_item]', $line, ( '-' === $first_character ? 'off' : 'on' ) ); } return do_shortcode( $output ); } endif; /** * Get all acceptable string value for given CSS property * * @since 4.15.0 Added background-size to allow only 'auto' and ''. * * @param string $property property name. * @return array of acceptable CSS string values of given property name. */ function et_builder_get_acceptable_css_string_values( $property = 'all' ) { $css_string_values = array( 'width' => array( 'auto', 'inherit', 'initial', 'unset', '', ), 'max-width' => array( 'none', 'inherit', 'initial', 'unset', ), 'margin' => array( 'auto', 'inherit', 'initial', 'unset', ), 'padding' => array( 'inherit', 'initial', 'unset', ), 'height' => array( 'auto', 'inherit', 'initial', 'unset', '', ), 'min-height' => array( 'auto', 'inherit', 'initial', 'unset', ), 'max-height' => array( 'none', 'inherit', 'initial', 'unset', ), 'z-index' => array( 'auto', ), 'line-height' => array( '', ), 'font-size' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw', ), 'background-size' => array( 'auto', '', ), ); $acceptable_strings = apply_filters( 'et_builder_acceptable_css_string_values', $css_string_values ); if ( 'all' === $property ) { return $acceptable_strings; } return isset( $acceptable_strings[ $property ] ) ? $acceptable_strings[ $property ] : array(); } if ( ! function_exists( 'et_builder_process_range_value' ) ) : /** * Process range setting field value. * * @param string $range Range value. * @param string $option_type CSS property. */ function et_builder_process_range_value( $range, $option_type = '' ) { $range = trim( $range ); $range_digit = ''; if ( 'none' !== $range ) { $range_digit = floatval( $range ); } $range_string = str_replace( et_()->to_css_decimal( $range_digit ), '', (string) $range ); if ( '' !== $option_type && in_array( $range, et_builder_get_acceptable_css_string_values( $option_type ), true ) ) { $result = $range; } else { if ( '' === $range_string ) { $range_string = 'line_height' === $option_type && 3 >= $range_digit ? 'em' : 'px'; } $result = et_()->to_css_decimal( $range_digit ) . $range_string; } return apply_filters( 'et_builder_processed_range_value', $result, $range, $range_string ); } endif; if ( ! function_exists( 'et_builder_get_border_styles' ) ) : /** * Return border styles options list. */ function et_builder_get_border_styles() { $styles = array( 'solid' => esc_html__( 'Solid', 'et_builder' ), 'dashed' => esc_html__( 'Dashed', 'et_builder' ), 'dotted' => esc_html__( 'Dotted', 'et_builder' ), 'double' => esc_html__( 'Double', 'et_builder' ), 'groove' => esc_html__( 'Groove', 'et_builder' ), 'ridge' => esc_html__( 'Ridge', 'et_builder' ), 'inset' => esc_html__( 'Inset', 'et_builder' ), 'outset' => esc_html__( 'Outset', 'et_builder' ), 'none' => et_builder_i18n( 'None' ), ); return apply_filters( 'et_builder_border_styles', $styles ); } endif; if ( ! function_exists( 'et_builder_font_options' ) ) : /** * Return an array of font options. */ function et_builder_font_options() { $options = array(); $default_options = array( 'default' => array( 'name' => et_builder_i18n( 'Default' ), ), ); $fonts = array_merge( $default_options, et_builder_get_fonts() ); foreach ( $fonts as $font_name => $font_settings ) { $options[ $font_name ] = 'default' !== $font_name ? $font_name : $font_settings['name']; } return $options; } endif; if ( ! function_exists( 'et_builder_get_google_font_items' ) ) : /** * Return google font list items. */ function et_builder_get_google_font_items() { $output = ''; $font_options = et_builder_font_options(); foreach ( $font_options as $key => $value ) { $output .= sprintf( '<li class="select-option-item select-option-item-%3$s" data-value="%1$s">%2$s</li>', esc_attr( $key ), esc_html( $value ), str_replace( ' ', '_', esc_attr( $key ) ) ); } return $output; } endif; if ( ! function_exists( 'et_builder_set_element_font' ) ) : /** * Return element font style. * * @param string $font Font style value. * @param bool $use_important Whether use !important. * @param bool $default Default font style value. e.x global font value. */ function et_builder_set_element_font( $font, $use_important = false, $default = false ) { static $cache = array(); $style = ''; if ( '' === $font ) { return $style; } $font_values = explode( '|', $font ); $default = ! $default ? '||||||||' : $default; $font_values_default = explode( '|', $default ); if ( ! empty( $font_values ) ) { // backward compatibility with old version of theme. if ( isset( $font_values[1] ) ) { $font_values[1] = 'on' === $font_values[1] ? '700' : $font_values[1]; } $font_values = array_map( 'trim', $font_values ); $font_name = $font_values[0]; $font_weight = isset( $font_values[1] ) && '' !== $font_values[1] ? $font_values[1] : ''; $is_font_italic = isset( $font_values[2] ) && 'on' === $font_values[2] ? true : false; $is_font_uppercase = isset( $font_values[3] ) && 'on' === $font_values[3] ? true : false; $is_font_underline = isset( $font_values[4] ) && 'on' === $font_values[4] ? true : false; $is_font_small_caps = isset( $font_values[5] ) && 'on' === $font_values[5] ? true : false; $is_font_line_through = isset( $font_values[6] ) && 'on' === $font_values[6] ? true : false; $font_line_color = isset( $font_values[7] ) ? $font_values[7] : ''; $font_line_style = isset( $font_values[8] ) ? $font_values[8] : ''; $font_name_default = $font_values_default[0]; $font_weight_default = isset( $font_values_default[1] ) && '' !== $font_values_default[1] ? $font_values_default[1] : ''; $is_font_italic_default = isset( $font_values_default[2] ) && 'on' === $font_values_default[2] ? true : false; $is_font_uppercase_default = isset( $font_values_default[3] ) && 'on' === $font_values_default[3] ? true : false; $is_font_underline_default = isset( $font_values_default[4] ) && 'on' === $font_values_default[4] ? true : false; $is_font_small_caps_default = isset( $font_values_default[5] ) && 'on' === $font_values_default[5] ? true : false; $is_font_line_through_default = isset( $font_values_default[6] ) && 'on' === $font_values_default[6] ? true : false; $font_line_color_default = isset( $font_values_default[7] ) ? $font_values_default[7] : ''; $font_line_style_default = isset( $font_values_default[8] ) ? $font_values_default[8] : ''; if ( '' !== $font_name && $font_name_default !== $font_name && 'Default' !== $font_name ) { if ( empty( $cache[ $font_name ] ) ) { et_builder_enqueue_font( $font_name ); $font_family = et_builder_get_font_family( $font_name ); $cache[ $font_name ] = $font_family; } else { $font_family = $cache[ $font_name ]; } if ( $use_important ) { $font_family = rtrim( $font_family, ';' ) . ' !important;'; } $style .= "$font_family "; } // Parse global font weight value. $is_global_font_weigth = in_array( $font_weight, array( '--et_global_heading_font_weight', '--et_global_body_font_weight' ), true ); $font_weight = $is_global_font_weigth ? '--et_global_heading_font_weight' === $font_weight ? et_get_option( 'heading_font_weight', '' ) : et_get_option( 'body_font_weight', '' ) : $font_weight; $style .= et_builder_set_element_font_style( 'font-weight', ( '' !== $font_weight_default && ( '' === $font_weight || $font_weight_default === $font_weight ) ), ( '' !== $font_weight ), 'normal', $font_weight, $use_important ); $style .= et_builder_set_element_font_style( 'font-style', $is_font_italic_default, $is_font_italic, 'normal', 'italic', $use_important ); $style .= et_builder_set_element_font_style( 'text-transform', $is_font_uppercase_default, $is_font_uppercase, 'none', 'uppercase', $use_important ); $style .= et_builder_set_element_font_style( 'text-decoration', $is_font_underline_default, $is_font_underline, 'none', 'underline', $use_important ); $style .= et_builder_set_element_font_style( 'font-variant', $is_font_small_caps_default, $is_font_small_caps, 'none', 'small-caps', $use_important ); $style .= et_builder_set_element_font_style( 'text-decoration', $is_font_line_through_default, $is_font_line_through, 'none', 'line-through', $use_important ); $style .= et_builder_set_element_font_style( 'text-decoration-style', ( '' !== $font_line_style_default ), ( '' !== $font_line_style ), 'solid', $font_line_style, $use_important ); $style .= et_builder_set_element_font_style( '-webkit-text-decoration-color', ( '' !== $font_line_color_default ), ( '' !== $font_line_color ), '', $font_line_color, $use_important ); $style .= et_builder_set_element_font_style( 'text-decoration-color', ( '' !== $font_line_color_default ), ( '' !== $font_line_color ), '', $font_line_color, $use_important ); $style = rtrim( $style ); } return $style; } endif; if ( ! function_exists( 'et_builder_set_element_font_style' ) ) : /** * Set element font style. * * @param string $property CSS property. * @param bool $default @todo Add parameter doc. * @param bool $value @todo Add parameter doc. * @param string $property_default @todo Add parameter doc. * @param string $property_value Property value. * @param string $use_important Whether use !important specificity. */ function et_builder_set_element_font_style( $property, $default, $value, $property_default, $property_value, $use_important ) { $style = ''; if ( $value && ! $default ) { $style = sprintf( '%1$s: %2$s%3$s; ', esc_html( $property ), $property_value, ( $use_important ? ' !important' : '' ) ); } elseif ( ! $value && $default ) { $style = sprintf( '%1$s: %2$s%3$s; ', esc_html( $property ), $property_default, ( $use_important ? ' !important' : '' ) ); } return $style; } endif; if ( ! function_exists( 'et_builder_set_reset_font_style' ) ) : /** * Set reset CSS style declaration to normalize the existing font styles value from another font * options group. * * @since 3.23 * * @param string $current_value Current font option value. * @param string $compared_value Compared or parent font option value. * @param boolean $use_important Imporant status. * @return string Generated reset font styles. */ function et_builder_set_reset_font_style( $current_value, $compared_value, $use_important = false ) { // Being save, ensure current and compared values are valid string. if ( ! is_string( $current_value ) || ! is_string( $compared_value ) ) { return ''; } $current_pieces = explode( '|', $current_value ); $compared_pieces = explode( '|', $compared_value ); if ( empty( $current_pieces ) || empty( $compared_pieces ) ) { return ''; } // Current value font style status. $is_current_italic = isset( $current_pieces[2] ) && 'on' === $current_pieces[2]; $is_current_uppercase = isset( $current_pieces[3] ) && 'on' === $current_pieces[3]; $is_current_underline = isset( $current_pieces[4] ) && 'on' === $current_pieces[4]; $is_current_small_caps = isset( $current_pieces[5] ) && 'on' === $current_pieces[5]; $is_current_line_through = isset( $current_pieces[6] ) && 'on' === $current_pieces[6]; // Compated value font style status. $is_compared_italic = isset( $compared_pieces[2] ) && 'on' === $compared_pieces[2]; $is_compared_uppercase = isset( $compared_pieces[3] ) && 'on' === $compared_pieces[3]; $is_compared_underline = isset( $compared_pieces[4] ) && 'on' === $compared_pieces[4]; $is_compared_small_caps = isset( $compared_pieces[5] ) && 'on' === $compared_pieces[5]; $is_compared_line_through = isset( $compared_pieces[6] ) && 'on' === $compared_pieces[6]; $style = ''; $important = $use_important ? ' !important' : ''; // Reset italic. if ( ! $is_current_italic && $is_compared_italic ) { $style .= "font-style: normal{$important};"; } // Reset uppercase. if ( ! $is_current_uppercase && $is_compared_uppercase ) { $style .= "text-transform: none{$important};"; } // Reset small caps. if ( ! $is_current_small_caps && $is_compared_small_caps ) { $style .= "font-variant: none{$important};"; } // Reset underline. if ( ! $is_current_underline && $is_compared_underline ) { $underline_value = $is_current_line_through || $is_compared_line_through ? 'line-through' : 'none'; $style .= "text-decoration: {$underline_value}{$important};"; } // Reset line through. if ( ! $is_current_line_through && $is_compared_line_through ) { $line_through_value = $is_current_underline || $is_compared_underline ? 'underline' : 'none'; $style .= "text-decoration: {$line_through_value}{$important};"; } return $style; } endif; if ( ! function_exists( 'et_builder_get_element_style_css' ) ) : /** * Return element css style. * * @param string $value Property value. * @param string $property Css property. * @param bool $use_important Whether add !important specificity. */ function et_builder_get_element_style_css( $value, $property = 'margin', $use_important = false ) { $style = ''; $values = explode( '|', $value ); if ( ! empty( $values ) ) { $element_style = ''; $values = array_map( 'trim', $values ); $positions = array( 'top', 'right', 'bottom', 'left', ); foreach ( $positions as $i => $position ) { if ( ! isset( $values[ $i ] ) || '' === $values[ $i ] ) { continue; } $element_style .= sprintf( '%3$s-%1$s: %2$s%4$s; ', esc_attr( $position ), esc_attr( et_builder_process_range_value( $values[ $i ], $property ) ), esc_attr( $property ), ( $use_important ? ' !important' : '' ) ); } $style .= rtrim( $element_style ); } return $style; } endif; if ( ! function_exists( 'et_builder_enqueue_font' ) ) : /** * Enqueue fonts. * * @param string $font_name font name. */ function et_builder_enqueue_font( $font_name ) { global $et_fonts_queue, $et_user_fonts_queue; $fonts = et_builder_get_fonts(); $websafe_fonts = et_builder_get_websafe_fonts(); $user_fonts = et_builder_get_custom_fonts(); $removed_fonts_mapping = et_builder_old_fonts_mapping(); if ( array_key_exists( $font_name, $user_fonts ) ) { $et_user_fonts_queue[ $font_name ] = $user_fonts[ $font_name ]; return; } // Skip enqueueing if font name is not found. Possibly happen if support for particular font need to be dropped. if ( ! array_key_exists( $font_name, $fonts ) && ! isset( $removed_fonts_mapping[ $font_name ] ) ) { return; } // Skip enqueueing for websafe fonts. if ( array_key_exists( $font_name, $websafe_fonts ) ) { return; } if ( isset( $removed_fonts_mapping[ $font_name ] ) ) { $font_name = $removed_fonts_mapping[ $font_name ]['parent_font']; } $font_character_set = $fonts[ $font_name ]['character_set']; global $shortname; // Force enabled subsets for existing sites once. if ( ! et_get_option( "{$shortname}_skip_font_subset_force", false ) ) { et_update_option( "{$shortname}_gf_enable_all_character_sets", 'on' ); et_update_option( "{$shortname}_skip_font_subset_force", true ); } // By default, only latin and latin-ext subsets are loaded, all available subsets can be enabled in ePanel. if ( 'false' === et_get_option( "{$shortname}_gf_enable_all_character_sets", 'false' ) ) { $latin_ext = ''; if ( false !== strpos( $fonts[ $font_name ]['character_set'], 'latin-ext' ) ) { $latin_ext = ',latin-ext'; } $font_character_set = "latin{$latin_ext}"; } $font_name_slug = sprintf( 'et-gf-%1$s', strtolower( str_replace( ' ', '-', $font_name ) ) ); $queued_font = array( 'font' => sprintf( '%s:%s', str_replace( ' ', '+', $font_name ), apply_filters( 'et_builder_set_styles', $fonts[ $font_name ]['styles'], $font_name ) ), 'subset' => apply_filters( 'et_builder_set_character_set', $font_character_set, $font_name ), ); // Enqueue google fonts. $et_fonts_queue[ $font_name_slug ] = $queued_font; } endif; if ( ! function_exists( 'et_builder_enqueue_user_fonts' ) ) : /** * Load user fonts. * * @param array $et_user_fonts User fonts. * * @return The @font-face CSS at-rule. */ function et_builder_enqueue_user_fonts( $et_user_fonts ) { $output = ''; // load user fonts. if ( ! empty( $et_user_fonts ) ) { foreach ( $et_user_fonts as $font_name => $font_data ) { if ( is_array( $font_data['font_url'] ) && ! empty( $font_data['font_url'] ) ) { // generate the @font-face src from the uploaded font files // all the font formats have to be added in certain order to provide the best browser support. $uploaded_files = array( 'eot' => array( 'url' => isset( $font_data['font_url']['eot'] ) ? $font_data['font_url']['eot'] : false, 'format' => 'embedded-opentype', ), 'woff2' => array( 'url' => isset( $font_data['font_url']['woff2'] ) ? $font_data['font_url']['woff2'] : false, 'format' => 'woff2', ), 'woff' => array( 'url' => isset( $font_data['font_url']['woff'] ) ? $font_data['font_url']['woff'] : false, 'format' => 'woff', ), 'ttf' => array( 'url' => isset( $font_data['font_url']['ttf'] ) ? $font_data['font_url']['ttf'] : false, 'format' => 'truetype', ), 'otf' => array( 'url' => isset( $font_data['font_url']['otf'] ) ? $font_data['font_url']['otf'] : false, 'format' => 'opentype', ), ); $font_src = ''; foreach ( $uploaded_files as $ext => $file_data ) { if ( ! $file_data['url'] ) { continue; } $font_src .= '' === $font_src ? 'src: ' : ', '; $font_src .= sprintf( 'url("%1$s%2$s") format("%3$s")', esc_url( $file_data['url'] ), 'eot' === $ext ? '?#iefix' : '', esc_attr( $file_data['format'] ) ); } $output .= sprintf( '@font-face { font-family: "%1$s"; font-display: swap; %2$s %3$s; }', esc_attr( $font_name ), isset( $font_data['font_url']['eot'] ) ? sprintf( 'src: url(%1$s);', esc_url( $font_data['font_url']['eot'] ) ) : '', // Make sure to properly escape each individual piece of $font_src above. et_core_esc_previously( $font_src ) ); } else { $output .= sprintf( '@font-face { font-family: "%1$s"; font-display: swap; src: url(%2$s);}', esc_attr( $font_name ), esc_url( $font_data['font_url'] ) ); } } } return $output; } endif; if ( ! function_exists( 'et_font_subset_force_check' ) ) : /** * Font subset force check on theme activation. */ function et_font_subset_force_check() { global $shortname; if ( empty( $shortname ) || ! in_array( $shortname, array( 'divi', 'extra' ), true ) ) { return; } if ( ! et_get_option( "{$shortname}_skip_font_subset_force", false ) ) { et_update_option( "{$shortname}_skip_font_subset_force", true ); } } endif; add_action( 'after_switch_theme', 'et_font_subset_force_check' ); /** * Preconnect to Google Fonts to allow async dns lookup. */ function et_builder_preconnect_google_fonts() { $feature_manager = ET_Builder_Google_Fonts_Feature::instance(); $output_inline = $feature_manager->is_option_enabled( 'google_fonts_inline' ); if ( $output_inline && et_core_use_google_fonts() ) { echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />'; } } add_action( 'wp_enqueue_scripts', 'et_builder_preconnect_google_fonts', 9 ); /** * Enqueue queued Google Fonts into WordPress' wp_enqueue_style as one request * * @return void */ function et_builder_print_font() { global $et_fonts_queue, $et_fonts_cache; // Bail if no queued google font found. if ( empty( $et_fonts_queue ) ) { return; } $fonts = wp_list_pluck( $et_fonts_queue, 'font' ); $subsets = wp_list_pluck( $et_fonts_queue, 'subset' ); $unique_subsets = array_unique( explode( ',', implode( ',', $subsets ) ) ); // Get the google fonts for the current page that are stored as an option. $post_fonts_data = array(); $post_id = is_singular() ? get_the_ID() : false; if ( false !== $post_id ) { $post_fonts_data = get_post_meta( $post_id, 'et_enqueued_post_fonts', true ); } if ( ! is_array( $post_fonts_data ) ) { $post_fonts_data = array(); } if ( empty( $post_fonts_data ) || ! is_array( $post_fonts_data['family'] ) || ! is_array( $post_fonts_data['subset'] ) ) { $post_fonts_data = array( 'family' => array(), 'subset' => array(), ); } $google_fonts_feature_cache_key = ET_Builder_Google_Fonts_Feature::_get_cache_index(); // We only need the difference in the fonts since the subsets might be needed // in cases where a new font is added to the page and it is not yet present // in the option cache. $cached_fonts = $post_fonts_data['family']; $cached_subsets = $post_fonts_data['subset']; $unique_cached_subsets = array_unique( explode( ',', implode( ',', $cached_subsets ) ) ); $cached_cache_key = ! empty( $post_fonts_data['cache_key'] ) ? $post_fonts_data['cache_key'] : $google_fonts_feature_cache_key; // compare if things have changed since the post cache. $fonts_diff = array_diff( $fonts, $cached_fonts ); $subsets_diff = array_diff( $unique_subsets, $unique_cached_subsets ); $cache_key_diff = $cached_cache_key !== $google_fonts_feature_cache_key; if ( ! $fonts_diff && ! $subsets_diff ) { // The `$fonts` variable stores all the fonts used on the page (cache does not matter) // while the `$cached_fonts` one only stores the fonts that were lastly saved into // the post meta. When we run `array_diff` we would only get a result if there // are new fonts present on the page that are not yet cached. However if some // of the cached fonts are no longer in use this will not be caught by the // `array_diff`. To fix this if the item count in `$fonts` is different // than the one in `$cached_fonts` we update the post meta with the // data from the `$fonts` variable to force unused fonts removal. if ( count( $fonts ) !== count( $cached_fonts ) || ! empty( $cache_key_diff ) ) { // Update the option for the current page with the new data. $post_fonts_data = array( 'family' => et_core_sanitized_previously( $fonts ), 'subset' => et_core_sanitized_previously( $unique_subsets ), ); // Do not update post meta here, save the value to global variable and update it at `shutdown` hook. // Prevents object cache error on GoDaddy + Woocommerce websites. $et_fonts_cache = et_core_sanitized_previously( $post_fonts_data ); } return; } if ( et_core_use_google_fonts() ) { // Append combined subset at the end of the URL as different query string. $google_fonts_url_args = array( 'family' => implode( '|', $fonts ), 'subset' => implode( ',', $unique_subsets ), 'display' => 'swap', ); $feature_manager = ET_Builder_Google_Fonts_Feature::instance(); $google_fonts_url = $feature_manager->get_google_fonts_url( $google_fonts_url_args ); $output_inline = $feature_manager->is_option_enabled( 'google_fonts_inline' ); if ( $output_inline ) { $contents = $feature_manager->get( 'google-fonts', function() use ( $feature_manager, $google_fonts_url ) { return $feature_manager->fetch( $google_fonts_url ); }, sanitize_text_field( et_core_esc_previously( $google_fonts_url ) ) ); // if something went wrong fetching the contents. if ( false === $contents ) { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters -- Google fonts api does not have versions wp_enqueue_style( 'et-builder-googlefonts', et_core_esc_previously( $google_fonts_url ), array(), null ); } else { echo '<style id="et-builder-googlefonts-inline">' . $contents . '</style>'; } } else { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters -- Google fonts api does not have versions wp_enqueue_style( 'et-builder-googlefonts', et_core_esc_previously( $google_fonts_url ), array(), null ); } } // Create a merge of the existing fonts and subsets in the option and the newly added ones. $updated_fonts = array_merge( $fonts, $post_fonts_data['family'] ); $updated_subsets = array_merge( $unique_subsets, $post_fonts_data['subset'] ); // Update the option for the current page with the new data. $post_fonts_data = array( 'family' => array_unique( $updated_fonts ), 'subset' => array_unique( $updated_subsets ), 'cache_key' => $google_fonts_feature_cache_key, ); // Do not update post meta here, save the value to global variable and update it at `shutdown` hook. // Prevents object cache error on GoDaddy + Woocommerce websites. $et_fonts_cache = et_core_sanitized_previously( $post_fonts_data ); } add_action( 'wp_footer', 'et_builder_print_font' ); /** * Update Fonts Cache in post meta * Run this function on shutdown hook to prevents object cache error on GoDaddy + Woocommerce websites * * @return void */ function et_builder_update_fonts_cache() { global $et_fonts_cache; if ( ! isset( $et_fonts_cache ) || empty( $et_fonts_cache ) ) { return; } $post_id = is_singular() ? get_the_ID() : false; if ( ! $post_id ) { return; } update_post_meta( $post_id, 'et_enqueued_post_fonts', et_core_sanitized_previously( $et_fonts_cache ) ); } add_action( 'shutdown', 'et_builder_update_fonts_cache' ); /** * Enqueue queued Google Fonts into WordPress' wp_enqueue_style as one request (cached version) * * @return void */ function et_builder_preprint_font() { // Return if this is not a post or a page. if ( ! is_singular() || ! et_core_use_google_fonts() ) { return; } $post_id = get_the_ID(); $post_fonts_data = get_post_meta( $post_id, 'et_enqueued_post_fonts', true ); // Bail early if the post fonts data is not an array. if ( ! is_array( $post_fonts_data ) ) { return; } $fonts_family = isset( $post_fonts_data['family'] ) ? $post_fonts_data['family'] : ''; $fonts_subset = isset( $post_fonts_data['subset'] ) ? $post_fonts_data['subset'] : ''; // We expect both 'family' and 'subset' to contain an array so bail early if one of them does not contain an array. if ( ! is_array( $fonts_family ) || ! is_array( $fonts_subset ) ) { return; } $unique_subsets = array_filter( $fonts_subset ); $google_fonts_url_args = array( 'family' => implode( '|', $fonts_family ), 'subset' => implode( ',', $unique_subsets ), 'display' => 'swap', ); $feature_manager = ET_Builder_Google_Fonts_Feature::instance(); $google_fonts_url = $feature_manager->get_google_fonts_url( $google_fonts_url_args ); $output_inline = $feature_manager->is_option_enabled( 'google_fonts_inline' ); if ( $output_inline ) { $contents = $feature_manager->get( 'google-fonts', function() use ( $feature_manager, $google_fonts_url ) { return $feature_manager->fetch( $google_fonts_url ); }, sanitize_text_field( et_core_esc_previously( $google_fonts_url ) ) ); // if something went wrong fetching the contents. if ( false === $contents ) { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters -- Google fonts api does not have versions wp_enqueue_style( 'et-builder-googlefonts-cached', et_core_esc_previously( $google_fonts_url ), array(), null ); } else { echo '<style id="et-builder-googlefonts-cached-inline">' . $contents . '</style>'; } } else { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters -- Google fonts api does not have versions. wp_enqueue_style( 'et-builder-googlefonts-cached', et_core_esc_previously( $google_fonts_url ), array(), null ); } } add_action( 'wp_enqueue_scripts', 'et_builder_preprint_font' ); if ( ! function_exists( 'et_pb_get_page_custom_css' ) ) : /** * Return page custom style. * * @param int $post_id post id. */ function et_pb_get_page_custom_css( $post_id = 0 ) { $post_id = $post_id ? $post_id : get_the_ID(); $post_type = get_post_type( $post_id ); $overflow = et_pb_overflow(); $page_id = apply_filters( 'et_pb_page_id_custom_css', $post_id ); $exclude_defaults = true; $page_settings = ET_Builder_Settings::get_values( 'page', $page_id, $exclude_defaults ); $selector_prefix = '.et-l--post'; switch ( $post_type ) { case ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE: $selector_prefix = '.et-l--header'; break; case ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE: $selector_prefix = '.et-l--body'; break; case ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE: $selector_prefix = '.et-l--footer'; break; } $wrap_post_id = $page_id; if ( et_theme_builder_is_layout_post_type( $post_type ) ) { $main_post_id = ET_Post_Stack::get_main_post_id(); if ( $main_post_id ) { $wrap_post_id = $main_post_id; } } $wrap_selector = et_pb_is_pagebuilder_used( $wrap_post_id ) && ( et_is_builder_plugin_active() || et_builder_post_is_of_custom_post_type( $wrap_post_id ) ); if ( $wrap_selector ) { $selector_prefix = ' ' . ET_BUILDER_CSS_PREFIX . $selector_prefix; } $output = get_post_meta( $page_id, '_et_pb_custom_css', true ); if ( isset( $page_settings['et_pb_light_text_color'] ) ) { $output .= sprintf( '%2$s .et_pb_bg_layout_dark { color: %1$s !important; }', esc_html( $page_settings['et_pb_light_text_color'] ), esc_html( $selector_prefix ) ); } if ( isset( $page_settings['et_pb_dark_text_color'] ) ) { $output .= sprintf( '%2$s .et_pb_bg_layout_light { color: %1$s !important; }', esc_html( $page_settings['et_pb_dark_text_color'] ), esc_html( $selector_prefix ) ); } if ( isset( $page_settings['et_pb_content_area_background_color'] ) ) { $content_area_bg_selector = et_is_builder_plugin_active() ? $selector_prefix : ' .page.et_pb_pagebuilder_layout #main-content'; $output .= sprintf( '%1$s { background-color: %2$s; }', esc_html( $content_area_bg_selector ), esc_html( $page_settings['et_pb_content_area_background_color'] ) ); } if ( isset( $page_settings['et_pb_section_background_color'] ) ) { $output .= sprintf( '%2$s > .et_builder_inner_content > .et_pb_section { background-color: %1$s; }', esc_html( $page_settings['et_pb_section_background_color'] ), esc_html( $selector_prefix ) ); } $overflow_x = $overflow->get_value_x( $page_settings, '', 'et_pb_' ); $overflow_y = $overflow->get_value_y( $page_settings, '', 'et_pb_' ); if ( ! empty( $overflow_x ) ) { $output .= sprintf( '%2$s .et_builder_inner_content { overflow-x: %1$s; }', esc_html( $overflow_x ), esc_html( $selector_prefix ) ); } if ( ! empty( $overflow_y ) ) { $output .= sprintf( '%2$s .et_builder_inner_content { overflow-y: %1$s; }', esc_html( $overflow_y ), esc_html( $selector_prefix ) ); } if ( isset( $page_settings['et_pb_page_z_index'] ) && '' !== $page_settings['et_pb_page_z_index'] ) { $output .= sprintf( '%2$s .et_builder_inner_content { z-index: %1$s; }', esc_html( $page_settings['et_pb_page_z_index'] ), esc_html( '.et-db #et-boc .et-l' . $selector_prefix ) ); } return apply_filters( 'et_pb_page_custom_css', $output ); } endif; if ( ! function_exists( 'et_pb_video_oembed_data_parse' ) ) : /** * Remove scheme from video url. * * @param string $return The returned oEmbed HTML. * @param object $data A data object result from an oEmbed provider. * @param string $url The URL of the content to be embedded. */ function et_pb_video_oembed_data_parse( $return, $data, $url ) { if ( isset( $data->thumbnail_url ) ) { return esc_url( str_replace( array( 'https://', 'http://' ), '//', $data->thumbnail_url ), array( 'http' ) ); } else { return false; } } endif; if ( ! function_exists( 'et_pb_check_oembed_provider' ) ) : /** * Returns the corresponding oEmbed provider's URL. * * @param string $url oembed url. * * @reurn string */ function et_pb_check_oembed_provider( $url ) { if ( version_compare( $GLOBALS['wp_version'], '5.3', '<' ) ) { require_once ABSPATH . WPINC . '/class-oembed.php'; } else { require_once ABSPATH . WPINC . '/class-wp-oembed.php'; } $oembed = _wp_oembed_get_object(); return $oembed->get_provider( esc_url( $url ), array( 'discover' => false ) ); } endif; if ( ! function_exists( 'et_builder_get_oembed' ) ) : /** * Get cached embedded item on page load. * * Use the item source as the key, so some modules with the same item can share it. * * @since 4.5.2 * * @param string $url Item URL. * @param string $group Item group to set different cache for the same key. * @param boolean $is_cache Whether to use WordPress Object Cache or not. * @return string */ function et_builder_get_oembed( $url, $group = 'video', $is_cache = true ) { $item_src = esc_url( $url ); // Temporarily save embedded item on page load only. Use the item source as the // key, so some modules with the same item can share it. $item_embed = $is_cache ? wp_cache_get( $item_src, $group ) : false; if ( ! $item_embed ) { $item_embed = wp_oembed_get( $item_src ); if ( $is_cache ) { wp_cache_set( $item_src, $item_embed, $group ); } } return apply_filters( 'et_builder_get_oembed', $item_embed, $url, $group, $is_cache ); } endif; if ( ! function_exists( 'et_pb_set_video_oembed_thumbnail_resolution' ) ) : /** * Replace YouTube video thumbnails to high resolution if the high resolution image exists. * * @param string $image_src thumbnail image src. * @param string $resolution thumbnail image resolutions. * * @return string */ function et_pb_set_video_oembed_thumbnail_resolution( $image_src, $resolution = 'default' ) { // Replace YouTube video thumbnails to high resolution if the high resolution image exists. if ( 'high' === $resolution && false !== strpos( $image_src, 'hqdefault.jpg' ) ) { $high_res_image_src = str_replace( 'hqdefault.jpg', 'maxresdefault.jpg', $image_src ); $protocol = is_ssl() ? 'https://' : 'http://'; $processed_image_url = esc_url( str_replace( '//', $protocol, $high_res_image_src ), array( 'http', 'https' ) ); $response = wp_remote_get( $processed_image_url, array( 'timeout' => 30 ) ); // Youtube doesn't guarantee that high res image exists for any video, so we need to check whether it exists and fallback to default image in case of error. if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return $image_src; } return $high_res_image_src; } return $image_src; } endif; /** * Return all registered sidebars. * * @return mixed|void */ function et_builder_get_widget_areas_list() { global $wp_registered_sidebars; $widget_areas = array(); foreach ( $wp_registered_sidebars as $sidebar_key => $sidebar ) { $widget_areas[ $sidebar_key ] = array( 'name' => $sidebar['name'], ); } return apply_filters( 'et_builder_get_widget_areas_list', $widget_areas ); } if ( ! function_exists( 'et_builder_get_widget_areas' ) ) : /** * Return widget areas dropdown html. */ function et_builder_get_widget_areas() { $wp_registered_sidebars = et_builder_get_widget_areas_list(); $et_pb_widgets = get_theme_mod( 'et_pb_widgets' ); $output = '<select name="et_pb_area" id="et_pb_area">'; foreach ( $wp_registered_sidebars as $id => $options ) { $selected = sprintf( '<%%= typeof( et_pb_area ) !== "undefined" && "%1$s" === et_pb_area ? " selected=\'selected\'" : "" %%>', esc_html( $id ) ); $output .= sprintf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $id ), $selected, esc_html( $options['name'] ) ); } $output .= '</select>'; return $output; } endif; if ( ! function_exists( 'et_pb_export_layouts_interface' ) ) : /** * Display 'Manage Categories' button at top in Layout wp admin edit screen. */ function et_pb_export_layouts_interface() { if ( ! current_user_can( 'export' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to export the content of this site.', 'et_builder' ) ); } ?> <a href="<?php echo et_core_esc_wp( admin_url( 'edit-tags.php?taxonomy=layout_category' ) ); ?>" id="et_load_category_page"><?php esc_html_e( 'Manage Categories', 'et_builder' ); ?></a> <a href="<?php echo et_core_esc_wp( admin_url( 'edit-tags.php?taxonomy=layout_tag' ) ); ?>" id="et_load_category_page"><?php esc_html_e( 'Manage Tags', 'et_builder' ); ?></a> <?php echo et_core_esc_previously( et_builder_portability_link( 'et_builder_layouts', array( 'class' => 'et-pb-portability-button' ) ) ); } endif; add_action( 'export_wp', 'et_pb_edit_export_query' ); /** * Add filter for the export query. */ function et_pb_edit_export_query() { add_filter( 'query', 'et_pb_edit_export_query_filter' ); } /** * Export query. * * @param WP_Query $query object. * * @return string|void */ function et_pb_edit_export_query_filter( $query ) { // Apply filter only once. remove_filter( 'query', 'et_pb_edit_export_query_filter' ); et_core_nonce_verified_previously(); // ensure user can export. if ( ! current_user_can( 'export' ) ) { return $query; } global $wpdb; $content = ! empty( $_GET['content'] ) ? sanitize_text_field( $_GET['content'] ) : ''; if ( ET_BUILDER_LAYOUT_POST_TYPE !== $content ) { return $query; } $sql = ''; $i = 0; $possible_types = array( 'layout', 'section', 'row', 'module', 'fullwidth_section', 'specialty_section', 'fullwidth_module', ); foreach ( $possible_types as $template_type ) { $selected_type = 'et_pb_template_' . $template_type; if ( isset( $_GET[ $selected_type ] ) ) { if ( 0 === $i ) { $sql = " AND ( `{$wpdb->term_relationships}`.term_taxonomy_id = %d"; } else { $sql .= " OR `{$wpdb->term_relationships}`.term_taxonomy_id = %d"; } $sql_args[] = (int) $_GET[ $selected_type ]; $i++; } } if ( '' !== $sql ) { $sql .= ' )'; $sql = sprintf( 'SELECT ID FROM %4$s INNER JOIN %3$s ON ( %4$s.ID = %3$s.object_id ) WHERE %4$s.post_type = "%1$s" AND %4$s.post_status != "auto-draft" %2$s', ET_BUILDER_LAYOUT_POST_TYPE, $sql, $wpdb->term_relationships, $wpdb->posts ); $query = $wpdb->prepare( $sql, $sql_args ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Value of the $sql is safely prepared above. } return $query; } /** * Initialize builder metabox in BFB. */ function et_pb_setup_theme() { add_action( 'add_meta_boxes', 'et_pb_add_custom_box', 10, 2 ); add_action( 'add_meta_boxes', 'et_builder_prioritize_meta_box', 999999 ); add_filter( 'hidden_meta_boxes', 'et_pb_hidden_meta_boxes' ); } add_action( 'init', 'et_pb_setup_theme', 11 ); /** * Override metaboxes order to ensure Divi Builder metabox has top position. * * @since 3.17.3 * * @param string $value Custom value. * * @return string */ function et_builder_override_meta_boxes_order( $value ) { static $custom = false; // Store the value on the first call;. $custom = false === $custom ? $value : $custom; return $custom; } /** * Forcefully prioritize the Divi Builder metabox to be at the top. * User drag&drop metabox order customizations are still supported. * Required since not all plugins properly register their metaboxes in the add_meta_boxes hook. * * @since 3.17.2 * * @return void */ function et_builder_prioritize_meta_box() { global $wp_meta_boxes; $screen = get_current_screen(); // Only prioritize Divi Builder metabox if current post type has Divi Builder enabled. if ( ! in_array( $screen->post_type, et_builder_get_enabled_builder_post_types(), true ) ) { return; } // Get custom order. $page = $screen->id; $option_name = "meta-box-order_$page"; $custom = get_user_option( $option_name ); foreach ( $wp_meta_boxes as $page => $contexts ) { foreach ( $contexts as $context => $priorities ) { foreach ( $priorities as $priority => $boxes ) { if ( ! isset( $boxes[ ET_BUILDER_LAYOUT_POST_TYPE ] ) ) { continue; } $divi = $boxes[ ET_BUILDER_LAYOUT_POST_TYPE ]; unset( $boxes[ ET_BUILDER_LAYOUT_POST_TYPE ] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Push "The Divi Builder" metabox at top by updating `$wp_meta_boxes`. $wp_meta_boxes[ $page ][ $context ][ $priority ] = array_merge( array( ET_BUILDER_LAYOUT_POST_TYPE => $divi ), $boxes ); // If our mbox is the first one in custom ordering. if ( is_array( $custom ) && isset( $custom[ $context ] ) && 0 === strpos( $custom[ $context ], ET_BUILDER_LAYOUT_POST_TYPE ) ) { // Find all metaboxes that are not included in custom order. $sorted = explode( ',', $custom[ $context ] ); $add = array_diff( array_keys( $boxes ), $sorted ); if ( $add ) { // Add them after Divi. $custom[ $context ] = implode( ',', array_merge( array( ET_BUILDER_LAYOUT_POST_TYPE ), $add, array_slice( $sorted, 1 ) ) ); // Store the custom value. et_builder_override_meta_boxes_order( $custom ); // and override `get_user_option` so WP will use it. add_filter( "get_user_option_{$option_name}", 'et_builder_override_meta_boxes_order' ); } } } } } } /** * The page builders require the WP Heartbeat script in order to function. We ensure the heartbeat * is loaded with the page builders by scheduling this callback to run right before scripts * are output to the footer. {@see 'admin_enqueue_scripts', 'wp_footer'} */ function et_builder_maybe_ensure_heartbeat_script() { // Don't perform any actions on 'wp_footer' if VB is not active. if ( 'wp_footer' === current_filter() && empty( $_GET['et_fb'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. return; } // We have to check both 'registered' AND 'enqueued' to cover cases where heartbeat has been // de-registered because 'enqueued' will return `true` for a de-registered script at this stage. $heartbeat_okay = wp_script_is( 'heartbeat', 'registered' ) && wp_script_is( 'heartbeat', 'enqueued' ); $autosave_okay = wp_script_is( 'autosave', 'registered' ) && wp_script_is( 'autosave', 'enqueued' ); if ( '1' === et_()->array_get( $_GET, 'et_bfb', '0' ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. // Do not enqueue WP autosave in the BFB iframe because it doesn't include #content / #excerpt / #title // and will result in empty (browser) backups (note: still included in top window). $autosave_okay = true; wp_dequeue_script( 'autosave' ); } if ( $heartbeat_okay && $autosave_okay ) { return; } $suffix = SCRIPT_DEBUG ? '' : '.min'; // phpcs:disable WordPress.WP.EnqueuedResourceParameters -- Version numbers are not set to load latest scripts from the WP Core. if ( ! $heartbeat_okay ) { $heartbeat_src = "/wp-includes/js/heartbeat{$suffix}.js"; // wp-hooks was introduced in WP 5.0. $deps = wp_script_is( 'wp-hooks', 'registered' ) ? array( 'jquery', 'wp-hooks' ) : array( 'jquery' ); wp_enqueue_script( 'heartbeat', $heartbeat_src, $deps, false, true ); wp_localize_script( 'heartbeat', 'heartbeatSettings', apply_filters( 'heartbeat_settings', array() ) ); } if ( ! $autosave_okay ) { $autosave_src = "/wp-includes/js/autosave{$suffix}.js"; wp_enqueue_script( 'autosave', $autosave_src, array( 'heartbeat' ), false, true ); } // phpcs:enable } add_action( 'admin_print_scripts-post-new.php', 'et_builder_maybe_ensure_heartbeat_script', 9 ); add_action( 'admin_print_scripts-post.php', 'et_builder_maybe_ensure_heartbeat_script', 9 ); add_action( 'wp_footer', 'et_builder_maybe_ensure_heartbeat_script', 19 ); /** * Set builder post type. * * @param string $post_type post type. */ function et_builder_set_post_type( $post_type = '' ) { global $et_builder_post_type, $post; $et_builder_post_type = ! empty( $post_type ) ? $post_type : $post->post_type; } /** * Saves Metabox settings. * * @since 3.29.2 Included check to verify if constant exists before use. * Throws error otherwise from PHP7.2.x * * @param int $post_id post id. * @param WP_Post $post object. * * @return int */ function et_pb_metabox_settings_save_details( $post_id, $post ) { global $pagenow; if ( 'post.php' !== $pagenow ) { return $post_id; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } // do not update builder post meta when Preview is loading. if ( isset( $_POST['wp-preview'] ) && 'dopreview' === $_POST['wp-preview'] ) { return $post_id; } $post_type = get_post_type_object( $post->post_type ); if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) ) { return $post_id; } if ( ! isset( $_POST['et_pb_settings_nonce'] ) || ! wp_verify_nonce( $_POST['et_pb_settings_nonce'], basename( __FILE__ ) ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. return $post_id; } if ( isset( $_POST['et_pb_use_builder'] ) ) { $et_pb_use_builder_input = sanitize_text_field( $_POST['et_pb_use_builder'] ); update_post_meta( $post_id, '_et_pb_use_builder', $et_pb_use_builder_input ); if ( ! empty( $_POST['et_builder_version'] ) ) { update_post_meta( $post_id, '_et_builder_version', sanitize_text_field( $_POST['et_builder_version'] ) ); } $et_pb_show_page_creation_input = isset( $_POST['et_pb_show_page_creation'] ) ? sanitize_text_field( $_POST['et_pb_show_page_creation'] ) : false; if ( 'on' === $et_pb_show_page_creation_input ) { // Set page creation flow to on. update_post_meta( $post_id, '_et_pb_show_page_creation', 'on' ); } elseif ( 'off' === $et_pb_show_page_creation_input ) { // Delete page creation flow. delete_post_meta( $post_id, '_et_pb_show_page_creation' ); } elseif ( false === $et_pb_show_page_creation_input && 'on' === $et_pb_use_builder_input ) { $et_pb_show_page_creation_meta = get_post_meta( $post_id, '_et_pb_show_page_creation', true ); // Strip non-printable characters. $post_content_cleaned = preg_replace( '/[\x00-\x1F\x7F]/u', '', $post->post_content ); preg_match_all( '/\[et_pb_section(.*?)?\]\[et_pb_row(.*?)?\]\[et_pb_column(.*?)?\](.+?)\[\/et_pb_column\]\[\/et_pb_row\]\[\/et_pb_section\]/m', $post_content_cleaned, $matches ); if ( isset( $matches[4] ) && ! empty( $matches[4] ) ) { if ( 'on' === $et_pb_show_page_creation_meta ) { // Set page creation flow to on. update_post_meta( $post_id, '_et_pb_show_page_creation', 'off' ); } } else { delete_post_meta( $post_id, '_et_pb_show_page_creation' ); } } if ( 'on' !== $et_pb_use_builder_input ) { if ( defined( 'ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY' ) ) { delete_post_meta( $post_id, ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY ); } } } else { delete_post_meta( $post_id, '_et_pb_use_builder' ); delete_post_meta( $post_id, '_et_builder_version' ); if ( defined( 'ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY' ) ) { delete_post_meta( $post_id, ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY ); } } /** * The et_save_post hook. * * @hooked et_builder_set_product_page_layout_meta - 10 * * @param int $post_id */ do_action( 'et_save_post', $post_id ); // Do not process Page Settings if BFB is enabled. Were saving it via ajax. if ( et_builder_bfb_enabled() ) { // But we still need to save old content. if ( isset( $_POST['et_pb_old_content'] ) ) { update_post_meta( $post_id, '_et_pb_old_content', $_POST['et_pb_old_content'] ); // WooCommerce Modules needs the following hook. /** * Fires after the `_et_pb_old_content` post meta is updated. * * In case you want to over-ride `_et_pb_old_content` content, this is the hook you should use. * * @see et_builder_wc_long_description_metabox_save() * * @since 3.29 * * @param int $post_id Post ID. * $param WP_Post $post The Post. * $param array $_POST Request variables. This could be used for Nonce verification, etc. */ do_action( 'et_pb_old_content_updated', $post_id, $post, $_POST ); } else { delete_post_meta( $post_id, '_et_pb_old_content' ); } return $post_id; } // Only run AB Testing-related update sequence if AB Testing is allowed. if ( et_pb_is_allowed( 'ab_testing' ) ) { // Delete AB Testing settings' autosave. delete_post_meta( $post_id, '_et_pb_use_ab_testing_draft' ); delete_post_meta( $post_id, '_et_pb_ab_subjects_draft' ); if ( isset( $_POST['et_pb_use_ab_testing'] ) && in_array( $_POST['et_pb_use_ab_testing'], array( 'on', 'off' ), true ) ) { update_post_meta( $post_id, '_et_pb_use_ab_testing', sanitize_text_field( $_POST['et_pb_use_ab_testing'] ) ); if ( 'on' === $_POST['et_pb_use_ab_testing'] ) { if ( ! get_post_meta( $post_id, '_et_pb_ab_testing_id', true ) ) { update_post_meta( $post_id, '_et_pb_ab_testing_id', wp_rand() ); } } else { delete_post_meta( $post_id, '_et_pb_ab_testing_id' ); delete_post_meta( $post_id, 'et_pb_subjects_cache' ); et_pb_ab_remove_stats( $post_id ); } } else { delete_post_meta( $post_id, '_et_pb_use_ab_testing' ); delete_post_meta( $post_id, '_et_pb_ab_testing_id' ); } if ( isset( $_POST['et_pb_ab_subjects'] ) && '' !== $_POST['et_pb_ab_subjects'] ) { update_post_meta( $post_id, '_et_pb_ab_subjects', et_prevent_duplicate_item( sanitize_text_field( $_POST['et_pb_ab_subjects'] ), ',' ) ); } else { delete_post_meta( $post_id, '_et_pb_ab_subjects' ); } if ( isset( $_POST['et_pb_ab_goal_module'] ) && '' !== $_POST['et_pb_ab_goal_module'] ) { update_post_meta( $post_id, '_et_pb_ab_goal_module', sanitize_text_field( $_POST['et_pb_ab_goal_module'] ) ); } else { delete_post_meta( $post_id, '_et_pb_ab_goal_module' ); } if ( isset( $_POST['et_pb_ab_stats_refresh_interval'] ) && '' !== $_POST['et_pb_ab_stats_refresh_interval'] ) { update_post_meta( $post_id, '_et_pb_ab_stats_refresh_interval', sanitize_text_field( $_POST['et_pb_ab_stats_refresh_interval'] ) ); } else { delete_post_meta( $post_id, '_et_pb_ab_stats_refresh_interval' ); } } if ( isset( $_POST['et_pb_old_content'] ) ) { update_post_meta( $post_id, '_et_pb_old_content', $_POST['et_pb_old_content'] ); /** * Fires after the `_et_pb_old_content` post meta is updated. * * In case you want to over-ride `_et_pb_old_content` content, this is the hook you should use. * * @see et_builder_wc_long_description_metabox_save() * * @since 3.29 * * @param int $post_id Post ID. * $param WP_Post $post The Post. * $param array $_POST Request variables. This could be used for Nonce verification, etc. */ do_action( 'et_pb_old_content_updated', $post_id, $post, $_POST ); } else { delete_post_meta( $post_id, '_et_pb_old_content' ); } et_builder_update_settings( null, $post_id ); if ( isset( $_POST['et_pb_unsynced_global_attrs'] ) ) { $unsynced_options_array = stripslashes( sanitize_text_field( $_POST['et_pb_unsynced_global_attrs'] ) ); update_post_meta( $post_id, '_et_pb_excluded_global_options', $unsynced_options_array ); } return $post_id; } add_action( 'save_post', 'et_pb_metabox_settings_save_details', 10, 2 ); /** * Set et-saved-post-* cookie and delete et-saving-post-* cookie after post save. * * @param int $post_id Post id. * @param WP_Post $post Object. * * @return mixed */ function et_pb_set_et_saved_cookie( $post_id, $post ) { global $pagenow; if ( 'post.php' !== $pagenow ) { return $post_id; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } $post_type = get_post_type_object( $post->post_type ); if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) ) { return $post_id; } if ( ! isset( $_POST['et_pb_settings_nonce'] ) || ! wp_verify_nonce( $_POST['et_pb_settings_nonce'], basename( __FILE__ ) ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. return $post_id; } // delete. setcookie( 'et-saving-post-' . $post_id . '-bb', 'bb', time() - DAY_IN_SECONDS, SITECOOKIEPATH, false, is_ssl() ); // set. setcookie( 'et-saved-post-' . $post_id . '-bb', 'bb', time() + MINUTE_IN_SECONDS * 5, SITECOOKIEPATH, false, is_ssl() ); } add_action( 'save_post', 'et_pb_set_et_saved_cookie', 10, 2 ); /** * Handling title-less & content-less switching from backend builder to normal editor * * @param int $maybe_empty whether the wp_insert_post content is empty or not. * @param array $postarr all $_POST data that is being passed to wp_insert_post(). * @return int whether wp_insert_post content should be considered empty or not */ function et_pb_ensure_builder_activation_switching( $maybe_empty, $postarr ) { // Consider wp_insert_post() content is not empty if incoming et_pb_use_builder is `off` while currently saved _et_pb_use_builder value is `on`. if ( isset( $postarr['et_pb_use_builder'] ) && 'off' === $postarr['et_pb_use_builder'] && isset( $postarr['post_ID'] ) && et_pb_is_pagebuilder_used( $postarr['post_ID'] ) ) { return false; } return $maybe_empty; } add_filter( 'wp_insert_post_empty_content', 'et_pb_ensure_builder_activation_switching', 10, 2 ); /** * Display buttons before main editor in BFB. * * @param WP_Post $post object. */ function et_pb_before_main_editor( $post ) { if ( ! et_builder_enabled_for_post( $post->ID ) ) { return; } $_et_builder_use_builder = get_post_meta( $post->ID, '_et_pb_use_builder', true ); $is_builder_used = 'on' === $_et_builder_use_builder; $last_builder_version_used = get_post_meta( $post->ID, '_et_builder_version', true ); // Examples: 'BB|Divi|3.0.30' 'VB|Divi|3.0.30'. $_et_builder_use_ab_testing = et_builder_bfb_enabled() ? false : get_post_meta( $post->ID, '_et_pb_use_ab_testing', true ); $_et_builder_ab_stats_refresh_interval = et_builder_bfb_enabled() ? false : et_pb_ab_get_refresh_interval( $post->ID ); $_et_builder_ab_subjects = et_builder_bfb_enabled() ? false : get_post_meta( $post->ID, '_et_pb_ab_subjects', true ); $_et_builder_ab_goal_module = et_builder_bfb_enabled() ? false : et_pb_ab_get_goal_module( $post->ID ); $builder_always_enabled = apply_filters( 'et_builder_always_enabled', false, $post->post_type, $post ); if ( 'et_pb_layout' === $post->post_type ) { // No matter what, in Divi Library we always want the builder. $builder_always_enabled = true; } if ( $builder_always_enabled ) { $is_builder_used = true; $_et_builder_use_builder = 'on'; } // TODO, need to change the output of these buttons if BFB. // Add button only if current user is allowed to use it otherwise display placeholder with all required data. if ( et_pb_is_allowed( 'divi_builder_control' ) ) { $buttons = sprintf( '<a href="#" id="et_pb_toggle_builder" data-builder="%2$s" data-editor="%3$s" class="button button-primary button-large%4$s%5$s">%1$s</a>', ( $is_builder_used ? esc_html__( 'Return To Standard Editor', 'et_builder' ) : esc_html__( 'Use The Divi Builder', 'et_builder' ) ), esc_html__( 'Use The Divi Builder', 'et_builder' ), esc_html__( 'Return To Standard Editor', 'et_builder' ), ( $is_builder_used ? ' et_pb_builder_is_used' : '' ), ( $builder_always_enabled ? ' et_pb_hidden' : '' ) ); // add in the visual builder button only on appropriate post types // also, don't add the button on page if it set as static posts page. if ( et_builder_fb_enabled_for_post( $post->ID ) && et_pb_is_allowed( 'use_visual_builder' ) && ! et_is_extra_library_layout( $post->ID ) && get_option( 'page_for_posts' ) !== $post->ID ) { $buttons .= sprintf( '<a href="%1$s" id="et_pb_fb_cta" class="button button-primary button-large%3$s%4$s">%2$s</a>', esc_url( et_fb_get_vb_url() ), esc_html__( 'Build On The Front End', 'et_builder' ), ( $builder_always_enabled ? ' et-first-child' : '' ), ( et_pb_is_pagebuilder_used( $post->ID ) ? ' et_pb_ready' : '' ) ); } printf( '<div class="et_pb_toggle_builder_wrapper%1$s"%4$s>%2$s</div><div id="et_pb_main_editor_wrap"%3$s>', ( $is_builder_used ? ' et_pb_builder_is_used' : '' ), et_core_esc_previously( $buttons ), ( $is_builder_used ? ' class="et_pb_post_body_hidden"' : '' ), ( et_builder_bfb_enabled() ? ' style="opacity: 0;"' : '' ) ); } else { printf( '<div class="et_pb_toggle_builder_wrapper%2$s"%3$s></div><div id="et_pb_main_editor_wrap"%1$s>', ( $is_builder_used ? ' class="et_pb_post_body_hidden"' : '' ), ( $is_builder_used ? ' et_pb_builder_is_used' : '' ), ( et_builder_bfb_enabled() ? ' style="opacity: 0;"' : '' ) ); } if ( ! et_builder_bfb_enabled() ) { $module_fields_dependencies = wp_json_encode( ET_Builder_Element::get_field_dependencies( $post->post_type ) ); echo et_core_esc_previously( " <script> window.et_pb_module_field_dependencies = JSON.parse( '{$module_fields_dependencies}' ); </script>" ); } ?> <p class="et_pb_page_settings" style="display: none;"> <?php wp_nonce_field( basename( __FILE__ ), 'et_pb_settings_nonce' ); ?> <input type="hidden" id="et_pb_last_post_modified" name="et_pb_last_post_modified" value="<?php echo esc_attr( $post->post_modified ); ?>" /> <input type="hidden" id="et_pb_use_builder" name="et_pb_use_builder" value="<?php echo esc_attr( $_et_builder_use_builder ); ?>" /> <input type="hidden" id="et_builder_version" name="et_builder_version" value="<?php echo esc_attr( $last_builder_version_used ); ?>" /> <input type="hidden" autocomplete="off" id="et_pb_use_ab_testing" name="et_pb_use_ab_testing" value="<?php echo esc_attr( $_et_builder_use_ab_testing ); ?>"> <input type="hidden" autocomplete="off" id="_et_pb_ab_stats_refresh_interval" name="et_pb_ab_stats_refresh_interval" value="<?php echo esc_attr( $_et_builder_ab_stats_refresh_interval ); ?>"> <input type="hidden" autocomplete="off" id="et_pb_ab_subjects" name="et_pb_ab_subjects" value="<?php echo esc_attr( $_et_builder_ab_subjects ); ?>"> <input type="hidden" autocomplete="off" id="et_pb_ab_goal_module" name="et_pb_ab_goal_module" value="<?php echo esc_attr( $_et_builder_ab_goal_module ); ?>"> <?php et_pb_builder_settings_hidden_inputs( $post->ID ); ?> <?php et_pb_builder_global_library_inputs( $post->ID ); ?> <textarea id="et_pb_old_content" name="et_pb_old_content"><?php echo esc_attr( get_post_meta( $post->ID, '_et_pb_old_content', true ) ); ?></textarea> </p> <?php } /** * Add #et_pb_main_editor_wrap closing div. * * @param WP_Post $post object. */ function et_pb_after_main_editor( $post ) { if ( ! et_builder_enabled_for_post( $post->ID ) ) { return; } echo '</div> <!-- #et_pb_main_editor_wrap -->'; } /** * Setup Divi Builder in BFB. */ function et_pb_setup_main_editor() { if ( ! et_core_is_gutenberg_enabled() ) { add_action( 'edit_form_after_title', 'et_pb_before_main_editor' ); add_action( 'edit_form_after_editor', 'et_pb_after_main_editor' ); } } add_action( 'add_meta_boxes', 'et_pb_setup_main_editor', 11 ); /** * Load scripts and styles in admin. * * @param string $hook The current admin page. */ function et_pb_admin_scripts_styles( $hook ) { global $typenow, $pagenow; // load css file for the Divi menu. wp_enqueue_style( 'library-menu-styles', ET_BUILDER_URI . '/styles/library_menu.css', array(), ET_BUILDER_VERSION ); if ( 'widgets.php' === $hook ) { wp_enqueue_script( 'et_pb_widgets_js', ET_BUILDER_URI . '/scripts/ext/widgets.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); $et_pb_options_admin = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'et_admin_load_nonce' => wp_create_nonce( 'et_admin_load_nonce' ), 'widget_info' => sprintf( '<div id="et_pb_widget_area_create"><p>%1$s.</p><p>%2$s.</p><p><label>%3$s <input id="et_pb_new_widget_area_name" value="" /></label><button class="button button-primary et_pb_create_widget_area">%4$s</button></p><p class="et_pb_widget_area_result"></p></div>', esc_html__( 'Here you can create new widget areas for use in the Sidebar module', 'et_builder' ), esc_html__( 'Note: Naming your widget area "sidebar 1", "sidebar 2", "sidebar 3", "sidebar 4" or "sidebar 5" will cause conflicts with this theme', 'et_builder' ), esc_html__( 'Widget Name', 'et_builder' ), esc_html__( 'Create', 'et_builder' ) ), 'delete_string' => esc_html__( 'Delete', 'et_builder' ), ); wp_localize_script( 'et_pb_widgets_js', 'et_pb_options', apply_filters( 'et_pb_options_admin', $et_pb_options_admin ) ); wp_enqueue_style( 'et_pb_widgets_css', ET_BUILDER_URI . '/styles/widgets.css', array(), ET_BUILDER_VERSION ); return; } // Do not enqueue BB assets if GB is active on this page. if ( et_core_is_gutenberg_enabled() ) { return; } if ( ! in_array( $hook, array( 'post-new.php', 'post.php' ), true ) ) { return; } /* * Load the builder javascript and css files for custom post types * custom post types can be added using et_builder_post_types filter */ $post_types = et_builder_get_builder_post_types(); $on_enabled_post_type = isset( $typenow ) && in_array( $typenow, $post_types, true ); $on_enabled_post = isset( $pagenow ) && 'post.php' === $pagenow && isset( $_GET['post'] ) && et_builder_enabled_for_post( intval( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( $on_enabled_post_type || $on_enabled_post ) { wp_enqueue_style( 'et_bb_bfb_common', ET_BUILDER_URI . '/styles/bb_bfb_common.css', array(), ET_BUILDER_VERSION ); // Boot one builders assets or the other. if ( et_builder_bfb_enabled() ) { et_bfb_enqueue_scripts(); // do not load BFB if builder is disabled on page. if ( ! et_pb_is_pagebuilder_used( get_the_ID() ) ) { return; } // BFB loads builder modal outside the iframe using react portal. external scripts // that is used on modal needs to be enqueued. et_builder_enqueue_assets_main(); et_builder_enqueue_open_sans(); $secondary_css_bundles = glob( ET_BUILDER_DIR . 'frontend-builder/build/bundle.*.css' ); if ( $secondary_css_bundles ) { $bundles = array( 'et-frontend-builder' ); foreach ( $secondary_css_bundles as $css_bundle ) { $slug = basename( $css_bundle, '.css' ); $parts = explode( '.', $slug, -1 ); // Drop "bundle" from array. array_shift( $parts ); $slug = implode( '-', $parts ); et_fb_enqueue_bundle( "et-fb-{$slug}", basename( $css_bundle ), $bundles, null ); $bundles[] = $slug; } } // Hooks for theme/plugin specific styling which complements visual builder. do_action( 'et_bfb_boot' ); } else { et_pb_add_builder_page_js_css(); } } } add_action( 'admin_enqueue_scripts', 'et_pb_admin_scripts_styles', 10, 1 ); /** * Disable emoji detection script on edit page which has Backend Builder on it. * WordPress automatically replaces emoji with plain image for backward compatibility * on older browsers. This causes issue when emoji is used on header or other input * text field because (when the modal is saved, shortcode is generated, and emoji * is being replaced with plain image) it creates incorrect attribute markup * such as `title="I <img class="emoji" src="../heart.png" /> WP"` and causes * the whole input text value to be disappeared * * @return void */ function et_pb_remove_emoji_detection_script() { // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. global $pagenow; $disable_emoji_detection = false; // Disable emoji detection script on editing page which has Backend Builder // global $post isn't available at admin_init, so retrieve $post data manually. if ( 'post.php' === $pagenow && isset( $_GET['post'] ) ) { $post_id = (int) $_GET['post']; $post = get_post( $post_id ); if ( is_a( $post, 'WP_POST' ) && et_builder_enabled_for_post( $post->ID ) ) { $disable_emoji_detection = true; } } // Disable emoji detection script on post new page which has Backend Builder. $has_post_type_query = isset( $_GET['post_type'] ); if ( 'post-new.php' === $pagenow && ( ! $has_post_type_query || ( $has_post_type_query && in_array( $_GET['post_type'], et_builder_get_builder_post_types(), true ) ) ) ) { $disable_emoji_detection = true; } if ( $disable_emoji_detection ) { remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); } // phpcs:enable } add_action( 'admin_init', 'et_pb_remove_emoji_detection_script' ); /** * Disable emoji detection script on visual builder * WordPress automatically replaces emoji with plain image for backward compatibility * on older browsers. This causes issue when emoji is used on header or other input * text field because the staticize emoji creates HTML markup which appears to be * invalid on input[type="text"] field such as `title="I <img class="emoji" * src="../heart.png" /> WP"` and causes the input text value to be escaped and * disappeared * * @return void */ function et_fb_remove_emoji_detection_script() { global $post; // Disable emoji detection script on visual builder. React's auto escaping will // remove all staticized emoji when being opened on modal's input field. if ( isset( $post->ID ) && et_fb_is_enabled( $post->ID ) ) { remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); } } add_action( 'wp', 'et_fb_remove_emoji_detection_script' ); /** * If the builder is used for the page, get rid of random p tags. * * @param string $content content. * * @return string|string[]|null */ function et_pb_fix_builder_shortcodes( $content ) { if ( is_admin() ) { // ET_Builder_Element is not loaded in the administration and some plugins call // the_content there (e.g. WP File Manager). return $content; } $is_theme_builder = ET_Builder_Element::is_theme_builder_layout(); $is_singular = is_singular() && 'on' === get_post_meta( get_the_ID(), '_et_pb_use_builder', true ); // if the builder is used for the page, get rid of random p tags. if ( $is_theme_builder || $is_singular ) { $content = et_pb_fix_shortcodes( $content ); } return $content; } add_filter( 'the_content', 'et_pb_fix_builder_shortcodes' ); add_filter( 'et_builder_render_layout', 'et_pb_fix_builder_shortcodes' ); /** * Prepare code module for wpautop. * * @param string $content content. * * @return string|string[]|null */ function et_pb_the_content_prep_code_module_for_wpautop( $content ) { if ( 'on' === get_post_meta( get_the_ID(), '_et_pb_use_builder', true ) ) { $content = et_pb_prep_code_module_for_wpautop( $content ); } return $content; } add_filter( 'the_content', 'et_pb_the_content_prep_code_module_for_wpautop', 0 ); add_filter( 'et_builder_render_layout', 'et_pb_the_content_prep_code_module_for_wpautop', 0 ); if ( ! function_exists( 'et_pb_generate_new_layout_modal' ) ) { /** * Generate the html for "Add new template" Modal in Library. * * @return mixed|void */ function et_pb_generate_new_layout_modal() { $template_type_option_output = ''; $layout_cat_option_output = ''; $layout_cats_list = ''; $layout_tags_list = ''; $template_type_options_list = ''; $new_layout_template_types = array( 'module' => esc_html__( 'Module', 'et_builder' ), 'fullwidth_module' => esc_html__( 'Fullwidth Module', 'et_builder' ), 'row' => esc_html__( 'Row', 'et_builder' ), 'section' => esc_html__( 'Section', 'et_builder' ), 'fullwidth_section' => esc_html__( 'Fullwidth Section', 'et_builder' ), 'specialty_section' => esc_html__( 'Specialty Section', 'et_builder' ), 'layout' => et_builder_i18n( 'Layout' ), ); $template_type_options = apply_filters( 'et_pb_new_layout_template_types', $new_layout_template_types ); // construct output for the template type option. if ( ! empty( $template_type_options ) ) { foreach ( $template_type_options as $option_id => $option_name ) { $template_type_options_list .= sprintf( '<option value="%1$s">%2$s</option>', esc_attr( $option_id ), esc_html( $option_name ) ); } $template_type_option_output = sprintf( '<br><label>%1$s:</label> <select id="new_template_type"> %2$s </select>', esc_html__( 'Layout Type', 'et_builder' ), $template_type_options_list ); } $template_global_option_output = apply_filters( 'et_pb_new_layout_global_option', sprintf( '<label>%1$s<input type="checkbox" value="global" id="et_pb_template_global"></label>', esc_html__( 'Save as Global', 'et_builder' ) ) ); $layout_categories = apply_filters( 'et_pb_new_layout_cats_array', get_terms( 'layout_category', array( 'hide_empty' => false ) ) ); if ( is_array( $layout_categories ) && ! empty( $layout_categories ) ) { foreach ( $layout_categories as $category ) { $layout_cats_list .= sprintf( '<label>%1$s<input type="checkbox" value="%2$s"/></label>', esc_html( $category->name ), esc_attr( $category->term_id ) ); } } $layout_tags = apply_filters( 'et_pb_new_layout_tags_array', get_terms( 'layout_tag', array( 'hide_empty' => false ) ) ); if ( is_array( $layout_tags ) && ! empty( $layout_tags ) ) { foreach ( $layout_tags as $tag ) { $layout_tags_list .= sprintf( '<label>%1$s<input type="checkbox" value="%2$s"/></label>', esc_html( $tag->name ), esc_attr( $tag->term_id ) ); } } // Construct output for the layout Tag option. $layout_cat_option_output = sprintf( '<br><label>%1$s</label> <div class="layout_cats_container"> %3$s </div> <input type="text" value="" id="et_pb_new_cat_name" class="regular-text" placeholder="%2$s">', esc_html__( 'Add To Categories', 'et_builder' ), esc_html__( 'Create new Category', 'et_builder' ), $layout_cats_list ); // Construct output for the layout Tag option. $layout_tag_option_output = sprintf( '<br><label>%1$s</label> <div class="layout_cats_container"> %3$s </div> <input type="text" value="" id="et_pb_new_tag_name" class="regular-text" placeholder="%2$s">', esc_html__( 'Add To Tags', 'et_builder' ), esc_html__( 'Create new Tag', 'et_builder' ), $layout_tags_list ); $output = sprintf( '<div class="et_pb_modal_overlay et_modal_on_top et_pb_new_template_modal"> <div class="et_pb_prompt_modal"> <h2>%1$s</h2> <div class="et_pb_prompt_modal_inside"> <label>%2$s:</label> <input type="text" value="" id="et_pb_new_template_name" class="regular-text"> %6$s %3$s %4$s %5$s %8$s %7$s <input id="et_builder_layout_built_for_post_type" type="hidden" value="page"> </div> <a href="#"" class="et_pb_prompt_dont_proceed et-pb-modal-close"></a> <div class="et_pb_prompt_buttons"> <br> <span class="spinner"></span> <input type="submit" class="et_pb_create_template button-primary et_pb_prompt_proceed"> </div> </div> </div>', esc_html__( 'Add New Layout', 'et_builder' ), esc_html__( 'Layout Name', 'et_builder' ), $template_type_option_output, $template_global_option_output, $layout_cat_option_output, // #5 apply_filters( 'et_pb_new_layout_before_options', '' ), apply_filters( 'et_pb_new_layout_after_options', '' ), $layout_tag_option_output ); return apply_filters( 'et_pb_new_layout_modal_output', $output ); } } if ( ! function_exists( 'et_pb_get_layout_type' ) ) : /** * Get layout type of given post ID. * * @param int $post_id post id. * * @return string|bool */ function et_pb_get_layout_type( $post_id ) { // Get taxonomies. $layout_type_data = wp_get_post_terms( $post_id, 'layout_type' ); if ( empty( $layout_type_data ) ) { return false; } // Pluck name out of taxonomies. $layout_type_array = wp_list_pluck( $layout_type_data, 'name' ); // Logically, a layout only have one layout type. $layout_type = implode( '|', $layout_type_array ); return $layout_type; } endif; if ( ! function_exists( 'et_pb_is_wp_old_version' ) ) : /** * Determine current wp version is less than 4.5. */ function et_pb_is_wp_old_version() { global $wp_version; $wp_major_version = substr( $wp_version, 0, 3 ); if ( version_compare( $wp_major_version, '4.5', '<' ) ) { return true; } return false; } endif; if ( ! function_exists( 'et_builder_theme_or_plugin_updated_cb' ) ) : /** * Delete cached definitions/helpers after theme or plugin update. */ function et_builder_theme_or_plugin_updated_cb() { // Delete cached definitions / helpers. et_fb_delete_builder_assets(); et_update_option( 'et_pb_clear_templates_cache', true ); } add_action( 'after_switch_theme', 'et_builder_theme_or_plugin_updated_cb' ); add_action( 'activated_plugin', 'et_builder_theme_or_plugin_updated_cb', 10, 0 ); add_action( 'deactivated_plugin', 'et_builder_theme_or_plugin_updated_cb', 10, 0 ); add_action( 'upgrader_process_complete', 'et_builder_theme_or_plugin_updated_cb', 10, 0 ); add_action( 'et_support_center_toggle_safe_mode', 'et_builder_theme_or_plugin_updated_cb', 10, 0 ); endif; /** * Enqueue scripts that are required by BFB and Layout Block. These scripts are abstracted into * separated file so Layout Block can enqueue the same sets of scripts without re-register and * re-enqueue them * * @since 4.1.0 */ function et_bfb_enqueue_scripts_dependencies() { global $wp_version, $post; $wp_major_version = substr( $wp_version, 0, 3 ); if ( et_pb_is_pagebuilder_used( get_the_ID() ) ) { wp_enqueue_editor(); } if ( version_compare( $wp_major_version, '4.5', '<' ) ) { $jquery_ui = 'et_pb_admin_date_js'; wp_register_script( $jquery_ui, ET_BUILDER_URI . '/scripts/ext/jquery-ui-1.10.4.custom.min.js', array( 'jquery' ), ET_BUILDER_PRODUCT_VERSION, true ); } else { $jquery_ui = 'jquery-ui-datepicker'; } // Load timepicker script on admin page in case of BFB to make it work with modals loaded on WP admin DOM. wp_enqueue_script( 'et_bfb_admin_date_addon_js', ET_BUILDER_URI . '/scripts/ext/jquery-ui-timepicker-addon.js', array( $jquery_ui ), ET_BUILDER_PRODUCT_VERSION, true ); // Load google maps script on admin page in case of BFB to make it work with modals loaded on WP admin DOM. if ( et_pb_enqueue_google_maps_script() ) { $bfb_google_maps_api_url_args = array( 'key' => et_pb_get_google_api_key(), 'callback' => 'initMap', ); $bfb_google_maps_api_url = add_query_arg( $bfb_google_maps_api_url_args, is_ssl() ? 'https://maps.googleapis.com/maps/api/js' : 'http://maps.googleapis.com/maps/api/js' ); wp_enqueue_script( 'et_bfb_google_maps_api', esc_url( $bfb_google_maps_api_url ), array(), '3', true ); } wp_enqueue_script( 'et_pb_media_library', ET_BUILDER_URI . '/scripts/ext/media-library.js', array( 'media-editor' ), ET_BUILDER_PRODUCT_VERSION, true ); if ( ! wp_script_is( 'wp-hooks', 'registered' ) ) { // Use bundled wp-hooks script when WP < 5.0. wp_enqueue_script( 'wp-hooks', ET_BUILDER_URI . '/frontend-builder/assets/backports/hooks.js', array(), ET_BUILDER_PRODUCT_VERSION, false ); } } if ( ! function_exists( 'et_bfb_enqueue_scripts' ) ) : /** * Register BFB scripts. */ function et_bfb_enqueue_scripts() { global $post; // Enqueue scripts required by BFB. et_bfb_enqueue_scripts_dependencies(); wp_enqueue_script( 'et_bfb_admin_js', ET_BUILDER_URI . '/scripts/bfb_admin_script.js', array( 'jquery', 'et_pb_media_library' ), ET_BUILDER_PRODUCT_VERSION, true ); $bfb_options = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'et_enable_bfb_nonce' => wp_create_nonce( 'et_enable_bfb_nonce' ), 'default_initial_column_type' => apply_filters( 'et_builder_default_initial_column_type', '4_4' ), 'default_initial_text_module' => apply_filters( 'et_builder_default_initial_text_module', 'et_pb_text' ), 'skip_default_content_adding' => apply_filters( 'et_builder_skip_content_activation', false, $post ) ? 'skip' : '', ); wp_localize_script( 'et_bfb_admin_js', 'et_bfb_options', apply_filters( 'et_bfb_options', $bfb_options ) ); // Add filter to register tinyMCE buttons that is missing from BFB. add_filter( 'mce_external_plugins', 'et_bfb_filter_mce_plugin' ); } endif; /** * BFB use built-in WordPress tinyMCE initialization while visual builder uses standalone tinyMCE * initialization which leads to several buttons in VB not available in BFB. This function register * them as plugins * * @since 4.0.9 * * @param array $plugins tinyMCE plugin list. * * @return array */ function et_bfb_filter_mce_plugin( $plugins ) { // NOTE: `ET_FB_ASSETS_URI` constant isn't available yet at this point, so use `ET_BUILDER_URI`. $plugins['table'] = ET_BUILDER_URI . '/frontend-builder/assets/vendors/plugins/table/plugin.min.js'; return $plugins; } /** * Tinymce to load in html mode for BB. * * @param array $settings Array of editor arguments. * @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block' * when called from block editor's Classic block. * * @return mixed */ function et_pb_wp_editor_settings( $settings, $editor_id ) { if ( 'content' === $editor_id ) { $settings['default_editor'] = 'html'; } return $settings; } if ( ! function_exists( 'et_pb_add_builder_page_js_css' ) ) : /** * Load builder js and css. */ function et_pb_add_builder_page_js_css() { global $typenow, $post, $wp_version; // Get WP major version. $wp_major_version = substr( $wp_version, 0, 3 ); // Avoid serving any data from object cache. if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); } // fix tinymce to load in html mode for BB. if ( et_pb_is_pagebuilder_used() ) { add_filter( 'wp_editor_settings', 'et_pb_wp_editor_settings', 10, 2 ); } // BEGIN Process shortcodes (for module settings migrations and Yoast SEO compatibility) // Get list of shortcodes that causes issue if being triggered in admin. $conflicting_shortcodes = et_pb_admin_excluded_shortcodes(); if ( ! empty( $conflicting_shortcodes ) ) { foreach ( $conflicting_shortcodes as $shortcode ) { remove_shortcode( $shortcode ); } } // save the original content of $post variable. $post_original = $post; // get the content for yoast. $post_content_processed = do_shortcode( $post->post_content ); // set the $post to the original content to make sure it wasn't changed by do_shortcode(). $post = $post_original; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- were restoring it to what it was beforea few lines above. // END Process shortcodes. $is_global_template = ''; $post_id = ''; $post_type = $typenow; $selective_sync_status = ''; $global_module_type = ''; $excluded_global_options = array(); $utils = ET_Core_Data_Utils::instance(); $updates_options = get_site_option( 'et_automatic_updates_options', array() ); $et_account = array( 'et_username' => $utils->array_get( $updates_options, 'username', '' ), 'et_api_key' => $utils->array_get( $updates_options, 'api_key', '' ), 'status' => get_site_option( 'et_account_status', 'not_active' ), ); // we need some post data when editing saved templates. if ( 'et_pb_layout' === $typenow ) { $template_scope = wp_get_object_terms( get_the_ID(), 'scope' ); $template_type = wp_get_object_terms( get_the_ID(), 'layout_type' ); $is_global_template = ! empty( $template_scope[0] ) ? $template_scope[0]->slug : 'regular'; $global_module_type = ! empty( $template_type[0] ) ? $template_type[0]->slug : ''; $post_id = get_the_ID(); // Check whether it's a Global item's page and display wp error if Global items disabled for current user. if ( ! et_pb_is_allowed( 'edit_global_library' ) && 'global' === $is_global_template ) { wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) ); } if ( 'global' === $is_global_template ) { $excluded_global_options = get_post_meta( $post_id, '_et_pb_excluded_global_options' ); $selective_sync_status = empty( $excluded_global_options ) ? '' : 'updated'; } $built_for_post_type = get_post_meta( get_the_ID(), '_et_pb_built_for_post_type', true ); $built_for_post_type = '' !== $built_for_post_type ? $built_for_post_type : 'page'; $post_type = apply_filters( 'et_pb_built_for_post_type', $built_for_post_type, get_the_ID() ); } // we need this data to create the filter when adding saved modules. $layout_categories = get_terms( 'layout_category' ); $layout_cat_data = array(); $layout_cat_data_json = ''; if ( is_array( $layout_categories ) && ! empty( $layout_categories ) ) { foreach ( $layout_categories as $category ) { $layout_cat_data[] = array( 'slug' => $category->slug, 'name' => $category->name, ); } } if ( ! empty( $layout_cat_data ) ) { $layout_cat_data_json = wp_json_encode( $layout_cat_data ); } // Set fixed protocol for preview URL to prevent cross origin issue. $preview_scheme = is_ssl() ? 'https' : 'http'; $preview_url = esc_url( home_url( '/' ) ); if ( 'https' === $preview_scheme && ! strpos( $preview_url, 'https://' ) ) { $preview_url = str_replace( 'http://', 'https://', $preview_url ); } // force update cache if et_pb_clear_templates_cache option is set to on. $force_cache_value = et_get_option( 'et_pb_clear_templates_cache', '', '', true ); $force_cache_update = '' !== $force_cache_value ? $force_cache_value : ET_BUILDER_FORCE_CACHE_PURGE; /** * Whether or not the backend builder should clear its Backbone template cache. * * @param bool $force_cache_update */ $force_cache_update = apply_filters( 'et_pb_clear_template_cache', $force_cache_update ); // delete et_pb_clear_templates_cache option it's not needed anymore. et_delete_option( 'et_pb_clear_templates_cache' ); wp_enqueue_script( 'jquery-ui-core' ); wp_enqueue_script( 'underscore' ); wp_enqueue_script( 'backbone' ); if ( et_pb_enqueue_google_maps_script() ) { $google_maps_api_url_args = array( 'v' => 3, 'key' => et_pb_get_google_api_key(), ); $google_maps_api_url = add_query_arg( $google_maps_api_url_args, is_ssl() ? 'https://maps.googleapis.com/maps/api/js' : 'http://maps.googleapis.com/maps/api/js' ); wp_enqueue_script( 'google-maps-api', esc_url_raw( $google_maps_api_url ), array(), '3', true ); } wp_enqueue_script( 'wp-color-picker' ); wp_enqueue_style( 'wp-color-picker' ); if ( version_compare( $wp_major_version, '4.9', '>=' ) ) { wp_enqueue_script( 'wp-color-picker-alpha', ET_BUILDER_URI . '/scripts/ext/wp-color-picker-alpha.min.js', array( 'jquery', 'wp-color-picker' ), ET_BUILDER_VERSION, true ); $color_picker_strings = array( 'legacy_pick' => esc_html__( 'Select', 'et_builder' ), 'legacy_current' => esc_html__( 'Current Color', 'et_builder' ), ); wp_localize_script( 'wp-color-picker-alpha', 'et_pb_color_picker_strings', apply_filters( 'et_pb_color_picker_strings_builder', $color_picker_strings ) ); } else { wp_enqueue_script( 'wp-color-picker-alpha', ET_BUILDER_URI . '/scripts/ext/wp-color-picker-alpha-48.min.js', array( 'jquery', 'wp-color-picker' ), ET_BUILDER_VERSION, true ); } wp_register_script( 'chart', ET_BUILDER_URI . '/scripts/ext/chart.min.js', array(), ET_BUILDER_VERSION, true ); wp_register_script( 'jquery-tablesorter', ET_BUILDER_URI . '/scripts/ext/jquery.tablesorter.min.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); // load 1.10.4 versions of jQuery-ui scripts if WP version is less than 4.5, load 1.11.4 version otherwise. if ( et_pb_is_wp_old_version() ) { $jquery_ui = 'et_pb_admin_date_js'; wp_enqueue_script( $jquery_ui, ET_BUILDER_URI . '/scripts/ext/jquery-ui-1.10.4.custom.min.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); } else { $jquery_ui = 'jquery-ui-datepicker'; } wp_enqueue_script( 'et_pb_admin_date_addon_js', ET_BUILDER_URI . '/scripts/ext/jquery-ui-timepicker-addon.js', array( $jquery_ui ), ET_BUILDER_VERSION, true ); wp_enqueue_script( 'validation', ET_BUILDER_URI . '/scripts/ext/jquery.validate.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); wp_enqueue_script( 'minicolors', ET_BUILDER_URI . '/scripts/ext/jquery.minicolors.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); wp_enqueue_script( 'et_pb_cache_notice_js', ET_BUILDER_URI . '/scripts/cache_notice.js', array( 'jquery', 'et_pb_admin_js' ), ET_BUILDER_VERSION, true ); $pb_notice_options = array( 'product_version' => ET_BUILDER_PRODUCT_VERSION, ); wp_localize_script( 'et_pb_cache_notice_js', 'et_pb_notice_options', apply_filters( 'et_pb_notice_options_builder', $pb_notice_options ) ); wp_enqueue_script( 'lz_string', ET_BUILDER_URI . '/scripts/ext/lz-string.min.js', array(), ET_BUILDER_VERSION, true ); // phpcs:disable WordPress.WP.EnqueuedResourceParameters -- The script version number are specified in the src. No need to set $ver explicitly. wp_enqueue_script( 'es6-promise', '//cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js', array(), null, true ); wp_enqueue_script( 'postmate', '//cdn.jsdelivr.net/npm/postmate@1.1.9/build/postmate.min.js', array( 'es6-promise' ), null, true ); // phpcs:enable wp_enqueue_script( 'et_pb_media_library', ET_BUILDER_URI . '/scripts/ext/media-library.js', array( 'media-editor' ), ET_BUILDER_PRODUCT_VERSION, true ); wp_enqueue_script( 'et_pb_admin_js', ET_BUILDER_URI . '/scripts/builder.js', array( 'jquery', 'jquery-ui-core', 'underscore', 'backbone', 'chart', 'jquery-tablesorter', 'et_pb_media_library', 'lz_string', 'es6-promise' ), ET_BUILDER_VERSION, true ); $saved_gutter_width = get_post_meta( get_the_ID(), '_et_pb_gutter_width', true ); $pb_options = array( 'debug' => defined( 'ET_DEBUG' ) && ET_DEBUG, 'wp_default_editor' => wp_default_editor(), 'et_account' => $et_account, 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'home_url' => home_url(), 'cookie_path' => SITECOOKIEPATH, 'preview_url' => add_query_arg( 'et_pb_preview', 'true', $preview_url ), 'et_admin_load_nonce' => wp_create_nonce( 'et_admin_load_nonce' ), 'images_uri' => ET_BUILDER_URI . '/images', 'postId' => $post->ID, 'post_type' => $post_type, 'is_third_party_post_type' => et_builder_is_post_type_custom( $post_type ) ? 'yes' : 'no', 'et_builder_module_parent_shortcodes' => ET_Builder_Element::get_parent_slugs_regex( $post_type ), 'et_builder_module_child_shortcodes' => ET_Builder_Element::get_child_slugs_regex( $post_type ), 'et_builder_module_raw_content_shortcodes' => ET_Builder_Element::get_raw_content_slugs( $post_type ), 'et_builder_modules' => ET_Builder_Element::get_modules_js_array( $post_type ), 'et_builder_modules_count' => ET_Builder_Element::get_modules_count( $post_type ), 'et_builder_modules_with_children' => ET_Builder_Element::get_slugs_with_children( $post_type ), 'et_builder_modules_featured_image_background' => ET_Builder_Element::get_featured_image_background_modules( $post_type ), 'et_builder_templates_amount' => ET_BUILDER_AJAX_TEMPLATES_AMOUNT, 'et_builder_edit_global_library' => et_pb_is_allowed( 'edit_global_library' ), 'default_initial_column_type' => apply_filters( 'et_builder_default_initial_column_type', '4_4' ), 'default_initial_text_module' => apply_filters( 'et_builder_default_initial_text_module', 'et_pb_text' ), 'section_only_row_dragged_away' => esc_html__( 'The section should have at least one row.', 'et_builder' ), 'fullwidth_module_dragged_away' => esc_html__( 'Fullwidth module can\'t be used outside of the Fullwidth Section.', 'et_builder' ), 'stop_dropping_3_col_row' => esc_html__( "This number of columns can't be used on this row.", 'et_builder' ), 'preview_image' => esc_html__( 'Preview', 'et_builder' ), 'empty_admin_label' => esc_html__( 'Module', 'et_builder' ), 'video_module_image_error' => esc_html__( 'Still images cannot be generated from this video service and/or this video format', 'et_builder' ), 'geocode_error' => esc_html__( 'Geocode was not successful for the following reason', 'et_builder' ), 'geocode_error_2' => esc_html__( 'Geocoder failed due to', 'et_builder' ), 'no_results' => esc_html__( 'No results found', 'et_builder' ), 'all_tab_options_hidden' => esc_html__( 'No available options for this configuration.', 'et_builder' ), 'update_global_module' => esc_html__( 'You\'re about to update global module. This change will be applied to all pages where you use this module. Press OK if you want to update this module', 'et_builder' ), 'global_row_alert' => esc_html__( 'You cannot add global rows into global sections', 'et_builder' ), 'global_module_alert' => esc_html__( 'You cannot add global modules into global sections or rows', 'et_builder' ), 'all_cat_text' => esc_html__( 'All Categories', 'et_builder' ), 'font_name_error' => esc_html__( 'Name Cannot be Empty', 'et_builder' ), 'font_file_error' => esc_html__( 'Please Select Font File', 'et_builder' ), 'font_weight_error' => esc_html__( 'Please Select Font Weight', 'et_builder' ), 'is_global_template' => $is_global_template, 'selective_sync_status' => $selective_sync_status, 'global_module_type' => $global_module_type, 'excluded_global_options' => isset( $excluded_global_options[0] ) ? json_decode( $excluded_global_options[0] ) : array(), 'template_post_id' => $post_id, 'layout_categories' => $layout_cat_data_json, 'map_pin_address_error' => esc_html__( 'Map Pin Address cannot be empty', 'et_builder' ), 'map_pin_address_invalid' => esc_html__( 'Invalid Pin and address data. Please try again.', 'et_builder' ), 'locked_section_permission_alert' => esc_html__( 'You do not have permission to unlock this section.', 'et_builder' ), 'locked_row_permission_alert' => esc_html__( 'You do not have permission to unlock this row.', 'et_builder' ), 'locked_module_permission_alert' => esc_html__( 'You do not have permission to unlock this module.', 'et_builder' ), 'locked_item_permission_alert' => esc_html__( 'You do not have permission to perform this task.', 'et_builder' ), 'localstorage_unavailability_alert' => esc_html__( 'Unable to perform copy/paste process due to inavailability of localStorage feature in your browser. Please use latest modern browser (Chrome, Firefox, or Safari) to perform copy/paste process', 'et_builder' ), 'invalid_color' => esc_html__( 'Invalid Color', 'et_builder' ), 'et_pb_preview_nonce' => wp_create_nonce( 'et_pb_preview_nonce' ), 'is_divi_library' => 'et_pb_layout' === $typenow ? 1 : 0, 'layout_type' => 'et_pb_layout' === $typenow ? et_pb_get_layout_type( get_the_ID() ) : 0, 'is_plugin_used' => et_is_builder_plugin_active(), 'yoast_content' => et_is_yoast_seo_plugin_active() ? $post_content_processed : '', 'ab_db_status' => true === et_pb_db_status_up_to_date() ? 'exists' : 'not_exists', 'ab_testing_builder_nonce' => wp_create_nonce( 'ab_testing_builder_nonce' ), 'page_color_palette' => get_post_meta( get_the_ID(), '_et_pb_color_palette', true ), 'default_color_palette' => implode( '|', et_pb_get_default_color_palette() ), 'page_section_bg_color' => get_post_meta( get_the_ID(), '_et_pb_section_background_color', true ), 'page_gutter_width' => '' !== $saved_gutter_width ? $saved_gutter_width : et_get_option( 'gutter_width', '3' ), 'product_version' => ET_BUILDER_PRODUCT_VERSION, 'active_plugins' => et_builder_get_active_plugins(), 'force_cache_purge' => $force_cache_update ? 'true' : 'false', 'memory_limit_increased' => esc_html__( 'Your memory limit has been increased', 'et_builder' ), 'memory_limit_not_increased' => esc_html__( "Your memory limit can't be changed automatically", 'et_builder' ), 'google_api_key' => et_pb_get_google_api_key(), 'options_page_url' => et_pb_get_options_page_link(), 'et_pb_google_maps_script_notice' => et_pb_enqueue_google_maps_script(), 'select_text' => esc_html__( 'Select', 'et_builder' ), 'et_fb_autosave_nonce' => wp_create_nonce( 'et_fb_autosave_nonce' ), 'et_builder_email_fetch_lists_nonce' => wp_create_nonce( 'et_builder_email_fetch_lists_nonce' ), 'et_builder_email_add_account_nonce' => wp_create_nonce( 'et_builder_email_add_account_nonce' ), 'et_builder_email_remove_account_nonce' => wp_create_nonce( 'et_builder_email_remove_account_nonce' ), 'et_pb_module_settings_migrations' => ET_Builder_Module_Settings_Migration::$migrated, 'acceptable_css_string_values' => et_builder_get_acceptable_css_string_values( 'all' ), 'upload_font_nonce' => wp_create_nonce( 'et_fb_upload_font_nonce' ), 'user_fonts' => et_builder_get_custom_fonts(), 'google_fonts' => et_builder_get_google_fonts(), 'supported_font_weights' => et_builder_get_font_weight_list(), 'supported_font_formats' => et_pb_get_supported_font_formats(), 'all_svg_icons' => et_pb_get_svg_icons_list(), 'library_get_layouts_data_nonce' => wp_create_nonce( 'et_builder_library_get_layouts_data' ), 'library_get_layout_nonce' => wp_create_nonce( 'et_builder_library_get_layout' ), 'library_update_account_nonce' => wp_create_nonce( 'et_builder_library_update_account' ), 'library_custom_tabs' => ET_Builder_Library::builder_library_modal_custom_tabs( $post_type ), ); $pb_options_builder = array_merge( $pb_options, et_pb_history_localization() ); wp_localize_script( 'et_pb_admin_js', 'et_pb_options', apply_filters( 'et_pb_options_builder', $pb_options_builder ) ); $ab_settings = et_builder_ab_labels(); $pb_ab_js_options = array( 'test_id' => $post->ID, 'has_report' => et_pb_ab_has_report( $post->ID ), 'has_permission' => et_pb_is_allowed( 'ab_testing' ), 'refresh_interval_duration' => et_pb_ab_get_refresh_interval_duration( $post->ID ), 'refresh_interval_durations' => et_pb_ab_refresh_interval_durations(), 'analysis_formula' => et_pb_ab_get_analysis_formulas(), 'have_conversions' => et_pb_ab_get_modules_have_conversions(), 'sales_title' => esc_html__( 'Sales', 'et_builder' ), 'force_cache_purge' => $force_cache_update, 'total_title' => esc_html__( 'Total', 'et_builder' ), // Saved data. 'subjects_rank' => ( 'on' === get_post_meta( $post->ID, '_et_pb_use_builder', true ) ) ? et_pb_ab_get_saved_subjects_ranks( $post->ID ) : false, // Rank color. 'subjects_rank_color' => et_pb_ab_get_subject_rank_colors(), // Configuration. 'has_no_permission' => array( 'title' => esc_html__( 'Unauthorized Action', 'et_builder' ), 'desc' => esc_html__( 'You do not have permission to edit the module, row or section in this split test.', 'et_builder' ), ), // AB Testing. 'select_ab_testing_subject' => $ab_settings['select_subject'], 'select_ab_testing_goal' => $ab_settings['select_goal'], 'configure_ab_testing_alternative' => $ab_settings['configure_alternative'], 'select_ab_testing_winner_first' => $ab_settings['select_winner_first'], 'select_ab_testing_subject_first' => $ab_settings['select_subject_first'], 'select_ab_testing_goal_first' => $ab_settings['select_goal_first'], 'cannot_select_subject_parent_as_goal' => $ab_settings['cannot_select_subject_parent_as_goal'], 'cannot_select_global_children_as_subject' => $ab_settings['cannot_select_global_children_as_subject'], 'cannot_select_global_children_as_goal' => $ab_settings['cannot_select_global_children_as_goal'], // Save to Library. 'cannot_save_app_layout_has_ab_testing' => $ab_settings['cannot_save_app_layout_has_ab_testing'], 'cannot_save_section_layout_has_ab_testing' => $ab_settings['cannot_save_section_layout_has_ab_testing'], 'cannot_save_row_layout_has_ab_testing' => $ab_settings['cannot_save_row_layout_has_ab_testing'], 'cannot_save_row_inner_layout_has_ab_testing' => $ab_settings['cannot_save_row_inner_layout_has_ab_testing'], 'cannot_save_module_layout_has_ab_testing' => $ab_settings['cannot_save_module_layout_has_ab_testing'], // Load / Clear Layout. 'cannot_load_layout_has_ab_testing' => $ab_settings['cannot_load_layout_has_ab_testing'], 'cannot_clear_layout_has_ab_testing' => $ab_settings['cannot_clear_layout_has_ab_testing'], // Cannot Import / Export Layout (Portability). 'cannot_import_export_layout_has_ab_testing' => $ab_settings['cannot_import_export_layout_has_ab_testing'], // Moving Goal / Subject. 'cannot_move_module_goal_out_from_subject' => $ab_settings['cannot_move_module_goal_out_from_subject'], 'cannot_move_row_goal_out_from_subject' => $ab_settings['cannot_move_row_goal_out_from_subject'], 'cannot_move_goal_into_subject' => $ab_settings['cannot_move_goal_into_subject'], 'cannot_move_subject_into_goal' => $ab_settings['cannot_move_subject_into_goal'], // Cloning + Has Goal. 'cannot_clone_section_has_goal' => $ab_settings['cannot_clone_section_has_goal'], 'cannot_clone_row_has_goal' => $ab_settings['cannot_clone_row_has_goal'], // Removing + Has Goal. 'cannot_remove_section_has_goal' => $ab_settings['cannot_remove_section_has_goal'], 'cannot_remove_row_has_goal' => $ab_settings['cannot_remove_row_has_goal'], // Removing + Has Unremovable Subjects. 'cannot_remove_section_has_unremovable_subject' => $ab_settings['cannot_remove_section_has_unremovable_subject'], 'cannot_remove_row_has_unremovable_subject' => $ab_settings['cannot_remove_row_has_unremovable_subject'], // View stats summary table heading. 'view_stats_thead_titles' => $ab_settings['view_stats_thead_titles'], ); wp_localize_script( 'et_pb_admin_js', 'et_pb_ab_js_options', apply_filters( 'et_pb_ab_js_options', $pb_ab_js_options ) ); $pb_help_options = array( 'shortcuts' => et_builder_get_shortcuts( 'bb' ), ); wp_localize_script( 'et_pb_admin_js', 'et_pb_help_options', apply_filters( 'et_pb_help_options', $pb_help_options ) ); et_core_load_main_fonts(); wp_enqueue_style( 'et_pb_admin_css', ET_BUILDER_URI . '/styles/style.css', array(), ET_BUILDER_VERSION ); wp_enqueue_style( 'et_pb_admin_date_css', ET_BUILDER_URI . '/styles/jquery-ui-1.12.1.custom.css', array(), ET_BUILDER_VERSION ); wp_add_inline_style( 'et_pb_admin_css', et_pb_ab_get_subject_rank_colors_style() ); ET_Cloud_App::load_js(); } endif; /** * Set et-editor-available-post-* cookie */ function et_pb_set_editor_available_cookie() { $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. $headers_sent = headers_sent(); if ( et_builder_should_load_framework() && is_admin() && ! $headers_sent && ! empty( $post_id ) ) { setcookie( 'et-editor-available-post-' . $post_id . '-bb', 'bb', time() + ( MINUTE_IN_SECONDS * 30 ), SITECOOKIEPATH, false, is_ssl() ); } } add_action( 'admin_init', 'et_pb_set_editor_available_cookie' ); /** * List of history meta. * * @return array History meta. */ function et_pb_history_localization() { return array( 'verb' => array( 'did' => esc_html__( 'Did', 'et_builder' ), 'added' => esc_html__( 'Added', 'et_builder' ), 'edited' => esc_html__( 'Edited', 'et_builder' ), 'removed' => esc_html__( 'Removed', 'et_builder' ), 'moved' => esc_html__( 'Moved', 'et_builder' ), 'expanded' => esc_html__( 'Expanded', 'et_builder' ), 'collapsed' => esc_html__( 'Collapsed', 'et_builder' ), 'locked' => esc_html__( 'Locked', 'et_builder' ), 'unlocked' => esc_html__( 'Unlocked', 'et_builder' ), 'cloned' => esc_html__( 'Cloned', 'et_builder' ), 'cleared' => esc_html__( 'Cleared', 'et_builder' ), 'enabled' => esc_html__( 'Enabled', 'et_builder' ), 'disabled' => esc_html__( 'Disabled', 'et_builder' ), 'copied' => esc_html__( 'Copied', 'et_builder' ), 'reset' => esc_html__( 'Reset', 'et_builder' ), 'cut' => esc_html__( 'Cut', 'et_builder' ), 'pasted' => esc_html__( 'Pasted', 'et_builder' ), 'pasted_styles' => esc_html__( 'Pasted Styles', 'et_builder' ), 'renamed' => esc_html__( 'Renamed', 'et_builder' ), 'loaded' => esc_html__( 'Loaded', 'et_builder' ), 'turnon' => esc_html__( 'Turned On', 'et_builder' ), 'turnoff' => esc_html__( 'Turned Off', 'et_builder' ), 'globalon' => esc_html__( 'Made Global', 'et_builder' ), 'globaloff' => esc_html__( 'Disabled Global', 'et_builder' ), 'configured' => esc_html__( 'Configured', 'et_builder' ), 'find_replace' => esc_html__( 'Find & Replace', 'et_builder' ), 'extend_styles' => esc_html__( 'Extend Styles', 'et_builder' ), 'imported' => esc_html__( 'Imported From Layout', 'et_builder' ), 'presetCreated' => esc_html__( 'Preset Created For', 'et_builder' ), 'presetNameChanged' => esc_html__( 'Preset Name Changed For', 'et_builder' ), 'presetDeleted' => esc_html__( 'Preset Deleted For', 'et_builder' ), 'presetAssignedAsDefault' => esc_html__( 'Preset Assigned As Default For', 'et_builder' ), ), 'noun' => array( 'section' => esc_html__( 'Section', 'et_builder' ), 'saved_section' => esc_html__( 'Saved Section', 'et_builder' ), 'fullwidth_section' => esc_html__( 'Fullwidth Section', 'et_builder' ), 'specialty_section' => esc_html__( 'Specialty Section', 'et_builder' ), 'column' => esc_html__( 'Column', 'et_builder' ), 'row' => esc_html__( 'Row', 'et_builder' ), 'saved_row' => esc_html__( 'Saved Row', 'et_builder' ), 'module' => esc_html__( 'Module', 'et_builder' ), 'saved_module' => esc_html__( 'Saved Module', 'et_builder' ), 'page' => esc_html__( 'Page', 'et_builder' ), 'layout' => et_builder_i18n( 'Layout' ), 'abtesting' => esc_html__( 'Split Testing', 'et_builder' ), 'settings' => esc_html__( 'Settings', 'et_builder' ), ), 'addition' => array( 'phone' => esc_html__( 'on Phone', 'et_builder' ), 'tablet' => esc_html__( 'on Tablet', 'et_builder' ), 'desktop' => esc_html__( 'on Desktop', 'et_builder' ), ), ); } /** * Page Settings Metabox code is included in builder.js which won't be loaded unless BB is. * In such cases (eg BFB or GB are enabled) we provide the mbox js logic in a separate file. * * @return void */ function et_pb_metabox_scripts() { // Only act if `builder.js` isn't enqueued. if ( ! wp_script_is( 'et_pb_admin_js' ) ) { global $typenow; wp_enqueue_script( 'et_page_settings_metabox_js', ET_BUILDER_URI . '/scripts/page-settings-metabox.js', array( 'jquery' ), ET_BUILDER_PRODUCT_VERSION, true ); $pb_options = array( 'post_type' => $typenow, 'is_third_party_post_type' => et_builder_is_post_type_custom( $typenow ) ? 'yes' : 'no', ); wp_localize_script( 'et_page_settings_metabox_js', 'et_pb_options', $pb_options ); } } /** * Prevents the Builder mbox from being hidden. * * @param string[] $hidden all hidden metaboxes. * * @return mixed */ function et_pb_hidden_meta_boxes( $hidden ) { $found = array_search( 'et_pb_layout', $hidden, true ); if ( false !== $found ) { unset( $hidden[ $found ] ); } return $hidden; } /** * Add "The Divi Builder" BB metabox. * * @param string $post_type post type. * @param WP_Post $post post object. */ function et_pb_add_custom_box( $post_type, $post ) { add_action( 'admin_enqueue_scripts', 'et_pb_metabox_scripts', 99 ); // Do not add BB metabox if GB is active on this page. if ( et_core_is_gutenberg_enabled() ) { return; } // Do not add BB metabox if builder is not activate on this page. if ( et_builder_bfb_enabled() && ! et_pb_is_pagebuilder_used( $post->ID ) ) { return; } $post_types = et_builder_get_builder_post_types(); $add = in_array( $post_type, $post_types, true ); if ( ! $add && ! empty( $post ) && et_builder_enabled_for_post( $post->ID ) ) { $add = true; } if ( $add ) { add_meta_box( ET_BUILDER_LAYOUT_POST_TYPE, esc_html__( 'The Divi Builder', 'et_builder' ), 'et_pb_pagebuilder_meta_box', $post_type, 'normal', 'high' ); } } if ( ! function_exists( 'et_pb_get_the_author_posts_link' ) ) : /** * Return a post author link markup. */ function et_pb_get_the_author_posts_link() { global $authordata, $post; // Fallback for preview. if ( empty( $authordata ) && isset( $post->post_author ) ) { $authordata = get_userdata( $post->post_author ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- If $authordata is not set then set it. } // If $authordata is empty, don't continue. if ( empty( $authordata ) ) { return; } $link = sprintf( '<a href="%1$s" title="%2$s" rel="author">%3$s</a>', esc_url( get_author_posts_url( $authordata->ID, $authordata->user_nicename ) ), // translators: post author name. esc_attr( sprintf( __( 'Posts by %s', 'et_builder' ), get_the_author() ) ), get_the_author() ); return apply_filters( 'the_author_posts_link', $link ); } endif; if ( ! function_exists( 'et_pb_get_comments_popup_link' ) ) : /** * Return comments link. * * @param bool|string $zero text to display when 0 comments. * @param bool|string $one text to display when 1 comment. * @param bool|string $more text to display for more than 1 comments. */ function et_pb_get_comments_popup_link( $zero = false, $one = false, $more = false ) { $id = get_the_ID(); $number = get_comments_number( $id ); if ( 0 === $number && ! comments_open() && ! pings_open() ) { return; } if ( $number > 1 ) { // translators: more comments text. $output = str_replace( '%', number_format_i18n( $number ), ( false === $more ) ? __( '% Comments', 'et_builder' ) : $more ); } elseif ( 0 === $number ) { $output = ( false === $zero ) ? __( 'No Comments', 'et_builder' ) : $zero; } else { // must be one. $output = ( false === $one ) ? __( '1 Comment', 'et_builder' ) : $one; } do_action( 'et_builder_before_comments_number' ); $link = '<span class="comments-number"><a href="' . esc_url( get_permalink() . '#respond' ) . '">' . apply_filters( 'comments_number', esc_html( $output ), esc_html( $number ) ) . '</a></span>'; do_action( 'et_builder_after_comments_number' ); return $link; } endif; if ( ! function_exists( 'et_pb_postinfo_meta' ) ) : /** * Return post meta. * * @param string[] $postinfo post info e.g date, author, categories. * @param string $date_format date format. * @param string $comment_zero text to display for 0 comments. * @param string $comment_one text to display for 1 comments. * @param string $comment_more text to display for more comments. */ function et_pb_postinfo_meta( $postinfo, $date_format, $comment_zero, $comment_one, $comment_more ) { $postinfo_meta = array(); if ( in_array( 'author', $postinfo, true ) ) { $postinfo_meta[] = ' ' . esc_html__( 'by', 'et_builder' ) . ' <span class="author vcard">' . et_pb_get_the_author_posts_link() . '</span>'; } if ( in_array( 'date', $postinfo, true ) ) { $postinfo_meta[] = '<span class="published">' . esc_html( get_the_time( $date_format ) ) . '</span>'; } if ( in_array( 'categories', $postinfo, true ) ) { $categories_list = get_the_category_list( ', ' ); // do not output anything if no categories retrieved. if ( '' !== $categories_list ) { $postinfo_meta[] = $categories_list; } } if ( in_array( 'comments', $postinfo, true ) ) { $postinfo_meta[] = et_pb_get_comments_popup_link( $comment_zero, $comment_one, $comment_more ); } return implode( ' | ', array_filter( $postinfo_meta ) ); } endif; if ( ! function_exists( 'et_pb_fix_shortcodes' ) ) { /** * Fix shortcodes? @todo Add function doc. * * @param string $content post content. * @param bool $is_raw_content whether content is row. * * @return string|string[]|null */ function et_pb_fix_shortcodes( $content, $is_raw_content = false ) { // Turn back the "data-et-target-link" attribute as "target" attribte // that has been made before saving the content in "et_fb_process_to_shortcode" function. if ( false !== strpos( $content, 'data-et-target-link=' ) ) { $content = str_replace( ' data-et-target-link=', ' target=', $content ); } if ( $is_raw_content ) { $content = et_builder_replace_code_content_entities( $content ); $content = ET_Builder_Element::convert_smart_quotes_and_amp( $content ); } $slugs = ET_Builder_Element::get_module_slugs_by_post_type(); // The current patterns take care to replace only the shortcodes that extends `ET_Builder_Element` class // In order to avoid cases like this: `[3:45]<br>` // The pattern looks like this `(\[\/?(et_pb_section|et_pb_column|et_pb_row)[^\]]*\])`. $shortcode_pattern = sprintf( '(\[\/?(%s)[^\]]*\])', implode( '|', $slugs ) ); $opening_pattern = '(<br\s*\/?>|<p>|\n)+'; $closing_pattern = '(<br\s*\/?>|<\/p>|\n)+'; $space_pattern = '[\s*|\n]*'; // Replace `]</p>`, `]<br>` `]\n` with `]` // Make sure to remove any closing `</p>` tags or line breaks or new lines after shortcode tag. $pattern_1 = sprintf( '/%1$s%2$s%3$s/', $shortcode_pattern, $space_pattern, $closing_pattern ); // Replace `<p>[`, `<br>[` `\n[` with `[` // Make sure to remove any opening `<p>` tags or line breaks or new lines before shortcode tag. $pattern_2 = sprintf( '/%1$s%2$s%3$s/', $opening_pattern, $space_pattern, $shortcode_pattern ); $content = preg_replace( $pattern_1, '$1', $content ); $content = preg_replace( $pattern_2, '$2', $content ); return $content; } } if ( ! function_exists( 'et_pb_load_global_module' ) ) { /** * Return gloval module content. * * @param integer $global_id layout id. * @param string $row_type row type. * @param string $prev_bg Previous background color. * @param string $next_bg next background color. * * @return string|string[]|null */ function et_pb_load_global_module( $global_id, $row_type = '', $prev_bg = '', $next_bg = '' ) { $global_shortcode = ''; if ( '' !== $global_id ) { $query = new WP_Query( array( 'p' => (int) $global_id, 'post_type' => array( ET_BUILDER_LAYOUT_POST_TYPE, ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE, ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE, ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE, ), ) ); if ( ! empty( $query->post ) ) { // Call the_post() to properly configure post data. Make sure to call the_post() and // wp_reset_postdata() only if the posts result exist to avoid unexpected issues. $query->the_post(); wp_reset_postdata(); $global_shortcode = $query->post->post_content; if ( '' !== $row_type && 'et_pb_row_inner' === $row_type ) { $global_shortcode = str_replace( 'et_pb_row', 'et_pb_row_inner', $global_shortcode ); $global_shortcode = str_replace( 'et_pb_column', 'et_pb_column_inner', $global_shortcode ); } } } // Set provided prev_background_color. if ( ! empty( $prev_bg ) ) { $global_shortcode = preg_replace( '/prev_background_color="(.*?)"/', 'prev_background_color="' . $prev_bg . '"', $global_shortcode, 1 ); } // Set provided next_background_color. if ( ! empty( $next_bg ) ) { $global_shortcode = preg_replace( '/next_background_color="(.*?)"/', 'next_background_color="' . $next_bg . '"', $global_shortcode, 1 ); } return $global_shortcode; } } if ( ! function_exists( 'et_pb_extract_shortcode_content' ) ) { /** * Return the shortcode content. * * @param string $content content. * @param string $shortcode_name shortcode name. * * @return bool|false|string */ function et_pb_extract_shortcode_content( $content, $shortcode_name ) { $start = strpos( $content, ']' ) + 1; $end = strrpos( $content, '[/' . $shortcode_name ); if ( false !== $end ) { $content = substr( $content, $start, $end - $start ); } else { $content = (bool) false; } return $content; } } if ( ! function_exists( 'et_pb_remove_shortcode_content' ) ) { /** * Remove the content part of the shortcode. * * @param string $content content. * @param string $shortcode_name shortcode name. * * @return string|string[] */ function et_pb_remove_shortcode_content( $content, $shortcode_name ) { $shortcode_content = et_pb_extract_shortcode_content( $content, $shortcode_name ); if ( $shortcode_content ) { // Anchor to the ][ brackets around the content so content that appears in // attributes does not get removed as well. return str_replace( ']' . $shortcode_content . '[', '][', $content ); } return $content; } } if ( ! function_exists( 'et_pb_get_global_module_content' ) ) { /** * Return global module content. * * @param string $content content. * @param string $shortcode_name shortcode slug. * @param bool $for_inner_row whether we getting module content for inner row. * * @return bool|false|string|string[]|null */ function et_pb_get_global_module_content( $content, $shortcode_name, $for_inner_row = false ) { /** * Filter list of modules where we don't need to apply autop to the global module content. * * @param array Module slugs list. */ $custom_autop_ignored_modules = apply_filters( 'et_builder_global_modules_ignore_autop', array() ); $custom_autop_ignored_modules = is_array( $custom_autop_ignored_modules ) ? $custom_autop_ignored_modules : array(); $default_autop_ignored_modules = array_merge( array( 'et_pb_code', 'et_pb_fullwidth_code' ), $custom_autop_ignored_modules ); // Do not apply autop to code modules. if ( in_array( $shortcode_name, $default_autop_ignored_modules, true ) ) { return et_pb_extract_shortcode_content( $content, $shortcode_name ); } $original_code_modules = array(); $shortcode_content = et_pb_extract_shortcode_content( $content, $shortcode_name ); // Getting content for Global row when it's turned to inner row in specialty section // Need to make sure it wrapped in et_pb_column_inner, not et_pb_column. if ( $for_inner_row && false === strpos( $shortcode_content, '[et_pb_column_inner' ) ) { $shortcode_content = str_replace( 'et_pb_column', 'et_pb_column_inner', $shortcode_content ); } // Get all the code and fullwidth code modules from content. preg_match_all( '/(\[et_pb(_fullwidth_code|_code).+?\[\/et_pb(_fullwidth_code|_code)\])/s', $shortcode_content, $original_code_modules ); $global_content = et_pb_fix_shortcodes( wpautop( $shortcode_content ) ); // Replace content modified by wpautop for code and fullwidth code modules with original content. if ( ! empty( $original_code_modules ) ) { global $et_pb_global_code_replacements; $et_pb_global_code_replacements = $original_code_modules[0]; $global_content = preg_replace_callback( '/(\[et_pb(_fullwidth_code|_code).+?\[\/et_pb(_fullwidth_code|_code)\])/s', 'et_builder_get_global_code_replacement', $global_content ); } return $global_content; } } if ( ! function_exists( 'et_builder_get_global_code_replacement' ) ) { /** * Retrieve the global code original instance to replace the modified in global code shortcode. * * @param array $matches found matches. * * @return mixed */ function et_builder_get_global_code_replacement( $matches ) { global $et_pb_global_code_replacements; return array_shift( $et_pb_global_code_replacements ); } } if ( ! function_exists( 'et_builder_activate_bfb_auto_draft' ) ) { /** * Force activate post_id which has auto-draft status */ function et_builder_activate_bfb_auto_draft() { et_core_security_check( 'edit_posts', 'et_enable_bfb_nonce' ); $post_id = ! empty( $_POST['et_post_id'] ) ? absint( $_POST['et_post_id'] ) : 0; if ( 0 === $post_id || ! current_user_can( 'edit_post', $post_id ) ) { die(); } // et_builder_activate_bfb_auto_draft() is executed when post title and content empty which means post_status is still lik. ely // to be "auto-draft". "auto-draft" status returns 404 page; thus post status needs to be updated to "draft". wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft', ) ); update_post_meta( $post_id, '_et_pb_use_builder', 'on' ); die(); } } add_action( 'wp_ajax_et_builder_activate_bfb_auto_draft', 'et_builder_activate_bfb_auto_draft' ); if ( ! function_exists( 'et_builder_ajax_toggle_bfb' ) ) { /** * Ajax Callback :: Switch To The New Divi Builder. */ function et_builder_ajax_toggle_bfb() { et_core_security_check( 'manage_options', 'et_builder_toggle_bfb', 'nonce', '_GET' ); $enable = isset( $_GET['enable'] ) && '1' === $_GET['enable']; $redirect = isset( $_GET['redirect'] ) ? esc_url_raw( $_GET['redirect'] ) : ''; if ( empty( $redirect ) && isset( $_SERVER['HTTP_REFERER'] ) ) { $redirect = esc_url_raw( $_SERVER['HTTP_REFERER'] ); } if ( empty( $redirect ) ) { $redirect = esc_url_raw( admin_url( '/' ) ); } et_builder_toggle_bfb( $enable ); set_transient( 'et_builder_show_bfb_welcome_modal', true, 0 ); wp_safe_redirect( $redirect ); exit; } } add_action( 'wp_ajax_et_builder_toggle_bfb', 'et_builder_ajax_toggle_bfb' ); /** * Return font weight select input element html. * * @return string */ function et_generate_font_weight_select_output() { $all_weights = et_builder_get_font_weight_list(); $output = ''; foreach ( $all_weights as $number => $name ) { $output .= sprintf( '<label><input type="checkbox" name="et_font_weight[]" value="%1$s" />%2$s %3$s</label>', esc_attr( $number ), esc_html( $name ), esc_html( $number ) ); } return $output; } /** * Return regular and specialty layouts. * * @return mixed|void */ function et_builder_get_columns() { $columns = array( 'specialty' => array( '1_2,1_2' => array( 'position' => '1,0', 'columns' => '3', ), '1_2,1_2' => array( 'position' => '0,1', 'columns' => '3', ), '1_4,3_4' => array( 'position' => '0,1', 'columns' => '3', ), '3_4,1_4' => array( 'position' => '1,0', 'columns' => '3', ), '1_4,1_4,1_2' => array( 'position' => '0,0,1', 'columns' => '3', ), '1_2,1_4,1_4' => array( 'position' => '1,0,0', 'columns' => '3', ), '1_4,1_2,1_4' => array( 'position' => '0,1,0', 'columns' => '3', ), '1_3,2_3' => array( 'position' => '0,1', 'columns' => '4', ), '2_3,1_3' => array( 'position' => '1,0', 'columns' => '4', ), ), 'regular' => array( '4_4', '1_2,1_2', '1_3,1_3,1_3', '1_4,1_4,1_4,1_4', '1_5,1_5,1_5,1_5,1_5', '1_6,1_6,1_6,1_6,1_6,1_6', '2_5,3_5', '3_5,2_5', '1_3,2_3', '2_3,1_3', '1_4,3_4', '3_4,1_4', '1_4,1_2,1_4', '1_5,3_5,1_5', '1_4,1_4,1_2', '1_2,1_4,1_4', '1_5,1_5,3_5', '3_5,1_5,1_5', '1_6,1_6,1_6,1_2', '1_2,1_6,1_6,1_6', ), ); return apply_filters( 'et_builder_get_columns', $columns ); } /** * Return columns layout. * * @return mixed|void */ function et_builder_get_columns_layout() { $layout_columns = '<% if ( typeof et_pb_specialty !== \'undefined\' && et_pb_specialty === \'on\' ) { %> <li data-layout="1_2,1_2" data-specialty="1,0" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_specialty_column"></div> </li> <li data-layout="1_2,1_2" data-specialty="0,1" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> </li> <li data-layout="1_4,3_4" data-specialty="0,1" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_3_4 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> </li> <li data-layout="3_4,1_4" data-specialty="1,0" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_3_4 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> </li> <li data-layout="1_4,1_2,1_4" data-specialty="0,1,0" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> </li> <li data-layout="1_2,1_4,1_4" data-specialty="1,0,0" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> </li> <li data-layout="1_4,1_4,1_2" data-specialty="0,0,1" data-specialty_columns="3"> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> <div class="et_pb_variation et_pb_variation_1_3"></div> </div> </div> </li> <li data-layout="1_3,2_3" data-specialty="0,1" data-specialty_columns="4"> <div class="et_pb_layout_column et_pb_column_layout_1_3 et_pb_specialty_column"></div> <div class="et_pb_layout_column et_pb_column_layout_2_3 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> </div> </div> </li> <li data-layout="2_3,1_3" data-specialty="1,0" data-specialty_columns="4"> <div class="et_pb_layout_column et_pb_column_layout_2_3 et_pb_variations et_pb_3_variations"> <div class="et_pb_variation et_pb_variation_full"></div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_2"></div> <div class="et_pb_variation et_pb_variation_1_2"></div> </div> <div class="et_pb_variation_row"> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> <div class="et_pb_variation et_pb_variation_1_4"></div> </div> </div> <div class="et_pb_layout_column et_pb_column_layout_1_3 et_pb_specialty_column"></div> </li> <% } else if ( typeof view !== \'undefined\' && typeof view.model.attributes.specialty_columns !== \'undefined\' ) { %> <li data-layout="4_4"> <div class="et_pb_layout_column et_pb_column_layout_fullwidth"></div> </li> <li data-layout="1_2,1_2"> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> </li> <% if ( view.model.attributes.layout === "2_3" ) { %> <li data-layout="1_4,1_4,1_4,1_4"> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> </li> <% } else { %> <li data-layout="1_3,1_3,1_3"> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> </li> <% } %> <% } else { %> <li data-layout="4_4"> <div class="et_pb_layout_column et_pb_column_layout_fullwidth"></div> </li> <li data-layout="1_2,1_2"> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> </li> <li data-layout="1_3,1_3,1_3"> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> </li> <li data-layout="1_4,1_4,1_4,1_4"> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> </li> <li data-layout="1_5,1_5,1_5,1_5,1_5"> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> </li> <li data-layout="1_6,1_6,1_6,1_6,1_6,1_6"> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> </li> <li data-layout="2_5,3_5"> <div class="et_pb_layout_column et_pb_column_layout_2_5"></div> <div class="et_pb_layout_column et_pb_column_layout_3_5"></div> </li> <li data-layout="3_5,2_5"> <div class="et_pb_layout_column et_pb_column_layout_3_5"></div> <div class="et_pb_layout_column et_pb_column_layout_2_5"></div> </li> <li data-layout="1_3,2_3"> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> <div class="et_pb_layout_column et_pb_column_layout_2_3"></div> </li> <li data-layout="2_3,1_3"> <div class="et_pb_layout_column et_pb_column_layout_2_3"></div> <div class="et_pb_layout_column et_pb_column_layout_1_3"></div> </li> <li data-layout="1_4,3_4"> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_3_4"></div> </li> <li data-layout="3_4,1_4"> <div class="et_pb_layout_column et_pb_column_layout_3_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> </li> <li data-layout="1_4,1_2,1_4"> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> </li> <li data-layout="1_5,3_5,1_5"> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_3_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> </li> <li data-layout="1_4,1_4,1_2"> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> </li> <li data-layout="1_2,1_4,1_4"> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> <div class="et_pb_layout_column et_pb_column_layout_1_4"></div> </li> <li data-layout="1_5,1_5,3_5"> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_3_5"></div> </li> <li data-layout="3_5,1_5,1_5"> <div class="et_pb_layout_column et_pb_column_layout_3_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> <div class="et_pb_layout_column et_pb_column_layout_1_5"></div> </li> <li data-layout="1_6,1_6,1_6,1_2"> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> </li> <li data-layout="1_2,1_6,1_6,1_6"> <div class="et_pb_layout_column et_pb_column_layout_1_2"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> <div class="et_pb_layout_column et_pb_column_layout_1_6"></div> </li> <% } %>'; return apply_filters( 'et_builder_layout_columns', $layout_columns ); } /** * Display meta box in admin screen. */ function et_pb_pagebuilder_meta_box() { global $typenow, $post; do_action( 'et_pb_before_page_builder' ); if ( et_builder_bfb_enabled() ) { $new_page_url = false; $is_new_page = false; $edit_page_id = get_the_ID(); $no_rtl_class = is_rtl() && 'on' === et_get_option( 'divi_disable_translations', 'off' ) ? 'et-fb-no-rtl' : ''; // Polylang creates copy of page and BFB should be loaded on page which is not saved yet and cannot be loaded on FE // Therefore load the homepage and replace the content for BFB to make it load with content from other post. if ( 'add' === get_current_screen()->action || (int) get_option( 'page_for_posts' ) === $edit_page_id ) { $new_page_url = get_home_url(); $is_new_page = true; } $bfb_url = et_core_intentionally_unescaped( et_fb_get_bfb_url( $new_page_url, $is_new_page, $edit_page_id ), 'fixed_string' ); // If Admin is SSL but FE is not, we need to fix VB url or it won't work // because trying to load insecure resource. $bfb_url = set_url_scheme( $bfb_url, is_ssl() ? 'https' : 'http' ); // phpcs:disable WordPress.Security.EscapeOutput -- XSS safe. echo " <div class='et_divi_builder et-bfb-page-preloading et_divi_builder_bottom_margin'> <script> var iframe = document.body.appendChild(document.createElement('iframe')); iframe.id = 'et-bfb-app-frame'; document.body.classList.add('et-db'); document.body.classList.add('et-bfb'); if ('' !== '{$no_rtl_class}') { document.body.classList.add('{$no_rtl_class}'); } document.addEventListener('DOMContentLoaded', function() { var outer = jQuery('<div>', {id: 'et-boc', class: 'et-boc'}); var inner = jQuery('<div>', {class: 'et-l'}); var app = jQuery('<div>', {id: 'et-fb-app'}); inner.append(app); outer.append(inner); jQuery('#wpwrap').wrap(outer); jQuery('#et-bfb-app-frame').appendTo('#et_pb_layout .et_divi_builder'); iframe.src = '{$bfb_url}'; // Add first-visible classname to first visible metabox on #normal-sortables jQuery('#et_pb_layout') .parent() .children(':visible') .first() .addClass('first-visible'); }); </script> </div> "; // phpcs:enable return; } $new_builder_url_args = array( 'action' => 'et_builder_toggle_bfb', 'enable' => '1', 'nonce' => wp_create_nonce( 'et_builder_toggle_bfb' ), ); $new_builder_url = add_query_arg( $new_builder_url_args, admin_url( 'admin-ajax.php' ) ); // Disable BFB notification on Extra category builder. BFB support for Extra category builder will be added post inital launch // This option available for admins only. if ( apply_filters( 'et_pb_display_bfb_notification_under_bb', true ) && current_user_can( 'manage_options' ) && et_pb_is_allowed( 'use_visual_builder' ) && et_pb_is_allowed( 'divi_builder_control' ) ) { echo '<div class="et-bfb-optin-cta">'; echo '<p class="et-bfb-optin-cta__message et-bfb-optin-cta__message--warning">'; echo esc_html__( 'A New And Improved Divi Builder Experience Is Available!', 'et_builder' ); echo '<a href="' . esc_url( $new_builder_url ) . '" class="et-bfb-optin-cta__button">'; echo esc_html__( 'Switch To The New Divi Builder', 'et_builder' ); echo '</a>'; echo '</p>'; echo '</div>'; } echo '<div id="et_pb_hidden_editor">'; echo '<div id="et_pb_content_editor">'; $content_editor_settings = array( 'media_buttons' => true, 'tinymce' => array( 'wp_autoresize_on' => true, ), ); wp_editor( '', 'et_pb_content', $content_editor_settings ); echo '</div>'; echo '<div id="et_pb_description_editor">'; $description_editor_settings = array( 'media_buttons' => true, 'tinymce' => array( 'wp_autoresize_on' => true, ), ); wp_editor( '', 'et_pb_description', $description_editor_settings ); echo '</div>'; echo '<div id="et_pb_footer_content_editor">'; $footer_content_editor_settings = array( 'media_buttons' => true, 'tinymce' => array( 'wp_autoresize_on' => true, ), ); wp_editor( '', 'et_pb_footer_content', $footer_content_editor_settings ); echo '</div>'; echo '</div>'; printf( '<div id="et_pb_main_container" class="post-type-%1$s%2$s"></div>', esc_attr( $typenow ), ! et_pb_is_allowed( 'move_module' ) ? ' et-pb-disable-sort' : '' ); $rename_module_menu = et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ) ? sprintf( '<%% if ( this.hasOption( "rename" ) ) { %%> <li><a class="et-pb-right-click-rename" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Rename', 'et_builder' ) ) : ''; $copy_module_menu = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( this.hasOption( "copy" ) ) { %%> <li><a class="et-pb-right-click-copy" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Copy', 'et_builder' ) ) : ''; $paste_after_menu = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( this.hasOption( "paste-after" ) ) { %%> <li><a class="et-pb-right-click-paste-after" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Paste After', 'et_builder' ) ) : ''; $paste_menu_item = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( this.hasOption( "paste-column" ) ) { %%> <li><a class="et-pb-right-click-paste-column" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Paste', 'et_builder' ) ) : ''; $paste_app_menu_item = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( this.hasOption( "paste-app" ) ) { %%> <li><a class="et-pb-right-click-paste-app" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Paste', 'et_builder' ) ) : ''; $save_to_lib_menu = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'save_library' ) ? sprintf( '<%% if ( this.hasOption( "save-to-library") ) { %%> <li><a class="et-pb-right-click-save-to-library" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Save to Library', 'et_builder' ) ) : ''; $lock_unlock_menu = et_pb_is_allowed( 'lock_module' ) ? sprintf( '<%% if ( this.hasOption( "lock" ) ) { %%> <li><a class="et-pb-right-click-lock" href="#"><span class="unlock">%1$s</span><span class="lock">%2$s</span></a></li> <%% } %%>', esc_html__( 'Unlock', 'et_builder' ), esc_html__( 'Lock', 'et_builder' ) ) : ''; $enable_disable_menu = et_pb_is_allowed( 'disable_module' ) ? sprintf( '<%% if ( this.hasOption( "disable" ) ) { %%> <li><a class="et-pb-right-click-disable" href="#"><span class="enable">%1$s</span><span class="disable">%2$s</span></a> <span class="et_pb_disable_on_options"><span class="et_pb_disable_on_option et_pb_disable_on_phone"></span><span class="et_pb_disable_on_option et_pb_disable_on_tablet"></span><span class="et_pb_disable_on_option et_pb_disable_on_desktop"></span></span> </li> <%% } %%>', esc_html__( 'Enable', 'et_builder' ), esc_html__( 'Disable', 'et_builder' ) ) : ''; // Hide AB Testing menu if current post is Divi Library. $is_divi_library = 'et_pb_layout' === $post->post_type; $start_ab_testing_menu = et_pb_is_allowed( 'ab_testing' ) && ! $is_divi_library ? sprintf( '<%% if ( this.hasOption( "start-ab-testing") ) { %%> <li><a class="et-pb-right-click-start-ab-testing" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Split Test', 'et_builder' ) ) : ''; $end_ab_testing_menu = et_pb_is_allowed( 'ab_testing' ) && ! $is_divi_library ? sprintf( '<%% if ( this.hasOption( "end-ab-testing") ) { %%> <li><a class="et-pb-right-click-end-ab-testing" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'End Split Test', 'et_builder' ) ) : ''; $disable_global_menu = et_pb_is_allowed( 'edit_module' ) && et_pb_is_allowed( 'edit_global_library' ) ? sprintf( '<%% if ( this.hasOption( "disable-global") ) { %%> <li><a class="et-pb-right-click-disable-global" href="#">%1$s</a></li> <%% } %%>', esc_html__( 'Disable Global', 'et_builder' ) ) : ''; // Right click options Template. printf( '<script type="text/template" id="et-builder-right-click-controls-template"> <ul class="options"> <%% if ( "module" !== this.options.model.attributes.type || _.contains( %13$s, this.options.model.attributes.module_type ) ) { %%> %1$s %15$s %16$s %17$s %8$s <%% if ( this.hasOption( "undo" ) ) { %%> <li><a class="et-pb-right-click-undo" href="#">%9$s</a></li> <%% } %%> <%% if ( this.hasOption( "redo" ) ) { %%> <li><a class="et-pb-right-click-redo" href="#">%10$s</a></li> <%% } %%> %2$s %3$s <%% if ( this.hasOption( "collapse" ) ) { %%> <li><a class="et-pb-right-click-collapse" href="#"><span class="expand">%4$s</span><span class="collapse">%5$s</span></a></li> <%% } %%> %6$s %7$s %12$s %11$s <%% } %%> <%% if ( this.hasOption( "preview" ) ) { %%> <li><a class="et-pb-right-click-preview" href="#">%14$s</a></li> <%% } %%> </ul> </script>', et_core_esc_previously( $rename_module_menu ), et_core_esc_previously( $enable_disable_menu ), et_core_esc_previously( $lock_unlock_menu ), et_builder_i18n( 'Expand' ), esc_html__( 'Collapse', 'et_builder' ), // #5 et_core_esc_previously( $copy_module_menu ), et_core_esc_previously( $paste_after_menu ), et_core_esc_previously( $save_to_lib_menu ), esc_html__( 'Undo', 'et_builder' ), esc_html__( 'Redo', 'et_builder' ), // #10 et_core_esc_previously( $paste_menu_item ), et_core_esc_previously( $paste_app_menu_item ), et_core_esc_previously( et_pb_allowed_modules_list() ), esc_html__( 'Preview', 'et_builder' ), et_core_esc_previously( $start_ab_testing_menu ), // #15 et_core_esc_previously( $end_ab_testing_menu ), et_core_esc_previously( $disable_global_menu ) ); // "Rename Module Admin Label" Modal Window Template printf( '<script type="text/template" id="et-builder-prompt-modal-rename_admin_label"> <div class="et_pb_prompt_modal"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_attr__( 'Save', 'et_builder' ) ); // "Rename Module Admin Label" Modal Content Template printf( '<script type="text/template" id="et-builder-prompt-modal-rename_admin_label-text"> <h3>%1$s</h3> <p>%2$s</p> <input type="text" value="" id="et_pb_new_admin_label" class="regular-text" /> </script>', esc_html__( 'Rename', 'et_builder' ), esc_html__( 'Enter a new name for this module', 'et_builder' ) ); // Builder's Main Buttons. $save_to_lib_button = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'save_library' ) ? sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-save" title="%1$s"> <span>%2$s</span> </a>', esc_attr__( 'Save to Library', 'et_builder' ), esc_html__( 'Save to Library', 'et_builder' ) ) : ''; $load_from_lib_button = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'load_layout' ) && et_pb_is_allowed( 'add_library' ) && et_pb_is_allowed( 'add_module' ) ? sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-load" title="%1$s"> <span>%2$s</span> </a>', esc_attr__( 'Load From Library', 'et_builder' ), esc_html__( 'Load Layout', 'et_builder' ) ) : ''; $clear_layout_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-clear" title="%1$s"> <span>%2$s</span> </a>', esc_attr__( 'Clear Layout', 'et_builder' ), esc_html__( 'Clear Layout', 'et_builder' ) ) : ''; // Builder's History Buttons. $history_button = sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-history" title="%1$s"> <span class="icon"></span><span class="label">%2$s</span> </a>', esc_attr__( 'See History', 'et_builder' ), esc_html__( 'See History', 'et_builder' ) ); $redo_button = sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-redo" title="%1$s"> <span class="icon"></span><span class="label">%2$s</span> </a>', esc_attr__( 'Redo', 'et_builder' ), esc_html__( 'Redo', 'et_builder' ) ); $undo_button = sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-undo" title="%1$s"> <span class="icon"></span><span class="label">%2$s</span> </a>', esc_attr__( 'Undo', 'et_builder' ), esc_html__( 'Undo', 'et_builder' ) ); // App View Stats Button. $view_ab_stats_button = sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-view-ab-stats" title="%1$s"> <span class="icon"></span><span class="label">%2$s</span> </a>', esc_attr__( 'View Stats', 'et_builder' ), esc_html__( 'View Stats', 'et_builder' ) ); // App Settings Button. $settings_button = sprintf( '<a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-settings" title="%1$s"> <span class="icon"></span><span class="label">%2$s</span> </a>', esc_attr__( 'Settings', 'et_builder' ), esc_html__( 'Settings', 'et_builder' ) ); // App Template. printf( '<script type="text/template" id="et-builder-app-template"> <div id="et_pb_layout_controls"> %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s </div> <div id="et-pb-histories-visualizer-overlay"></div> <ol id="et-pb-histories-visualizer"></ol> </script>', et_core_esc_previously( $save_to_lib_button ), et_core_esc_previously( $load_from_lib_button ), et_core_esc_previously( $clear_layout_button ), et_core_esc_previously( $history_button ), et_core_esc_previously( $redo_button ), et_core_esc_previously( $undo_button ), et_core_esc_previously( $view_ab_stats_button ), et_core_esc_previously( $settings_button ) ); // App Settings Buttons Template. $builder_button_ab_testing_conditional = '( typeof et_pb_ab_goal === "undefined" || et_pb_ab_goal === "off" || typeof et_pb_ab_subject !== "undefined" )'; $is_ab_active = isset( $post->ID ) && 'on' === get_post_meta( $post->ID, '_et_pb_use_ab_testing', true ); $view_stats_active_class = $is_ab_active ? 'active' : ''; $view_stats_button = et_pb_is_allowed( 'ab_testing' ) ? sprintf( '<a href="#" class="et-pb-app-view-ab-stats-button %1$s" title="%2$s"> <span class="icon"> <object type="image/svg+xml" data="%3$s/images/stats.svg"></object> </span> <span class="label">%2$s</span> </a>', esc_attr( $view_stats_active_class ), esc_attr__( 'View Split Testing Stats', 'et_builder' ), esc_url( ET_BUILDER_URI ) ) : ''; $portability_class = 'et-pb-app-portability-button'; if ( $is_ab_active ) { $portability_class .= ' et-core-disabled'; } $page_settings_button = et_pb_is_allowed( 'page_options' ) ? sprintf( '<a href="#" class="et-pb-app-settings-button" title="%1$s"> <span class="icon"> <object type="image/svg+xml" data="%3$s/images/menu.svg"></object> </span> <span class="label">%2$s</span> </a>', esc_attr__( 'Settings', 'et_builder' ), esc_html__( 'Settings', 'et_builder' ), esc_url( ET_BUILDER_URI ) ) : ''; printf( '<script type="text/template" id="et-builder-app-settings-button-template"> %1$s %2$s %3$s </script>', et_core_esc_previously( $page_settings_button ), et_core_esc_previously( et_builder_portability_link( 'et_builder', array( 'class' => $portability_class ) ) ), et_core_esc_previously( $view_stats_button ) ); // do not display settings on global sections if not allowed for current user. $global_settings_logic = ! et_pb_is_allowed( 'edit_global_library' ) ? ' && typeof et_pb_global_module === "undefined"' : ''; $section_settings_button = sprintf( '<%% if ( ( typeof et_pb_template_type === \'undefined\' || \'section\' === et_pb_template_type || \'\' === et_pb_template_type )%3$s ) { %%> <a href="#" class="et-pb-settings et-pb-settings-section" title="%1$s"><span>%2$s</span></a> <%% } %%>', esc_attr__( 'Settings', 'et_builder' ), esc_html__( 'Settings', 'et_builder' ), et_core_esc_previously( $global_settings_logic ) ); $section_clone_button = sprintf( '%3$s <a href="#" class="et-pb-clone et-pb-clone-section" title="%1$s"><span>%2$s</span></a> %4$s', esc_attr__( 'Clone Section', 'et_builder' ), esc_html__( 'Clone Section', 'et_builder' ), '<% if ( ' . et_core_esc_previously( $builder_button_ab_testing_conditional ) . ' ) { %>', '<% } %>' ); $section_remove_button = sprintf( '%3$s <a href="#" class="et-pb-remove et-pb-remove-section" title="%1$s"><span>%2$s</span></a> %4$s', esc_attr__( 'Delete Section', 'et_builder' ), esc_html__( 'Delete Section', 'et_builder' ), '<% if ( ' . et_core_esc_previously( $builder_button_ab_testing_conditional ) . ' ) { %>', '<% } %>' ); $section_unlock_button = sprintf( '<a href="#" class="et-pb-unlock" title="%1$s"><span>%2$s</span></a>', esc_attr__( 'Unlock Section', 'et_builder' ), esc_html__( 'Unlock Section', 'et_builder' ) ); // Section Template. $settings_controls = sprintf( '<div class="et-pb-controls"> %1$s <%% if ( typeof et_pb_template_type === \'undefined\' || ( \'section\' !== et_pb_template_type && \'row\' !== et_pb_template_type && \'module\' !== et_pb_template_type ) ) { %%> %2$s %3$s <%% } %%> <a href="#" class="et-pb-expand" title="%4$s"><span>%5$s</span></a> %6$s </div>', et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ) ? et_core_esc_previously( $section_settings_button ) : '', et_pb_is_allowed( 'add_module' ) ? et_core_esc_previously( $section_clone_button ) : '', et_pb_is_allowed( 'add_module' ) ? et_core_esc_previously( $section_remove_button ) : '', esc_attr__( 'Expand Section', 'et_builder' ), esc_html__( 'Expand Section', 'et_builder' ), et_pb_is_allowed( 'lock_module' ) ? et_core_esc_previously( $section_unlock_button ) : '' ); $settings_controls = apply_filters( 'et_builder_section_settings_controls', $settings_controls ); $add_from_lib_section = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'add_library' ) ? sprintf( '<span class="et-pb-section-add-saved">%1$s</span>', esc_html__( 'Add From Library', 'et_builder' ) ) : ''; $add_standard_section_button = sprintf( '<span class="et-pb-section-add-main">%1$s</span>', esc_html__( 'Standard Section', 'et_builder' ) ); $add_standard_section_button = apply_filters( 'et_builder_add_main_section_button', $add_standard_section_button ); $add_fullwidth_section_button = sprintf( '<span class="et-pb-section-add-fullwidth">%1$s</span>', esc_html__( 'Fullwidth Section', 'et_builder' ) ); $add_fullwidth_section_button = apply_filters( 'et_builder_add_fullwidth_section_button', $add_fullwidth_section_button ); $add_specialty_section_button = sprintf( '<span class="et-pb-section-add-specialty">%1$s</span>', esc_html__( 'Specialty Section', 'et_builder' ) ); $add_specialty_section_button = apply_filters( 'et_builder_add_specialty_section_button', $add_specialty_section_button ); $settings_add_controls = sprintf( '<%% if ( typeof et_pb_template_type === \'undefined\' || ( \'section\' !== et_pb_template_type && \'row\' !== et_pb_template_type && \'module\' !== et_pb_template_type ) ) { %%> <a href="#" class="et-pb-section-add"> %1$s %2$s %3$s %4$s </a> <%% } %%>', et_core_esc_previously( $add_standard_section_button ), et_core_esc_previously( $add_fullwidth_section_button ), et_core_esc_previously( $add_specialty_section_button ), et_core_esc_previously( $add_from_lib_section ) ); $settings_add_controls = et_pb_is_allowed( 'add_module' ) ? apply_filters( 'et_builder_section_add_controls', $settings_add_controls ) : ''; $insert_first_row_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<a href="#" class="et-pb-insert-row"> <span>%1$s</span> </a>', esc_html__( 'Insert Row(s)', 'et_builder' ) ) : ''; $disable_sort_logic = ! et_pb_is_allowed( 'move_module' ) ? ' et-pb-disable-sort' : ''; $disable_global_sort_logic = ! et_pb_is_allowed( 'edit_global_library' ) ? '<%= typeof et_pb_global_module !== \'undefined\' ? \' et-pb-disable-sort\' : \'\' %>' : ''; printf( '<script type="text/template" id="et-builder-section-template"> <div class="et-pb-right-click-trigger-overlay"></div> %1$s <div class="et-pb-section-content et-pb-data-cid%3$s%4$s<%%= typeof et_pb_template_type !== \'undefined\' && \'module\' === et_pb_template_type ? \' et_pb_hide_insert\' : \'\' %%>" data-cid="<%%= cid %%>" data-skip="<%%= typeof( et_pb_skip_module ) === \'undefined\' ? \'false\' : \'true\' %%>"> %5$s </div> %2$s <div class="et-pb-locked-overlay et-pb-locked-overlay-section"></div> <span class="et-pb-section-title"><%%= admin_label.replace( /%%22/g, """ ).replace( /%%91/g, "[" ).replace( /%%93/g, "]" ) %%></span> </script>', et_core_esc_previously( $settings_controls ), et_core_esc_previously( $settings_add_controls ), et_core_intentionally_unescaped( $disable_sort_logic, 'fixed_string' ), et_core_intentionally_unescaped( $disable_global_sort_logic, 'fixed_string' ), et_core_esc_previously( $insert_first_row_button ) ); $row_settings_button = sprintf( '<%% if ( ( typeof et_pb_template_type === \'undefined\' || et_pb_template_type !== \'module\' )%3$s ) { %%> <a href="#" class="et-pb-settings et-pb-settings-row" title="%1$s"><span>%2$s</span></a> <%% } %%>', esc_attr__( 'Settings', 'et_builder' ), esc_html__( 'Settings', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? ' && ( typeof et_pb_global_module === "undefined" || "" === et_pb_global_module ) && ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent )' : '' // do not display settings button on global rows if not allowed for current user. ); $row_clone_button = sprintf( '%3$s <a href="#" class="et-pb-clone et-pb-clone-row" title="%1$s"><span>%2$s</span></a> %4$s', esc_attr__( 'Clone Row', 'et_builder' ), esc_html__( 'Clone Row', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? '<% if ( ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent ) && ' . $builder_button_ab_testing_conditional . ' ) { %>' : '<% if ( ' . $builder_button_ab_testing_conditional . ' ) { %>', // do not display clone button on rows within global sections if not allowed for current user. '<% } %>' ); $row_remove_button = sprintf( '%3$s <a href="#" class="et-pb-remove et-pb-remove-row" title="%1$s"><span>%2$s</span></a> %4$s', esc_attr__( 'Delete Row', 'et_builder' ), esc_html__( 'Delete Row', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? '<% if ( ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent ) && ' . $builder_button_ab_testing_conditional . ') { %>' : '<% if ( ' . $builder_button_ab_testing_conditional . ' ) { %>', // do not display clone button on rows within global sections if not allowed for current user. '<% } %>' ); $row_change_structure_button = sprintf( '%3$s <a href="#" class="et-pb-change-structure" title="%1$s"><span>%2$s</span></a> %4$s', esc_attr__( 'Change Structure', 'et_builder' ), esc_html__( 'Change Structure', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? '<% if ( ( typeof et_pb_global_module === "undefined" || "" === et_pb_global_module ) && ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent ) ) { %>' : '', // do not display change structure button on global rows if not allowed for current user. ! et_pb_is_allowed( 'edit_global_library' ) ? '<% } %>' : '' ); $row_unlock_button = sprintf( '<a href="#" class="et-pb-unlock" title="%1$s"><span>%2$s</span></a>', esc_attr__( 'Unlock Row', 'et_builder' ), esc_html__( 'Unlock Row', 'et_builder' ) ); // Row Template. $settings = sprintf( '<div class="et-pb-controls"> %1$s <%% if ( typeof et_pb_template_type === \'undefined\' || \'section\' === et_pb_template_type ) { %%> %2$s <%% } if ( typeof et_pb_template_type === \'undefined\' || et_pb_template_type !== \'module\' ) { %%> %4$s <%% } if ( typeof et_pb_template_type === \'undefined\' || \'section\' === et_pb_template_type ) { %%> %3$s <%% } %%> <a href="#" class="et-pb-expand" title="%5$s"><span>%6$s</span></a> %7$s </div>', et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ) ? $row_settings_button : '', et_pb_is_allowed( 'add_module' ) ? $row_clone_button : '', et_pb_is_allowed( 'add_module' ) ? $row_remove_button : '', et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ) ? $row_change_structure_button : '', esc_attr__( 'Expand Row', 'et_builder' ), esc_html__( 'Expand Row', 'et_builder' ), et_pb_is_allowed( 'lock_module' ) ? $row_unlock_button : '' ); $settings = apply_filters( 'et_builder_row_settings_controls', $settings ); $row_class = sprintf( 'class="et-pb-row-content et-pb-data-cid%1$s%2$s <%%= typeof et_pb_template_type !== \'undefined\' && \'module\' === et_pb_template_type ? \' et_pb_hide_insert\' : \'\' %%>"', ! et_pb_is_allowed( 'move_module' ) ? ' et-pb-disable-sort' : '', ! et_pb_is_allowed( 'edit_global_library' ) ? sprintf( '<%%= typeof et_pb_global_parent !== \'undefined\' || typeof et_pb_global_module !== \'undefined\' ? \' et-pb-disable-sort\' : \'\' %%>' ) : '' ); $data_skip = 'data-skip="<%= typeof( et_pb_skip_module ) === \'undefined\' ? \'false\' : \'true\' %>"'; $add_row_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( ( typeof et_pb_template_type === \'undefined\' || \'section\' === et_pb_template_type )%2$s ) { %%> <a href="#" class="et-pb-row-add"> <span>%1$s</span> </a> <%% } %%>', esc_html__( 'Add Row', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? ' && typeof et_pb_global_parent === "undefined"' : '' // do not display add row buton on global sections if not allowed for current user. ) : ''; $insert_column_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<a href="#" class="et-pb-insert-column"> <span>%1$s</span> </a>', esc_html__( 'Insert Column(s)', 'et_builder' ) ) : ''; printf( '<script type="text/template" id="et-builder-row-template"> <div class="et-pb-right-click-trigger-overlay"></div> %1$s <div data-cid="<%%= cid %%>" %2$s %3$s> <div class="et-pb-row-container"></div> %4$s </div> %5$s <div class="et-pb-locked-overlay et-pb-locked-overlay-row"></div> <span class="et-pb-row-title"><%%= admin_label.replace( /%%22/g, """ ).replace( /%%91/g, "[" ).replace( /%%93/g, "]" ) %%></span> </script>', et_core_esc_previously( $settings ), et_core_intentionally_unescaped( $row_class, 'fixed_string' ), et_core_intentionally_unescaped( $data_skip, 'fixed_string' ), et_core_esc_previously( $insert_column_button ), et_core_esc_previously( $add_row_button ) ); // Module Block Template. $clone_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( ( typeof et_pb_template_type === \'undefined\' || et_pb_template_type !== \'module\' )%3$s && _.contains(%4$s, module_type) && ' . $builder_button_ab_testing_conditional . ' ) { %%> <a href="#" class="et-pb-clone et-pb-clone-module" title="%1$s"> <span>%2$s</span> </a> <%% } %%>', esc_attr__( 'Clone Module', 'et_builder' ), esc_html__( 'Clone Module', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? ' && ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent )' : '', et_pb_allowed_modules_list() ) : ''; $remove_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<%% if ( ( typeof et_pb_template_type === \'undefined\' || et_pb_template_type !== \'module\' )%3$s && (_.contains(%4$s, module_type) || "removed" === component_status) && ' . $builder_button_ab_testing_conditional . ' ) { %%> <a href="#" class="et-pb-remove et-pb-remove-module" title="%1$s"> <span>%2$s</span> </a> <%% } %%>', esc_attr__( 'Remove Module', 'et_builder' ), esc_html__( 'Remove Module', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? ' && ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent )' : '', et_pb_allowed_modules_list() ) : ''; $unlock_button = et_pb_is_allowed( 'lock_module' ) ? sprintf( '<%% if ( typeof et_pb_template_type === \'undefined\' || et_pb_template_type !== \'module\' ) { %%> <a href="#" class="et-pb-unlock" title="%1$s"> <span>%2$s</span> </a> <%% } %%>', esc_html__( 'Unlock Module', 'et_builder' ), esc_attr__( 'Unlock Module', 'et_builder' ) ) : ''; $settings_button = et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ) ? sprintf( '<%% if (%3$s _.contains( %4$s, module_type ) ) { %%> <a href="#" class="et-pb-settings" title="%1$s"> <span>%2$s</span> </a> <%% } %%>', esc_attr__( 'Module Settings', 'et_builder' ), esc_html__( 'Module Settings', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? ' ( typeof et_pb_global_parent === "undefined" || "" === et_pb_global_parent ) && ( typeof et_pb_global_module === "undefined" || "" === et_pb_global_module ) &&' : '', et_pb_allowed_modules_list() ) : ''; printf( '<script type="text/template" id="et-builder-block-module-template"> %1$s %2$s %3$s %4$s <span class="et-pb-module-title"><%%= admin_label.replace( /%%22/g, """ ).replace( /%%91/g, "[" ).replace( /%%93/g, "]" ) %%></span> </script>', et_core_esc_previously( $settings_button ), et_core_esc_previously( $clone_button ), et_core_esc_previously( $remove_button ), et_core_esc_previously( $unlock_button ) ); // Modal Template. $can_edit_or_has_modal_view_tab = et_pb_is_allowed( 'edit_module' ) && ( et_pb_is_allowed( 'general_settings' ) || et_pb_is_allowed( 'advanced_settings' ) || et_pb_is_allowed( 'custom_css_settings' ) ); $save_exit_button = $can_edit_or_has_modal_view_tab ? sprintf( '<a href="#" class="et-pb-modal-save button button-primary"> <span>%1$s</span> </a>', esc_html__( 'Save & Exit', 'et_builder' ) ) : ''; $save_template_button = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'save_library' ) ? sprintf( '<%% if ( typeof et_pb_template_type === \'undefined\' || \'\' === et_pb_template_type ) { %%> <a href="#" class="et-pb-modal-save-template button"> <span>%1$s</span> </a> <%% } %%>', esc_html__( 'Save & Add To Library', 'et_builder' ) ) : ''; $preview_template_button = sprintf( '<a href="#" class="et-pb-modal-preview-template button"> <span class="icon"></span> <span class="label">%1$s</span> </a>', esc_html__( 'Preview', 'et_builder' ) ); $single_button_class = ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'save_library' ) ? ' et_pb_single_button' : ''; $no_editing_class = $can_edit_or_has_modal_view_tab ? '' : ' et_pb_no_editing'; printf( '<script type="text/template" id="et-builder-modal-template"> <div class="et-pb-modal-container%6$s"> <a href="#" class="et-pb-modal-close"> <span>%1$s</span> </a> <%% if ( ! ( typeof open_view !== \'undefined\' && open_view === \'column_specialty_settings\' ) && typeof type !== \'undefined\' && ( type === \'module\' || type === \'section\' || type === \'row_inner\' || ( type === \'row\' && typeof open_view === \'undefined\' ) ) ) { %%> <div class="et-pb-modal-bottom-container%4$s"> %2$s %5$s %3$s </div> <%% } %%> </div> </script>', et_builder_i18n( 'Cancel' ), et_core_esc_previously( $save_template_button ), et_core_esc_previously( $save_exit_button ), et_core_intentionally_unescaped( $single_button_class, 'fixed_string' ), et_core_esc_previously( $preview_template_button ), et_core_intentionally_unescaped( $no_editing_class, 'fixed_string' ) ); // Column Settings Template. $columns_number = '<% if ( view.model.attributes.specialty_columns === 3 ) { %> 3 <% } else { %> 2 <% } %>'; $data_specialty_columns = sprintf( '<%% if ( typeof view !== \'undefined\' && typeof view.model.attributes.specialty_columns !== \'undefined\' ) { %%> data-specialty_columns="%1$s" <%% } %%>', $columns_number ); $saved_row_tab = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'add_library' ) ? sprintf( '<li class="et-pb-saved-module" data-open_tab="et-pb-saved-modules-tab"> <a href="#">%1$s</a> </li>', esc_html__( 'Add From Library', 'et_builder' ) ) : ''; $saved_row_container = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'add_library' ) ? '<% if ( ( typeof change_structure === \'undefined\' || \'true\' !== change_structure ) && ( typeof et_pb_specialty === \'undefined\' || et_pb_specialty !== \'on\' ) ) { %> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-saved-modules-tab"></div> <% } %>' : ''; printf( '<script type="text/template" id="et-builder-column-settings-template"> <h3 class="et-pb-settings-heading" data-current_row="<%%= cid %%>">%1$s</h3> <%% if ( ( typeof change_structure === \'undefined\' || \'true\' !== change_structure ) && ( typeof et_pb_specialty === \'undefined\' || et_pb_specialty !== \'on\' ) ) { %%> <ul class="et-pb-options-tabs-links et-pb-saved-modules-switcher" %2$s> <li class="et-pb-saved-module et-pb-options-tabs-links-active" data-open_tab="et-pb-new-modules-tab" data-content_loaded="true"> <a href="#">%3$s</a> </li> %4$s </ul> <%% } %%> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-new-modules-tab active-container"> <ul class="et-pb-column-layouts"> %5$s </ul> </div> %6$s </script>', esc_html__( 'Insert Columns', 'et_builder' ), et_core_intentionally_unescaped( $data_specialty_columns, 'fixed_string' ), esc_html__( 'New Row', 'et_builder' ), et_core_esc_previously( $saved_row_tab ), et_core_intentionally_unescaped( et_builder_get_columns_layout(), 'fixed_string' ), et_core_intentionally_unescaped( $saved_row_container, 'fixed_string' ) ); // "Add Module" Template $fullwidth_class = '<% if ( typeof module.fullwidth_only !== \'undefined\' && module.fullwidth_only === \'on\' ) { %> et_pb_fullwidth_only_module<% } %>'; $saved_modules_tab = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'add_library' ) ? sprintf( '<li class="et-pb-saved-module" data-open_tab="et-pb-saved-modules-tab"> <a href="#">%1$s</a> </li>', esc_html__( 'Add From Library', 'et_builder' ) ) : ''; $saved_modules_container = et_pb_is_allowed( 'divi_library' ) && et_pb_is_allowed( 'add_library' ) ? '<div class="et-pb-main-settings et-pb-main-settings-full et-pb-saved-modules-tab"></div>' : ''; printf( '<script type="text/template" id="et-builder-modules-template"> <h3 class="et-pb-settings-heading">%1$s</h3> <ul class="et-pb-options-tabs-links et-pb-saved-modules-switcher"> <li class="et-pb-new-module et-pb-options-tabs-links-active" data-open_tab="et-pb-all-modules-tab"> <a href="#">%2$s</a> </li> %3$s </ul> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-all-modules-tab active-container"> <ul class="et-pb-all-modules"> <%% _.each(modules, function(module) { %%> <%% if ( "et_pb_row" !== module.label && "et_pb_section" !== module.label && "et_pb_column" !== module.label && "et_pb_row_inner" !== module.label && _.contains(%6$s, module.label ) ) { %%> <li class="<%%= module.label %%>%4$s"> <span class="et_module_title"><%%= module.title %%></span> </li> <%% } %%> <%% }); %%> </ul> </div> %5$s </script>', esc_html__( 'Insert Module', 'et_builder' ), esc_html__( 'New Module', 'et_builder' ), et_core_esc_previously( $saved_modules_tab ), et_core_intentionally_unescaped( $fullwidth_class, 'fixed_string' ), et_core_intentionally_unescaped( $saved_modules_container, 'fixed_string' ), et_core_esc_previously( et_pb_allowed_modules_list() ) ); // Load Layout Template. printf( '<script type="text/template" id="et-builder-load_layout-template"> <h3 class="et-pb-settings-heading">%1$s</h3> <%% if ( typeof display_switcher !== \'undefined\' && display_switcher === \'on\' ) { %%> <ul class="et-pb-options-tabs-links et-pb-saved-modules-switcher"> <li class="et-pb-new-module et-pb-options-tabs-links-active" data-open_tab="et-pb-all-modules-tab"> <a href="#">%2$s</a> </li> <li class="et-pb-saved-module" data-open_tab="et-pb-saved-modules-tab"> <a href="#">%3$s</a> </li> <%% if (!_.isEmpty(et_pb_options.library_custom_tabs)) { %%> <%% _.each(et_pb_options.library_custom_tabs, function(tab_name, tab_id) { %%> <li class="et-pb-saved-module" data-open_tab="et-pb-<%%= tab_id %%>-tab" data-custom_tab_id="<%%= tab_id %%>"> <a href="#"><%%= tab_name %%></a> </li> <%% }) %%> <%% } %%> </ul> <%% } %%> <%% if ( typeof display_switcher !== \'undefined\' && display_switcher === \'on\' ) { %%> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-all-modules-tab active-container"><div id="et-cloud-app" class="et-fb-library-container"></div></div> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-saved-modules-tab" style="display: none;"></div> <%% if (!_.isEmpty(et_pb_options.library_custom_tabs)) { %%> <%% _.each(et_pb_options.library_custom_tabs, function(tab_name, tab_id) { %%> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-<%%= tab_id %%>-tab" style="display: none;"></div> <%% }) %%> <%% } %%> <%% } else { %%> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-saved-modules-tab active-container"></div> <%% } %%> </script>', esc_html__( 'Load Layout', 'et_builder' ), esc_html__( 'Premade Layouts', 'et_builder' ), esc_html__( 'Your Saved Layouts', 'et_builder' ) ); // Library Account Status Error. $root_directory = defined( 'ET_BUILDER_PLUGIN_ACTIVE' ) ? ET_BUILDER_PLUGIN_DIR : get_template_directory(); $library_i18n = require $root_directory . '/cloud/i18n/library.php'; printf( ' <script type="text/template" id="et-builder-library-account-status-error-template"> <div class="et-pb-library-account-status-error"> <%% if ( expired ) { %%> <h2>%1$s</h2> <p>%2$s</p> <%% } else { %%> <h2>%3$s</h2> <p>%4$s</p> <div class="et-pb-option et-pb-option--text"> <label for="et_username">%5$s</label> <div class="et-pb-option-container et-pb-option-container--text"> <input id="et_username" type="text" class="regular-text" value="" /> <p class="description">%6$s</p> </div> </div> <div class="et-pb-option et-pb-option--text"> <label for="et_api_key">%7$s</label> <div class="et-pb-option-container et-pb-option-container--text"> <input id="et_api_key" type="text" class="regular-text" value="" /> <p class="description">%8$s</p> </div> </div> <div class="et-pb-option-container"> <a href="#" class="button">%9$s</a> </div> <%% } %%> </div> </script>', et_core_esc_previously( $library_i18n['Uh Oh!'] ), et_core_esc_previously( $library_i18n['$expiredAccount'] ), et_core_esc_previously( $library_i18n['Authentication Required'] ), et_core_esc_previously( $library_i18n['$noAccount'] ), et_core_esc_previously( $library_i18n['Username'] ), et_core_esc_previously( $library_i18n['$usernameHelp'] ), et_core_esc_previously( $library_i18n['API Key'] ), et_core_esc_previously( $library_i18n['$apiKeyHelp'] ), et_core_esc_previously( $library_i18n['Submit'] ) ); // Library Back Button. echo ' <script type="text/template" id="et-builder-library-back-button-template"> <div class="et-pb-library-back-button" aria-role="button" aria-label="Back To Layouts List"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet"shapeRendering="geometricPrecision"> <g> <path d="M14.988 10.963h-3v-2.52a.393.393 0 0 0-.63-.361l-5.2 4.5a.491.491 0 0 0 0 .72l5.2 4.5a.393.393 0 0 0 .63-.36v-2.52h2.99a2.992 2.992 0 0 1 2.99 2.972v1.287a.7.7 0 0 0 .7.694h2.59a.7.7 0 0 0 .7-.694v-1.3a6.948 6.948 0 0 0-6.97-6.918z" fillRule="evenodd" /> </g> </svg> </div> </script> '; $insert_module_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '%2$s <a href="#" class="et-pb-insert-module<%%= typeof et_pb_template_type === \'undefined\' || \'module\' !== et_pb_template_type ? \'\' : \' et_pb_hidden_button\' %%>"> <span>%1$s</span> </a> %3$s', esc_html__( 'Insert Module(s)', 'et_builder' ), ! et_pb_is_allowed( 'edit_global_library' ) ? '<% if ( typeof et_pb_global_parent === "undefined" ) { %>' : '', ! et_pb_is_allowed( 'edit_global_library' ) ? '<% } %>' : '' ) : ''; // Column Template. printf( '<script type="text/template" id="et-builder-column-template"> %1$s </script>', et_core_esc_previously( $insert_module_button ) ); // Insert Row(s). $insert_row_button = et_pb_is_allowed( 'add_module' ) ? sprintf( '<a href="#" class="et-pb-insert-row"> <span>%1$s</span> </a>', esc_html__( 'Insert Row(s)', 'et_builder' ) ) : ''; // Insert Row Template. printf( '<script type="text/template" id="et-builder-specialty-column-template"> %1$s </script>', et_core_esc_previously( $insert_row_button ) ); // Advanced Settings Buttons Module. printf( '<script type="text/template" id="et-builder-advanced-setting"> <%% if ( \'et_pb_column\' !== module_type && \'et_pb_column_inner\' !== module_type ) { %%> <a href="#" class="et-pb-advanced-setting-remove"> <span>%1$s</span> </a> <%% } %%> <a href="#" class="et-pb-advanced-setting-options"> <span>%2$s</span> </a> <%% if ( \'et_pb_column\' !== module_type && \'et_pb_column_inner\' !== module_type ) { %%> <a href="#" class="et-pb-clone et-pb-advanced-setting-clone"> <span>%3$s</span> </a> <%% } %%> </script>', esc_html__( 'Delete', 'et_builder' ), esc_html__( 'Settings', 'et_builder' ), esc_html__( 'Clone Module', 'et_builder' ) ); // Advanced Settings Modal Buttons Template. printf( '<script type="text/template" id="et-builder-advanced-setting-edit"> <div class="et-pb-modal-container"> <a href="#" class="et-pb-modal-close"> <span>%1$s</span> </a> <div class="et-pb-modal-bottom-container"> <a href="#" class="et-pb-modal-save"> <span>%2$s</span> </a> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_html__( 'Save', 'et_builder' ) ); // "Deactivate Builder" Modal Message Template printf( '<script type="text/template" id="et-builder-prompt-modal-deactivate_builder-text"> <h3>%1$s</h3> <p>%2$s</p> <p>%3$s</p> </script>', esc_html__( 'Disable Builder', 'et_builder' ), esc_html__( 'All content created in the Divi Builder will be lost. Previous content will be restored.', 'et_builder' ), esc_html__( 'Do you wish to proceed?', 'et_builder' ) ); // "Clear Layout" Modal Window Template printf( '<script type="text/template" id="et-builder-prompt-modal-clear_layout-text"> <h3>%1$s</h3> <p>%2$s</p> <p>%3$s</p> </script>', esc_html__( 'Clear Layout', 'et_builder' ), esc_html__( 'All of your current page content will be lost.', 'et_builder' ), esc_html__( 'Do you wish to proceed?', 'et_builder' ) ); // "Reset Advanced Settings" Modal Template printf( '<script type="text/template" id="et-builder-prompt-modal-reset_advanced_settings-text"> <p>%1$s</p> <p>%2$s</p> </script>', esc_html__( 'All advanced module settings in will be lost.', 'et_builder' ), esc_html__( 'Do you wish to proceed?', 'et_builder' ) ); // "Save Layout" Modal Window Template printf( '<script type="text/template" id="et-builder-prompt-modal-save_layout"> <div class="et_pb_prompt_modal"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_html__( 'Save', 'et_builder' ) ); // "Save Layout" Modal Content Template printf( '<script type="text/template" id="et-builder-prompt-modal-save_layout-text"> <h3>%1$s</h3> <p>%2$s</p> <label>%3$s</label> <input type="text" value="" id="et_pb_new_layout_name" class="regular-text" /> </script>', esc_html__( 'Save To Library', 'et_builder' ), esc_html__( 'Save your current page to the Divi Library for later use.', 'et_builder' ), esc_html__( 'Layout Name', 'et_builder' ) ); // "Delete Font" Modal Text printf( '<script type="text/template" id="et-builder-prompt-modal-delete_font-text"> <h3>%1$s</h3> <p>%2$s</p> </script>', esc_html__( 'Delete Font', 'et_builder' ), sprintf( '%1$s %2$s?', esc_html__( 'Are you sure want to delete', 'et_builder' ), '<%= font_name %>' ) ); // "Upload Font" Modal Template printf( '<script type="text/template" id="et-builder-prompt-modal-upload_font"> <div class="et_pb_prompt_modal et-pb-font-uploader"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_html__( 'Upload', 'et_builder' ) ); // "Upload Font" Modal Text printf( '<script type="text/template" id="et-builder-prompt-modal-upload_font-text"> <h3>%1$s</h3> <div class="et-font-uploader-content"> <div class="et-font-uploader-error"></div> <h4>%2$s</h4> <input type="text" value="" id="et-font-uploader-name" class="regular-text" /> <form class="et-core-portability-import-form"> <span class="et-core-portability-import-placeholder">%3$s</span> <button class="et-core-button">%4$s</button> <input type="file" accept=".ttf, .otf, .eot, .woff2, .woff" multiple> <div class="et-core-clearfix"></div> <p class="et-font-uploader-hint">%7$s: ttf, otf</p> <div class="et-font-uploader-selected-fonts et-font-uploader-hidden-field"><h4>%8$s</h4></div> <h4>%5$s</h4> <p class="et-font-uploader-hint">%9$s</p> <label><input type="checkbox" name="et-font-uploader-all-weight" class="et-font-uploader-all-weights" checked>%6$s</label> <div class="et-font-uploader-weight-values et-font-uploader-hidden-section"> %10$s </div> </form> </div> </script>', esc_html__( 'Upload Font', 'et_builder' ), esc_html__( 'Font Name', 'et_builder' ), esc_html__( 'Drag Files Here', 'et_builder' ), esc_html__( 'Choose Font Files', 'et_builder' ), esc_html__( 'Supported Font Weights', 'et_builder' ), esc_html__( 'All', 'et_builder' ), esc_html__( 'Supported File Formats', 'et_builder' ), esc_html__( 'Selected Font Files', 'et_builder' ), esc_html__( 'Choose the font weights supported by your font. Select "All" if you don\'t know this information or if your font includes all weights.', 'et_builder' ), et_core_esc_previously( et_generate_font_weight_select_output() ) ); // "Save Template" Modal Window Layout printf( '<script type="text/template" id="et-builder-prompt-modal-save_template"> <div class="et_pb_prompt_modal et_pb_prompt_modal_save_library"> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%1$s" /> </div> </div> </script>', esc_attr__( 'Save And Add To Library', 'et_builder' ) ); // "Save Template" Content Layout $layout_categories = get_terms( 'layout_category', array( 'hide_empty' => false ) ); $categories_output = sprintf( '<div class="et-pb-option"><label>%1$s</label>', esc_html__( 'Add To Categories', 'et_builder' ) ); if ( is_array( $layout_categories ) && ! empty( $layout_categories ) ) { $categories_output .= '<div class="et-pb-option-container layout_cats_container">'; foreach ( $layout_categories as $category ) { $categories_output .= sprintf( '<label>%1$s<input type="checkbox" value="%2$s"/></label>', esc_html( $category->name ), esc_attr( $category->term_id ) ); } $categories_output .= '</div></div>'; } $categories_output .= sprintf( ' <div class="et-pb-option"> <label>%1$s:</label> <div class="et-pb-option-container"> <input type="text" value="" id="et_pb_new_cat_name" class="regular-text" /> </div> </div>', esc_html__( 'Create New Category', 'et_builder' ) ); $general_checkbox = sprintf( '<label> %1$s <input type="checkbox" value="general" id="et_pb_template_general" checked /> </label>', esc_html__( 'Include General settings', 'et_builder' ) ); $advanced_checkbox = sprintf( '<label> %1$s <input type="checkbox" value="advanced" id="et_pb_template_advanced" checked /> </label>', esc_html__( 'Include Advanced Design settings', 'et_builder' ) ); $css_checkbox = sprintf( '<label> %1$s <input type="checkbox" value="css" id="et_pb_template_css" checked /> </label>', esc_html__( 'Include Custom CSS', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-prompt-modal-save_template-text"> <div class="et-pb-main-settings"> <p>%1$s</p> <div class="et-pb-option"> <label>%2$s:</label> <div class="et-pb-option-container"> <input type="text" value="" id="et_pb_new_template_name" class="regular-text" /> </div> </div> <%% if ( \'global\' !== is_global && \'global\' !== is_global_child ) { %%> <div class="et-pb-option"> <label>%3$s</label> <div class="et-pb-option-container"> <label> %4$s <input type="checkbox" value="" id="et_pb_template_global" /> </label> </div> </div> <%% } %%> %5$s </div> </script>', esc_html__( 'Here you can save the current item and add it to your Divi Library for later use as well.', 'et_builder' ), esc_html__( 'Template Name', 'et_builder' ), esc_html__( 'Save as Global:', 'et_builder' ), esc_html__( 'Make this a global item', 'et_builder' ), et_core_esc_previously( $categories_output ) ); // Prompt Modal Window Template. printf( '<script type="text/template" id="et-builder-prompt-modal"> <div class="et_pb_prompt_modal"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s<span> </a> <div class="et_pb_prompt_buttons"> <a href="#" class="et_pb_prompt_proceed">%2$s</a> </div> </div> </script>', et_builder_i18n( 'No' ), et_builder_i18n( 'Yes' ) ); // "Open Settings" Modal Window Template printf( '<script type="text/template" id="et-builder-prompt-modal-open_settings"> <div class="et_pb_prompt_modal"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_html__( 'Save', 'et_builder' ) ); $utils = ET_Core_Data_Utils::instance(); $fields = array(); // Filter out fields not supposed to show in BB. foreach ( ET_Builder_Settings::get_fields() as $key => $field ) { if ( true === $utils->array_get( $field, 'show_in_bb', true ) ) { $fields[ $key ] = $field; } } // "Open Settings" Modal Content Template printf( '<script type="text/template" id="et-builder-prompt-modal-open_settings-text"> <h3>%1$s</h3> <div class="et_pb_prompt_fields"> %2$s </div><!-- .et_pb_prompt_fields --> </script>', esc_html__( 'Divi Builder Settings', 'et_builder' ), et_core_esc_previously( et_pb_get_builder_settings_fields( $fields ) ) ); // AB Testing. $ab_testing = et_builder_ab_labels(); // "Turn off AB Testing" Modal Window Template printf( '<script type="text/template" id="et-builder-prompt-modal-turn_off_ab_testing"> <div class="et_pb_prompt_modal"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), et_builder_i18n( 'Yes' ) ); // "Turn off AB Testing" Modal Content Template printf( '<script type="text/template" id="et-builder-prompt-modal-turn_off_ab_testing-text"> <h3>%1$s</h3> <p>%2$s</p> <p>%3$s</p> </script>', esc_html__( 'End Split Test?', 'et_builder' ), esc_html__( 'Upon ending your split test, you will be asked to select which subject variation you would like to keep. Remaining subjects will be removed.', 'et_builder' ), esc_html__( 'Note: this process cannot be undone.', 'et_builder' ) ); // AB Testing Alert :: Modal Window Template. printf( '<script type="text/template" id="et-builder-prompt-modal-ab_testing_alert"> <div class="et_pb_prompt_modal"> <div class="et_pb_prompt_buttons"> <br/> <input type="submit" class="et_pb_prompt_proceed" value="%1$s" /> </div> </div> </script>', esc_html__( 'Ok', 'et_builder' ) ); // AB Testing Alert :: Modal Content Template. printf( '<script type="text/template" id="et-builder-prompt-modal-ab_testing_alert-text"> <%% if ( ! _.isUndefined( et_pb_ab_js_options[id] ) ) { %%> <h3><%%= et_pb_ab_js_options[id].title %%></h3> <p><%%= et_pb_ab_js_options[id].desc %%></p> <%% } else { %%> <h3>%1$s</h3> <p>%2$s</p> <%% } %%> </script>', esc_html__( 'An Error Occurred', 'et_builder' ), esc_html__( 'For some reason, you cannot perform this task.', 'et_builder' ) ); // AB Testing Alert Yes/No :: Modal Window Template. printf( '<script type="text/template" id="et-builder-prompt-modal-ab_testing_alert_yes_no"> <div class="et_pb_prompt_modal"> <div class="et_pb_prompt_buttons"> <br/> <button class="et_pb_prompt_proceed_alternative et_pb_prompt_cancel">%1$s</button> <input type="submit" class="et_pb_prompt_proceed has_alternative has_cancel_alternative" value="%2$s" /> </div> </div> </script>', et_builder_i18n( 'Cancel' ), esc_html__( 'Proceed', 'et_builder' ) ); // AB Testing Alert Yes/No :: Modal Content Template. printf( '<script type="text/template" id="et-builder-prompt-modal-ab_testing_alert_yes_no-text"> <%% if ( ! _.isUndefined( et_pb_ab_js_options[id] ) ) { %%> <h3><%%= et_pb_ab_js_options[id].title %%></h3> <p><%%= et_pb_ab_js_options[id].desc %%></p> <%% } else { %%> <h3>%1$s</h3> <p>%2$s</p> <%% } %%> </script>', esc_html__( 'An Error Occurred', 'et_builder' ), esc_html__( 'For some reason, you cannot perform this task.', 'et_builder' ) ); /** * Split Testing :: Set global item winner status */ printf( '<script type="text/template" id="et-builder-prompt-modal-set_global_subject_winner"> <div class="et_pb_prompt_modal"> <div class="et_pb_prompt_buttons"> <br/> <button class="et_pb_prompt_proceed_alternative">%1$s</button> <input type="submit" class="et_pb_prompt_proceed has_alternative" value="%2$s" /> </div> </div> </script>', et_core_esc_previously( $ab_testing['set_global_winner_status']['cancel'] ), et_core_esc_previously( $ab_testing['set_global_winner_status']['proceed'] ) ); // AB Testing :: Set global item winner status template. printf( '<script type="text/template" id="et-builder-prompt-modal-set_global_subject_winner-text"> <h3>%1$s</h3> <p>%2$s</p> <ol> <li>%3$s</li> <li>%4$s</li> </ol> </script>', et_core_esc_previously( $ab_testing['set_global_winner_status']['title'] ), et_core_esc_previously( $ab_testing['set_global_winner_status']['desc'] ), et_core_esc_previously( $ab_testing['set_global_winner_status']['option_1'] ), et_core_esc_previously( $ab_testing['set_global_winner_status']['option_2'] ) ); /** * AB Testing :: View Stats Template */ printf( '<script type="text/template" id="et-builder-prompt-modal-view_ab_stats"> <div class="et_pb_prompt_modal et_pb_ab_view_stats"> <a href="#" class="et_pb_prompt_dont_proceed et-pb-modal-close"> <span>%1$s</span> </a> </div> </script>', et_builder_i18n( 'Cancel' ) ); $view_stats_tabs = ''; foreach ( et_pb_ab_get_analysis_types() as $analysis ) { $view_stats_tabs .= sprintf( '<div class="view-stats-tab tab-%1$s" data-analysis="%1$s"> <ul class="et-pb-ab-view-stats-time-filter"> <li><a href="#" data-duration="day">%2$s</a></li> <li><a href="#" data-duration="week">%3$s</a></li> <li><a href="#" data-duration="month">%4$s</a></li> <li><a href="#" data-duration="all">%5$s</a></li> </ul><!-- .et-pb-ab-view-stats-time-filter --> <ul class="et-pb-ab-view-stats-subjects-filter"> </ul><!-- .et-pb-ab-view-stats-subjects-filter --> <div class="view-stats-main-stats"> <canvas id="ab-testing-stats-%1$s" class="ab-testing-stats" width="913" height="330"></canvas> </div> <h2 class="sub-heading">%6$s</h2> <div class="view-stats-table-wrapper"> <table id="view-stats-table-%1$s" class="view-stats-table"> <thead></thead> <tbody></tbody> <tfoot></tfoot> </table> </div><!-- .view-stats-table-wrapper --> <div class="view-stats-pie-wrapper"> <canvas id="ab-testing-stats-pie-%1$s" class="ab-testing-stats-pie" width="200" height="200"></canvas> <ul class="ab-testing-stats-pie-legends"> </ul><!-- .ab-testing-stats-pie-legends --> </div><!-- .view-stats-pie-wrapper --> <div class="no-stats"> <span class="icon"> <object type="image/svg+xml" data="%7$s/images/stats-no-data.svg"></object> </span> <h2>%8$s</h2> <p>%9$s</p> </div><!-- .no-stats --> </div>', esc_attr( $analysis ), esc_html__( 'Last 24 Hours', 'et_builder' ), esc_html__( 'Last 7 Days', 'et_builder' ), esc_html__( 'Last Month', 'et_builder' ), esc_html__( 'All Time', 'et_builder' ), esc_html__( 'Summary & Data', 'et_builder' ), esc_url( ET_BUILDER_URI ), esc_html__( 'Statistics are still being collected for this time frame', 'et_builder' ), esc_html__( 'Stats will be displayed upon sufficient data collection', 'et_builder' ) ); } // AB Testing :: View Stats content. printf( '<script type="text/template" id="et-builder-prompt-modal-view_ab_stats-text"> <h3>%1$s</h3> <ul class="et-pb-options-tabs-links"> <li class="et_pb_options_tab_ab_stat_conversion et-pb-options-tabs-links-active" data-analysis="conversions"> <a href="#">%6$s</a> </li> <li class="et_pb_options_tab_ab_stat_clicks" data-analysis="clicks"> <a href="#">%2$s</a> </li> <li class="et_pb_options_tab_ab_stat_reads" data-analysis="reads"> <a href="#">%3$s</a> </li> <li class="et_pb_options_tab_ab_stat_bounces" data-analysis="bounces"> <a href="#">%4$s</a> </li> <li class="et_pb_options_tab_ab_stat_engagements" data-analysis="engagements"> <a href="#">%5$s</a> </li> <li class="et_pb_options_tab_ab_stat_shortcode_conversions" data-analysis="shortcode_conversions"> <a href="#">%13$s</a> </li> <li class="et_pb_ab_refresh_button"> <a href="#" class="et-pb-ab-refresh-stats" title="%11$s"> <span class="icon"></span><span class="label">%12$s</span> </a> </li> </ul><!-- .et-pb-options-tabs-links --> <div class="et-pb-ab-view-stats-content has-data"> %7$s </div> <div class="et-pb-ab-view-stats-content no-data"> <span class="icon"> <object type="image/svg+xml" data="%8$s/images/stats-no-data.svg"></object> </span> <h2>%9$s</h2> <p>%10$s</p> </div> <div class="et_pb_prompt_buttons"> <input type="submit" class="et_pb_prompt_proceed" value="%14$s"> </div> </script>', esc_html__( 'Split Testing Statistics', 'et_builder' ), esc_html__( 'Clicks', 'et_builder' ), esc_html__( 'Reads', 'et_builder' ), esc_html__( 'Bounces', 'et_builder' ), esc_html__( 'Goal Engagement', 'et_builder' ), // 5 esc_html__( 'Conversions', 'et_builder' ), et_core_esc_previously( $view_stats_tabs ), esc_url( ET_BUILDER_URI ), esc_html__( 'Statistics are being collected', 'et_builder' ), esc_html__( 'Stats will be displayed upon sufficient data collection', 'et_builder' ), // 10 esc_attr__( 'Refresh Stats', 'et_builder' ), esc_html__( 'Refresh Stats', 'et_builder' ), esc_html__( 'Shortcode Conversions', 'et_builder' ), esc_attr__( 'End Split Test & Pick Winner', 'et_builder' ) ); // "Add Specialty Section" Button Template printf( '<script type="text/template" id="et-builder-add-specialty-section-button"> <a href="#" class="et-pb-section-add-specialty et-pb-add-specialty-template" data-is_template="true">%1$s</a> </script>', esc_html__( 'Add Specialty Section', 'et_builder' ) ); // Saved Entry Template. echo '<script type="text/template" id="et-builder-saved-entry"> <a class="et_pb_saved_entry_item"><%= title %></a> </script>'; // Font Family Select Template. $font_marker = et_pb_is_allowed( 'custom_fonts_management' ) ? '<span class="et-pb-user-font-marker"></span>' : ''; $upload_button = et_pb_is_allowed( 'custom_fonts_management' ) ? sprintf( '<input type="button" class="button button-upload et-pb-font-upload-button" value="%1$s">', esc_html__( 'Upload', 'et_builder' ) ) : ''; printf( '<script type="text/template" id="et-builder-google-fonts-options-items"> <li class="et-pb-option-subgroup et-pb-option-subgroup-uploaded"> <p class="et-pb-subgroup-title">%1$s</p> <ul class="et-pb-option-subgroup-container"> <%% _.each(this.et_builder_template_options.user_fonts, function(font_data, font_name) { %%> <li class="select-option-item select-option-item-custom-font select-option-item-<%%= font_name.replace( / /g, "_" ) %%>" data-value="<%%= font_name %%>"><%%= font_name %%>%2$s</li> <%% }); %%> </ul> %3$s </li> %4$s </script>', esc_html__( 'Uploaded', 'et_builder' ), et_core_intentionally_unescaped( $font_marker, 'fixed_string' ), et_core_esc_previously( $upload_button ), et_core_esc_previously( et_builder_get_google_font_items() ) ); // Font Icons Template. printf( '<script type="text/template" id="et-builder-font-icon-list-items"> %1$s </script>', et_core_esc_previously( et_pb_get_font_icon_list_items() ) ); // Histories Visualizer Item Template. printf( '<script type="text/template" id="et-builder-histories-visualizer-item-template"> <li id="et-pb-history-<%%= this.options.get( "timestamp" ) %%>" class="<%%= this.options.get( "current_active_history" ) ? "active" : "undo" %%>" data-timestamp="<%%= this.options.get( "timestamp" ) %%>"> <span class="datetime"><%%= this.options.get( "datetime" ) %%></span> <span class="verb"> <%%= this.getVerb() %%></span> <span class="noun"> <%%= this.getNoun() %%></span> <%% if ( typeof this.getAddition === "function" && "" !== this.getAddition() ) { %%> <span class="addition"> <%%= this.getAddition() %%></span> <%% } %%> </li> </script>' ); // Font Down Icons Template. printf( '<script type="text/template" id="et-builder-font-down-icon-list-items"> %1$s </script>', et_core_esc_previously( et_pb_get_font_down_icon_list_items() ) ); printf( '<script type="text/template" id="et-builder-preview-icons-template"> <ul class="et-pb-preview-screensize-switcher"> <li><a href="#" class="et-pb-preview-mobile" data-width="375"><span class="label">%1$s</span></a></li> <li><a href="#" class="et-pb-preview-tablet" data-width="768"><span class="label">%2$s</span></a></li> <li><a href="#" class="et-pb-preview-desktop active"><span class="label">%3$s</span></a></li> </ul> </script>', esc_html__( 'Mobile', 'et_builder' ), et_builder_i18n( 'Tablet' ), et_builder_i18n( 'Desktop' ) ); printf( '<script type="text/template" id="et-builder-options-tabs-links-template"> <ul class="et-pb-options-tabs-links"> <%% _.each(this.et_builder_template_options.tabs.options, function(tab, index) { %%> <li class="et_pb_options_tab_<%%= tab.slug %%><%%= \'1\' === index ? \' et-pb-options-tabs-links-active\' : \'\' %%>"> <a href="#"><%%= tab.label %%></a> </li> <%% }); %%> </ul> </script>' ); printf( '<script type="text/template" id="et-builder-mobile-options-tabs-template"> <div class="et_pb_mobile_settings_tabs et_pb_tabs_mobile"> <a href="#" class="et_pb_mobile_settings_tab et_pb_mobile_settings_active_tab" data-settings_tab="desktop"> %1$s </a> <a href="#" class="et_pb_mobile_settings_tab" data-settings_tab="tablet"> %2$s </a> <a href="#" class="et_pb_mobile_settings_tab" data-settings_tab="phone"> %3$s </a> </div> </script>', et_builder_i18n( 'Desktop' ), et_builder_i18n( 'Tablet' ), esc_html__( 'Smartphone', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-hover-options-tabs-template"> <div class="et_pb_mobile_settings_tabs et_pb_tabs_hover et_pb_tabs_hover_only"> <a href="#" class="et_pb_mobile_settings_tab et_pb_mobile_settings_active_tab et_pb_hover" data-settings_tab="default"> %1$s </a> <a href="#" class="et_pb_mobile_settings_tab et_pb_hover" data-settings_tab="hover"> %2$s </a> </div> </script>', et_builder_i18n( 'Default' ), esc_html__( 'Hover', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-mobile-hover-options-tabs-template"> <div class="et_pb_mobile_settings_tabs et_pb_tabs_mobile et_pb_tabs_hover"> <a href="#" class="et_pb_mobile_settings_tab et_pb_mobile_settings_active_tab" data-settings_tab="desktop"> %1$s </a> <a href="#" class="et_pb_mobile_settings_tab et_pb_hover" data-settings_tab="hover"> %2$s </a> <a href="#" class="et_pb_mobile_settings_tab et_pb_mobile" data-settings_tab="tablet"> %3$s </a> <a href="#" class="et_pb_mobile_settings_tab et_pb_mobile" data-settings_tab="phone"> %4$s </a> </div> </script>', et_builder_i18n( 'Desktop' ), esc_html__( 'Hover', 'et_builder' ), et_builder_i18n( 'Tablet' ), esc_html__( 'Smartphone', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-hover-icon-template"><span class="et-pb-hover-settings-toggle" data-id="placeholder"> <svg width="18" height="18" viewBox="-2 -3 16 16"> <path d="M8.69 9.43l2.22-.84a.5.5 0 0 0 .19-.8L5.22 1.28A.7.7 0 0 0 4 1.75v8.73a.5.5 0 0 0 .68.47l2.14-.81 1 2.42a1 1 0 1 0 1.86-.75z"></path> </svg> <input type="hidden" id="placeholder"/> </span></script>' ); printf( '<script type="text/template" id="et-builder-padding-option-template"> <label> <%%= this.et_builder_template_options.padding.options.label %%> <input type="text" class="et_custom_margin et_custom_margin_<%%= this.et_builder_template_options.padding.options.side %%><%%= this.et_builder_template_options.padding.options.class %%><%%= \'need_mobile\' === this.et_builder_template_options.padding.options.need_mobile ? \' et_pb_setting_mobile et_pb_setting_mobile_desktop et_pb_setting_mobile_active\' : \'\' %%>"<%%= \'need_mobile\' === this.et_builder_template_options.padding.options.need_mobile ? \' data-device="desktop"\' : \'\' %%> /> <%% if ( \'need_mobile\' === this.et_builder_template_options.padding.options.need_mobile ) { %%> <input type="text" class="et_custom_margin et_pb_setting_mobile et_pb_setting_mobile_tablet et_custom_margin_<%%= this.et_builder_template_options.padding.options.side %%><%%= this.et_builder_template_options.padding.options.class %%>" data-device="tablet" /> <input type="text" class="et_custom_margin et_pb_setting_mobile et_pb_setting_mobile_phone et_custom_margin_<%%= this.et_builder_template_options.padding.options.side %%><%%= this.et_builder_template_options.padding.options.class %%>" data-device="phone" /> <%% } %%> </label> </script>' ); printf( '<script type="text/template" id="et-builder-yes_no_button-option-template"> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value"><%%= this.et_builder_template_options.yes_no_button.options.on %%></span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value"><%%= this.et_builder_template_options.yes_no_button.options.off %%></span> </div> </script>' ); print( '<script type="text/template" id="et-builder-animation_buttons-option-template"> <div class="et_pb_animation_buttons"> <% _.each(this.et_builder_template_options.animation_buttons.options, function(option_title, option_name) { %> <div class="et_animation_button"> <a href="#"> <span class="et_animation_button_title" data-value="<%= option_name %>"><%= option_title %></span> <span class="et_animation_button_icon"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "animation-" + option_name ] %></svg> </div> </span> </a> </div> <% }); %> </div> </script>' ); print( '<script type="text/template" id="et-builder-background_tabs_nav-option-template"> <ul class="et_pb_background-tab-navs"> <% _.each(this.et_builder_template_options.background_tabs_nav.options, function(tab_nav_name, index) { %> <li><a href="#" class="et_pb_background-tab-nav et_pb_background-tab-nav--<%= tab_nav_name %>" data-tab="<%= tab_nav_name %>" title="<%= tab_nav_name %>"><div class="et-pb-icon"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "background-" + tab_nav_name ] %></svg></div></a></li> <% }); %> </ul> </script>' ); print( '<script type="text/template" id="et-builder-background_gradient_buttons-option-template"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "add" ] %></svg> </div> </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--swap"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "swap" ] %></svg> </div> </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "delete" ] %></svg> </div> </button> </div> </script>' ); print( '<script type="text/template" id="et-builder-option_preview_buttons-option-template"> <div class="et-pb-option-preview et-pb-option-preview--empty"> <button class="et-pb-option-preview-button et-pb-option-preview-button--add"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "add" ] %></svg> </div> </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--edit"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "setting" ] %></svg> </div> </button> <button class="et-pb-option-preview-button et-pb-option-preview-button--delete"> <div class="et-pb-icon"> <svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= this.et_builder_template_options.options_icons[ "delete" ] %></svg> </div> </button> </div> </script>' ); printf( '<script type="text/template" id="et-builder-multiple_buttons-option-template"> <div class="et_pb_multiple_buttons"> <%% _.each(this.et_builder_template_options.multiple_buttons.options, function(button_options, button_id) { %%> <div class="et_builder_<%%= button_id %%>_button et_builder_multiple_buttons_button mce-widget mce-btn" data-value="<%%= button_id %%>"> <button type="button"> <%%= button_options.title %%> </button> </div> <%% }); %%> </div> </script>' ); printf( '<script type="text/template" id="et-builder-font-weight-items"> <div class="et_pb_font_weight_container"> <label for="et_builder_font_weight">%1$s:</label> <select class="et_builder_font_weight"> <%% _.each(this.et_builder_template_options.font_weights, function(font_weight_name, font_weight) { %%> <option value="<%%= font_weight %%>"><%%= font_weight_name %%></option> <%% }); %%> <select> </div> </script>', esc_html__( 'Font Weight', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-font_buttons-option-template"> <div class="et_pb_font_style_container"> <label for="et_builder_font_style">%1$s:</label> <%% _.each(this.et_builder_template_options.font_buttons.options, function(font_button) { %%> <div class="et_builder_<%%= font_button %%>_font et_builder_font_style mce-widget mce-btn" data-button_name="<%%= font_button %%>"> <button type="button"> <i class="mce-ico mce-i-<%%= font_button %%>"></i> </button> </div> <%% }); %%> </div> </script>', esc_html__( 'Font Style', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-font_line_styles-option-template"> <div class="et_pb_font_line_settings"> <div class="et_pb_font_line_color"> <label for="et_builder_font_style" data-underline_label="%5$s %7$s" data-strikethrough_label="%6$s %7$s">%1$s:</label> <span class="et-pb-custom-color-button et-pb-choose-custom-color-button"><span>%4$s</span></span> <div class="et-pb-custom-color-container et_pb_hidden"> <input class="et-pb-color-picker-hex et-pb-color-picker-hex-alpha" type="text" data-alpha="true" placeholder="%2$s" data-selected-value="" value="" /> <input class="et-pb-custom-color-picker et-pb-font-line-color-value" type="hidden" value="" /> </div> </div> <div class="et_pb_font_line_style"> <label for="et_builder_font_style" data-underline_label="%5$s %8$s" data-strikethrough_label="%6$s %8$s">%3$s:</label> <select class="et_pb_font_line_style_select"> <option value="solid">solid</option> <option value="double">double</option> <option value="dotted">dotted</option> <option value="dashed">dashed</option> <option value="wavy">wavy</option> </select> </div> </div> </script>', esc_html__( 'Line Color', 'et_builder' ), esc_attr__( 'Hex Value', 'et_builder' ), esc_attr__( 'Line Style', 'et_builder' ), esc_attr__( 'Choose Custom Color', 'et_builder' ), esc_attr__( 'Underline', 'et_builder' ), esc_attr__( 'Strikethrough', 'et_builder' ), esc_attr__( 'Color', 'et_builder' ), esc_attr__( 'Style', 'et_builder' ) ); printf( '<script type="text/template" id="et-builder-text-align-buttons-option-template"> <%% _.each(this.et_builder_template_options.text_align_buttons.options, function(text_align_button) { %%> <%% var text_align_button_classname = text_align_button === "justified" ? "justify" : text_align_button; text_align_button_classname = text_align_button === "force_left" ? "left" : text_align_button_classname; var text_align_button_type = this.et_builder_template_options.text_align_buttons.type; %%> <div class="et_builder_<%%= text_align_button %%>_text_align et_builder_text_align mce-widget mce-btn" data-value="<%%= text_align_button %%>"> <button type="button"> <i class="mce-ico align-<%%= text_align_button_type %%> mce-i-align<%%= text_align_button_classname %%>"></i> </button> </div> <%% }); %%> </script>' ); printf( '<script type="text/template" id="et-builder-select-option-template"> <%% _.each(this.et_builder_template_options.select.options.list, function(option_label, option_value) { var data = ""; var option_label_updated = option_label; if ( _.isObject( option_label ) ) { if ( ! _.isUndefined( option_label["data"] ) ) { var data_key_name = _.keys( option_label["data"] ); data = " data-" + _.escape( data_key_name[0] ) + "=\'" + _.escape( option_label["data"][ data_key_name[0] ] ) + "\'"; } var option_label_updated = option_label["value"]; } var select_name = this.et_builder_template_options.select.options.select_name.replace( "data.", "" ); var select_value = this.et_builder_template_options.select.data[ select_name ]; var select_value_escaped = _.escape( select_value ); var option_value_escaped = _.escape( option_value ); var default_value = this.et_builder_template_options.select.options.default; var selected_attr = ! _.isUndefined( select_value ) && "" !== select_value && option_value_escaped === select_value_escaped || ( _.isUndefined( select_value ) && default_value !== "" && option_value_escaped === default_value ) ? \' selected="selected"\' : ""; %%> <option <%%= data %%> value="<%%= option_value_escaped %%>" <%%= selected_attr %%>><%%= _.escape( option_label_updated ) %%></option> <%% }); %%> </script>' ); printf( '<script type="text/template" id="et-builder-failure-notice-template"> %1$s </script>', et_core_esc_previously( et_builder_get_failure_notification_modal() ) ); printf( '<script type="text/template" id="et-builder-cache-notice-template"> %1$s </script>', et_core_esc_previously( et_builder_get_cache_notification_modal() ) ); printf( '<script type="text/template" id="et-builder-page-creation-template"> %1$s </script>', et_core_esc_previously( et_builder_page_creation_modal() ) ); // Help Template. printf( '<script type="text/template" id="et-builder-help-template"> <h3 class="et-pb-settings-heading">%1$s</h3> <ul class="et-pb-options-tabs-links et-pb-help-switcher"> <li class="et-pb-new-module et-pb-options-tabs-links-active" data-open_tab="et-pb-shortcuts-tab"> <a href="#">%2$s</a> </li> </ul> <div class="et-pb-main-settings et-pb-main-settings-full et-pb-shortcuts-tab active-container"></div> </script>', esc_html__( 'Divi Builder Helper', 'et_builder' ), esc_html__( 'Shortcuts', 'et_builder' ) ); do_action( 'et_pb_after_page_builder' ); } /** * Returns builder settings markup * * @param array $options builder settings' configuration. * @return string builder settings' markup */ function et_pb_get_builder_settings_fields( $options ) { $outputs = ''; $defaults = et_pb_get_builder_settings_configuration_default(); foreach ( $options as $option ) { $option = wp_parse_args( $option, $defaults ); $type = $option['type']; $field_list_class = $type; $affecting = ! empty( $option['affects'] ) ? implode( '|', $option['affects'] ) : ''; if ( $option['depends_show_if'] ) { $field_list_class .= ' et-pb-display-conditionally'; } if ( isset( $option['class'] ) ) { $field_list_class .= ' ' . $option['class']; } $outputs .= sprintf( '<div class="et_pb_prompt_field_list et-pb-option-container %1$s" data-id="%2$s" data-type="%3$s" data-autoload="%4$s" data-affects="%5$s" data-visibility-dependency="%6$s">', esc_attr( $field_list_class ), esc_attr( $option['id'] ), esc_attr( $type ), esc_attr( $option['autoload'] ), esc_attr( $affecting ), esc_attr( $option['depends_show_if'] ) ); switch ( $option['type'] ) { case 'yes_no_button': $outputs .= sprintf( '<label>%2$s</label> <div class="et_pb_prompt_field"> <div class="et_pb_yes_no_button_wrapper "> <div class="et_pb_yes_no_button et_pb_off_state"> <span class="et_pb_value_text et_pb_on_value">%3$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%4$s</span> </div> <select name="%1$s" id="%1$s" class="et-pb-main-setting regular-text"> <option value="off">%5$s</option> <option value="on">%6$s</option> </select> </div><span class="et-pb-reset-setting"></span> </div>', esc_attr( $option['id'] ), esc_html( $option['label'] ), isset( $option['values'] ) ? esc_html( $option['values']['yes'] ) : et_builder_i18n( 'Yes' ), isset( $option['values'] ) ? esc_html( $option['values']['no'] ) : et_builder_i18n( 'No' ), et_builder_i18n( 'Off' ), et_builder_i18n( 'On' ) ); break; case 'codemirror': case 'textarea': $outputs .= sprintf( '<label for="%1$s">%2$s</label> <div class="et_pb_prompt_field"> <textarea id="%1$s" name="%1$s"%3$s></textarea> </div>', esc_attr( $option['id'] ), esc_html( $option['label'] ), isset( $option['readonly'] ) && 'readonly' === $option['readonly'] ? ' readonly' : '' ); break; case 'colorpalette': $outputs .= sprintf( '<label>%1$s</label><div class="et_pb_prompt_field">', esc_html( $option['label'] ) ); $outputs .= '<div class="et_pb_colorpalette_overview">'; for ( $colorpalette_index = 1; $colorpalette_index < 9; $colorpalette_index++ ) { $outputs .= sprintf( '<span class="colorpalette-item colorpalette-item-%1$s" data-index="%1$s"></span>', esc_attr( $colorpalette_index ) ); } $outputs .= '</div>'; for ( $colorpicker_index = 1; $colorpicker_index < 9; $colorpicker_index++ ) { $outputs .= sprintf( '<div class="colorpalette-colorpicker" data-index="%2$s"> <input id="%1$s-%2$s" name="%1$s-%2$s" data-index="%2$s" type="text" class="input-colorpalette-colorpicker" data-alpha="true" /> </div>', esc_attr( $option['id'] ), esc_attr( $colorpicker_index ) ); } $outputs .= '</div>'; break; case 'color-alpha': $outputs .= sprintf( '<label for="%1$s">%2$s</label> <div class="et_pb_prompt_field"> <input id="%1$s" name="%1$s" type="text" class="input-colorpicker" data-alpha="true" data-default-color="%3$s" /> </div>', esc_attr( $option['id'] ), esc_html( $option['label'] ), esc_attr( $option['default'] ) ); break; case 'range': $outputs .= sprintf( '<label for="%1$s">%2$s</label> <div class="et_pb_prompt_field"> <input id="%1$s" name="%1$s" type="range" class="range" step="%3$s" min="%4$s" max="%5$s" /> </div>', esc_attr( $option['id'] ), esc_html( $option['label'] ), esc_attr( $option['range_settings']['step'] ), esc_attr( $option['range_settings']['min'] ), esc_attr( $option['range_settings']['max'] ) ); break; case 'select': $options = ''; foreach ( $option['options'] as $value => $text ) { $options .= sprintf( '<option value="%1$s">%2$s</option>', esc_attr( $value ), esc_html( $text ) ); } $outputs .= sprintf( '<label for="%1$s">%2$s</label> <div class="et_pb_prompt_field"> <select id="%1$s" name="%1$s"> %3$s </select> </div>', esc_attr( $option['id'] ), esc_html( $option['label'] ), et_core_esc_previously( $options ) ); break; } $outputs .= sprintf( '</div><!-- .et_pb_prompt_field_list.et-pb-option-container.%1$s -->', esc_attr( $option['type'] ) ); } return $outputs; } /** * Prints hidden inputs for passing settings data to database * * @param integer $post_id post id. * * @return void|bool */ function et_pb_builder_settings_hidden_inputs( $post_id ) { if ( ! class_exists( 'ET_Builder_Settings' ) ) { return false; } $settings = ET_Builder_Settings::get_fields(); $defaults = et_pb_get_builder_settings_configuration_default(); if ( empty( $settings ) ) { return; } if ( empty( $settings ) ) { return; } foreach ( $settings as $setting ) { $setting = wp_parse_args( $setting, $defaults ); if ( ! $setting['autoload'] ) { continue; } $id = '_' . $setting['id']; $meta_key = isset( $setting['meta_key'] ) ? $setting['meta_key'] : $id; $value = get_post_meta( $post_id, $meta_key, true ); if ( ( ! $value || '' === $value ) && $setting['default'] ) { $value = $setting['default']; } printf( '<input type="hidden" id="%1$s" name="%1$s" value="%2$s" />', esc_attr( $id ), esc_attr( $value ) ); } } /** * Prints hidden inputs for passing global modules data to database. * * @param integer $post_id post id. * * @return void */ function et_pb_builder_global_library_inputs( $post_id ) { global $typenow; if ( 'et_pb_layout' !== $typenow ) { return; } $template_scope = wp_get_object_terms( get_the_ID(), 'scope' ); $template_type = wp_get_object_terms( get_the_ID(), 'layout_type' ); $is_global_template = ! empty( $template_scope[0] ) ? $template_scope[0]->slug : 'regular'; $template_type_slug = ! empty( $template_type[0] ) ? $template_type[0]->slug : ''; if ( 'global' !== $is_global_template || 'module' !== $template_type_slug ) { return; } $excluded_global_options = get_post_meta( $post_id, '_et_pb_excluded_global_options' ); printf( '<input type="hidden" id="et_pb_unsynced_global_attrs" name="et_pb_unsynced_global_attrs" value="%1$s" />', isset( $excluded_global_options[0] ) ? esc_attr( $excluded_global_options[0] ) : wp_json_encode( array() ) ); } /** * Returns array of default builder settings configuration item * * @return array */ function et_pb_get_builder_settings_configuration_default() { return array( 'id' => '', 'type' => '', 'label' => '', 'min' => '', 'max' => '', 'step' => '', 'autoload' => true, 'default' => false, 'affects' => array(), 'depends_show_if' => false, ); } /** * Update a builder setting. * * @param array $settings The new option value. * @param string $post_id The post id or 'global' for global settings. */ function et_builder_update_settings( $settings, $post_id = 'global' ) { // Allow the use of uppercase in $is_bb variable as BB is common abbreviation. et_core_nonce_verified_previously(); $is_global = 'global' === $post_id; $is_bb = null === $settings; $settings = $is_bb ? $_POST : $settings; $fields = $is_global ? ET_Builder_Settings::get_fields( 'builder' ) : ET_Builder_Settings::get_fields(); $utils = ET_Core_Data_Utils::instance(); $update = array(); foreach ( (array) $settings as $setting_key => $setting_value ) { $raw_setting_value = $setting_value; $setting_key = $is_bb ? substr( $setting_key, 1 ) : $setting_key; // Verify setting key. if ( ! isset( $fields[ $setting_key ] ) || ! isset( $fields[ $setting_key ]['type'] ) ) { continue; } // Auto-formatting subjects' value format. if ( 'et_pb_ab_subjects' === $setting_key && is_array( $setting_value ) ) { $setting_value = implode( ',', $setting_value ); } // TODO Possibly move sanitization.php to builder dir // Sanitize value. switch ( $fields[ $setting_key ]['type'] ) { case 'colorpalette': $palette_colors = explode( '|', $setting_value ); $setting_value = implode( '|', array_map( 'et_sanitize_alpha_color', $palette_colors ) ); break; case 'range': // Avoid setting absolute value for range if option is z_index. if ( 'et_pb_page_z_index' === $setting_key ) { break; } $setting_value = absint( $setting_value ); $range_min = isset( $fields[ $setting_key ]['range_settings'] ) && isset( $fields[ $setting_key ]['range_settings']['min'] ) ? absint( $fields[ $setting_key ]['range_settings']['min'] ) : -1; $range_max = isset( $fields[ $setting_key ]['range_settings'] ) && isset( $fields[ $setting_key ]['range_settings']['max'] ) ? absint( $fields[ $setting_key ]['range_settings']['max'] ) : -1; if ( $setting_value < $range_min || $range_max < $setting_value ) { continue 2; } break; case 'color-alpha': $setting_value = et_sanitize_alpha_color( $setting_value ); break; case 'codemirror': case 'textarea': // Allow HTML content on Excerpt field. if ( 'et_pb_post_settings_excerpt' === $setting_key ) { $setting_value = wp_kses_post( $setting_value ); } else { $setting_value = sanitize_textarea_field( $setting_value ); } break; case 'categories': $setting_value = array_map( 'intval', explode( ',', $setting_value ) ); break; default: $setting_value = sanitize_text_field( $setting_value ); break; } // check whether or not the defined value === default value. $is_default = isset( $fields[ $setting_key ]['default'] ) && $setting_value === $fields[ $setting_key ]['default']; // Auto-formatting AB Testing status' meta key. if ( 'et_pb_enable_ab_testing' === $setting_key ) { $setting_key = 'et_pb_use_ab_testing'; } /** * Fires before updating a builder setting in the database. * * @param string $setting_key The option name/id. * @param string $setting_value The new option value. * @param string|int $post_id The post id or 'global' for global settings. */ do_action( 'et_builder_settings_update_option', $setting_key, $setting_value, $post_id ); // If `post_field` is defined, we need to update the post. $post_field = $utils->array_get( $fields, "{$setting_key}.post_field", false ); if ( false !== $post_field ) { // Only allowed in VB. if ( ! ( $is_global || $is_bb ) ) { // Save the post field so we can do a single update. // Use the raw value and rely on wp_update_post to sanitize it in order to allow certain HTML tags. $update[ $post_field ] = $raw_setting_value; } continue; } // If `taxonomy_name` is defined, we need to update the post terms. $taxonomy_name = $utils->array_get( $fields, "{$setting_key}.taxonomy_name", false ); if ( false !== $taxonomy_name ) { // Only allowed in VB. if ( ! ( $is_global || $is_bb ) ) { $post_type = $utils->array_get( $fields, "{$setting_key}.post_type", false ); if ( get_post_type( $post_id ) === $post_type ) { // Only update if the post type matches. wp_set_object_terms( $post_id, $setting_value, $taxonomy_name ); } } continue; } // Save the setting in a post meta. $meta_key = $utils->array_get( $fields, $setting_key . '.meta_key', false ) ? $fields[ $setting_key ]['meta_key'] : "_{$setting_key}"; $save_post = $utils->array_get( $fields, $setting_key . '.save_post', true ); if ( $is_bb && false === $save_post ) { // This meta key must be ignored during classic-editor / BB save action or it will // overwrite values in the WP edit page. continue; } // remove if value is default. if ( $is_default ) { $is_global ? et_delete_option( $setting_key ) : delete_post_meta( $post_id, $meta_key ); } else { // Update. $is_global ? et_update_option( $setting_key, $setting_value ) : update_post_meta( $post_id, $meta_key, $setting_value ); } // Removing autosave. delete_post_meta( $post_id, "{$meta_key}_draft" ); } // Removing builder settings autosave. $current_user_id = get_current_user_id(); delete_post_meta( $post_id, "_et_builder_settings_autosave_{$current_user_id}" ); if ( count( $update ) > 0 ) { // This MUST NOT be executed while saving data in the BB or it will generate // an update loop that will end the universe as we know it. if ( ! ( $is_bb || wp_is_post_revision( $post_id ) ) ) { $update['ID'] = $post_id; wp_update_post( $update ); } } } /** * Returns array of default color pallete. * * @param integer $post_id post id. * * @return array default color palette */ function et_pb_get_default_color_palette( $post_id = 0 ) { $default_palette = array( '#000000', '#FFFFFF', '#E02B20', '#E09900', '#EDF000', '#7CDA24', '#0C71C3', '#8300E9', ); $saved_global_palette = et_get_option( 'divi_color_palette', false ); $palette = $saved_global_palette && '' !== str_replace( '|', '', $saved_global_palette ) ? explode( '|', $saved_global_palette ) : $default_palette; return apply_filters( 'et_pb_get_default_color_palette', $palette, $post_id ); } /** * Modify builder editor's TinyMCE configuration * * @param array $mce_init An array with TinyMCE config. * @param string $editor_id Unique editor identifier. * * @return array */ function et_pb_content_mce_config( $mce_init, $editor_id ) { if ( 'et_pb_content' === $editor_id && isset( $mce_init['toolbar1'] ) ) { // Get toolbar as array. $toolbar1 = explode( ',', $mce_init['toolbar1'] ); // Look for read more (wp_more)'s array' key. $wp_more_key = array_search( 'wp_more', $toolbar1, true ); if ( $wp_more_key ) { unset( $toolbar1[ $wp_more_key ] ); } // Update toolbar1 configuration. $mce_init['toolbar1'] = implode( ',', $toolbar1 ); } return $mce_init; } add_filter( 'tiny_mce_before_init', 'et_pb_content_mce_config', 10, 2 ); /** * Get post format with filterable output * * @todo once WordPress provide filter for get_post_format() output, this function can be retired * @see get_post_format() * * @return mixed string|bool string of post format or false for default */ function et_pb_post_format() { return apply_filters( 'et_pb_post_format', get_post_format(), get_the_ID() ); } /** * Return post format into false when using pagebuilder. * * @param string $post_format post format. * @param integer $post_id post id. * * @return mixed string|bool string of post format or false for default */ function et_pb_post_format_in_pagebuilder( $post_format, $post_id ) { if ( et_pb_is_pagebuilder_used( $post_id ) ) { return false; } return $post_format; } add_filter( 'et_pb_post_format', 'et_pb_post_format_in_pagebuilder', 10, 2 ); if ( ! function_exists( 'et_get_first_audio_block' ) ) : /** * Return the first audio block from the post content. */ function et_get_first_audio_block() { $content = get_the_content(); // It is assumed that audio module figures will not contain other figures. preg_match( '/<figure\s+[^>]*?class=([\'"])[^\'"]*?wp-block-audio[^\'"]*?\1[^>]*?>.*?<\/figure>/', $content, $matches ); if ( empty( $matches ) ) { return ''; } return $matches[0]; } endif; if ( ! function_exists( 'et_pb_get_audio_player' ) ) : /** * Return audio player. */ function et_pb_get_audio_player() { global $_et_pbgap_audio_to_remove; $shortcode_audio = ''; $regex = get_shortcode_regex( array( 'audio' ) ); preg_match_all( "/{$regex}/s", get_the_content(), $matches ); foreach ( $matches[2] as $key => $shortcode_match ) { // Remove audio shortcode if its contains first attached audio file URL // first attached audio file is automatically appended on post's format content. if ( 'audio' === $shortcode_match ) { $_et_pbgap_audio_to_remove = $matches[0][0]; $shortcode_audio = do_shortcode( $_et_pbgap_audio_to_remove ); break; } } if ( '' === $shortcode_audio ) { $_et_pbgap_audio_to_remove = et_get_first_audio_block(); $shortcode_audio = $_et_pbgap_audio_to_remove; } if ( '' === $shortcode_audio ) { return false; } $output = sprintf( '<div class="et_audio_container"> %1$s </div>', $shortcode_audio ); add_filter( 'the_content', 'et_delete_post_audio' ); return $output; } endif; if ( ! function_exists( 'et_divi_post_format_content' ) ) : /** * Displays post audio, quote and link post formats content */ function et_divi_post_format_content() { $post_format = et_pb_post_format(); $text_color_class = et_divi_get_post_text_color(); $inline_style = et_divi_get_post_bg_inline_style(); global $post; if ( post_password_required( $post ) ) { return; } switch ( $post_format ) { case 'audio': printf( '<div class="et_audio_content%4$s"%5$s> <h2><a href="%3$s">%1$s</a></h2> %2$s </div>', esc_html( get_the_title() ), et_core_intentionally_unescaped( et_pb_get_audio_player(), 'html' ), esc_url( get_permalink() ), esc_attr( $text_color_class ), et_core_esc_previously( $inline_style ) ); break; case 'quote': printf( '<div class="et_quote_content%4$s"%5$s> %1$s <a href="%2$s" class="et_quote_main_link">%3$s</a> </div>', et_core_intentionally_unescaped( et_get_blockquote_in_content(), 'html' ), esc_url( get_permalink() ), esc_html__( 'Read more', 'et_builder' ), esc_attr( $text_color_class ), et_core_esc_previously( $inline_style ) ); break; case 'link': printf( '<div class="et_link_content%5$s"%6$s> <h2><a href="%2$s">%1$s</a></h2> <a href="%3$s" class="et_link_main_url">%4$s</a> </div>', esc_html( get_the_title() ), esc_url( get_permalink() ), esc_url( et_get_link_url() ), esc_html( et_get_link_url() ), esc_attr( $text_color_class ), et_core_esc_previously( $inline_style ) ); break; } } endif; if ( ! function_exists( 'et_get_blockquote_in_content' ) ) : /** * Extract and return the first blockquote from content. */ function et_get_blockquote_in_content() { global $more; $more_default = $more; $more = 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Disable `$more` flag to get the blockquote that may exist after <!--more--> tag. remove_filter( 'the_content', 'et_remove_blockquote_from_content' ); $content = apply_filters( 'the_content', get_the_content() ); add_filter( 'the_content', 'et_remove_blockquote_from_content' ); $more = $more_default; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore default `$more` flag. if ( preg_match( '/<blockquote(.+?)<\/blockquote>/is', $content, $matches ) ) { return $matches[0]; } else { return false; } } endif; if ( ! function_exists( 'et_get_link_url' ) ) : /** * Return link from the post content. */ function et_get_link_url() { $link_url = get_post_meta( get_the_ID(), '_format_link_url', true ); if ( '' !== $link_url ) { return $link_url; } $content = get_the_content(); $has_url = get_url_in_content( $content ); return ( $has_url ) ? $has_url : apply_filters( 'the_permalink', get_permalink() ); } endif; if ( ! function_exists( 'et_get_first_video' ) ) : /** * Fix the issue with thumbnail video player, not working, when video url is added to content without shortcode */ function et_get_first_video() { $first_url = ''; $first_video = ''; $video_width = (int) apply_filters( 'et_blog_video_width', 1080 ); $video_height = (int) apply_filters( 'et_blog_video_height', 630 ); $i = 0; $content = get_the_content(); preg_match_all( '|^\s*https?://[^\s"]+\s*$|im', $content, $urls ); foreach ( $urls[0] as $url ) { $i++; if ( 1 === $i ) { $first_url = trim( $url ); } $oembed = wp_oembed_get( esc_url( $url ) ); if ( ! $oembed ) { continue; } $first_video = $oembed; $first_video = preg_replace( '/<embed /', '<embed wmode="transparent" ', $first_video ); $first_video = preg_replace( '/<\/object>/', '<param name="wmode" value="transparent" /></object>', $first_video ); // If the url comes from a GB embed block. if ( preg_match( '|wp-block-embed.+?' . preg_quote( $url, null ) . '|s', $content ) ) { // We need to remove some useless markup later. add_filter( 'the_content', 'et_delete_post_video' ); } break; } if ( '' === $first_video ) { // Gutenberg compatibility. if ( ! has_shortcode( $content, 'video' ) && empty( $first_url ) ) { preg_match( '/<!-- wp:video[^\]]+?class="wp-block-video"><video[^\]]+?src="([^\]]+?)"[^\]]+?<!-- \/wp:video -->/', $content, $gb_video ); $first_url = isset( $gb_video[1] ) ? $gb_video[1] : false; } if ( ! has_shortcode( $content, 'video' ) && ! empty( $first_url ) ) { $video_shortcode = sprintf( '[video src="%1$s" /]', esc_attr( $first_url ) ); if ( ! empty( $gb_video ) ) { $content = str_replace( $gb_video[0], $video_shortcode, $content ); } else { $content = str_replace( $first_url, $video_shortcode, $content ); } } if ( has_shortcode( $content, 'video' ) ) { $regex = get_shortcode_regex(); preg_match( "/{$regex}/s", $content, $match ); $first_video = preg_replace( '/width="[0-9]*"/', "width=\"{$video_width}\"", $match[0] ); $first_video = preg_replace( '/height="[0-9]*"/', "height=\"{$video_height}\"", $first_video ); add_filter( 'the_content', 'et_delete_post_video' ); $first_video = do_shortcode( et_pb_fix_shortcodes( $first_video ) ); } } return ( '' !== $first_video ) ? $first_video : false; } endif; if ( ! function_exists( 'et_delete_post_video' ) ) : /** * Removes the first video shortcode from content on single pages since it is displayed * at the top of the page. This will also remove the video shortcode url from archive pages content * * @param string $content post content. */ function et_delete_post_video( $content ) { if ( has_post_format( 'video' ) ) : if ( has_shortcode( $content, 'video' ) ) { $regex = get_shortcode_regex(); preg_match_all( "/{$regex}/s", $content, $matches ); // $matches[2] holds an array of shortcodes names in the post foreach ( $matches[2] as $key => $shortcode_match ) { if ( 'video' === $shortcode_match ) { $content = str_replace( $matches[0][ $key ], '', $content ); if ( is_single() && is_main_query() ) { break; } } } } else { // Gutenberg compatibility. preg_match( '/<figure class="wp-block-video"[^\]]+?<video[^\]]+?src="([^\]]+?)"[^\]]+?<\/figure>/', $content, $gb_video ); if ( ! empty( $gb_video[0] ) ) { $content = str_replace( $gb_video[0], '', $content ); } else { // Remove GB embed caption for the first video. $content = preg_replace( '|<figure class="wp-block-embed.+?><div.+?>\s*?</div>.+?</figure>|', '', $content, 1 ); } } endif; return $content; } endif; if ( ! function_exists( 'et_delete_post_audio' ) ) : /** * Removes the audio shortcode of the first attached (NOT embedded) audio from content on single pages since * it is displayed at the top of the page. This will also remove the audio shortcode url from archive pages content * * @see https://www.elegantthemes.com/documentation/divi/post-formats/ * * @param string $content post content. */ function et_delete_post_audio( $content ) { global $_et_pbgap_audio_to_remove; if ( has_post_format( 'audio' ) && $_et_pbgap_audio_to_remove ) { $content = str_replace( $_et_pbgap_audio_to_remove, '', $content ); } return $content; } endif; if ( ! function_exists( 'et_delete_post_first_video' ) ) : /** * Delete the first video url from the post content. * * @param string $content post content. */ function et_delete_post_first_video( $content ) { if ( 'video' !== et_pb_post_format() ) { return $content; } $first_video = et_get_first_video(); if ( false !== $first_video ) { preg_match_all( '|^\s*https?:\/\/[^\s"]+\s*|im', $content, $urls ); if ( ! empty( $urls[0] ) ) { $content = str_replace( $urls[0], '', $content ); } } return $content; } endif; /** * Fix JetPack post excerpt shortcode issue. * * @param array $results related posts. * * @return mixed */ function et_jetpack_post_excerpt( $results ) { foreach ( $results as $key => $post ) { if ( isset( $post['excerpt'] ) ) { // Remove ET shortcodes from JetPack excerpt. $results[ $key ]['excerpt'] = preg_replace( '#\[et_pb(.*)\]#', '', $post['excerpt'] ); } } return $results; } add_filter( 'jetpack_relatedposts_returned_results', 'et_jetpack_post_excerpt' ); /** * Adds a Divi gallery type when the Jetpack plugin is enabled. * * @param array $types gallery types. */ function et_jetpack_gallery_type( $types ) { $types['divi'] = 'Divi'; return $types; } add_filter( 'jetpack_gallery_types', 'et_jetpack_gallery_type' ); if ( ! function_exists( 'et_get_gallery_attachments' ) ) : /** * Fetch the gallery attachments * * @param array $attr gallery shortcode attributes. */ function et_get_gallery_attachments( $attr ) { // We're trusting author input, so let's at least make sure it looks like a valid orderby statement. if ( isset( $attr['orderby'] ) ) { $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] ); if ( ! $attr['orderby'] ) { unset( $attr['orderby'] ); } } $html5 = current_theme_supports( 'html5', 'gallery' ); $pairs = array( 'order' => 'ASC', 'orderby' => 'menu_order ID', 'id' => get_the_ID() ? get_the_ID() : 0, 'itemtag' => $html5 ? 'figure' : 'dl', 'icontag' => $html5 ? 'div' : 'dt', 'captiontag' => $html5 ? 'figcaption' : 'dd', 'columns' => 3, 'size' => 'thumbnail', 'include' => '', 'exclude' => '', 'link' => '', ); $atts = shortcode_atts( $pairs, $attr, 'gallery' ); $id = intval( $atts['id'] ); if ( 'RAND' === $atts['order'] ) { $atts['orderby'] = 'none'; } if ( ! empty( $atts['include'] ) ) { $_attachments = get_posts( array( 'include' => $atts['include'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], ) ); $attachments = array(); foreach ( $_attachments as $key => $val ) { $attachments[ $val->ID ] = $_attachments[ $key ]; } } elseif ( ! empty( $atts['exclude'] ) ) { $attachments = get_children( array( 'post_parent' => $id, 'exclude' => $atts['exclude'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], ) ); } else { $attachments = get_children( array( 'post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], ) ); } return $attachments; } endif; /** * Generate the HTML for custom gallery layouts. * * @param string $val output. * @param string $attr gallery settings. * * @return string */ function et_gallery_layout( $val, $attr ) { // check to see if the gallery output is already rewritten. if ( ! empty( $val ) ) { return $val; } // Do not filter gallery items in plugin to not break custom styles which may be applied by 3rd party theme. if ( et_is_builder_plugin_active() ) { return $val; } if ( ! apply_filters( 'et_gallery_layout_enable', false ) ) { return $val; } $output = ''; if ( ! is_singular() && ! et_pb_is_pagebuilder_used( get_the_ID() ) && ! is_et_pb_preview() ) { $attachments = et_get_gallery_attachments( $attr ); $gallery_output = ''; foreach ( $attachments as $attachment ) { $attachment_image = wp_get_attachment_url( $attachment->ID, 'et-pb-post-main-image-fullwidth' ); $gallery_output .= sprintf( '<div class="et_pb_slide" style="background: url(%1$s);"></div>', esc_attr( $attachment_image ) ); } $output = sprintf( '<div class="et_pb_slider et_pb_slider_fullwidth_off et_pb_gallery_post_type"> <div class="et_pb_slides"> %1$s </div> </div>', $gallery_output ); } else { if ( ! isset( $attr['type'] ) || ! in_array( $attr['type'], array( 'rectangular', 'square', 'circle', 'rectangle' ), true ) ) { $attachments = et_get_gallery_attachments( $attr ); $gallery_output = ''; foreach ( $attachments as $attachment ) { $gallery_output .= sprintf( '<li class="et_gallery_item et_pb_gallery_image"> <a href="%1$s" title="%3$s"> <span class="et_portfolio_image"> %2$s <span class="et_overlay"></span> </span> </a> %4$s </li>', esc_url( wp_get_attachment_url( $attachment->ID, 'full' ) ), wp_get_attachment_image( $attachment->ID, 'et-pb-portfolio-image' ), esc_attr( $attachment->post_title ), ! empty( $attachment->post_excerpt ) ? sprintf( '<p class="et_pb_gallery_caption">%1$s</p>', esc_html( $attachment->post_excerpt ) ) : '' ); } $output = sprintf( '<ul class="et_post_gallery clearfix"> %1$s </ul>', $gallery_output ); } } return $output; } add_filter( 'post_gallery', 'et_gallery_layout', 1000, 2 ); if ( ! function_exists( 'et_pb_gallery_images' ) ) : /** * Display image gallery. * * @param string $force_gallery_layout Optional. Slider gallery layout. */ function et_pb_gallery_images( $force_gallery_layout = '' ) { if ( 'slider' === $force_gallery_layout ) { // Get the post content. $post = get_post( get_the_ID() ); if ( ! $post ) { return ''; } // We want to include GB galleries in results so need to convert them to shortcodes // because `get_post_gallery` won't find them otherwise. $post->post_content = apply_filters( 'et_gb_galleries_to_shortcodes', $post->post_content ); $attachments = get_post_gallery( $post, false ); $gallery_output = ''; $output = ''; $images_array = ! empty( $attachments['ids'] ) ? explode( ',', $attachments['ids'] ) : array(); if ( empty( $images_array ) ) { return $output; } foreach ( $images_array as $attachment ) { $image_src = wp_get_attachment_url( $attachment, 'et-pb-post-main-image-fullwidth' ); $gallery_output .= sprintf( '<div class="et_pb_slide" style="background: url(%1$s);"></div>', esc_url( $image_src ) ); } printf( '<div class="et_pb_slider et_pb_slider_fullwidth_off et_pb_gallery_post_type"> <div class="et_pb_slides"> %1$s </div> </div>', et_core_esc_previously( $gallery_output ) ); } else { add_filter( 'et_gallery_layout_enable', 'et_gallery_layout_turn_on' ); printf( do_shortcode( '%1$s' ), et_core_intentionally_unescaped( get_post_gallery(), 'html' ) ); remove_filter( 'et_gallery_layout_enable', 'et_gallery_layout_turn_on' ); } } endif; /** * Used to always use divi gallery on et_pb_gallery_images */ function et_gallery_layout_turn_on() { return true; } /** * Remove Elegant Builder plugin filter, that activates visual mode on each page load in WP-Admin */ function et_pb_remove_lb_plugin_force_editor_mode() { remove_filter( 'wp_default_editor', 'et_force_tmce_editor' ); } add_action( 'admin_init', 'et_pb_remove_lb_plugin_force_editor_mode' ); /** * * Generates array of all Role options */ function et_pb_all_role_options() { // get all the roles that can edit theme options. $applicability_roles = et_core_get_roles_by_capabilities( [ 'edit_theme_options' ] ); // Get all the roles that can edit theme options and edit others posts. $tb_applicability_roles = et_core_get_roles_by_capabilities( [ 'edit_theme_options', 'edit_others_posts' ], true ); // get all the modules and build array of capabilities for them. $all_modules_array = ET_Builder_Element::get_modules_array(); $custom_user_tabs = ET_Builder_Element::get_tabs(); $options_categories = ET_Builder_Element::get_options_categories(); $module_capabilies = array(); $tabs_array = array( 'general_settings' => array( 'name' => esc_html__( 'Content Settings', 'et_builder' ), ), 'advanced_settings' => array( 'name' => esc_html__( 'Design Settings', 'et_builder' ), ), 'custom_css_settings' => array( 'name' => esc_html__( 'Advanced Settings', 'et_builder' ), ), ); // add all custom user tabs into list. if ( ! empty( $custom_user_tabs ) ) { foreach ( $custom_user_tabs as $module => $tabs_data ) { if ( ! empty( $tabs_data ) ) { foreach ( $tabs_data as $tab_slug => $tab_data ) { $tabs_array[ $tab_slug ] = array( 'name' => $tab_data['name'], ); } } } } foreach ( $all_modules_array as $module => $module_details ) { if ( ! in_array( $module_details['label'], array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner', 'et_pb_column' ), true ) ) { $module_capabilies[ $module_details['label'] ] = array( 'name' => sanitize_text_field( $module_details['title'] ), ); } } // we need to display some options only when theme activated. $theme_only_options = ! et_is_builder_plugin_active() ? array( 'theme_customizer' => array( 'name' => esc_html__( 'Theme Customizer', 'et_builder' ), 'applicability' => $applicability_roles, ), 'page_options' => array( 'name' => esc_html__( 'Page Options', 'et_builder' ), ), ) : array(); $all_role_options = array( 'general_capabilities' => array( 'section_title' => '', 'options' => array( 'theme_options' => array( 'name' => et_is_builder_plugin_active() ? esc_html__( 'Plugin Options', 'et_builder' ) : esc_html__( 'Theme Options', 'et_builder' ), 'applicability' => $applicability_roles, ), // Added capabilities, so we can control menu role wise effectively. 'divi_library' => array( 'name' => esc_html__( 'Divi Library', 'et_builder' ), 'applicability' => $applicability_roles, 'capabilities' => 'export', ), 'theme_builder' => array( 'name' => esc_html__( 'Theme Builder', 'et_builder' ), 'applicability' => $tb_applicability_roles, ), 'support_center' => array( 'name' => esc_html__( 'Support Center', 'et_builder' ), 'applicability' => $applicability_roles, ), 'divi_ai' => array( 'name' => esc_html__( 'Divi AI', 'et_builder' ), 'applicability' => array( 'administrator', 'editor' ), ), 'ab_testing' => array( 'name' => esc_html__( 'Split Testing', 'et_builder' ), ), ), ), 'builder_capabilities' => array( 'section_title' => esc_html__( 'Builder Interface', 'et_builder' ), 'options' => array( 'add_module' => array( 'name' => esc_html__( 'Add/Delete Item', 'et_builder' ), ), 'edit_module' => array( 'name' => esc_html__( 'Edit Item', 'et_builder' ), ), 'move_module' => array( 'name' => esc_html__( 'Move Item', 'et_builder' ), ), 'disable_module' => array( 'name' => esc_html__( 'Disable Item', 'et_builder' ), ), 'lock_module' => array( 'name' => esc_html__( 'Lock Item', 'et_builder' ), ), 'divi_builder_control' => array( 'name' => esc_html__( 'Toggle Divi Builder', 'et_builder' ), ), 'load_layout' => array( 'name' => esc_html__( 'Load Layout', 'et_builder' ), ), 'use_visual_builder' => array( 'name' => esc_html__( 'Use Visual Builder', 'et_builder' ), ), 'custom_fonts_management' => array( 'name' => esc_html__( 'Upload/Remove Fonts', 'et_builder' ), ), 'read_dynamic_content_custom_fields' => array( 'name' => esc_html__( 'Dynamic Content Custom Fields', 'et_builder' ), ), ), ), 'library_capabilities' => array( 'section_title' => esc_html__( 'Library Settings', 'et_builder' ), 'options' => array( 'save_library' => array( 'name' => esc_html__( 'Save To Library', 'et_builder' ), ), 'add_library' => array( 'name' => esc_html__( 'Add From Library', 'et_builder' ), ), 'edit_global_library' => array( 'name' => esc_html__( 'Edit Global Items', 'et_builder' ), ), ), ), 'module_tabs' => array( 'section_title' => esc_html__( 'Settings Tabs', 'et_builder' ), 'options' => $tabs_array, ), 'general_module_capabilities' => array( 'section_title' => esc_html__( 'Settings Types', 'et_builder' ), 'options' => $options_categories, ), 'module_capabilies' => array( 'section_title' => esc_html__( 'Module Use', 'et_builder' ), 'options' => $module_capabilies, ), ); $all_role_options = apply_filters( 'add_et_builder_role_options', $all_role_options ); $all_role_options['general_capabilities']['options'] = array_merge( $all_role_options['general_capabilities']['options'], $theme_only_options ); // Set portability capabilities. $registered_portabilities = et_core_cache_get_group( 'et_core_portability' ); if ( ! empty( $registered_portabilities ) ) { $all_role_options['general_capabilities']['options']['portability'] = array( 'name' => esc_html__( 'Portability', 'et_builder' ), ); $all_role_options['portability'] = array( 'section_title' => esc_html__( 'Portability', 'et_builder' ), 'options' => array(), ); // Dynamically create an option foreach portability. foreach ( $registered_portabilities as $portability_context => $portability_instance ) { $portability_options = array( 'name' => esc_html( $portability_instance->name ), ); if ( isset( $portability_instance->applicability ) ) { $portability_options['applicability'] = $portability_instance->applicability; } $all_role_options['portability']['options'][ "{$portability_context}_portability" ] = $portability_options; } } return $all_role_options; } /** * * Prints the admin page for Role Editor */ function et_pb_display_role_editor() { $all_role_options = et_pb_all_role_options(); $option_tabs = ''; $menu_tabs = ''; $builder_roles_array = et_pb_get_all_roles_list(); foreach ( $builder_roles_array as $role => $role_title ) { $option_tabs .= et_pb_generate_roles_tab( $all_role_options, $role ); $menu_tabs .= sprintf( '<a href="#" class="et-pb-layout-buttons%4$s" data-open_tab="et_pb_role-%3$s_options" title="%1$s"> <span>%2$s</span> </a>', esc_attr( $role_title ), esc_html( $role_title ), esc_attr( $role ), 'administrator' === $role ? ' et_pb_roles_active_menu' : '' ); } printf( '<div class="et_pb_roles_main_container"> <a href="#" id="et_pb_save_roles" class="button button-primary button-large">%3$s</a> <h3 class="et_pb_roles_title"><span>%2$s</span></h3> <div id="et_pb_main_container" class="post-type-page"> <div id="et_pb_layout_controls"> %1$s <a href="#" class="et-pb-layout-buttons et-pb-layout-buttons-reset" title="Reset all settings"> <span class="icon"></span><span class="label">Reset</span> </a> %4$s </div> </div> <div class="et_pb_roles_container_all"> %5$s </div> </div>', et_core_esc_previously( $menu_tabs ), esc_html__( 'Divi Role Editor', 'et_builder' ), esc_html__( 'Save Divi Roles', 'et_builder' ), et_core_esc_previously( et_builder_portability_link( 'et_pb_roles', array( 'class' => 'et-pb-layout-buttons et-pb-portability-button' ) ) ), et_core_esc_previously( $option_tabs ) ); } /** * Generates the options tab for specified role. * * @param array $all_role_options array of all Role options. * @param string $role role. * * @return string */ function et_pb_generate_roles_tab( $all_role_options, $role ) { $form_sections = ''; // generate all sections of the form for current role. if ( ! empty( $all_role_options ) ) { foreach ( $all_role_options as $capability_id => $capability_options ) { if ( isset( $capability_options['applicability'] ) && ! in_array( $role, $capability_options['applicability'], true ) ) { continue; } $form_sections .= sprintf( '<div class="et_pb_roles_section_container"> %1$s <div class="et_pb_roles_options_internal"> %2$s </div> </div>', ! empty( $capability_options['section_title'] ) ? sprintf( '<h4 class="et_pb_roles_divider">%1$s <span class="et_pb_toggle_all"></span></h4>', esc_html( $capability_options['section_title'] ) ) : '', et_pb_generate_capabilities_output( $capability_options['options'], $role ) ); } } $output = sprintf( '<div class="et_pb_roles_options_container et_pb_role-%2$s_options%3$s"> <p class="et_pb_roles_notice">%1$s</p> <form id="et_pb_%2$s_role" data-role_id="%2$s"> %4$s </form> </div>', esc_html__( 'Using the Divi Role Editor, you can limit the types of actions that can be taken by WordPress users of different roles. This is a great way to limit the functionality available to your customers or guest authors to ensure that they only have the necessary options available to them.', 'et_builder' ), esc_attr( $role ), 'administrator' === $role ? ' active-container' : '', $form_sections // #4 ); return $output; } /** * Generates the enable/disable buttons list based on provided capabilities array and role. * * @param array $cap_array capabilities. * @param string $role user role. * * @return string */ function et_pb_generate_capabilities_output( $cap_array, $role ) { $output = ''; if ( ! empty( $cap_array ) ) { $user_has_all_capabilities = true; $role_obj = get_role( $role ); foreach ( $cap_array as $capability => $capability_details ) { if ( empty( $capability_details['applicability'] ) || ( ! empty( $capability_details['applicability'] ) && in_array( $role, $capability_details['applicability'], true ) ) ) { // $capability_details['capabilities'] is an array of capabilities that are required to see this option. if ( isset( $capability_details['capabilities'] ) ) { if ( is_string( $capability_details['capabilities'] ) && ! $role_obj->has_cap( $capability_details['capabilities'] ) ) { $user_has_all_capabilities = false; } elseif ( is_array( $capability_details['capabilities'] ) ) { foreach ( $capability_details['capabilities'] as $capability ) { if ( ! $role_obj->has_cap( $capability ) ) { $user_has_all_capabilities = false; break; } } } if ( ! $user_has_all_capabilities ) { continue; } } $output .= sprintf( '<div class="et_pb_capability_option"> <span class="et_pb_capability_title">%4$s</span> <div class="et_pb_yes_no_button_wrapper"> <div class="et_pb_yes_no_button et_pb_on_state"> <span class="et_pb_value_text et_pb_on_value">%1$s</span> <span class="et_pb_button_slider"></span> <span class="et_pb_value_text et_pb_off_value">%2$s</span> </div> <select name="%3$s" id="%3$s" class="et-pb-main-setting regular-text"> <option value="on" %5$s>Yes</option> <option value="off" %6$s>No</option> </select> </div> </div>', esc_html__( 'Enabled', 'et_builder' ), esc_html__( 'Disabled', 'et_builder' ), esc_attr( $capability ), esc_html( $capability_details['name'] ), selected( true, et_pb_is_allowed( $capability, $role ), false ), selected( false, et_pb_is_allowed( $capability, $role ), false ) ); } } } return $output; } /** * Loads scripts and styles for Role Editor Admin page * * @param string $hook hook name. */ function et_pb_load_roles_admin( $hook ) { // load scripts only on role editor page. if ( apply_filters( 'et_pb_load_roles_admin_hook', 'divi_page_et_divi_role_editor' ) !== $hook ) { return; } et_core_load_main_fonts(); wp_enqueue_style( 'builder-roles-editor-styles', ET_BUILDER_URI . '/styles/roles_style.css', array( 'et-core-admin' ), ET_BUILDER_VERSION ); wp_enqueue_script( 'builder-roles-editor-scripts', ET_BUILDER_URI . '/scripts/roles_admin.js', array( 'jquery' ), ET_BUILDER_VERSION, true ); $pb_roles_options = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'et_roles_nonce' => wp_create_nonce( 'et_roles_nonce' ), 'modal_title' => esc_html__( 'Reset Roles', 'et_builder' ), 'modal_message' => esc_html__( 'All of your current role settings will be set to defaults. Do you wish to proceed?', 'et_builder' ), 'modal_yes' => et_builder_i18n( 'Yes' ), 'modal_no' => et_builder_i18n( 'No' ), ); wp_localize_script( 'builder-roles-editor-scripts', 'et_pb_roles_options', $pb_roles_options ); } add_action( 'admin_enqueue_scripts', 'et_pb_load_roles_admin' ); /** * Generates the array of allowed modules in jQuery Array format. * * @param string $role the user role. * * @return string */ function et_pb_allowed_modules_list( $role = '' ) { global $typenow; // always return empty array if user doesn't have the edit_posts capability. if ( ! current_user_can( 'edit_posts' ) ) { return '[]'; } $saved_capabilities = et_pb_get_role_settings(); $role = '' === $role ? et_pb_get_current_user_role() : $role; $all_modules_array = ET_Builder_Element::get_modules_array( $typenow ); $saved_modules_capabilities = isset( $saved_capabilities[ $role ] ) ? $saved_capabilities[ $role ] : array(); $alowed_modules = '['; foreach ( $all_modules_array as $module => $module_details ) { if ( ! in_array( $module_details['label'], array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner', 'et_pb_column' ), true ) ) { // Add module into the list if it's not saved or if it's saved not with "off" state. if ( ! isset( $saved_modules_capabilities[ $module_details['label'] ] ) || ( isset( $saved_modules_capabilities[ $module_details['label'] ] ) && 'off' !== $saved_modules_capabilities[ $module_details['label'] ] ) ) { $alowed_modules .= "'" . esc_attr( $module_details['label'] ) . "',"; } } } $alowed_modules .= ']'; return $alowed_modules; } if ( ! function_exists( 'et_divi_get_post_text_color' ) ) { /** * Return text color css class. * * @return mixed|string */ function et_divi_get_post_text_color() { $text_color_class = ''; $post_format = et_pb_post_format(); if ( in_array( $post_format, array( 'audio', 'link', 'quote' ), true ) ) { $text_color = get_post_meta( get_the_ID(), '_et_post_bg_layout', true ); $text_color_class = $text_color ? $text_color : 'light'; $text_color_class = ' et_pb_text_color_' . $text_color_class; } return $text_color_class; } } if ( ! function_exists( 'et_divi_get_post_bg_inline_style' ) ) { /** * Return css style attribute that ho;d background color inline style. * * @return string */ function et_divi_get_post_bg_inline_style() { $inline_style = ''; $post_id = get_the_ID(); $post_use_bg_color = get_post_meta( $post_id, '_et_post_use_bg_color', true ) ? true : false; $bg_color = get_post_meta( $post_id, '_et_post_bg_color', true ); $post_bg_color = $bg_color && '' !== $bg_color ? $bg_color : '#ffffff'; if ( $post_use_bg_color ) { $inline_style = sprintf( ' style="background-color: %1$s;"', esc_html( $post_bg_color ) ); } return $inline_style; } } /** * Remove the blockquote from post content. * * @param string $content post content. * * @return string|string[]|null */ function et_remove_blockquote_from_content( $content ) { if ( 'quote' !== et_pb_post_format() ) { return $content; } if ( et_theme_builder_overrides_layout( ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ) ) { // Do not remove quotes when TB has taken over. return $content; } $content = preg_replace( '/<blockquote(.+?)<\/blockquote>/is', '', $content, 1 ); return $content; } add_filter( 'the_content', 'et_remove_blockquote_from_content' ); /** * Register rewrite rule and tag for preview page * * @return void */ function et_pb_register_preview_endpoint() { add_rewrite_tag( '%et_pb_preview%', 'true' ); } add_action( 'init', 'et_pb_register_preview_endpoint', 11 ); /** * Flush rewrite rules to fix the issue "preg_match" issue with 2.5 * * @return void */ function et_pb_maybe_flush_rewrite_rules() { et_builder_maybe_flush_rewrite_rules( '2_5_flush_rewrite_rules' ); } add_action( 'init', 'et_pb_maybe_flush_rewrite_rules', 9 ); /** * Register template for preview page. * * @param string $template The path of the template to include. * * @return string path to template file */ function et_pb_register_preview_page( $template ) { global $wp_query; if ( 'true' === $wp_query->get( 'et_pb_preview' ) && isset( $_GET['et_pb_preview_nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. show_admin_bar( false ); return ET_BUILDER_DIR . 'template-preview.php'; } return $template; } add_action( 'template_include', 'et_pb_register_preview_page' ); /** * Disable all the dynamic assets for preview page so all the styles can be rendered correctly. * * @return void */ function et_pb_preview_page_disable_dynamic_assets() { global $wp_query; if ( 'true' === $wp_query->get( 'et_pb_preview' ) && isset( $_GET['et_pb_preview_nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. // Instruct Shortcode Manager to register/load all modules/shortcodes. add_filter( 'et_builder_should_load_all_module_data', '__return_true' ); // Disable Feature: Dynamic Assets. add_filter( 'et_disable_js_on_demand', '__return_true' ); add_filter( 'et_use_dynamic_css', '__return_false' ); add_filter( 'et_should_generate_dynamic_assets', '__return_false' ); // Disable Feature: Critical CSS. add_filter( 'et_builder_critical_css_enabled', '__return_false' ); // Disable Cache in Feature Manager. add_filter( 'et_builder_post_feature_cache_enabled', '__return_false' ); } } add_action( 'wp', 'et_pb_preview_page_disable_dynamic_assets' ); if ( ! function_exists( 'et_builder_replace_code_content_entities' ) ) : /** * The do_shortcode() replaces square brackers with html entities, * convert them back to make sure js code works ok * * @param string $content post content. */ function et_builder_replace_code_content_entities( $content ) { $content = str_replace( '[', '[', $content ); $content = str_replace( ']', ']', $content ); $content = str_replace( '×', 'x', $content ); return $content; } endif; if ( ! function_exists( 'et_builder_convert_line_breaks' ) ) : /** * We use placeholders to preserve the line-breaks, convert them back to "\n". * * @param string $content content. * @param string $line_breaks_format line break format e.g \n or <br>. */ function et_builder_convert_line_breaks( $content, $line_breaks_format = "\n" ) { // before we swap out the placeholders, // remove all the <p> tags and \n that wpautop added! $content = preg_replace( '/\n/smi', '', $content ); $content = preg_replace( '/<p>/smi', '', $content ); $content = preg_replace( '/<\/p>/smi', '', $content ); $content = str_replace( array( '<!– [et_pb_line_break_holder] –>', '<!-- [et_pb_line_break_holder] -->', '||et_pb_line_break_holder||' ), $line_breaks_format, $content ); $content = str_replace( '<!–- [et_pb_br_holder] -–>', '<br />', $content ); // convert the <pee tags back to <p // see et_pb_prep_code_module_for_wpautop(). $content = str_replace( '<pee', '<p', $content ); $content = str_replace( '</pee>', '</p> ', $content ); return $content; } endif; /** * Adjust the number of all layouts displayed on library page to exclude predefined layouts. * * @param object $counts An object containing the current post_type's post * counts by status. * * @return mixed */ function et_pb_fix_count_library_items( $counts ) { // do nothing if get_current_screen function doesn't exists at this point to avoid php errors in some plugins. if ( ! function_exists( 'get_current_screen' ) ) { return $counts; } $current_screen = get_current_screen(); if ( isset( $current_screen->id ) && 'edit-et_pb_layout' === $current_screen->id && isset( $counts->publish ) ) { // perform query to get all the not predefined layouts. $query = new WP_Query( array( 'meta_query' => array( array( 'key' => '_et_pb_predefined_layout', 'value' => 'on', 'compare' => 'NOT EXISTS', ), ), 'post_type' => ET_BUILDER_LAYOUT_POST_TYPE, 'posts_per_page' => '-1', ) ); // set the $counts->publish = amount of non predefined layouts. $counts->publish = isset( $query->post_count ) ? (int) $query->post_count : 0; } return $counts; } add_filter( 'wp_count_posts', 'et_pb_fix_count_library_items' ); /** * Mobile settings tabs: Desktop, Table, Smartphone. * * @return string */ function et_pb_generate_mobile_settings_tabs() { $mobile_settings_tabs = '<%= window.et_builder.mobile_tabs_output() %>'; return $mobile_settings_tabs; } /** * Generates the css code for responsive options. * * Uses array of values for each device as input parameter and css_selector with property to * apply the css * * @deprecated See ET_Builder_Module_Helper_ResponsiveOptions::instance()->generate_responsive_css(). * * @since 3.23 Deprecated. * * @param array $values_array All device values. * @param mixed $css_selector CSS selector. * @param string $css_property CSS property. * @param string $function_name Module slug. * @param string $additional_css Additional CSS. */ function et_pb_generate_responsive_css( $values_array, $css_selector, $css_property, $function_name, $additional_css = '' ) { if ( ! empty( $values_array ) ) { foreach ( $values_array as $device => $current_value ) { if ( '' === $current_value ) { continue; } $declaration = ''; // value can be provided as a string or array in following format - array( 'property_1' => 'value_1', 'property_2' => 'property_2', ... , 'property_n' => 'value_n' ). if ( is_array( $current_value ) && ! empty( $current_value ) ) { foreach ( $current_value as $this_property => $this_value ) { if ( '' === $this_value ) { continue; } $declaration .= sprintf( '%1$s: %2$s%3$s', $this_property, esc_html( et_builder_process_range_value( $this_value, $this_property ) ), '' !== $additional_css ? $additional_css : ';' ); } } else { $declaration = sprintf( '%1$s: %2$s%3$s', $css_property, esc_html( et_builder_process_range_value( $current_value, $css_property ) ), '' !== $additional_css ? $additional_css : ';' ); } if ( '' === $declaration ) { continue; } $style = array( 'selector' => $css_selector, 'declaration' => $declaration, ); if ( 'desktop_only' === $device ) { $style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } elseif ( 'desktop' !== $device ) { $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $style['media_query'] = ET_Builder_Element::get_media_query( $current_media_query ); } ET_Builder_Element::set_style( $function_name, $style ); } } } /** * Search module: search the posts. * * @param mixed $query search WP_Query object. */ function et_pb_custom_search( $query = false ) { if ( is_admin() || ! is_a( $query, 'WP_Query' ) || ! $query->is_search ) { return; } $utils = ET_Core_Data_Utils::instance(); // phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( isset( $_GET['et_pb_searchform_submit'] ) ) { $post_types = array(); if ( ! isset( $_GET['et_pb_include_posts'] ) && ! isset( $_GET['et_pb_include_pages'] ) ) { $post_types = array( 'post' ); } if ( isset( $_GET['et_pb_include_pages'] ) ) { $post_types = array( 'page' ); } if ( isset( $_GET['et_pb_include_posts'] ) ) { $post_types[] = 'post'; } // $postTypes is allowlisted values only $query->set( 'post_type', $post_types ); if ( ! empty( $_GET['et_pb_search_cat'] ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- Search categories sanitization has been done below on each cat. $categories_array = explode( ',', $_GET['et_pb_search_cat'] ); $categories_array = $utils->sanitize_text_fields( $categories_array ); $query->set( 'category__not_in', $categories_array ); } if ( isset( $_GET['et-posts-count'] ) ) { $query->set( 'posts_per_page', (int) $_GET['et-posts-count'] ); } } // phpcs:enable } add_action( 'pre_get_posts', 'et_pb_custom_search' ); if ( ! function_exists( 'et_custom_comments_display' ) ) : /** * Custom callback function to control the look of the comment. * * @param WP_Comment $comment comment object. * @param WP_Comment $args args. * @param WP_Comment $depth comment depth. */ function et_custom_comments_display( $comment, $args, $depth ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Set the current comment. $GLOBALS['comment'] = $comment; $default_avatar = get_option( 'avatar_default' ) ? get_option( 'avatar_default' ) : 'mystery'; ?> <li <?php comment_class(); ?> id="li-comment-<?php comment_ID(); ?>"> <article id="comment-<?php comment_ID(); ?>" class="comment-body clearfix"> <div class="comment_avatar"> <?php echo get_avatar( $comment, $size = '80', esc_attr( $default_avatar ), esc_attr( get_comment_author() ) ); ?> </div> <div class="comment_postinfo"> <?php printf( '<span class="fn">%s</span>', get_comment_author_link() ); ?> <span class="comment_date"> <?php /* translators: 1: date, 2: time */ printf( esc_html__( 'on %1$s at %2$s', 'et_builder' ), esc_html( get_comment_date() ), esc_html( get_comment_time() ) ); ?> </span> <?php edit_comment_link( esc_html__( '(Edit)', 'et_builder' ), ' ' ); ?> <?php $comment_reply_link_args = array( 'reply_text' => esc_html__( 'Reply', 'et_builder' ), 'depth' => (int) $depth, 'max_depth' => (int) $args['max_depth'], ); $et_comment_reply_link = get_comment_reply_link( array_merge( $args, $comment_reply_link_args ) ); ?> </div> <div class="comment_area"> <?php if ( '0' === $comment->comment_approved ) : ?> <em class="moderation"><?php esc_html_e( 'Your comment is awaiting moderation.', 'et_builder' ); ?></em> <br /> <?php endif; ?> <div class="comment-content clearfix"> <?php comment_text(); if ( $et_comment_reply_link ) { echo '<span class="reply-container">' . et_core_esc_previously( $et_comment_reply_link ) . '</span>'; } ?> </div> </div> </article> <?php } endif; /** * Exclude library related taxonomies from Yoast SEO Sitemap. * * @param array $value excluded taxonomies. * @param string $taxonomy taxonomy name. * * @return bool */ function et_wpseo_sitemap_exclude_taxonomy( $value, $taxonomy ) { $excluded = array( 'scope', 'module_width', 'layout_type', 'layout_category', 'layout', 'layout_pack' ); if ( in_array( $taxonomy, $excluded, true ) ) { return true; } return false; } add_filter( 'wpseo_sitemap_exclude_taxonomy', 'et_wpseo_sitemap_exclude_taxonomy', 10, 2 ); if ( ! function_exists( 'et_is_wp_job_manager_plugin_active' ) ) : /** * Is WP Job Manager plugin active? * * @return bool True - if the plugin is active */ function et_is_wp_job_manager_plugin_active() { return class_exists( 'WP_Job_Manager' ); } endif; if ( ! function_exists( 'et_is_gutenberg_active' ) ) : /** * Is Gutenberg active? * * @deprecated See {@see et_core_is_gutenberg_active()} * * @since 3.19.2 Renamed and moved to core. * @since 3.18 * * @return bool True - if the plugin is active */ function et_is_gutenberg_active() { return et_core_is_gutenberg_active(); } endif; if ( ! function_exists( 'et_is_gutenberg_enabled' ) ) : /** * Is Gutenberg active and enabled for the current post * WP 5.0 WARNING - don't use before global post has been set * * @deprecated See {@see et_core_is_gutenberg_enabled()} * * @since 3.19.2 Renamed and moved to core. * @since 3.18 * * @return bool True - if the plugin is active and enabled. */ function et_is_gutenberg_enabled() { return et_core_is_gutenberg_enabled(); } endif; /** * Modify comment count for preview screen. Somehow WordPress' get_comments_number() doesn't get correct $post_id * param and doesn't have proper fallback to global $post if $post_id variable isn't found. This causes incorrect * comment count in preview screen * * @see get_comments_number() * @see get_comments_number_text() * @see comments_number() * * @param integer $count comment count. * @param integer $post_id post id. * @return string */ function et_pb_preview_comment_count( $count, $post_id ) { if ( is_et_pb_preview() ) { global $post; $count = isset( $post->comment_count ) ? $post->comment_count : $count; } return $count; } add_filter( 'get_comments_number', 'et_pb_preview_comment_count', 10, 2 ); /** * List of shortcodes that triggers error if being used in admin * * @return array shortcode tag */ function et_pb_admin_excluded_shortcodes() { $shortcodes = array(); // Triggers issue if Sensei and YOAST SEO are activated. if ( et_is_yoast_seo_plugin_active() && function_exists( 'Sensei' ) ) { $shortcodes[] = 'usercourses'; } // WPL real estate prints unwanted on-page JS that caused an issue on BB. if ( class_exists( 'wpl_extensions' ) ) { $shortcodes[] = 'WPL'; } // [submit_job_form] shortcode prints wp_editor this creating problems post edit page render if ( et_is_wp_job_manager_plugin_active() ) { $shortcodes[] = 'submit_job_form'; } // [shop_messages] shortcode causes a fatal error when rendered too soon if ( et_is_woocommerce_plugin_active() ) { $shortcodes[] = 'shop_messages'; } return apply_filters( 'et_pb_admin_excluded_shortcodes', $shortcodes ); } /** * Get GMT offset string that can be used for parsing date into correct timestamp * * @return string */ function et_pb_get_gmt_offset_string() { $gmt_offset = get_option( 'gmt_offset' ); $gmt_divider = '-' === substr( $gmt_offset, 0, 1 ) ? '-' : '+'; $gmt_offset_hour = str_pad( abs( intval( $gmt_offset ) ), 2, '0', STR_PAD_LEFT ); $gmt_offset_minute = str_pad( ( ( abs( $gmt_offset ) * 100 ) % 100 ) * ( 60 / 100 ), 2, '0', STR_PAD_LEFT ); $gmt_offset_string = "GMT{$gmt_divider}{$gmt_offset_hour}{$gmt_offset_minute}"; return $gmt_offset_string; } /** * Get post's category label and permalink to be used on frontend * * @param int $post_id post id. * @param WP_Term[] $default term objects. * * @return array categories */ function et_pb_get_post_categories( $post_id, $default = array() ) { $categories = get_the_category( $post_id ); $post_categories = array(); if ( $default && ! $categories ) { $categories = array_values( $default ); foreach ( array_keys( $categories ) as $key ) { _make_cat_compat( $categories[ $key ] ); } } // Filter out any falsy values that may appear due from $default. $categories = array_filter( $categories ); if ( ! empty( $categories ) ) { foreach ( $categories as $category ) { $post_categories[ $category->cat_ID ] = array( 'id' => $category->cat_ID, 'label' => $category->cat_name, 'permalink' => get_category_link( $category->cat_ID ), ); } } return $post_categories; } /** * Generates "Use Visual Builder" button url * * @return string */ function et_fb_get_page_url() { global $wp; $post_id = get_the_ID(); $is_divi_library = 'et_pb_layout' === get_post_type( $post_id ); if ( $is_divi_library ) { return get_edit_post_link( $post_id ); } elseif ( is_singular() ) { return get_permalink( $post_id ); } return add_query_arg( $wp->query_string, '', home_url( $wp->request ) ); } /** * Add "Use Visual Builder" link to WP-Admin bar * * @return void */ function et_fb_add_admin_bar_link() { $is_not_builder_enabled_single = ! is_singular() || ! et_builder_fb_enabled_for_post( get_the_ID() ); $is_not_in_wc_shop = ! et_builder_used_in_wc_shop(); $not_allowed_fb_access = ! et_pb_is_allowed( 'use_visual_builder' ); $app_preferences = et_fb_app_preferences_settings(); $default_visual_theme_builder = et_()->array_get( $app_preferences, 'enable_visual_theme_builder.default' ); $is_visual_theme_builder_enabled = et_get_option( 'et_fb_pref_builder_enable_visual_theme_builder', $default_visual_theme_builder ); $is_not_theme_builder_used = ! et_fb_is_theme_builder_used_on_page() || ! et_pb_is_allowed( 'theme_builder' ) || ! filter_var( $is_visual_theme_builder_enabled, FILTER_VALIDATE_BOOLEAN ); $is_enabled_for_post_type = ! is_singular() || et_builder_enabled_for_post_type( get_post_type( get_the_ID() ) ); // Return if builder is not allowed for the user. if ( $not_allowed_fb_access ) { return; } // Return if builder is not enabled for the post type. if ( ! $is_enabled_for_post_type && $is_not_builder_enabled_single ) { return; } // Return for non-singular pages if they are not WC shop page and Theme Builder is not used. if ( $is_not_builder_enabled_single && $is_not_in_wc_shop && $is_not_theme_builder_used ) { return; } global $wp_admin_bar, $wp_the_query; $post_id = get_the_ID(); // WooCommerce Shop Page replaces main query, thus it has to be normalized. if ( et_builder_used_in_wc_shop() && method_exists( $wp_the_query, 'get_queried_object' ) && isset( $wp_the_query->get_queried_object()->ID ) ) { $post_id = $wp_the_query->get_queried_object()->ID; } $page_url = et_fb_get_page_url(); // Don't add the link, if Frontend Builder has been loaded already. if ( et_fb_is_enabled() || et_fb_is_enabled_on_any_template() ) { $wp_admin_bar->add_menu( array( 'id' => 'et-disable-visual-builder', 'title' => esc_html__( 'Exit Visual Builder', 'et_builder' ), 'href' => esc_url( $page_url ), ) ); return; } $current_object = $wp_the_query->get_queried_object(); $user_is_allowed_to_edit = isset( $current_object->ID ) ? current_user_can( 'edit_post', $current_object->ID ) : ( et_fb_is_theme_builder_used_on_page() && et_pb_is_allowed( 'theme_builder' ) ); if ( is_admin() || ! $user_is_allowed_to_edit || ! et_pb_is_allowed( 'divi_builder_control' ) ) { return; } $use_visual_builder_url = et_pb_is_pagebuilder_used( $post_id ) || ( et_fb_is_theme_builder_used_on_page() && ! is_singular() ) ? et_fb_get_builder_url( $page_url ) : add_query_arg( array( 'et_fb_activation_nonce' => wp_create_nonce( 'et_fb_activation_nonce_' . $post_id ), ), $page_url ); $wp_admin_bar->add_menu( array( 'id' => 'et-use-visual-builder', 'title' => esc_html__( 'Enable Visual Builder', 'et_builder' ), 'href' => esc_url( $use_visual_builder_url ), ) ); } add_action( 'admin_bar_menu', 'et_fb_add_admin_bar_link', 999 ); /** * Retrieve and process saved Layouts. * It different than the function which retrieves saved Sections, Rows and Modules from library because layouts require different processing */ function et_fb_get_saved_layouts() { if ( ! isset( $_POST['et_fb_retrieve_library_modules_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_retrieve_library_modules_nonce'], 'et_fb_retrieve_library_modules_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } // Reduce number of results per page if we're hosted on wpengine to avoid 500 error due to memory allocation. // This is caused by one of their custom mu-plugins doing additional stuff but we have no control over there. $page_size = function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ? 25 : 50; $post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post'; $layouts_type = ! empty( $_POST['et_load_layouts_type'] ) ? sanitize_text_field( $_POST['et_load_layouts_type'] ) : 'all'; $start_from = ! empty( $_POST['et_templates_start_page'] ) ? sanitize_text_field( $_POST['et_templates_start_page'] ) : 0; $post_type = apply_filters( 'et_pb_show_all_layouts_built_for_post_type', $post_type, $layouts_type ); $all_layouts_data = et_pb_retrieve_templates( 'layout', '', 'false', '0', $post_type, $layouts_type, array( $start_from, $page_size ) ); $all_layouts_data_processed = $all_layouts_data; $next_page = 'none'; if ( 0 !== $start_from && empty( $all_layouts_data ) ) { $all_layouts_data_processed = array(); } else { if ( empty( $all_layouts_data ) ) { $all_layouts_data_processed = array( 'error' => esc_html__( 'You have not saved any items to your Divi Library yet. Once an item has been saved to your library, it will appear here for easy use.', 'et_builder' ) ); } else { foreach ( $all_layouts_data as $index => $data ) { $all_layouts_data_processed[ $index ]['shortcode'] = et_fb_process_shortcode( $data['shortcode'] ); } $next_page = $start_from + $page_size; } } $json_templates = wp_json_encode( array( 'templates_data' => $all_layouts_data_processed, 'next_page' => $next_page, ) ); die( et_core_intentionally_unescaped( $json_templates, 'html' ) ); } add_action( 'wp_ajax_et_fb_get_saved_layouts', 'et_fb_get_saved_layouts' ); /** * Ajax Callback: Process imported content. */ function et_fb_process_imported_content() { if ( ! isset( $_POST['et_fb_process_imported_data_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_process_imported_data_nonce'], 'et_fb_process_imported_data_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. die( -1 ); } if ( ! current_user_can( 'edit_posts' ) ) { die( -1 ); } // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- Value in `$_POST['et_raw_shortcode']` is processed by `et_fb_process_shortcode` and being returned in ajax response. $processed_shortcode = isset( $_POST['et_raw_shortcode'] ) ? et_fb_process_shortcode( stripslashes( $_POST['et_raw_shortcode'] ) ) : ''; die( wp_json_encode( $processed_shortcode ) ); } add_action( 'wp_ajax_et_fb_process_imported_content', 'et_fb_process_imported_content' ); /** * Builder initial content. * * @param string $content post content. * @param integer $post_id post id. * * @return string */ function et_fb_maybe_get_bfb_initial_content( $content, $post_id ) { $from_post = isset( $_GET['from_post'] ) ? sanitize_text_field( $_GET['from_post'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF. if ( ! empty( $from_post ) && 'empty' !== $from_post ) { $copy_content_from = get_post( $from_post ); $existing_content = $copy_content_from->post_content; if ( '' !== $existing_content && has_shortcode( $existing_content, 'et_pb_section' ) ) { return $existing_content; } } // process the content only for BFB. if ( ! et_builder_bfb_enabled() ) { return $content; } // If content already has a section, it means builder is active and activation has to be // skipped to avoid nested and unwanted builder structure. if ( has_shortcode( $content, 'et_pb_section' ) ) { return $content; } // Save old content. $saved_old_content = get_post_meta( $post_id, '_et_pb_old_content', true ); $save_old_content = false; $post = get_post( $post_id ); if ( '' !== $content ) { $save_old_content = update_post_meta( $post_id, '_et_pb_old_content', $content ); } /** * Filters the flag that sets default Content during Builder activation. * * @since 3.29 * * @used-by et_builder_wc_init() */ if ( apply_filters( 'et_builder_skip_content_activation', false, $post ) ) { return $content; } if ( true !== $save_old_content && $saved_old_content !== $content && '' !== $content ) { return $content; } $text_module = '' !== $content ? '[et_pb_text admin_label="Text"]' . $content . '[/et_pb_text]' : ''; // Re-format content. $updated_content = '[et_pb_section admin_label="section"] [et_pb_row admin_label="row"] [et_pb_column type="4_4"]' . $text_module . '[/et_pb_column] [/et_pb_row] [/et_pb_section]'; return $updated_content; } /** * Called via async AJAX call after the builder rendered. It will regenerate both helper/definitions files. * If their content changed, the builder will trigger a page reload to use the updated cached files. */ function et_fb_update_builder_assets() { if ( ! isset( $_POST['et_fb_helper_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_helper_nonce'], 'et_fb_update_helper_assets_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. die( -1 ); } $post_id = ! empty( $_POST['et_post_id'] ) ? sanitize_text_field( $_POST['et_post_id'] ) : ''; if ( ! current_user_can( 'edit_post', $post_id ) ) { die( -1 ); } // Set current post as global $post. $post = get_post( $post_id ); // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited $post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post'; // Update helpers cached js file. $helpers = et_fb_get_dynamic_asset( 'helpers', $post_type, true ); // Update definitions cached js file. $definitions = et_fb_get_dynamic_asset( 'definitions', $post_type, true ); // When either definitions or helpers needs an update, also clear modules cache. if ( $definitions['updated'] || $helpers['updated'] ) { $modules_cache = ET_Builder_Element::get_cache_filename( $post_type ); if ( file_exists( $modules_cache ) ) { @unlink( $modules_cache ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- `unlink` may fail with the permissions denied error. } } die( wp_json_encode( array( 'helpers' => $helpers, 'definitions' => $definitions, ) ) ); } add_action( 'wp_ajax_et_fb_update_builder_assets', 'et_fb_update_builder_assets' ); /** * Returns builder definitions. * * @param string $post_type post type. * * @return array */ function et_fb_get_builder_definitions( $post_type ) { // force render builder data when retrieving builder definition to ensure definitions retrieved via ajax call // equal to definitions retrieved on wp_footer when no dynamic asset cache found. add_filter( 'et_builder_module_force_render', '__return_true' ); $fields_data = array(); $fields_data['custom_css'] = ET_Builder_Element::get_custom_css_fields( $post_type ); $fields_data['advanced_fields'] = ET_Builder_Element::get_advanced_fields( $post_type ); $fields_data['general_fields'] = ET_Builder_Element::get_general_fields( $post_type ); $fields_data['childModuleTitles'] = ET_Builder_Element::get_child_module_titles( $post_type ); $fields_data['optionsToggles'] = ET_Builder_Element::get_toggles( $post_type ); $fields_data['customTabs'] = ET_Builder_Element::get_tabs( $post_type ); $fields_data['customTabsFields'] = ET_Builder_Element::get_settings_modal_tabs_fields( $post_type ); $fields_data['customLayoutsTabs'] = ET_Builder_Library::builder_library_modal_custom_tabs( $post_type ); $fields_data['moduleItemsConfig'] = ET_Builder_Element::get_module_items_configs( $post_type ); $fields_data['moduleTransitions'] = ET_Builder_Element::get_modules_transitions( $post_type ); $fields_data['contact_form_input_defaults'] = et_fb_process_shortcode( sprintf( '[et_pb_contact_field field_title="%1$s" field_type="input" field_id="Name" required_mark="on" fullwidth_field="off" /][et_pb_contact_field field_title="%2$s" field_type="email" field_id="Email" required_mark="on" fullwidth_field="off" /][et_pb_contact_field field_title="%3$s" field_type="text" field_id="Message" required_mark="on" fullwidth_field="on" /]', esc_attr__( 'Name', 'et_builder' ), esc_attr__( 'Email Address', 'et_builder' ), esc_attr__( 'Message', 'et_builder' ) ) ); // Remove duplicates from field definitions. $map = array(); $unique_fields = array(); $unique_count = 0; foreach ( array( 'custom_css', 'general_fields', 'advanced_fields' ) as $source ) { $definitions = &$fields_data[ $source ]; $module_names = array_keys( $definitions ); foreach ( $module_names as $module_name ) { $module = &$definitions[ $module_name ]; $setting_names = array_keys( $module ); foreach ( $setting_names as $setting_name ) { $setting = &$module[ $setting_name ]; if ( 'advanced_defaults' === $setting_name ) { // advanced_defaults are just duplicated data, we can rebuilt them later. $setting = false; continue; } $key = wp_json_encode( $setting ); if ( ! isset( $map[ $key ] ) ) { // Found a duplicate here. $unique_fields[] = $setting; $map[ $key ] = $unique_count++; } $setting = $map[ $key ]; } } } // Remove force builder data render. remove_filter( 'et_builder_module_force_render', '__return_true' ); // No longer needed. unset( $map ); // Include the unique fields in the AJAX payload. $fields_data['unique_fields'] = $unique_fields; return $fields_data; } /** * Returns builder shortcode object. * * @param string $post_type the post type. * @param integer $post_id the post id. * @param string $layout_type layout type. * * @return array */ function et_fb_get_builder_shortcode_object( $post_type, $post_id, $layout_type ) { // We need to store the current post when this function is executed in a wp-admin page // to prevent post based modules included in the shortcode from altering the loop. global $post; $backup = $post; $fields_data = array(); add_filter( 'et_builder_module_force_render', '__return_true' ); $post_data = get_post( $post_id ); $post_data_post_modified = is_object( $post_data ) ? gmdate( 'U', strtotime( $post_data->post_modified ) ) : 0; $post_content = is_object( $post_data ) ? $post_data->post_content : ''; $is_theme_builder = et_builder_tb_enabled(); $is_backend_builder = et_builder_bfb_enabled(); $theme_builder_layouts = et_theme_builder_get_template_layouts(); // Unset main template from Theme Builder layouts to avoid PHP Notices. if ( isset( $theme_builder_layouts['et_template'] ) ) { unset( $theme_builder_layouts['et_template'] ); } // Get the content for all theme builder posts. foreach ( $theme_builder_layouts as $key => $theme_builder_layout ) { if ( 0 !== $theme_builder_layout['id'] && $theme_builder_layout['enabled'] && $theme_builder_layout['override'] ) { $post_data = get_post( $theme_builder_layout['id'] ); $theme_builder_layouts[ $key ]['post_content'] = $post_data->post_content; } } // if autosave exists here, return it with the real content, autosave.js and getServerSavedPostData() will look for it. $current_user_id = get_current_user_id(); // Store one autosave per author. If there is already an autosave, overwrite it. $autosave = wp_get_post_autosave( $post_id, $current_user_id ); if ( ! empty( $autosave ) ) { $autosave_post_modified = gmdate( 'U', strtotime( $autosave->post_modified ) ); if ( $autosave_post_modified > $post_data_post_modified ) { $fields_data['autosave_shortcode_object'] = et_fb_process_shortcode( $autosave->post_content ); $fields_data['has_newer_autosave'] = true; } else { $fields_data['has_newer_autosave'] = false; } // Delete the autosave, becuase we will present the option to use the autosave to the user, and they will use it or not // we need to delete the db copy now. wp_delete_post_revision( $autosave->ID ); } switch ( $layout_type ) { case 'module': $use_fullwidth_section = false !== strpos( $post_content, '[et_pb_fullwidth_' ) ? true : false; // Remove module placeholders. $post_content = false !== strpos( $post_content, 'et_pb_fullwidth_module_placeholder' ) || false !== strpos( $post_content, 'et_pb_module_placeholder' ) ? '' : $post_content; if ( ! $use_fullwidth_section ) { $post_content = sprintf( '[et_pb_row][et_pb_column type="4_4"]%1$s[/et_pb_column][/et_pb_row]', $post_content ); } $post_content = sprintf( '[et_pb_section%2$s]%1$s[/et_pb_section]', $post_content, $use_fullwidth_section ? ' fullwidth="on"' : '' ); break; case 'row': $post_content = '[et_pb_section]' . $post_content . '[/et_pb_section]'; break; } $post_content = et_fb_maybe_get_bfb_initial_content( $post_content, $post_id ); /** * Filters the raw post content when the Builder is loaded. * * @since 3.29 * * @param string $post_content The raw/unprocessed post content. * @param int $post_id Post ID. */ $post_content = apply_filters( 'et_fb_load_raw_post_content', $post_content, $post_id ); $fields_data['shortcode_object'] = array(); // Process main post content fields data. $post_fields_data = et_fb_process_shortcode( $post_content ); $fields_data['shortcode_object'] = array(); // In Visual Builder get All Theme Builder Areas. if ( et_pb_is_allowed( 'theme_builder' ) && ! $is_theme_builder && ! $is_backend_builder && ! empty( $theme_builder_layouts ) ) { // Process Theme Builder Header area fields data. if ( isset( $theme_builder_layouts['et_header_layout']['post_content'] ) ) { $theme_builder_header = apply_filters( 'et_fb_load_raw_post_content', $theme_builder_layouts['et_header_layout']['post_content'], $theme_builder_layouts['et_header_layout']['id'] ); $tb_header_fields_data = et_fb_process_shortcode( $theme_builder_header, '', '', '', '', 'et_header_layout' ); $processed_fields_data[] = $tb_header_fields_data; } // Process Theme Builder Body area. if ( isset( $theme_builder_layouts['et_body_layout']['post_content'] ) ) { $theme_builder_body = apply_filters( 'et_fb_load_raw_post_content', $theme_builder_layouts['et_body_layout']['post_content'], $theme_builder_layouts['et_body_layout']['id'] ); $tb_body_fields_data = et_fb_process_shortcode( $theme_builder_body, '', '', '', '', 'et_body_layout' ); $theme_builder_post_content_selector = et_fb_generate_post_content_module_selector( $tb_body_fields_data, 'theme_builder_content' ); if ( $theme_builder_post_content_selector && ! is_home() && ! is_archive() && ! is_404() ) { // If Theme Builder Body contains Post Content Module, replace it with real post content. $post_content_fields_data = et_fb_process_shortcode( $post_content, '', '', '', '', 'post_content' ); $processed_fields_data[] = et_fb_generate_tb_body_area_with_post_content( $tb_body_fields_data, $theme_builder_post_content_selector, $post_content_fields_data ); } else { // if not, just add Theme Builder Body area content. $processed_fields_data[] = $tb_body_fields_data; // Add Post content too, so it can be loaded when post content module is added. if ( ! is_home() && ! is_archive() && ! is_404() ) { $initial_post_content_fields_data = et_fb_process_shortcode( $post_content, '', '', '', '', 'initial_post_content' ); $processed_fields_data[] = $initial_post_content_fields_data; } } } elseif ( ! is_home() && et_pb_is_pagebuilder_used( $post_id ) ) { // If there is no Theme Builder Body, load post content. $processed_fields_data[] = $post_fields_data; } // Process Theme Builder Header area fields data. if ( isset( $theme_builder_layouts['et_footer_layout']['post_content'] ) ) { $theme_builder_footer = apply_filters( 'et_fb_load_raw_post_content', $theme_builder_layouts['et_footer_layout']['post_content'], $theme_builder_layouts['et_footer_layout']['id'] ); $tb_footer_fields_data = et_fb_process_shortcode( $theme_builder_footer, '', '', '', '', 'et_footer_layout' ); $processed_fields_data[] = $tb_footer_fields_data; } // Build the shortcode_object from Theme Builder Areas. foreach ( $processed_fields_data as $processed_field_data ) { if ( is_array( $processed_field_data ) ) { $fields_data['shortcode_object'] = array_merge( $fields_data['shortcode_object'], $processed_field_data ); } } } else { // In Theme Builder and Backend Builder show only main post. $fields_data['shortcode_object'] = $post_fields_data; } remove_filter( 'et_builder_module_force_render', '__return_true' ); // Restore post. $post = $backup; // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- This is legit way of setting global $post. setup_postdata( $post ); return $fields_data; } /** * This function searches the multidimensional array of post content fields for post content module * and generates array of keys that are used to select post content module. * * @param array $array Array of post fields data. * @param string $element_type Type of an element that is analyzes at the moment. * * @return array */ function et_fb_generate_post_content_module_selector( array $array, $element_type ) { global $current_section; global $current_row; global $current_row_inner; global $current_column; global $current_column_inner; global $current_module; global $post_content_module_selector; if ( 'theme_builder_content' === $element_type ) { // Loop through Theme Builder Area sections. foreach ( $array as $key => $value ) { if ( isset( $array[ $key ]['content'] ) && is_array( $array[ $key ]['content'] ) ) { $current_section = $key; et_fb_generate_post_content_module_selector( $array[ $key ]['content'], $array[ $key ]['type'] ); } } } elseif ( 'et_pb_section' === $element_type ) { // Loop through rows. foreach ( $array as $key => $value ) { if ( 'et_pb_fullwidth_post_content' === $array[ $key ]['type'] ) { et_fb_generate_post_content_module_selector( $array, 'et_pb_column' ); } elseif ( isset( $array[ $key ]['content'] ) && is_array( $array[ $key ]['content'] ) ) { $current_row = $key; et_fb_generate_post_content_module_selector( $array[ $key ]['content'], $array[ $key ]['type'] ); } } } elseif ( 'et_pb_row' === $element_type ) { // Loop through columns. foreach ( $array as $key => $value ) { if ( isset( $array[ $key ]['content'] ) && is_array( $array[ $key ]['content'] ) ) { $current_column = $key; et_fb_generate_post_content_module_selector( $array[ $key ]['content'], $array[ $key ]['type'] ); } } } elseif ( 'et_pb_row_inner' === $element_type ) { // Loop through columns. foreach ( $array as $key => $value ) { if ( isset( $array[ $key ]['content'] ) && is_array( $array[ $key ]['content'] ) ) { $current_column_inner = $key; et_fb_generate_post_content_module_selector( $array[ $key ]['content'], $array[ $key ]['type'] ); } } } elseif ( 'et_pb_column' === $element_type || 'et_pb_column_inner' === $element_type ) { // Loop through modules. foreach ( $array as $key => $value ) { if ( 'et_pb_row_inner' === $array[ $key ]['type'] ) { foreach ( $array as $key => $value ) { if ( isset( $array[ $key ]['content'] ) && is_array( $array[ $key ]['content'] ) ) { $current_row_inner = $key; et_fb_generate_post_content_module_selector( $array[ $key ]['content'], $array[ $key ]['type'] ); } } } elseif ( 'et_pb_post_content' === $array[ $key ]['type'] ) { // If Post Content Module is Found build the selector from current Section, Row, and Column. $current_module = $key; $is_column_inner = 'et_pb_column_inner' === $array[ $key ]['parent_slug']; $post_content_module_selector = $is_column_inner ? array( 'section' => $current_section, 'row_inner' => $current_row_inner, 'column' => $current_row, 'column_inner' => $current_column_inner, 'module' => $current_module, ) : array( 'section' => $current_section, 'row' => $current_row, 'column' => $current_column, 'module' => $current_module, ); } elseif ( 'et_pb_fullwidth_post_content' === $array[ $key ]['type'] ) { // If Post Content Module is FullWidth create selector with section and module id. $current_module = $key; $post_content_module_selector = array( 'section' => $current_section, 'module' => $current_module, ); } } } return $post_content_module_selector; } /** * This function is used to generate the Theme Builder Body area that has Post * Content module inside. It replaces Post Content module with sections of a * post that is being currently edited. * * @param array $theme_builder_body_fields Theme Builder Body area fields data. * @param array $selector Array of keys for selecting Post Content Module. * @param array $post_content_fields Post Content fields data that should * replace the Post Content Module. * * @return array */ function et_fb_generate_tb_body_area_with_post_content( $theme_builder_body_fields, $selector, $post_content_fields ) { if ( ! isset( $selector['row'] ) && ! isset( $selector['column'] ) ) { $original_post_content_module = $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['module'] ]; $theme_builder_body_fields[ $selector['section'] ]['attrs']['post_content_module_attrs'] = $original_post_content_module['attrs']; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['module'] ]['content'] = $post_content_fields; } elseif ( null === $selector['column'] ) { $original_post_content_module = $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['content'][ $selector['module'] ]; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['attrs']['post_content_module_attrs'] = $original_post_content_module['attrs']; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['content'][ $selector['module'] ]['content'] = $post_content_fields; } elseif ( isset( $selector['row_inner'] ) ) { $original_post_content_module = $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['column'] ]['content'][ $selector['row_inner'] ]['content'][ $selector['column_inner'] ]['content'][ $selector['module'] ]; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['column'] ]['content'][ $selector['row_inner'] ]['content'][ $selector['column_inner'] ]['attrs']['post_content_module_attrs'] = $original_post_content_module['attrs']; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['column'] ]['content'][ $selector['row_inner'] ]['content'][ $selector['column_inner'] ]['content'][ $selector['module'] ]['content'] = $post_content_fields; } else { $original_post_content_module = $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['content'][ $selector['column'] ]['content'][ $selector['module'] ]; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['content'][ $selector['column'] ]['attrs']['post_content_module_attrs'] = $original_post_content_module['attrs']; $theme_builder_body_fields[ $selector['section'] ]['content'][ $selector['row'] ]['content'][ $selector['column'] ]['content'][ $selector['module'] ]['content'] = $post_content_fields; } return $theme_builder_body_fields; } /** * Ajax Callback: Retrieve builder data on frontend app load. */ function et_fb_retrieve_builder_data() { if ( ! isset( $_POST['et_fb_helper_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_helper_nonce'], 'et_fb_load_helper_assets_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`. die( -1 ); } $post_id = ! empty( $_POST['et_post_id'] ) ? sanitize_text_field( $_POST['et_post_id'] ) : ''; if ( ! current_user_can( 'edit_posts' ) || ! current_user_can( 'edit_post', $post_id ) ) { die( -1 ); } $post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post'; $layout_type = ! empty( $_POST['et_layout_type'] ) ? sanitize_text_field( $_POST['et_layout_type'] ) : ''; $fields_data = array_merge( et_fb_get_builder_definitions( $post_type ), et_fb_get_builder_shortcode_object( $post_type, $post_id, $layout_type ) ); // Enable zlib compression. et_builder_enable_zlib_compression(); die( wp_json_encode( $fields_data ) ); } add_action( 'wp_ajax_et_fb_retrieve_builder_data', 'et_fb_retrieve_builder_data' ); /** * Replaces site_url in a json string with its protocol-less version. * * @param string $json The json string that contain site url. * * @return string */ function et_fb_remove_site_url_protocol( $json ) { $no_proto = str_replace( '/', '\/', preg_replace( '#^\w+:#', '', get_site_url() ) ); $from = array( "https:$no_proto" => $no_proto, "http:$no_proto" => $no_proto, ); return strtr( $json, $from ); } /** * Used to update the content of the cached definitions js file. * * @param string $content content? @todo Add param description. * @param string $post_type Post type? @todo Add param description. * * @return string */ function et_fb_get_asset_definitions( $content, $post_type ) { $definitions = et_fb_get_builder_definitions( $post_type ); return sprintf( 'window.ETBuilderBackend=jQuery.extend(true,%s,window.ETBuilderBackend)', et_fb_remove_site_url_protocol( wp_json_encode( $definitions, ET_BUILDER_JSON_ENCODE_OPTIONS ) ) ); } add_filter( 'et_fb_get_asset_definitions', 'et_fb_get_asset_definitions', 10, 2 ); /** * Return Divi options setting page link. * * @return mixed|string|void */ function et_pb_get_options_page_link() { // Builder plugin has different path to options page. if ( et_is_builder_plugin_active() ) { return admin_url( 'admin.php?page=et_divi_options#tab_et_dashboard_tab_content_api_main' ); } return apply_filters( 'et_pb_theme_options_link', admin_url( 'admin.php?page=et_divi_options' ) ); } /** * Localization: Product tour text. * * @param integer $post_id The post id to determine the Save/Publish button text from post status. * * @return array */ function et_fb_get_product_tour_text( $post_id ) { $post_status = get_post_status( $post_id ); $product_tour_text = array( 'start' => array( 'title' => esc_html__( 'Welcome To The Divi Builder', 'et_builder' ), 'description' => sprintf( // translators: %10$s: Tour video overlay, %1$s: "Section" - label, %2$s: Add icon markup, %3$s: "Row" - label, %4$s: Add icon markup, %5$s: "Modules" - label, %6$s: Add icon markup, %7$s: Settings gear icon markup, %9$s: Documentation link markup. __( '%10$sBuilding beautiful pages is a breeze using the Visual Builder. To get started, add a new %1$s to your page by pressing the %2$s button. Next, add a %3$s of columns inside your section by pressing the %4$s button. Finally, start adding some content %5$s inside your columns by pressing the %6$s button. You can customize the design and content of any element on the page by pressing the %7$s button. If you ever need help, visit our %9$s page for a full list of tutorials.', 'et_builder' ), sprintf( '<span class="et_fb_tour_text et_fb_tour_text_blue">%1$s</span>', esc_html__( 'Section' ) ), '<span class="et_fb_tour_icon et_fb_tour_icon_blue"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', sprintf( '<span class="et_fb_tour_text et_fb_tour_text_green">%1$s</span>', esc_html__( 'Row' ) ), '<span class="et_fb_tour_icon et_fb_tour_icon_green"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', sprintf( '<span class="et_fb_tour_text et_fb_tour_text_black">%1$s</span>', esc_html__( 'Modules' ) ), '<span class="et_fb_tour_icon"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', '<span class="et_fb_tour_icon"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M20.426 13.088l-1.383-.362a.874.874 0 0 1-.589-.514l-.043-.107a.871.871 0 0 1 .053-.779l.721-1.234a.766.766 0 0 0-.116-.917 6.682 6.682 0 0 0-.252-.253.768.768 0 0 0-.917-.116l-1.234.722a.877.877 0 0 1-.779.053l-.107-.044a.87.87 0 0 1-.513-.587l-.362-1.383a.767.767 0 0 0-.73-.567h-.358a.768.768 0 0 0-.73.567l-.362 1.383a.878.878 0 0 1-.513.589l-.107.044a.875.875 0 0 1-.778-.054l-1.234-.722a.769.769 0 0 0-.918.117c-.086.082-.17.166-.253.253a.766.766 0 0 0-.115.916l.721 1.234a.87.87 0 0 1 .053.779l-.043.106a.874.874 0 0 1-.589.514l-1.382.362a.766.766 0 0 0-.567.731v.357a.766.766 0 0 0 .567.731l1.383.362c.266.07.483.26.588.513l.043.107a.87.87 0 0 1-.053.779l-.721 1.233a.767.767 0 0 0 .115.917c.083.087.167.171.253.253a.77.77 0 0 0 .918.116l1.234-.721a.87.87 0 0 1 .779-.054l.107.044a.878.878 0 0 1 .513.589l.362 1.383a.77.77 0 0 0 .731.567h.356a.766.766 0 0 0 .73-.567l.362-1.383a.878.878 0 0 1 .515-.589l.107-.044a.875.875 0 0 1 .778.054l1.234.721c.297.17.672.123.917-.117.087-.082.171-.166.253-.253a.766.766 0 0 0 .116-.917l-.721-1.234a.874.874 0 0 1-.054-.779l.044-.107a.88.88 0 0 1 .589-.513l1.383-.362a.77.77 0 0 0 .567-.731v-.357a.772.772 0 0 0-.569-.724v-.005zm-6.43 3.9a2.986 2.986 0 1 1 2.985-2.986 3 3 0 0 1-2.985 2.987v-.001z" fillRule="evenodd" /></g></svg></span>', '<span class="et_fb_tour_text et_fb_tour_text_black">?</span>', sprintf( '<a target="_blank" href="https://www.elegantthemes.com/documentation/divi/" class="et_fb_tour_text et_fb_tour_text_black">%1$s</a>', esc_html__( 'Documentation' ) ), sprintf( '<div class="et-fb-tour-video-overlay" data-video="https://www.youtube.com/embed/JXZIGZqr9OE?rel=0&autoplay=1"> <img src="%1$s"/> <div class="et-fb-play-overlay"></div> </div>', esc_url( ET_BUILDER_URI . '/frontend-builder/assets/img/product-tour-intro.jpg' ) ) ), 'endButtonText' => esc_html__( 'Start Building', 'et_builder' ), 'skipButtonText' => esc_html__( 'Take the Tour', 'et_builder' ), ), 'loadLayout' => array( 'title' => esc_html__( 'Load A New Layout', 'et_builder' ), 'description' => esc_html__( 'Loading pre-made layouts is a great way to jump-start your new page. The Divi Builder comes with dozens of layouts to choose from, and you can find lots of great free layouts online too. You can save your favorite layouts to the Divi Library and load them on new pages or share them with the community. Click the highlighted button to open the layouts menu and select a pre-made layout.', 'et_builder' ), ), 'selectLayoutPack' => array( 'title' => esc_html__( 'Choose A Layout Pack', 'et_builder' ), 'description' => esc_html__( 'Here you can see a list of pre-made layout packs that ship with the Divi Builder. You can also access layouts that you have saved to your Divi Library. Choose the “Divi Builder Demo” layout pack to see the layouts it includes.', 'et_builder' ), ), 'loadLayoutItem' => array( 'title' => esc_html__( 'Choose A Layout To Start With', 'et_builder' ), 'description' => esc_html__( 'Now you can see more details about the layout pack as well as a list of the layouts it includes. Click “Use Layout” to apply the layout to your page.', 'et_builder' ), ), 'addSection' => array( 'title' => esc_html__( 'Add A New Section', 'et_builder' ), 'description' => sprintf( // translators: %1$s: "Sections" - label, %2$s: "Rows" - label. __( 'Now that your pre-made layout has been loaded, we can start adding new content to the page. The Divi Builder organizes content using %1$s, %2$s and Modules. Sections are the largest organizational element. Click the highlighted button to add a new section to the page.', 'et_builder' ), sprintf( '<span class="et_fb_tour_text_blue">%1$s</span>', esc_html__( 'Sections' ) ), sprintf( '<span class="et_fb_tour_text_green">%1$s</span>', esc_html__( 'Rows' ) ) ), ), 'selectSectionType' => array( 'title' => esc_html__( 'Choose A Section Type', 'et_builder' ), 'description' => sprintf( // translators: %1$s: "Regular" - label text, %2$s: "Specialty" - label tex, %3$s: "Fullwidth" - label tex. __( 'The Divi Builder has three different section types. %1$s sections conform to the standard width of your page layout. %2$s Sections can be used to create advanced sidebar layouts. %3$s sections extend the full width of your page and can be used with fullwidth modules. Click the “Regular” section button to add a new section to your page.', 'et_builder' ), sprintf( '<span class="et_fb_tour_text_blue">%1$s</span>', esc_html__( 'Regular' ) ), sprintf( '<span class="et_fb_tour_text_red">%1$s</span>', esc_html__( 'Specialty' ) ), sprintf( '<span class="et_fb_tour_text_purple">%1$s</span>', esc_html__( 'Fullwidth' ) ) ), ), 'selectRow' => array( 'title' => esc_html__( 'Add A New Row Of Columns', 'et_builder' ), 'description' => sprintf( // translators: %1$s: "Rows" - label. __( 'Every section contains one or more %1$s of columns. You can choose between various column layouts for each row you add to your page. Click the highlighted three-column layout to add a new row to your section.', 'et_builder' ), sprintf( '<span class="et_fb_tour_text_green">%1$s</span>', esc_html__( 'Rows' ) ) ), ), 'selectModule' => array( 'title' => esc_html__( 'Add A Module To The Column', 'et_builder' ), 'description' => esc_html__( 'Within each column you can add one or more Modules. A module is basic content element. The Divi Builder comes with over 40 different content elements to choose from, such as Images, Videos, Text, and Buttons. Click the highlighted Blurb button to add a new Blurb module to the first column in your row.', 'et_builder' ), ), 'configureModule' => array( 'title' => esc_html__( 'Adjust Your Module Settings', 'et_builder' ), 'description' => esc_html__( 'Each Module comes with various settings. These settings are separated into three tabs: Content, Design and Advanced. Inside the content tab you can modify the module content elements, such as text and images. If you need more control over the appearance of your module, head over to the Design tab. For more advanced modifications, such as custom CSS and HTML attributes, explore the Advanced tab. Try adjusting the Title of your blurb by clicking into the highlighted field.', 'et_builder' ), ), 'saveModule' => array( 'title' => esc_html__( 'Accept Or Discard Your Changes', 'et_builder' ), 'description' => esc_html__( 'Whenever you make changes in the Divi Builder, these changes can be Undone, Redone, Discarded or Accepted. Now that you have adjusted your module’s title, you can click the red discard button to cancel these changes, or your can click the green button to accept them.', 'et_builder' ), ), 'duplicateModule' => array( 'title' => esc_html__( 'Hover To Access Action Buttons', 'et_builder' ), 'description' => esc_html__( 'Whenever you hover over a Section, Row or Module in the Divi Builder, action buttons will appear. These buttons can be used to move, modify, duplicate or delete your content. Click the highlighted “duplicate” icon to duplicate the blurb module that you just added to the page.', 'et_builder' ), ), 'moveModule' => array( 'title' => __( 'Drag & Drop Content', 'et_builder' ), 'description' => esc_html__( 'Every item on the page can be dragged and dropped to new locations. Using your mouse, click the highlighted move icon and hold down the mouse button. While holding down the mouse button, move your cursor over to the empty column and then release your mouse button to drop the module into the new column.', 'et_builder' ), ), 'rightClickCopy' => array( 'title' => esc_html__( 'Access Right Click Options', 'et_builder' ), 'description' => esc_html__( 'In addition to hover actions, additional options can be accessed by Right Clicking or Cmd + Clicking on any module, row or section. Using the right click menu shown, click the highlighted “Copy Module” button to copy the blurb module that you just moved.', 'et_builder' ), ), 'rightClickPaste' => array( 'title' => esc_html__( 'Paste Your Copied Module', 'et_builder' ), 'description' => esc_html__( 'Now that you have copied a module using the Right Click menu, you can Right Click in a new location to paste that module. Using the right click options shown, click the “Paste Module” button to paste the module you just copied into the empty column.', 'et_builder' ), ), 'rowOptions' => array( 'title' => esc_html__( 'Access Your Row Options', 'et_builder' ), 'description' => esc_html__( 'Every Row and Section has its own set of options that can be used to adjust the item’s appearance. You can adjust its width, padding, background and more. To access a row’s settings, hover over the row and click the highlighted options button.', 'et_builder' ), ), 'editRow' => array( 'title' => esc_html__( 'Adjust Your Row Setting', 'et_builder' ), 'description' => esc_html__( 'Just like Modules, Rows come with a lot of settings that are separated into the Content, Design and Advanced tabs. Click the highlighted button to add a new background color to your row.', 'et_builder' ), ), 'saveRow' => array( 'title' => esc_html__( 'Accept Your Changes', 'et_builder' ), 'description' => esc_html__( 'Click the highlighted green check mark button to accept your changes. ', 'et_builder' ), ), 'pageSettings' => array( 'title' => esc_html__( 'Open Your Page Settings', 'et_builder' ), 'description' => esc_html__( 'While using the Divi Builder, you can access your page settings by toggling the page settings bar at the bottom of your screen. Click the highlighted button to reveal your page settings.', 'et_builder' ), ), 'tabletPreview' => array( 'title' => esc_html__( 'Preview Your Page On Mobile', 'et_builder' ), 'description' => esc_html__( 'While editing your page, it’s easy to see what your design will look like on mobile devices. You can also make adjustments to your module, row and section settings for each mobile breakpoint. Click the highlighted “Tablet” icon to enter Tablet preview mode. ', 'et_builder' ), ), 'desktopPreview' => array( 'title' => esc_html__( 'Switch Back To Desktop Mode', 'et_builder' ), 'description' => esc_html__( 'You can switch back and forth between each preview mode freely while editing your page. Now that we have previewed our page on Tablet, let’s switch back to Desktop preview mode by clicking the highlighted button.', 'et_builder' ), ), 'openHistory' => array( 'title' => esc_html__( 'Access Your Editing History', 'et_builder' ), 'description' => esc_html__( 'Every change you make while editing your page is saved in your editing history. You can navigate backwards and forwards through time to any point during your current editing session, as well as undo and redo recent changes. Click the highlighted History button to access your editing history. ', 'et_builder' ), ), 'editHistory' => array( 'title' => esc_html__( 'Undo, Redo And Restore', 'et_builder' ), 'description' => esc_html__( 'Here you can undo, redo or restore a saved history state. If you change your mind about recent changes, simply click back in time and start building again. You can also undo and redo recent changes. Click the undo and redo buttons and then accept your changes by clicking the green check mark.', 'et_builder' ), ), 'savePage' => array( 'title' => esc_html__( 'Save Your Page', 'et_builder' ), 'description' => sprintf( // translators: %1$s: "Save" or "Publish" - label. esc_html__( 'When you are all done, you can save your changes by clicking the %1$s button inside of your page settings bar. You can also press Ctrl + S at any time to save your changes. Click the highlighted Save button to save your changes. Don’t worry, the page you were working on before starting this tour will not be lost!', 'et_builder' ), in_array( $post_status, array( 'private', 'publish' ), true ) ? esc_html__( 'Save', 'et_builder' ) : esc_html__( 'Publish', 'et_builder' ) ), ), 'finish' => array( 'title' => esc_html__( 'You’re Ready To Go!', 'et_builder' ), 'description' => sprintf( // translators: %10$s: Tour video overlay, %1$s: "Section" - label, %2$s: Add icon markup, %3$s: "Row" - label, %4$s: Add icon markup, %5$s: "Modules" - label, %6$s: Add icon markup, %7$s: Settings gear icon markup, %9$s: Documentation link markup. __( '%10$sBuilding beautiful pages is a breeze using the Visual Builder. To get started, add a new %1$s to your page by pressing the %2$s button. Next, add a %3$s of columns inside your section by pressing the %4$s button. Finally, start adding some content %5$s inside your columns by pressing the %6$s button. You can customize the design and content of any element on the page by pressing the %7$s button. If you ever need help, visit our %9$s page for a full list of tutorials.', 'et_builder' ), sprintf( '<span class="et_fb_tour_text et_fb_tour_text_blue">%1$s</span>', esc_html__( 'Section' ) ), '<span class="et_fb_tour_icon et_fb_tour_icon_blue"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', sprintf( '<span class="et_fb_tour_text et_fb_tour_text_green">%1$s</span>', esc_html__( 'Row' ) ), '<span class="et_fb_tour_icon et_fb_tour_icon_green"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', sprintf( '<span class="et_fb_tour_text et_fb_tour_text_black">%1$s</span>', esc_html__( 'Modules' ) ), '<span class="et_fb_tour_icon"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M18 13h-3v-3a1 1 0 0 0-2 0v3h-3a1 1 0 0 0 0 2h3v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2z" fillRule="evenodd" /></g></svg></span>', '<span class="et_fb_tour_icon"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M20.426 13.088l-1.383-.362a.874.874 0 0 1-.589-.514l-.043-.107a.871.871 0 0 1 .053-.779l.721-1.234a.766.766 0 0 0-.116-.917 6.682 6.682 0 0 0-.252-.253.768.768 0 0 0-.917-.116l-1.234.722a.877.877 0 0 1-.779.053l-.107-.044a.87.87 0 0 1-.513-.587l-.362-1.383a.767.767 0 0 0-.73-.567h-.358a.768.768 0 0 0-.73.567l-.362 1.383a.878.878 0 0 1-.513.589l-.107.044a.875.875 0 0 1-.778-.054l-1.234-.722a.769.769 0 0 0-.918.117c-.086.082-.17.166-.253.253a.766.766 0 0 0-.115.916l.721 1.234a.87.87 0 0 1 .053.779l-.043.106a.874.874 0 0 1-.589.514l-1.382.362a.766.766 0 0 0-.567.731v.357a.766.766 0 0 0 .567.731l1.383.362c.266.07.483.26.588.513l.043.107a.87.87 0 0 1-.053.779l-.721 1.233a.767.767 0 0 0 .115.917c.083.087.167.171.253.253a.77.77 0 0 0 .918.116l1.234-.721a.87.87 0 0 1 .779-.054l.107.044a.878.878 0 0 1 .513.589l.362 1.383a.77.77 0 0 0 .731.567h.356a.766.766 0 0 0 .73-.567l.362-1.383a.878.878 0 0 1 .515-.589l.107-.044a.875.875 0 0 1 .778.054l1.234.721c.297.17.672.123.917-.117.087-.082.171-.166.253-.253a.766.766 0 0 0 .116-.917l-.721-1.234a.874.874 0 0 1-.054-.779l.044-.107a.88.88 0 0 1 .589-.513l1.383-.362a.77.77 0 0 0 .567-.731v-.357a.772.772 0 0 0-.569-.724v-.005zm-6.43 3.9a2.986 2.986 0 1 1 2.985-2.986 3 3 0 0 1-2.985 2.987v-.001z" fillRule="evenodd" /></g></svg></span>', '<span class="et_fb_tour_text et_fb_tour_text_black">?</span>', sprintf( '<a target="_blank" href="https://www.elegantthemes.com/documentation/divi/" class="et_fb_tour_text et_fb_tour_text_black">%1$s</a>', esc_html__( 'Documentation' ) ), sprintf( '<div class="et-fb-tour-video-overlay" data-video="https://www.youtube.com/embed/JXZIGZqr9OE?rel=0&autoplay=1"> <img src="%1$s"/> <div class="et-fb-play-overlay"></div> </div>', esc_url( ET_BUILDER_URI . '/frontend-builder/assets/img/product-tour-intro.jpg' ) ) ), 'endButtonText' => esc_html__( 'Start Building', 'et_builder' ), ), 'endButtonTextDefault' => esc_html__( 'End the Tour', 'et_builder' ), 'skipButtonTextDefault' => esc_html__( 'Skip This Step', 'et_builder' ), ); return $product_tour_text; } /** * Process builder shortcode into object. * * The standard do_shortcode filter should be removed, and * this function hooked instead. * * This function is very similar to `do_shortcode`, * with the main differences being: * - Its main design is to allow recursive array to be built out of wp shortcode * - Allows shortcode callback to return an array rather than a string * - It tracks the inner `index` / `_i` of each child shortcode to the passed content, which is used in the address creation as well * - It uses and passes `$address` & `$parent_address`, which are used by FB app. * * @param string $content post content. * @param string $parent_address parent shortcode address. * @param string $global_parent ?? @todo Add param doc. * @param string $global_parent_type ?? @todo Add param doc. * @param string $parent_type ?? @todo Add param doc. * * @return mixed */ function et_fb_process_shortcode( $content, $parent_address = '', $global_parent = '', $global_parent_type = '', $parent_type = '', $theme_builder_area = '' ) { global $shortcode_tags, $fb_processing_counter; if ( false === strpos( $content, '[' ) ) { return $content; } // Count started processes. $fb_processing_counter = isset( $fb_processing_counter ) ? $fb_processing_counter + 1 : 1; // Find all registered tag names in $content. preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches ); // Only need unique tag names. $unique_matches = array_unique( $matches[1] ); $tagnames = array_intersect( array_keys( $shortcode_tags ), $unique_matches ); $pattern = get_shortcode_regex( $unique_matches ); $content = preg_match_all( "/$pattern/", $content, $matches, PREG_SET_ORDER ); $_matches = array(); $_index = 0; foreach ( $matches as $match ) { $tag = $match[2]; // reset global parent data to calculate it correctly for next modules. if ( $global_parent_type === $tag && '' !== $global_parent ) { $global_parent = ''; $global_parent_type = ''; } $attr = shortcode_parse_atts( $match[3] ); if ( ! is_array( $attr ) ) { $attr = array(); } $index = $_index++; $address = isset( $parent_address ) && '' !== $parent_address ? (string) $parent_address . '.' . (string) $index : (string) $index; // set global parent and global parent tag if current module is global and can be a parent. $possible_global_parents = array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner' ); if ( '' === $global_parent && in_array( $tag, $possible_global_parents, true ) ) { $global_parent = isset( $attr['global_module'] ) ? $attr['global_module'] : ''; $global_parent_type = $tag; } // As responsive content attributes value might be has been encoded before saving to database, // so we need to decode it before passing back to builder. if ( $attr ) { $decoded_content_fields = array( 'content__hover', 'content_tablet', 'content_phone', 'raw_content__hover', 'raw_content_tablet', 'raw_content_phone', ); foreach ( $decoded_content_fields as $decoded_content_field ) { if ( array_key_exists( $decoded_content_field, $attr ) ) { $attr[ $decoded_content_field ] = str_replace( array( '%22', '%92', '%91', '%93' ), array( '"', '\\', '[', ']' ), $attr[ $decoded_content_field ] ); } } } $attr['_i'] = $index; $attr['_address'] = $address; // Builder shortcode which exist on page but not registered in WP i.e. 3rd party shortcode when 3rd party module disabled // Add dummy object to render it in Divi Builder. if ( ! in_array( $tag, $tagnames, true ) ) { $_matches[] = array( '_i' => $index, '_order' => $index, 'address' => $address, 'vb_support' => 'off', 'component_path' => 'et-fb-removed-component', 'type' => $tag, 'attrs' => $attr, ); continue; } // Flag that the shortcode object is being built. $GLOBALS['et_fb_processing_shortcode_object'] = true; if ( isset( $match[5] ) ) { // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- `call_user_func` calls the registered shortcode callback. $output = call_user_func( $shortcode_tags[ $tag ], $attr, $match[5], $tag, $parent_address, $global_parent, $global_parent_type, $parent_type, $theme_builder_area ); } else { // self-closing tag. // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- `call_user_func` calls the registered shortcode callback. $output = call_user_func( $shortcode_tags[ $tag ], $attr, null, $tag ); } $_matches[] = et_fb_add_additional_attrs( $attr, $output ); } // Count finished processes. $fb_processing_counter = $fb_processing_counter - 1; // phpcs:ignore Squiz.Operators.IncrementDecrementUsage -- This is more readable. // Make sure ALL the processes finished to avoid wrong disabling of `et_fb_processing_shortcode_object` when several concurrent instances of `et_fb_process_shortcode` running. if ( 0 === $fb_processing_counter ) { // Turn off the flag since the shortcode object is done being built. et_fb_reset_shortcode_object_processing(); } return $_matches; } /** * Allowlist any additional attributes. * * @param array $processed_attrs Shortcode's processed attributes. * @param array $output Shortcode output. * * @return mixed */ function et_fb_add_additional_attrs( $processed_attrs, $output ) { if ( empty( $output['attrs'] ) ) { return $output; } // A list of all the attributes that are already returned after the shortcode is processed. $safe_attrs = array_keys( $output['attrs'] ); $allowed_attrs = array(); foreach ( $processed_attrs as $attr => $value ) { if ( ! preg_match( '~_hover(_enabled)?$~', $attr ) ) { continue; } // if color value includes `gcid-`, check for associated Global Color value. if ( empty( $value ) || false === strpos( $value, 'gcid-' ) ) { continue; } $global_color_info = et_builder_get_all_global_colors( true ); // If there are no matching Global Colors, return null. if ( ! is_array( $global_color_info ) ) { continue; } foreach ( $global_color_info as $gcid => $details ) { if ( false !== strpos( $value, $gcid ) ) { // Match substring (needed for attrs like gradient stops). $value = str_replace( $gcid, $details['color'], $value ); } } // Finally, escape the output. if ( ! empty( $global_color_info['color'] ) ) { $value = esc_attr( $value ); } $allowed_attrs[ $attr ] = $value; } // Extra conversion for the case with the `font_icon__hover` option. if ( ! empty( $allowed_attrs['font_icon__hover'] ) && et_pb_maybe_old_divi_font_icon( $allowed_attrs['font_icon__hover'] ) ) { $allowed_attrs['font_icon__hover'] = et_pb_build_extended_font_icon_value( $allowed_attrs['font_icon__hover'], null, null, true ); } if ( $allowed_attrs ) { $output['attrs'] = array_merge( $output['attrs'], $allowed_attrs ); } return $output; } /** * Parse builder shortcode into an array. * * @param string $content Builder built post content. * * @return array Array representation of the builder shortcode. */ function et_pb_parse_shortcode_to_array( $content ) { global $shortcode_tags; if ( false === strpos( $content, '[' ) ) { return $content; } // Find all registered tag names in $content. preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches ); $tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] ); $pattern = get_shortcode_regex( $matches[1] ); $content = preg_match_all( "/$pattern/", $content, $matches, PREG_SET_ORDER ); $shortcode_data = array(); foreach ( $matches as $match ) { $tag = $match[2]; $attr = shortcode_parse_atts( $match[3] ); if ( ! is_array( $attr ) ) { $attr = array(); } $_shortcode_data = array( 'type' => $tag, 'attrs' => $attr, ); if ( ! empty( $match[5] ) ) { $_shortcode_data['content'] = et_pb_parse_shortcode_to_array( $match[5] ); } $shortcode_data[] = $_shortcode_data; } return $shortcode_data; } /** * Parse post content that includes builder shortcode to an array, * then run it back through "some sanity check" and "some sanitization" * and then form into a shortcode again. * * @see et_fb_process_to_shortcode() for exact sanitizations performed. * * @param string $content Builder built post content. * @param bool $force_valid_builder_slugs Whether to force the shortcode to allow valid builder shortcode slugs only. * * @return string Sanitized builder built post content. */ function et_pb_sanitize_shortcode( $content, $force_valid_builder_slugs = false ) { global $shortcode_tags; if ( false === strpos( $content, '[' ) ) { return $content; } $content_array = et_pb_parse_shortcode_to_array( $content ); $options = array( 'force_valid_slugs' => $force_valid_builder_slugs, ); $new_content = et_fb_process_to_shortcode( $content_array, $options ); return $new_content; } /** * Use shortcode tag which renders the content to correctly display its properties. * * @param string $tag shortcode tag. * * @return string */ function et_fb_prepare_tag( $tag ) { // List of aliases. $aliases = apply_filters( 'et_fb_prepare_tag_aliases', array( 'et_pb_accordion_item' => 'et_pb_toggle', ) ); return isset( $aliases[ $tag ] ) ? $aliases[ $tag ] : $tag; } if ( ! function_exists( 'et_strip_shortcodes' ) ) : /** * Strip builder shortcodes only, leaving default WordPress shortcodes intact. * * @param string $content the content. * @param string $truncate_post_based_shortcodes_only Optional. Whether trunct only post based shortcodes. */ function et_strip_shortcodes( $content, $truncate_post_based_shortcodes_only = false ) { global $shortcode_tags; $content = trim( $content ); $strip_content_shortcodes = array( 'et_pb_code', 'et_pb_fullwidth_code', 'et_pb_social_media_follow_network', ); // list of post-based shortcodes. if ( $truncate_post_based_shortcodes_only ) { $strip_content_shortcodes = array( 'et_pb_post_slider', 'et_pb_fullwidth_post_slider', 'et_pb_blog', 'et_pb_comments', ); } foreach ( $strip_content_shortcodes as $shortcode_name ) { $regex = sprintf( '(\[%1$s[^\]]*\][^\[]*\[\/%1$s\]|\[%1$s[^\]]*\])', esc_html( $shortcode_name ) ); $content = preg_replace( $regex, '', $content ); } // do not proceed if we need to truncate post-based shortcodes only. if ( $truncate_post_based_shortcodes_only ) { return $content; } $shortcode_tag_names = array(); foreach ( $shortcode_tags as $shortcode_tag_name => $shortcode_tag_cb ) { if ( 0 !== strpos( $shortcode_tag_name, 'et_pb_' ) ) { continue; } $shortcode_tag_names[] = $shortcode_tag_name; } $et_shortcodes = implode( '|', $shortcode_tag_names ); $regex_opening_shortcodes = sprintf( '(\[(%1$s)[^\]]+\])', esc_html( $et_shortcodes ) ); $regex_closing_shortcodes = sprintf( '(\[\/(%1$s)\])', esc_html( $et_shortcodes ) ); $content = preg_replace( $regex_opening_shortcodes, '', $content ); $content = preg_replace( $regex_closing_shortcodes, '', $content ); return $content; } endif; /** * Reset shortcode object processing. */ function et_fb_reset_shortcode_object_processing() { $GLOBALS['et_fb_processing_shortcode_object'] = false; } add_action( 'et_fb_enqueue_assets', 'et_fb_backend_helpers' ); if ( ! function_exists( 'et_builder_maybe_flush_rewrite_rules' ) ) : /** * Flush rewrite rules if theme option saved value and passed $value are not same. * * @param string $setting_name The theme option. * @param string $value The value to be compared. */ function et_builder_maybe_flush_rewrite_rules( $setting_name, $value = 'done' ) { $string_value = (string) $value; $saved_value = et_get_option( $setting_name ); if ( $saved_value && $saved_value === $string_value ) { return; } flush_rewrite_rules(); et_update_option( $setting_name, $string_value ); } endif; /** * Flush rewrite rules to fix the issue Layouts, not being visible on front-end and visual builder, * if pretty permalinks were enabled * * @return void */ function et_pb_maybe_flush_rewrite_rules_library() { // Run flush rewrite only when et_pb_layout post type registered. if ( post_type_exists( 'et_pb_layout' ) ) { et_builder_maybe_flush_rewrite_rules( 'et_flush_rewrite_rules_library', ET_BUILDER_PRODUCT_VERSION ); } } add_action( 'init', 'et_pb_maybe_flush_rewrite_rules_library', 9 ); /** * Remove et_builder_maybe_flush_rewrite_rules flag if flush_rewrite_rules() is called while * `et_pb_layout` post type hasn't been registered * * @since 3.19.18 * * @param string|array $old_value old option value. * @param string|array $value new option value. * @param string $option option name. */ function et_pb_maybe_remove_flush_rewrite_rules_library_flag( $old_value, $value, $option ) { // rewrite rules for CPT that are rebuilt by flush_rewrite_rules() are based on // get_post_types( array( '_builtin' => false ) ) value; Hence if flush_rewrite_rules() is // executed while `et_pb_layout` CPT hasn't been registered (usually by third party plugin) // et_pb_maybe_flush_rewrite_rules_library() flag has to be removed to trigger flush_rewrite_rules() // via et_pb_maybe_flush_rewrite_rules_library() which contains `et_pb_layout` rewrite rules // because et_pb_maybe_flush_rewrite_rules_library() checks for `et_pb_layout` first. if ( '' === $value && ! post_type_exists( 'et_pb_layout' ) ) { et_update_option( 'et_flush_rewrite_rules_library', '' ); } } add_action( 'update_option_rewrite_rules', 'et_pb_maybe_remove_flush_rewrite_rules_library_flag', 10, 3 ); if ( ! function_exists( 'et_builder_get_shortcuts' ) ) : /** * Get list of shortcut available on BB and FB * * @param string $on (fb|bb) shortcut mode. * @return array shortcut list */ function et_builder_get_shortcuts( $on = 'fb' ) { $shortcuts = array( 'page' => array( 'page_title' => array( 'title' => esc_html__( 'Page Shortcuts', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'undo' => array( 'kbd' => array( 'super', 'z' ), 'desc' => esc_html__( 'Undo', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'redo' => array( 'kbd' => array( 'super', 'y' ), 'desc' => esc_html__( 'Redo', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'save' => array( 'kbd' => array( 'super', 's' ), 'desc' => esc_html__( 'Save Page', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'save_as_draft' => array( 'kbd' => array( 'super', 'shift', 's' ), 'desc' => esc_html__( 'Save Page As Draft', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'exit' => array( 'kbd' => array( 'super', 'e' ), 'desc' => esc_html__( 'Exit Visual Builder', 'et_builder' ), 'on' => array( 'fb', ), ), 'exit_to_backend_builder' => array( 'kbd' => array( 'super', 'shift', 'e' ), 'desc' => esc_html__( 'Exit To Backend Builder', 'et_builder' ), 'on' => array( 'fb', ), ), 'toggle_settings_bar' => array( 'kbd' => array( 't' ), 'desc' => esc_html__( 'Toggle Settings Bar', 'et_builder' ), 'on' => array( 'fb', ), ), 'open_page_settings' => array( 'kbd' => array( 'o' ), 'desc' => esc_html__( 'Open Page Settings', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'open_history' => array( 'kbd' => array( 'h' ), 'desc' => esc_html__( 'Open History Window', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'open_portability' => array( 'kbd' => array( 'p' ), 'desc' => esc_html__( 'Open Portability Window', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'zoom_in' => array( 'kbd' => array( 'super', '+' ), 'desc' => esc_html__( 'Responsive Zoom In', 'et_builder' ), 'on' => array( 'fb', ), ), 'zoom_out' => array( 'kbd' => array( 'super', '-' ), 'desc' => esc_html__( 'Responsive Zoom Out', 'et_builder' ), 'on' => array( 'fb', ), ), 'wireframe' => array( 'kbd' => array( 'shift', 'w' ), 'desc' => esc_html__( 'Wireframe Mode', 'et_builder' ), 'on' => array( 'fb', ), ), 'click_mode' => array( 'kbd' => array( 'super', 'shift', 'c' ), 'desc' => esc_html__( 'Click Mode', 'et_builder' ), 'on' => array( 'fb', ), ), 'grid_mode' => array( 'kbd' => array( 'super', 'shift', 'g' ), 'desc' => esc_html__( 'Grid Mode', 'et_builder' ), 'on' => array( 'fb', ), ), 'hover_mode' => array( 'kbd' => array( 'super', 'shift', 'h' ), 'desc' => esc_html__( 'Hover Mode', 'et_builder' ), 'on' => array( 'fb', ), ), 'help' => array( 'kbd' => array( '?' ), 'desc' => esc_html__( 'List All Shortcuts', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), ), 'inline' => array( 'inline_title' => array( 'title' => esc_html__( 'Inline Editor Shortcuts', 'et_builder' ), 'on' => array( 'fb', ), ), 'escape' => array( 'kbd' => array( 'esc' ), 'desc' => esc_html__( 'Exit Inline Editor', 'et_builder' ), 'on' => array( 'fb', ), ), ), 'module' => array( 'module_title' => array( 'title' => esc_html__( 'Module Shortcuts', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'module_copy' => array( 'kbd' => array( 'super', 'c' ), 'desc' => esc_html__( 'Copy Module', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'module_cut' => array( 'kbd' => array( 'super', 'x' ), 'desc' => esc_html__( 'Cut Module', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'module_paste' => array( 'kbd' => array( 'super', 'v' ), 'desc' => esc_html__( 'Paste Module', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'module_copy_styles' => array( 'kbd' => array( 'super', 'alt', 'c' ), 'desc' => esc_html__( 'Copy Module Styles', 'et_builder' ), 'on' => array( 'fb', ), ), 'module_paste_styles' => array( 'kbd' => array( 'super', 'alt', 'v' ), 'desc' => esc_html__( 'Paste Module Styles', 'et_builder' ), 'on' => array( 'fb', ), ), 'module_reset_styles' => array( 'kbd' => array( 'super', 'alt', 'r' ), 'desc' => esc_html__( 'Reset Module Styles', 'et_builder' ), 'on' => array( 'fb', ), ), 'module_lock' => array( 'kbd' => array( 'super', 'shift', 'l' ), 'desc' => esc_html__( 'Lock Module', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'module_disable' => array( 'kbd' => array( 'super', 'shift', 'd' ), 'desc' => esc_html__( 'Disable Module', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'drag_auto_copy' => array( 'kbd' => array( 'alt', 'module move' ), 'desc' => esc_html__( 'Move and copy module into dropped location', 'et_builder' ), 'on' => array( 'fb', ), ), 'column_change_structure' => array( 'kbd' => array( 'c', array( '1', '2', '3', '4', '5', '...' ) ), 'desc' => esc_html__( 'Change Column Structure', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'row_make_fullwidth' => array( 'kbd' => array( 'r', 'f' ), 'desc' => esc_html__( 'Make Row Fullwidth', 'et_builder' ), 'on' => array( 'fb', ), ), 'row_edit_gutter' => array( 'kbd' => array( 'g', array( '1', '2', '3', '4' ) ), 'desc' => esc_html__( 'Change Gutter Width', 'et_builder' ), 'on' => array( 'fb', ), ), 'add_new_row' => array( 'kbd' => array( 'r', array( '1', '2', '3', '4', '5', '...' ) ), 'desc' => esc_html__( 'Add New Row', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'add_new_section' => array( 'kbd' => array( 's', array( '1', '2', '3' ) ), 'desc' => esc_html__( 'Add New Section', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'resize_padding_auto_opposite' => array( 'kbd' => array( 'shift', 'Drag Padding' ), 'desc' => esc_html__( 'Restrict padding to 10px increments', 'et_builder' ), 'on' => array( 'fb', ), ), 'resize_padding_limited' => array( 'kbd' => array( 'alt', 'Drag Padding' ), 'desc' => esc_html__( 'Padding limited to opposing value', 'et_builder' ), 'on' => array( 'fb', ), ), 'resize_padding_10' => array( 'kbd' => array( 'shift', 'alt', 'Drag Padding' ), 'desc' => esc_html__( 'Mirror padding on both sides', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_row' => array( 'kbd' => array( 'r', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Row Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_row' => array( 'kbd' => array( 'r', 'alt', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Row Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_section' => array( 'kbd' => array( 's', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Section Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_section' => array( 'kbd' => array( 's', 'alt', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Section Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_module' => array( 'kbd' => array( 'm', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Module Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_module' => array( 'kbd' => array( 'm', 'alt', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Module Padding', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_row_10' => array( 'kbd' => array( 'r', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Row Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_row_10' => array( 'kbd' => array( 'r', 'alt', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Row Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_section_10' => array( 'kbd' => array( 's', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Section Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_section_10' => array( 'kbd' => array( 's', 'alt', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Section Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), 'increase_padding_module_10' => array( 'kbd' => array( 'm', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Increase Module Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), 'decrease_padding_module_10' => array( 'kbd' => array( 'm', 'alt', 'shift', array( 'left', 'right', 'up', 'down' ) ), 'desc' => esc_html__( 'Decrease Module Padding By 10px', 'et_builder' ), 'on' => array( 'fb', ), ), ), 'modal' => array( 'modal_title' => array( 'title' => esc_html__( 'Modal Shortcuts', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'escape' => array( 'kbd' => array( 'esc' ), 'desc' => esc_html__( 'Close Modal', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'save_changes' => array( 'kbd' => array( 'enter' ), 'desc' => esc_html__( 'Save Changes', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'undo' => array( 'kbd' => array( 'super', 'z' ), 'desc' => esc_html__( 'Undo', 'et_builder' ), 'on' => array( 'fb', ), ), 'redo' => array( 'kbd' => array( 'super', 'shift', 'z' ), 'desc' => esc_html__( 'Redo', 'et_builder' ), 'on' => array( 'fb', ), ), 'switch_tabs' => array( 'kbd' => array( 'shift', 'tab' ), 'desc' => esc_html__( 'Switch Tabs', 'et_builder' ), 'on' => array( 'fb', 'bb', ), ), 'toggle_expand' => array( 'kbd' => array( 'super', 'enter' ), 'desc' => esc_html__( 'Expand Modal Fullscreen', 'et_builder' ), 'on' => array( 'fb', ), ), 'toggle_snap' => array( 'kbd' => array( 'super', array( 'left', 'right' ) ), 'desc' => esc_html__( 'Snap Modal Left / Right', 'et_builder' ), 'on' => array( 'fb', ), ), 'quick_actions' => array( 'kbd' => array( 'shift', 'space' ), 'desc' => esc_html__( 'Quick Actions', 'et_builder' ), 'on' => array( 'fb', ), ), 'layers_view' => array( 'kbd' => array( 'super', 'l' ), 'desc' => esc_html__( 'Layers View', 'et_builder' ), 'on' => array( 'fb', ), ), ), ); $shortcuts = apply_filters( 'et_builder_get_shortcuts', $shortcuts ); // Filter shortcuts. $filtered_shortcuts = array(); foreach ( $shortcuts as $group_key => $group ) { foreach ( $group as $shortcut_key => $shortcut ) { if ( in_array( $on, $shortcut['on'], true ) ) { $filtered_shortcuts[ $group_key ][ $shortcut_key ] = $shortcut; } } } return $filtered_shortcuts; } endif; if ( ! function_exists( 'et_pb_get_responsive_status' ) ) : /** * Parsed *_last_edited value and determine wheter the passed string means it has responsive value or not * *_last_edited holds two values (responsive status and last opened tabs) in the following format: status|last_opened_tab * * @param string $last_edited last_edited data. * @return bool */ function et_pb_get_responsive_status( $last_edited ) { $parsed_last_edited = is_string( $last_edited ) ? explode( '|', $last_edited ) : array( 'off', 'desktop' ); return isset( $parsed_last_edited[0] ) ? 'on' === $parsed_last_edited[0] : false; } endif; if ( ! function_exists( 'et_pb_get_value_unit' ) ) : /** * Get unit of given value * * @param string $value string with unit. * @param string $default_unit default unit. * * @return string unit name */ function et_pb_get_value_unit( $value, $default_unit = 'px' ) { $value = isset( $value ) ? $value : ''; $valid_one_char_units = array( '%', 'x' ); $valid_two_chars_units = array( 'em', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw', 'ms' ); $valid_three_chars_units = array( 'deg', 'rem' ); $important = '!important'; $important_length = strlen( $important ); $value_length = strlen( $value ); if ( '' === $value || is_numeric( $value ) ) { return $default_unit; } if ( substr( $value, ( 0 - $important_length ), $important_length ) === $important ) { $value_length = $value_length - $important_length; $value = trim( substr( $value, 0, $value_length ) ); } if ( in_array( substr( $value, -3, 3 ), $valid_three_chars_units, true ) ) { return substr( $value, -3, 3 ); } if ( in_array( substr( $value, -2, 2 ), $valid_two_chars_units, true ) ) { return substr( $value, -2, 2 ); } if ( in_array( substr( $value, -1, 1 ), $valid_one_char_units, true ) ) { return substr( $value, -1, 1 ); } return $default_unit; } endif; if ( ! function_exists( 'et_sanitize_input_unit' ) ) : /** * Sanitized value and its unit * * @param mixed $value Input value. * @param string $auto_important Whether add !important specificity. Default false. * @param string|bool $default_unit The default unit. * * @return string sanitized input and its unit */ function et_sanitize_input_unit( $value = '', $auto_important = false, $default_unit = false ) { $value = (string) $value; $valid_one_char_units = array( '%', 'x' ); $valid_two_chars_units = array( 'em', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw', 'ms' ); $valid_three_chars_units = array( 'deg', 'rem' ); $important = '!important'; $important_length = strlen( $important ); $has_important = false; $value_length = strlen( $value ); $unit_value; // Check for important. if ( substr( $value, ( 0 - $important_length ), $important_length ) === $important ) { $has_important = true; $value_length = $value_length - $important_length; $value = trim( substr( $value, 0, $value_length ) ); } if ( in_array( substr( $value, -3, 3 ), $valid_three_chars_units, true ) ) { $unit_value = floatval( $value ) . substr( $value, -3, 3 ); // Re-add !important tag. if ( $has_important && ! $auto_important ) { $unit_value = $unit_value . ' ' . $important; } return $unit_value; } if ( in_array( substr( $value, -2, 2 ), $valid_two_chars_units, true ) ) { $unit_value = floatval( $value ) . substr( $value, -2, 2 ); // Re-add !important tag. if ( $has_important && ! $auto_important ) { $unit_value = $unit_value . ' ' . $important; } return $unit_value; } if ( in_array( substr( $value, -1, 1 ), $valid_one_char_units, true ) ) { $unit_value = floatval( $value ) . substr( $value, -1, 1 ); // Re-add !important tag. if ( $has_important && ! $auto_important ) { $unit_value = $unit_value . ' ' . $important; } return $unit_value; } $result = floatval( $value ); if ( 'no_default_unit' === $default_unit ) { return $result; } if ( $default_unit ) { return $result . $default_unit; } if ( ! $default_unit ) { $result .= 'px'; } // Return and automatically append px (default value). return $result; } endif; if ( ! function_exists( 'et_builder_get_taxonomies' ) ) : /** * Get taxonomies for modules * * @param array $args Optional. Uses 'use_terms' argument to retrieve the terms in a given taxonomy. * Uses 'term_name' to specify Taxonomy to retrieve terms. * * @return array Array of WP taxonomies splitted into the taxonomy types */ function et_builder_get_shop_categories( $args = array() ) { $defaults = apply_filters( 'et_builder_include_categories_shop_defaults', array( 'use_terms' => true, 'term_name' => 'product_cat', ) ); $term_args = apply_filters( 'et_builder_include_categories_shop_args', array( 'hide_empty' => false ) ); $args = wp_parse_args( $args, $defaults ); $product_categories = $args['use_terms'] ? get_terms( $args['term_name'], $term_args ) : get_categories( apply_filters( 'et_builder_get_categories_shop_args', 'hide_empty=0' ) ); return $product_categories; } endif; if ( ! function_exists( 'et_pb_get_spacing' ) ) : /** * Return spacing value. * * @param string $spacing spacing string. * @param string $corner spacing corner. * @param string $default default value. */ function et_pb_get_spacing( $spacing, $corner, $default = '0px' ) { $corners = array( 'top', 'right', 'bottom', 'left' ); $corner_index = array_search( $corner, $corners, true ); $spacing_array = explode( '|', $spacing ); return isset( $spacing_array[ $corner_index ] ) && '' !== $spacing_array[ $corner_index ] ? $spacing_array[ $corner_index ] : $default; } endif; if ( ! function_exists( 'et_fb_enqueue_bundle' ) ) : /** * Enqueue a bundle * * @param string $id Name of the stylesheet. * @param string $resource Resource file name. * @param array $deps Resource dependencies. * @param mixed $ver Resource version number. */ function et_fb_enqueue_bundle( $id, $resource, $deps, $ver = false ) { $debug = defined( 'ET_DEBUG' ) && ET_DEBUG; $ver = false === $ver ? ET_BUILDER_VERSION : $ver; $build = 'frontend-builder/build'; $bundle = sprintf( '%s/%s/%s', ET_BUILDER_URI, $build, $resource ); $type = pathinfo( $resource, PATHINFO_EXTENSION ); switch ( $type ) { case 'css': if ( file_exists( sprintf( '%s%s/%s', ET_BUILDER_DIR, $build, $resource ) ) || ! $debug ) { wp_enqueue_style( $id, $bundle, $deps, $ver ); } elseif ( $debug ) { // Style is already embedded in the bundle but we still need to enqueue its deps. foreach ( $deps as $dep ) { wp_enqueue_style( $dep ); } } break; case 'js': if ( file_exists( sprintf( '%s%s/%s', ET_BUILDER_DIR, $build, $resource ) ) || ! $debug ) { // If the file exists on disk, enqueue it. wp_enqueue_script( $id, $bundle, $deps, $ver, true ); } else { // Otherwise load `hot` from webpack-dev-server. $site_url = wp_parse_url( get_site_url() ); $hot_bundle_url = "{$site_url['scheme']}://{$site_url['host']}:31495/$resource"; wp_enqueue_script( $id, $hot_bundle_url, $deps, $ver, true ); } wp_add_inline_script( $id, 'window.et_gb = (window.top && window.top.Cypress && window.parent === window.top && window) || (window.top && window.top.Cypress && window.parent !== window.top && window.parent) || window.top || window;', 'before' ); break; } } endif; if ( ! function_exists( 'et_builder_get_active_plugins' ) ) : /** * Get list of all active plugins (single, network active, and mu) * * @return array active plugins */ function et_builder_get_active_plugins() { $active_plugins = get_option( 'active_plugins' ); // Returned format must be array. if ( ! is_array( $active_plugins ) ) { $active_plugins = array(); } // Get mu-plugins (must-use) // mu-plugins data is returned in array( "plugin/name.php" => array( 'data' => 'value' ) ) format. $mu_plugins = get_mu_plugins(); if ( is_array( $mu_plugins ) ) { $active_plugins = array_merge( $active_plugins, array_keys( $mu_plugins ) ); } // Get network active plugins // Network active plugin data is returned in array( "plugin/name.php" => active_timestamp_int format. if ( is_multisite() ) { $network_active_plugins = get_site_option( 'active_sitewide_plugins' ); if ( is_array( $network_active_plugins ) ) { $active_plugins = array_merge( $active_plugins, array_keys( $network_active_plugins ) ); } } return apply_filters( 'et_builder_get_active_plugins', $active_plugins ); } endif; if ( ! function_exists( 'et_has_hover_enabled' ) ) : /** * Determine whether hover is enable or not. * * @param array $props element's props. */ function et_has_hover_enabled( $props ) { $et_has_hover_enabled = false; $prop_names = array_keys( $props ); $suffix = et_pb_hover_options()->get_enabled_suffix(); foreach ( $prop_names as $prop_name ) { if ( preg_match( "~{$suffix}$~", $prop_name ) && 'on' === $props[ $prop_name ] ) { $et_has_hover_enabled = true; break; } } return $et_has_hover_enabled; } endif; if ( ! function_exists( 'et_builder_is_hover_enabled' ) ) : /** * Check if the setting has enabled hover options * * @param string $setting setting name. * @param array $props element's props. */ function et_builder_is_hover_enabled( $setting, $props ) { return et_pb_hover_options()->is_enabled( $setting, $props ); } endif; if ( ! function_exists( 'et_builder_add_prefix' ) ) { /** * Prefixes a string key with a prefix string using the provided delimiter * In case the prefix is empty, original key is returned * * @param string $prefix prefix string. * @param string $key string key. * @param string $delimiter delimiter. * * @return string */ function et_builder_add_prefix( $prefix, $key, $delimiter = '_' ) { return '' === $prefix ? $key : $prefix . $delimiter . $key; } } if ( ! function_exists( 'et_builder_has_value' ) ) { /** * Check if value is not an empty value * Empty values are considered: * - null * - '' * - false * * @param string $value value to check. * * @return bool */ function et_builder_has_value( $value ) { return null !== $value && '' !== $value && false !== $value; } } if ( ! function_exists( 'et_builder_get_or' ) ) { /** * Returns the value in case it is not empty * Otherwise, return the default value * * @param string $value the builder value. * @param string $default default value. * * @return string */ function et_builder_get_or( $value, $default = '' ) { return et_builder_has_value( $value ) ? $value : $default; } } if ( ! function_exists( 'et_builder_module_prop' ) ) { /** * Returns props value by provided key, if the value is empty, returns the default value * * @param string $prop provided key. * @param array $props all props. * @param mixed $default default value. * * @return mixed|null */ function et_builder_module_prop( $prop, $props, $default ) { return et_builder_get_or( et_()->array_get( $props, $prop ), $default ); } } if ( ! function_exists( 'et_pb_get_column_svg' ) ) { /** * Returns svg which represents the requried columns type * * @param string $type Column layout type. * * @return string svg code. */ function et_pb_get_column_svg( $type ) { $svg = ''; switch ( $type ) { case '4_4': $svg = '<rect width="100%" height="20" y="5" rx="5" ry="5" />'; break; case '1_2,1_2': $svg = '<rect width="48.5%" height="20" y="5" rx="5" ry="5" /> <rect width="48.5%" height="20" y="5" rx="5" ry="5" x="51.5%" />'; break; case '1_3,1_3,1_3': $svg = '<rect width="31.3%" height="20" y="5" rx="5" ry="5" /> <rect width="31.3%" height="20" y="5" rx="5" ry="5" x="34.3%" /> <rect width="31.3%" height="20" y="5" rx="5" ry="5" x="68.6%" />'; break; case '1_4,1_4,1_4,1_4': $svg = '<rect width="22.75%" height="20" y="5" rx="5" ry="5" /> <rect width="22.75%" height="20" y="5" rx="5" ry="5" x="25.75%" /> <rect width="22.75%" height="20" y="5" rx="5" ry="5" x="51.5%" /> <rect width="22.75%" height="20" y="5" rx="5" ry="5" x="77.25%" />'; break; case '1_5,1_5,1_5,1_5,1_5': $svg = '<rect width="17.6%" height="20" y="5" rx="5" ry="5" /> <rect width="17.6%" height="20" y="5" rx="5" ry="5" x="20.6%" /> <rect width="17.6%" height="20" y="5" rx="5" ry="5" x="41.2%" /> <rect width="17.6%" height="20" y="5" rx="5" ry="5" x="61.8%" /> <rect width="17.6%" height="20" y="5" rx="5" ry="5" x="82.4%" />'; break; case '1_6,1_6,1_6,1_6,1_6,1_6': $svg = '<rect width="14.16%" height="20" y="5" rx="5" ry="5" /> <rect width="14.16%" height="20" y="5" rx="5" ry="5" x="17.16%" /> <rect width="14.16%" height="20" y="5" rx="5" ry="5" x="34.32%" /> <rect width="14.16%" height="20" y="5" rx="5" ry="5" x="51.48%" /> <rect width="14.16%" height="20" y="5" rx="5" ry="5" x="68.64%" /> <rect width="14.16%" height="20" y="5" rx="5" ry="5" x="85.8%" />'; break; case '2_5,3_5': $svg = '<rect width="38.5%" height="20" y="5" rx="5" ry="5" /> <rect width="58.5%" height="20" y="5" rx="5" ry="5" x="41.5%" />'; break; case '3_5,2_5': $svg = '<rect width="58.5%" height="20" y="5" rx="5" ry="5" /> <rect width="38.5%" height="20" y="5" rx="5" ry="5" x="61.5%" />'; break; case '1_3,2_3': $svg = '<rect width="31.5%" height="20" y="5" rx="5" ry="5" /> <rect width="65.5%" height="20" y="5" rx="5" ry="5" x="34.5%" />'; break; case '2_3,1_3': $svg = '<rect width="65.5%" height="20" y="5" rx="5" ry="5" /> <rect width="31.5%" height="20" y="5" rx="5" ry="5" x="68.5%" />'; break; case '1_4,3_4': $svg = '<rect width="23.5%" height="20" y="5" rx="5" ry="5" /> <rect width="73.5%" height="20" y="5" rx="5" ry="5" x="26.5%" />'; break; case '3_4,1_4': $svg = '<rect width="73.5%" height="20" y="5" rx="5" ry="5" /> <rect width="23.5%" height="20" y="5" rx="5" ry="5" x="76.5%" />'; break; case '1_4,1_2,1_4': $svg = '<rect width="23.5%" height="20" y="5" rx="5" ry="5" /> <rect width="47%" height="20" y="5" rx="5" ry="5" x="26.5%" /> <rect width="23.5%" height="20" y="5" rx="5" ry="5" x="76.5%" />'; break; case '1_5,3_5,1_5': $svg = '<rect width="18.5%" height="20" y="5" rx="5" ry="5" /> <rect width="57%" height="20" y="5" rx="5" ry="5" x="21.5%" /> <rect width="18.5%" height="20" y="5" rx="5" ry="5" x="81.5%" />'; break; case '1_4,1_4,1_2': $svg = '<rect width="23.5%" height="20" y="5" rx="5" ry="5" /> <rect width="23.5%" height="20" y="5" rx="5" ry="5" x="26.5%" /> <rect width="47%" height="20" y="5" rx="5" ry="5" x="53%" />'; break; case '1_2,1_4,1_4': $svg = '<rect width="47%" height="20" y="5" rx="5" ry="5" /> <rect width="23.5%" height="20" y="5" rx="5" ry="5" x="50%" /> <rect width="23.5%" height="20" y="5" rx="5" ry="5" x="76.5%" />'; break; case '1_5,1_5,3_5': $svg = '<rect width="18.5%" height="20" y="5" rx="5" ry="5" /> <rect width="18.5%" height="20" y="5" rx="5" ry="5" x="21.5%" /> <rect width="57%" height="20" y="5" rx="5" ry="5" x="43%" />'; break; case '3_5,1_5,1_5': $svg = '<rect width="57%" height="20" y="5" rx="5" ry="5" /> <rect width="18.5%" height="20" y="5" rx="5" ry="5" x="60%" /> <rect width="18.5%" height="20" y="5" rx="5" ry="5" x="81.5%" />'; break; case '1_6,1_6,1_6,1_2': $svg = '<rect width="14.6%" height="20" y="5" rx="5" ry="5" /> <rect width="14.6%" height="20" y="5" rx="5" ry="5" x="18.1%" /> <rect width="14.6%" height="20" y="5" rx="5" ry="5" x="36.2%" /> <rect width="45.7%" height="20" y="5" rx="5" ry="5" x="54.3%" />'; break; case '1_2,1_6,1_6,1_6': $svg = '<rect width="47%" height="20" y="5" rx="5" ry="5" /> <rect width="14.6%" height="20" y="5" rx="5" ry="5" x="50%" /> <rect width="14.6%" height="20" y="5" rx="5" ry="5" x="67.6%" /> <rect width="14.6%" height="20" y="5" rx="5" ry="5" x="85.2%" />'; break; } return $svg; } } /** * Get image metadata responsive sizes * * @since 3.27.3 * * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * @param array $size Array of width and height values in pixels (in that order). * * @return array|bool */ function et_builder_responsive_image_metadata( $image_src, $image_meta = null, $size = null ) { $cache = ET_Core_Cache_File::get( 'image_responsive_metadata' ); // Normalize image URL. $normalized_url = et_attachment_normalize_url( $image_src ); if ( isset( $cache[ $normalized_url ] ) ) { if ( et_core_is_uploads_dir_url( $normalized_url ) ) { return $cache[ $normalized_url ]; } unset( $cache[ $normalized_url ] ); ET_Core_Cache_File::set( 'image_responsive_metadata', $cache ); } $responsive_sizes = array(); $image_id = is_numeric( $image_src ) ? intval( $image_src ) : et_get_attachment_id_by_url( $image_src ); if ( ! $image_id ) { return array(); } if ( is_null( $image_meta ) ) { $image_meta = wp_get_attachment_metadata( $image_id ); } if ( ! $image_meta || empty( $image_meta['sizes'] ) ) { return array(); } if ( is_null( $size ) ) { $size = et_get_attachment_size_by_url( $image_src ); } if ( 'full' === $size && isset( $image_meta['width'] ) && isset( $image_meta['height'] ) ) { $size = array( absint( $image_meta['width'] ), absint( $image_meta['height'] ), ); } elseif ( is_string( $size ) && ! empty( $image_meta['sizes'][ $size ] ) ) { $size = array( absint( $image_meta['sizes'][ $size ]['width'] ), absint( $image_meta['sizes'][ $size ]['height'] ), ); } if ( ! $size || ! is_array( $size ) ) { return array(); } foreach ( $image_meta['sizes'] as $size_key => $size_data ) { if ( strpos( $size_key, 'et-pb-image--responsive--' ) !== 0 ) { continue; } if ( is_array( $size ) && $size[0] < $size_data['width'] ) { $responsive_sizes[ $size_data['width'] ] = false; } else { $responsive_sizes[ $size_data['width'] ] = $size_data; } } if ( $responsive_sizes ) { ksort( $responsive_sizes ); // Cache the responsive sizes data. if ( et_core_is_uploads_dir_url( $normalized_url ) ) { $cache[ $normalized_url ] = $responsive_sizes; ET_Core_Cache_File::set( 'image_responsive_metadata', $cache ); } } return $responsive_sizes; } if ( ! function_exists( 'et_filter_wp_calculate_image_srcset' ) ) : /** * Filters an image's 'srcset' sources. * * @since 3.27 * * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * * @type array $width { * @type string $url The URL of an image source. * @type string $descriptor The descriptor type used in the image candidate string, * either 'w' or 'x'. * @type int $value The source width if paired with a 'w' descriptor, or a * pixel density value if paired with an 'x' descriptor. * } * } * @param array $size_array Array of width and height values in pixels (in that order). * @param string $image_src The 'src' of the image. * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. * * @return array */ function et_filter_wp_calculate_image_srcset( $sources, $size_array, $image_src, $image_meta ) { // Do not filter when in wp-admin area. if ( is_admin() ) { return $sources; } $responsive_sources = array(); if ( ! et_is_responsive_images_enabled() ) { return $responsive_sources; } if ( is_string( $size_array ) ) { $size_array = et_get_attachment_size_by_url( $image_src ); } if ( is_string( $size_array ) && $image_meta ) { if ( 'full' === $size_array ) { $size_array = array( absint( $image_meta['width'] ), absint( $image_meta['height'] ), ); } elseif ( ! empty( $image_meta['sizes'][ $size_array ] ) ) { $size_array = array( absint( $image_meta['sizes'][ $size_array ]['width'] ), absint( $image_meta['sizes'][ $size_array ]['height'] ), ); } } if ( ! is_array( $size_array ) ) { return $responsive_sources; } $responsive_metadata = et_builder_responsive_image_metadata( $image_src, $image_meta, $size_array ); if ( $responsive_metadata ) { foreach ( $responsive_metadata as $max_width => $size_data ) { if ( ! $size_data ) { continue; } // In some SVG images, the value of `$max_width` is 0, in those cases we can set `$max_width` from `$size_array`. if ( ! $max_width ) { $max_width = $size_array[0]; } $responsive_sources[ $max_width ] = array( 'url' => str_replace( basename( $image_src ), $size_data['file'], $image_src ), 'descriptor' => 'w', 'value' => $max_width, ); } if ( $responsive_sources && $size_array[0] > $max_width ) { $responsive_sources[ $size_array[0] ] = array( 'url' => $image_src, 'descriptor' => 'w', 'value' => $size_array[0], ); } if ( $responsive_sources ) { krsort( $responsive_sources ); } } else { $responsive_sources = $sources; } return $responsive_sources; } endif; add_filter( 'wp_calculate_image_srcset', 'et_filter_wp_calculate_image_srcset', 10, 4 ); if ( ! function_exists( 'et_filter_wp_calculate_image_sizes' ) ) : /** * Filters the output of 'wp_calculate_image_sizes()'. * * @since 3.27.3 * * @param string $sizes A source size value for use in a 'sizes' attribute. * @param array|string $size Requested size. Image size or array of width and height values * in pixels (in that order). * @param string|null $image_src The URL to the image file or null. * @param array|null $image_meta The image meta data as returned by wp_get_attachment_metadata() or null. * * @return string|bool A valid source size value for use in a 'sizes' attribute or false. */ function et_filter_wp_calculate_image_sizes( $sizes, $size, $image_src, $image_meta ) { // Do not filter when in wp-admin area. if ( is_admin() ) { return $sizes; } $responsive_sizes = ''; if ( ! et_is_responsive_images_enabled() ) { return $responsive_sizes; } if ( is_string( $size ) ) { $size = et_get_attachment_size_by_url( $image_src ); } if ( is_string( $size ) && $image_meta ) { if ( 'full' === $size ) { $size = array( absint( $image_meta['width'] ), absint( $image_meta['height'] ), ); } elseif ( ! empty( $image_meta['sizes'][ $size ] ) ) { $size = array( absint( $image_meta['sizes'][ $size ]['width'] ), absint( $image_meta['sizes'][ $size ]['height'] ), ); } } if ( ! is_array( $size ) ) { return $responsive_sizes; } $responsive_metadata = et_builder_responsive_image_metadata( $image_src, $image_meta, $size ); if ( $responsive_metadata ) { $max_width = 0; $prev_width = 0; $sizes_temp = array(); foreach ( $responsive_metadata as $max_width => $size_data ) { if ( ! $size_data ) { continue; } if ( $prev_width ) { $sizes_temp[ $max_width ] = sprintf( '(min-width: %2$dpx) and (max-width: %1$dpx) %1$dpx', $max_width, ( $prev_width + 1 ) ); } else { $sizes_temp[ $max_width ] = sprintf( '(min-width: %2$dpx) and (max-width: %1$dpx) %1$dpx', $max_width, $prev_width ); } $prev_width = $max_width; } if ( $sizes_temp && $size[0] > $prev_width ) { $sizes_temp[ $size[0] ] = sprintf( '(min-width: %2$dpx) %1$dpx', $size[0], ( $prev_width + 1 ) ); } if ( $sizes_temp ) { $sizes_temp[] = '100vw'; } $responsive_sizes = implode( ', ', $sizes_temp ); } else { $responsive_sizes = $sizes; } return $responsive_sizes; } endif; add_filter( 'wp_calculate_image_sizes', 'et_filter_wp_calculate_image_sizes', 10, 4 ); /** * Register and localize assets early enough to avoid conflicts * with third party plugins that use the same assets. * * @since 4.0.9 */ function et_builder_register_assets() { global $wp_version; $root = ET_BUILDER_URI; $wp_major_version = substr( $wp_version, 0, 3 ); // phpcs:disable WordPress.WP.EnqueuedResourceParameters -- These scripts are inside WordPress core. In order to always load latest script, version number is set false. wp_register_script( 'iris', admin_url( 'js/iris.min.js' ), array( 'jquery-ui-draggable', 'jquery-ui-slider', 'jquery-touch-punch' ), false, 1 ); wp_register_script( 'wp-color-picker', admin_url( 'js/color-picker.min.js' ), array( 'iris' ), false, 1 ); // phpcs:enable $wp_color_picker_l10n = array( 'clear' => esc_html__( 'Clear', 'et_builder' ), 'defaultString' => et_builder_i18n( 'Default' ), 'pick' => esc_html__( 'Select Color', 'et_builder' ), ); if ( version_compare( $wp_major_version, '4.9', '>=' ) ) { wp_register_script( 'wp-color-picker-alpha', "{$root}/scripts/ext/wp-color-picker-alpha.min.js", array( 'jquery', 'wp-color-picker' ), ET_BUILDER_VERSION, true ); } else { wp_register_script( 'wp-color-picker-alpha', "{$root}/scripts/ext/wp-color-picker-alpha-48.min.js", array( 'jquery', 'wp-color-picker' ), ET_BUILDER_VERSION, true ); $wp_color_picker_l10n['current'] = esc_html__( 'Current Color', 'et_builder' ); } wp_localize_script( 'wp-color-picker', 'wpColorPickerL10n', $wp_color_picker_l10n ); } add_action( 'init', 'et_builder_register_assets', 11 ); if ( ! function_exists( 'et_set_parallax_bg_wrap_border_radius' ) ) : /** * Set border radius to parallax background wrapper. * * @since 4.4.8 * * @param array $props Module settings. * @param string $module Module slug. * @param string $order_class Module main css element. * * @return void */ function et_set_parallax_bg_wrap_border_radius( $props, $module, $order_class ) { $border_radius_values = et_pb_responsive_options()->get_property_values( $props, 'border_radii' ); $border_radius_hover_enabled = et_builder_module_prop( 'border_radii__hover_enabled', $props, '' ); $border_radius_hover_values = et_builder_module_prop( 'border_radii__hover', $props, '' ); foreach ( et_pb_responsive_options()->get_modes() as $device ) { if ( 'on||||' === $border_radius_values[ $device ] ) { $border_radius_values[ $device ] = ''; continue; } $border_radius_values[ $device ] = et_format_parallax_bg_wrap_radius_values( $border_radius_values[ $device ] ); } et_pb_responsive_options()->generate_responsive_css( $border_radius_values, $order_class . ' .et_parallax_bg_wrap', 'border-radius', $module, '', 'border' ); if ( 'on|hover' === $border_radius_hover_enabled ) { $radius_hover_values = et_format_parallax_bg_wrap_radius_values( $border_radius_hover_values ); } else { $radius_hover_values = $border_radius_values['desktop']; } if ( $radius_hover_values ) { $el_style = array( 'selector' => $order_class . ':hover .et_parallax_bg_wrap', 'declaration' => esc_html( sprintf( 'border-radius: %1$s;', $radius_hover_values ) ), ); ET_Builder_Element::set_style( $module, $el_style ); } } endif; if ( ! function_exists( 'et_format_parallax_bg_wrap_radius_values' ) ) : /** * Get formatted border radius of parallax background wrapper * * @since 4.4.8 * * @param string $border_radius_values border radius values. * * @return string */ function et_format_parallax_bg_wrap_radius_values( $border_radius_values ) { $radius_values = array(); $radius_array = explode( '|', $border_radius_values ); $radius_count = count( $radius_array ); for ( $i = 1; $i < $radius_count; $i++ ) { $radius_values[] = $radius_array[ $i ] ? $radius_array[ $i ] : 0; } return trim( implode( ' ', $radius_values ) ); } endif; if ( ! function_exists( 'et_builder_generate_css' ) ) { /** * Generate CSS. * * @param array $args Styles arg. * * @return string|void */ function et_builder_generate_css( $args ) { $defaults = array( 'prefix' => '', 'suffix' => '', ); $args = wp_parse_args( $args, $defaults ); /* * Bail early if we have no $selector elements or properties and $value. */ if ( ! $args['value'] || ! $args['selector'] ) { return; } return sprintf( '%s { %s: %s; }', $args['selector'], $args['style'], $args['prefix'] . $args['value'] . $args['suffix'] ); } } if ( ! function_exists( 'et_builder_generate_css_style' ) ) { /** * Generate CSS property. * * @param array $args Styles arg. * * @return string|void */ function et_builder_generate_css_style( $args ) { $defaults = array( 'prefix' => '', 'suffix' => '', ); $args = wp_parse_args( $args, $defaults ); /* * Bail early if we have no style and $value. */ if ( ! $args['value'] || ! $args['style'] ) { return; } return sprintf( '%s: %s;', $args['style'], $args['prefix'] . $args['value'] . $args['suffix'] ); } } if ( ! function_exists( 'et_builder_default_colors_ajax_update_handler' ) ) : /** * Default colors AJAX update handler. * * @since 4.9.0 */ function et_builder_default_colors_ajax_update_handler() { // Get nonce from $_POST. $nonce = isset( $_POST['et_builder_default_colors_nonce'] ) ? sanitize_text_field( $_POST['et_builder_default_colors_nonce'] ) : ''; if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'et_builder_default_colors_update' ) ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } // Get default_colors from $_POST. $default_colors = isset( $_POST['default_colors'] ) ? sanitize_text_field( wp_unslash( $_POST['default_colors'] ) ) : ''; et_update_option( 'divi_color_palette', str_replace( ',', '|', $default_colors ) ); wp_send_json_success(); } endif; add_action( 'wp_ajax_et_builder_default_colors_update', 'et_builder_default_colors_ajax_update_handler' ); if ( ! function_exists( 'et_builder_global_colors_ajax_save_handler' ) ) : /** * Global colors AJAX save handler. * * @since 4.9.0 */ function et_builder_global_colors_ajax_save_handler() { // Get nonce from $_POST. $nonce = isset( $_POST['et_builder_global_colors_save_nonce'] ) ? sanitize_text_field( $_POST['et_builder_global_colors_save_nonce'] ) : ''; if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'et_builder_global_colors_save' ) ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } // Get colors from $_POST. $post_colors = filter_input( INPUT_POST, 'global_colors', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); $global_colors = array(); if ( is_array( $post_colors ) ) { foreach ( $post_colors as $data_id => $data ) { // Drop bad data. if ( 'undefined' === $data_id || empty( $data ) ) { continue; } // Sanitize data_id (e.g: gcid-3330f0vf7 ). $global_id = sanitize_text_field( $data_id ); foreach ( $data as $type => $value ) { // Sanitize both type (e.g: color, active) and value (color value, yes/no). $global_colors[ $global_id ][ sanitize_text_field( $type ) ] = sanitize_text_field( $value ); } } } if ( empty( $global_colors ) ) { wp_send_json_error(); } /** * Fires after global colors are processed. * * @since 4.25.0 */ do_action( 'et_global_colors_saved', $global_colors ); // Do not save customizer colors into Global Colors setting. $excluded_keys = [ 'gcid-primary-color', 'gcid-secondary-color', 'gcid-heading-color', 'gcid-body-color', ]; foreach ( $excluded_keys as $excluded_key ) { unset( $global_colors[ $excluded_key ] ); } // Global Color data has been sanitized above. et_update_option( 'et_global_colors', $global_colors ); ET_Core_PageResource::remove_static_resources( 'all', 'all' ); wp_send_json_success(); } endif; add_action( 'wp_ajax_et_builder_global_colors_save', 'et_builder_global_colors_ajax_save_handler' ); if ( ! function_exists( 'et_builder_global_colors_ajax_get_handler' ) ) : /** * Global colors AJAX get handler. * * @since 4.19.2 */ function et_builder_global_colors_ajax_get_handler() { // Get nonce from $_GET. et_core_security_check( 'edit_posts', 'et_builder_global_colors_get', 'et_builder_global_colors_get_nonce', '_GET' ); wp_send_json_success( [ 'global_colors' => et_builder_get_all_global_colors( true ) ] ); } endif; add_action( 'wp_ajax_et_builder_global_colors_get', 'et_builder_global_colors_ajax_get_handler' ); /** * Get a global color info by id. * * @since 4.9.0 * * @param string $color_id Id of global color. * * @return array */ function et_builder_get_global_color_info( $color_id ) { $colors = et_builder_get_all_global_colors( true ); if ( empty( $colors ) || ! array_key_exists( $color_id, $colors ) ) { return null; } // if replaced value exists, return color info with that replaced id. if ( isset( $colors[ $color_id ]['replaced_with'] ) ) { $replaced_id = $colors[ $color_id ]['replaced_with']; return $colors[ $replaced_id ]; } return $colors[ $color_id ]; } /** * Check if given value is a Global Color Id. * * @since 4.21.1 * * @param string $attr_value Color value. * @return bool */ function et_builder_is_global_color( $attr_value ) { return 0 === strpos( $attr_value, 'gcid-' ); } /** * Get Global Color by Color Id. * * @since 4.21.1 * * @param string $color_id Color ID. * @return string */ function et_builder_get_global_color( $color_id ) { $color_info = et_builder_get_global_color_info( $color_id ); return isset( $color_info['color'] ) ? $color_info['color'] : $color_id; } /** * Checks if overflow CSS property should be set or not. * * @since 4.17.4 * * @param bool|string $overflow_enabled If overflow is enabled (true) or disabled (false) or -x or -y. * @param string $function_name Module slug. * @param ET_Builder_Element $module Module object. * * @return bool|string */ function et_process_border_radii_options_overflow( $overflow_enabled, $function_name, $module ) { if ( in_array( $function_name, [ 'et_pb_section', 'et_pb_row' ], true ) && ET_Builder_Element::module_contains( $module->_original_content, [ 'et_pb_menu', 'et_pb_fullwidth_menu' ] ) ) { $overflow_enabled = false; } return $overflow_enabled; } add_filter( 'et_builder_process_advanced_borders_options_radii_overflow_enabled', 'et_process_border_radii_options_overflow', 10, 3 ); /** * Adds `fitvidsignore` class to vimeo videos parent tags which have unusual aspect ratios. * * WordPress adds extra `div` tag as a parent on vimeo videos with unusual aspect ratios so * videos would have proper aspect ratio responsively. That causes issues with jQuery `fitvids()`. * Ref: https://github.com/elegantthemes/Divi/issues/16116 * * @since 4.17.5 */ add_filter( 'oembed_dataparse', function( $html, $data ) { if ( ! class_exists( 'DOMDocument' ) ) { return $html; } if ( 'Vimeo' !== $data->provider_name ) { return $html; } $doc = new DOMDocument(); $doc_load_html_state = $doc->loadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); if ( false === $doc_load_html_state ) { return $html; } $extra_div_nodes = $doc->getElementsByTagName( 'div' ); if ( 0 === $extra_div_nodes->length ) { return $html; } $extra_div_node = $extra_div_nodes[0]; $extra_div_node->setAttribute( 'class', 'fitvidsignore' ); $output = $doc->saveHTML(); $output = false === $output ? $html : $output; return $output; }, 10, 2 ); if ( ! function_exists( 'et_pb_get_youtube_url_regex' ) ) : /** * Regex to match a YouTube URL from any known/common YouTube URL format. * * Expected YouTube URL Formats. * - https://www.youtube.com/watch?v=XXXX. * - https://www.youtube.com/embed/XXXX. * - https://youtu.be/XXXX. * * To check regex, see: https://regex101.com/r/4FbeMZ/1. * * @since 4.18.1 * * @return string YouTube video URL regex. */ function et_pb_get_youtube_url_regex() { return '/^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/i'; } endif; if ( ! function_exists( 'et_pb_normalize_youtube_url' ) ) : /** * Normalize a YouTube URL from any known/common YouTube URL format. * * Convert YouTube URL into normalized form: https://www.youtube.com/watch?v=XXXX. * For https://www.youtube.com/watch?v=XXXX to check regex is https://regex101.com/r/B2qLJy/1. * For https://www.youtube.com/embed/XXXX to check regex is https://regex101.com/r/oZ3iNP/1. * For https://youtu.be/XXXX to check regex is https://regex101.com/r/5nqmhF/1. * * @param string $url youtube video url. * * @since 4.18.1 * * @return string Normalized YouTube URL. */ function et_pb_normalize_youtube_url( $url ) { preg_match( et_pb_get_youtube_url_regex(), esc_url( $url ), $youtube_embed_video ); return 'https://www.youtube.com/watch?v=' . $youtube_embed_video[1]; } endif; if ( ! function_exists( 'et_pb_validate_youtube_url' ) ) : /** * Validate a YouTube URL from any known/common YouTube URL format. * * For https://www.youtube.com/watch?v=XXXX to check regex is https://regex101.com/r/B2qLJy/1. * For https://www.youtube.com/embed/XXXX to check regex is https://regex101.com/r/oZ3iNP/1. * For https://youtu.be/XXXX to check regex is https://regex101.com/r/5nqmhF/1. * * @param string $url youtube video url. * * @since 4.18.1 * * @return bool Whether provided URL is a valid YouTube URL or not. */ function et_pb_validate_youtube_url( $url ) { preg_match( et_pb_get_youtube_url_regex(), $url, $youtube_embed_video ); return is_array( $youtube_embed_video ) && ! empty( $youtube_embed_video ); } endif; if ( ! function_exists( 'et_update_customizer_colors' ) ) : /** * Update customizer colors. * * @param array $global_colors Global colors. * * @since 4.25.0 * * @return void */ function et_update_customizer_colors( $global_colors ) { $primary_color = isset( $global_colors['gcid-primary-color']['color'] ) ? $global_colors['gcid-primary-color']['color'] : ''; $secondary_color = isset( $global_colors['gcid-secondary-color']['color'] ) ? $global_colors['gcid-secondary-color']['color'] : ''; $heading_color = isset( $global_colors['gcid-header-color']['color'] ) ? $global_colors['gcid-header-color']['color'] : ''; $body_color = isset( $global_colors['gcid-font-color']['color'] ) ? $global_colors['gcid-font-color']['color'] : ''; if ( ! empty( $primary_color ) ) { et_update_option( 'accent_color', $primary_color ); } if ( ! empty( $secondary_color ) ) { et_update_option( 'secondary_accent_color', $secondary_color ); } if ( ! empty( $heading_color ) ) { et_update_option( 'header_color', $heading_color ); } if ( ! empty( $body_color ) ) { et_update_option( 'font_color', $body_color ); } } endif; add_action( 'et_global_colors_saved', 'et_update_customizer_colors' ); /** * Ajax Callback :: Update cusomizer fonts. */ function et_update_customizer_fonts() { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'et_pb_save_customizer_fonts_nonce' ) ) { die( -1 ); } if ( ! current_user_can( 'edit_theme_options' ) ) { die( -1 ); } $new_heading_font = isset( $_POST['et_pb_heading_font'] ) ? sanitize_text_field( $_POST['et_pb_heading_font'] ) : ''; $new_body_font = isset( $_POST['et_pb_body_font'] ) ? sanitize_text_field( $_POST['et_pb_body_font'] ) : ''; if ( $new_heading_font ) { et_update_option( 'heading_font', $new_heading_font ); } if ( $new_body_font ) { et_update_option( 'body_font', $new_body_font ); } } add_action( 'wp_ajax_et_update_customizer_fonts', 'et_update_customizer_fonts' ); PK X�\ߢ�f� � 0 builder/class-et-builder-global-feature-base.phpnu �[��� <?php /** * Feature base class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Base Feature feature. * * @since 4.10.0 */ class ET_Builder_Global_Feature_Base { const CACHE_META_KEY = '_et_builder_global_feature_cache'; /** * Primed status. * * @access protected * @var bool */ protected $_primed = false; /** * Cache array. * * @access protected * @var array */ protected $_cache = []; /** * Cache status. * * @access protected * @var array */ protected $_cache_dirty = false; /** * Construct instance. */ public function __construct() { if ( self::enabled() ) { $this->cache_prime(); if ( ! has_action( 'shutdown', [ $this, 'cache_save' ] ) ) { add_action( 'shutdown', [ $this, 'cache_save' ] ); } } } /** * Purge the features cache. * * @since 4.10.0 */ public static function purge_cache() { delete_option( static::CACHE_META_KEY ); } /** * Get the features cache. * * @since 4.10.0 * * @return mixed */ public static function load_cache() { return get_option( static::CACHE_META_KEY ); } /** * Tell whether we should use cache or not. * * @since 4.10.0 * * @return bool */ public static function enabled() { /** * Whether Global Feature Cache should be enabled or not. * * @since ? * * @param bool $enabled. */ return apply_filters( 'et_builder_global_feature_cache_enabled', et_builder_is_frontend() ); } /** * Prime the cache. * * @since 4.10.0 */ public function cache_prime() { if ( empty( $this->_primed ) ) { $meta = self::load_cache(); if ( isset( $meta[1] ) ) { list( $stored_index, $cache ) = $meta; $current_index = (string) self::_get_cache_index(); $this->_cache = ( $current_index === $stored_index ) ? $cache : []; } $this->_primed = true; } } /** * Save the cache. * * @since 4.10.0 */ public function cache_save() { // Only if cache is "dirty" and builder is used. if ( $this->_cache_dirty ) { $cache = array( self::_get_cache_index(), $this->_cache ); update_option( static::CACHE_META_KEY, $cache ); } } /** * Get Cache Version Index Items. * * @since 4.10.0 * @access protected * @return array Cache version items. */ protected static function _get_cache_index_items() { global $wp_version; /** * Filters global feature cache index items. * * @since 4.10.0 * * @param array Assoc array of cache index items. * @param string The cache meta key that the cache index items belong to. */ return apply_filters( 'et_global_feature_cache_index_items', array( 'gph' => ET_Builder_Global_Presets_History::instance()->get_global_history_index(), 'divi' => et_get_theme_version(), 'wp' => $wp_version, ), static::CACHE_META_KEY ); } /** * Get Cache Version Index. * * @since 4.10.0 * @access protected * @return string .Cache version index. */ public static function _get_cache_index() { return wp_json_encode( self::_get_cache_index_items() ); } /** * Get cache * * @since 4.10.0 * @param string $key Cache key. * @param string $group Cache Group. */ public function cache_get( $key, $group = 'default' ) { $exists = isset( $this->_cache[ $group ] ) && isset( $this->_cache[ $group ][ $key ] ); return $exists ? $this->_cache[ $group ][ $key ] : null; } /** * Set cache * * @since 4.10.0 * @param string $key Cache key. * @param mixed $value To be cached. * @param string $group Cache group. */ public function cache_set( $key, $value, $group = 'default' ) { $this->_cache[ $group ][ $key ] = $value; $this->_cache_dirty = true; } /** * Check for cached value. * * First check cache if present, if not, determine * from calling the callback. * * @param string $key Name of item. * @param function $cb Callback function to perform logic. * @param string $group Cache group. * * @return bool/mixed Result. */ public function get( $key, $cb, $group = 'default' ) { // short circuit. $result = $this->cache_get( $key, $group ); if ( is_null( $result ) ) { // Set cache for next time. $result = $cb(); $this->cache_set( $key, $result, $group ); } return $result; } } PK X�\�a-� � 1 builder/class-et-builder-google-fonts-feature.phpnu �[��� <?php /** * Google_Fonts feature class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Handles Google_Fonts feature. * * @since 4.10.0 */ class ET_Builder_Google_Fonts_Feature extends ET_Builder_Global_Feature_Base { /** * `ET_Builder_Google_Fonts_Feature` instance. * * @var ET_Builder_Google_Fonts_Feature */ private static $_instance; const CACHE_META_KEY = '_et_builder_gf_feature_cache'; /** * Returns instance of the class. * * @since 4.10.0 */ public function __construct() { // need to set this filter up front, bc as soon as the parent __construct // is called then it will prime the cache so it needs // this cache index filter to be setup right away. add_filter( 'et_global_feature_cache_index_items', [ 'ET_Builder_Google_Fonts_Feature', 'cache_index_items' ], 10, 2 ); parent::__construct(); if ( self::enabled() ) { self::setup_transient(); } } /** * Initialize ET_Builder_Google_Fonts_Feature class. */ public static function instance() { if ( ! self::$_instance ) { self::$_instance = new self(); } return self::$_instance; } /** * Setup transient to purge cache once a day. * * @since 4.10.0 */ public static function setup_transient() { if ( ! get_transient( self::CACHE_META_KEY ) ) { self::purge_cache(); set_transient( self::CACHE_META_KEY, '1', DAY_IN_SECONDS ); } } /** * Add to cache index items. * * @since 4.10.0 * * @param array $items Assoc array of cache index items. * @param string $key The cache meta key that the cache index items belong to. * * @return array $items Assoc array of cache index items. */ public static function cache_index_items( $items, $key ) { global $shortname; if ( self::CACHE_META_KEY === $key ) { $items['enable_all_character_sets'] = (string) et_get_option( "{$shortname}_gf_enable_all_character_sets", 'false' ); } return $items; } /** * Determines if an option is enabled. * * @since 4.10.0 * * @param string $sub_option Google Fonts Sub Option. * * @return bool Whether the sub option is enabled. */ public function is_option_enabled( $sub_option = '' ) { global $shortname; if ( ! self::enabled() ) { return false; } if ( et_is_builder_plugin_active() ) { $options = get_option( 'et_pb_builder_options', array() ); $option_state = isset( $options[ 'performance_main_' . $sub_option ] ) ? $options[ 'performance_main_' . $sub_option ] : 'on'; } else { $option_state = et_get_option( $shortname . '_' . $sub_option, 'on' ); } /** * Filters the option state before it's returned. * Allows other plugins to enable or disable a Google Font feature from code. * * @since 4.19.1 * * @param bool $is_enabled Whether the sub option is enabled. * @param string $sub_option Google Fonts sub option. */ return apply_filters( 'et_builder_google_fonts_is_enabled', 'on' === $option_state, $sub_option ); } /** * Builds the full URL to the Google Font css file to load the fonts * provided by the parameter. * * @since 4.19.1 * * @param array $google_fonts_url_args An array containing query-parameters. * * @return string The full URL to the Google Fonts resource. */ public function get_google_fonts_url( $google_fonts_url_args ) { $url = add_query_arg( $google_fonts_url_args, 'https://fonts.googleapis.com/css' ); $url = esc_url_raw( $url ); /** * Filters the URL of the requested Google Font CSS file. * Allows swapping Google Fonts with other font sources. * * @since 4.19.1 * * @param string $url The URL to the Google Fonts CSS file to fetch. */ return apply_filters( 'et_builder_google_fonts_get_url', $url ); } /** * Get User Agents. * * @since 4.10.0 * * @return array[string] List of user agents. */ protected function _get_user_agents() { $limit_support = $this->is_option_enabled( 'limit_google_fonts_support_for_legacy_browsers' ); $uas = []; if ( ! $limit_support ) { /** * IE9 Compat Modes */ $uas['eot'] = 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)'; /** * Legacy iOS */ $uas['svg'] = 'Mozilla/4.0 (iPad; CPU OS 4_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/4.1 Mobile/9A405 Safari/7534.48.3'; } /** * Safari, Android, iOS */ $uas['ttf'] = 'Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) Safari/538.1 Daum/4.1'; /** * Modern Browsers. * Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ $uas['woff'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0'; /** * Super Modern Browsers. * hrome 26+, Opera 23+, Firefox 39+ */ $uas['woff2'] = 'Mozilla/5.0 (Windows NT 6.3; rv:39.0) Gecko/20100101 Firefox/39.0'; /** * Filters which user agents to use to get google fonts. * * @since 4.10.0 */ return apply_filters( 'et_builder_google_fonts_user_agents', $uas ); } /** * Fetch CSS file contents from google fonts URL. * * @since 4.10.0 * * @param string $url The Google Fonts URL to fetch the contents for. * * @return string $url CSS file contents. */ public function fetch( $url ) { $all_contents = '/* Original: ' . esc_url( $url ) . ' */'; foreach ( $this->_get_user_agents() as $ua ) { $response = wp_remote_get( esc_url_raw( $url ), [ 'user-agent' => $ua, ] ); // Return if there is no response or if there is an error. if ( ! is_array( $response ) || is_wp_error( $response ) ) { return false; } $contents = wp_remote_retrieve_body( $response ); $contents = '/* User Agent: ' . $ua . ' */' . "\n" . $contents; $all_contents .= "\n\n" . $contents; } /** * Filter the unminified CSS file contents after loading it from Googles servers. * * @since 4.19.1 * * @param string $all_contents The concatenated CSS files content that was fetched from Google. * @param string $url The Google Fonts URL that was fetched. */ $all_contents = apply_filters( 'et_builder_google_fonts_fetch_content', $all_contents, $url ); $all_contents = self::minify( $all_contents ); return $all_contents; } /** * Minify CSS string. * * @since 4.10.0 * * @param string $data Multiline CSS data. * * @return string Minifed CSS data. */ public static function minify( $data ) { $data = trim( $data ); $data = preg_replace( '/\n/smi', '', $data ); $data = preg_replace( '/\s\s/smi', '', $data ); return $data; } } PK X�\��B5 5 1 builder/class-et-builder-theme-compat-handler.phpnu �[��� <?php /** * Load theme compatibility file for current theme. * * @since 4.10.0 * @package Divi */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class ET_Builder_Plugin_Compat_Loader. */ class ET_Builder_Theme_Compat_Handler { /** * Unique instance of class. * * @var ET_Builder_Theme_Compat_Handler */ public static $instance; /** * Constructor. */ private function __construct() { $this->_init_hooks(); } /** * Gets the instance of the class. */ public static function init() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Hook methods to WordPress action and filter. * * @return void */ private function _init_hooks() { // Load plugin.php for frontend usage. if ( ! function_exists( 'wp_get_theme' ) ) { include_once ABSPATH . 'wp-admin/includes/theme.php'; } $current_theme = wp_get_theme(); $current_theme_name = $current_theme->get( 'TextDomain' ); $theme_compat_url = apply_filters( "et_builder_theme_compat_path_{$current_theme_name}", ET_BUILDER_DIR . "theme-compat/{$current_theme_name}.php", $current_theme_name ); if ( file_exists( $theme_compat_url ) ) { require_once $theme_compat_url; } } } ET_Builder_Theme_Compat_Handler::init(); PK X�\�N��_ _ 3 builder/class-et-builder-dynamic-assets-feature.phpnu �[��� <?php /** * Dynamic_Assets feature class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Handles Dynamic_Assets feature. * * @since 4.10.0 */ class ET_Builder_Dynamic_Assets_Feature extends ET_Builder_Global_Feature_Base { /** * Hold the class instance. * * @var null */ private static $_instance = null; const CACHE_META_KEY = '_et_builder_da_feature_cache'; /** * Initialize ET_Builder_Dynamic_Assets_Feature class. */ public static function instance() { if ( ! self::$_instance ) { self::$_instance = new self(); } return self::$_instance; } } PK X�\/�R=� =� builder/ab-testing.phpnu �[��� <?php /** * Builder ab testing. * * @package Divi * @subpackage Builder * @since 4.6.2 */ // Prevent file from being loaded directly. if ( ! defined( 'ABSPATH' ) ) { die( -1 ); } if ( ! defined( 'ET_PB_AB_DB_VERSION' ) ) { define( 'ET_PB_AB_DB_VERSION', '1.1' ); } /** * AB Testing related data * * @param integer $post_id Post id. * * @return mixed|void {array} AB Testing related data */ function et_builder_ab_options( $post_id ) { $ab_options = array( 'db_status' => true === et_pb_db_status_up_to_date() ? 'on' : 'off', 'test_id' => $post_id, 'has_report' => et_pb_ab_has_report( $post_id ), 'has_permission' => et_pb_is_allowed( 'ab_testing' ), 'refresh_interval_duration' => et_pb_ab_get_refresh_interval_duration( $post_id ), 'refresh_interval_durations' => et_pb_ab_refresh_interval_durations(), 'analysis_formula' => et_pb_ab_get_analysis_formulas(), 'have_conversions' => et_pb_ab_get_modules_have_conversions(), 'sales_title' => esc_html__( 'Sales', 'et_builder' ), 'total_title' => esc_html__( 'Total', 'et_builder' ), // Saved data. 'subjects_rank' => ( 'on' === get_post_meta( $post_id, '_et_pb_use_builder', true ) ) ? et_pb_ab_get_saved_subjects_ranks( $post_id ) : false, // Rank color. 'subjects_rank_color' => et_pb_ab_get_subject_rank_colors(), ); return apply_filters( 'et_builder_ab_options', $ab_options ); } /** * Filterable AB Testing labels * * @return {array} AB Testing labels */ function et_builder_ab_labels() { $ab_settings = array( 'alert_modal_defaults' => array( 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'select_subject' => array( 'title' => esc_html__( 'Select Split Testing Subject', 'et_builder' ), 'desc' => esc_html__( 'You have activated the Divi Leads Split Testing System. Using split testing, you can create different element variations on your page to find out which variation most positively affects the conversion rate of your desired goal. After closing this window, please click on the section, row or module that you would like to split test.', 'et_builder' ), ), 'select_goal' => array( 'title' => esc_html__( 'Select Your Goal', 'et_builder' ), 'desc' => esc_html__( 'Congratulations, you have selected a split testing subject! Next you need to select your goal. After closing this window, please click the section, row or module that you want to use as your goal. Depending on the element you choose, Divi will track relevant conversion rates for clicks, reads or sales. For example, if you select a Call To Action module as your goal, then Divi will track how variations in your test subjects affect how often visitors read and click the button in your Call To Action module. The test subject itself can also be selected as your goal.', 'et_builder' ), 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'configure_alternative' => array( 'title' => esc_html__( 'Configure Subject Variations', 'et_builder' ), 'desc' => esc_html__( 'Congratulations, your split test is ready to go! You will notice that your split testing subject has been duplicated. Each split testing variation will be displayed to your visitors and statistics will be collected to figure out which variation results in the highest goal conversion rate. Your test will begin when you save this page.', 'et_builder' ), 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'select_winner_first' => array( 'title' => esc_html__( 'Select Split Testing Winner', 'et_builder' ), 'desc' => esc_html__( 'Before ending your split test, you must choose which split testing variation to keep. Please select your favorite or highest converting subject. Alternative split testing subjects will be removed and stats will be cleared.', 'et_builder' ), ), 'select_subject_first' => array( 'title' => esc_html__( 'Select Split Testing Subject', 'et_builder' ), 'desc' => esc_html__( 'You need to select a split testing subject first.', 'et_builder' ), ), 'select_goal_first' => array( 'title' => esc_html__( 'Select Split Testing Goal', 'et_builder' ), 'desc' => esc_html__( 'You need to select a split testing goal first. ', 'et_builder' ), ), 'cannot_select_subject_parent_as_goal' => array( 'title' => esc_html__( 'Select A Different Goal', 'et_builder' ), 'desc' => esc_html__( 'This element cannot be used as a your split testing goal. Please select a different module, or section.', 'et_builder' ), 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'cannot_select_global_children_as_subject' => array( 'title' => esc_html__( 'Select a Different Subject', 'et_builder' ), 'desc' => esc_html__( 'This element cannot be used as split testing subject because it is part of global module. Please select different module, row, or section', 'et_builder' ), ), 'cannot_select_global_children_as_goal' => array( 'title' => esc_html__( 'Select a Different Goal', 'et_builder' ), 'desc' => esc_html__( 'This element cannot be used as split testing goal because it is part of global module. Please select different module, row, or section', 'et_builder' ), ), 'cannot_publish_finish_configuration_first' => array( 'title' => esc_html__( 'Setup Split Test First', 'et_builder' ), 'desc' => esc_html__( 'You cannot publish the layout right now because you have incomplete split test configuration. Please finish the split test configuration first, then try saving again.', 'et_builder' ), 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'cannot_save_draft_finish_configuration_first' => array( 'title' => esc_html__( 'Setup Split Test First', 'et_builder' ), 'desc' => esc_html__( 'You cannot save the layout right now because you have incomplete split test configuration. Please finish the split test configuration first, then try save draft again.', 'et_builder' ), 'proceed_label' => esc_html__( 'Ok', 'et_builder' ), ), 'view_stats_thead_titles' => array( 'clicks' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Impressions', 'et_builder' ), esc_html__( 'Clicks', 'et_builder' ), esc_html__( 'Clickthrough Rate', 'et_builder' ), ), 'reads' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Impressions', 'et_builder' ), esc_html__( 'Reads', 'et_builder' ), esc_html__( 'Reading Rate', 'et_builder' ), ), 'bounces' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Impressions', 'et_builder' ), esc_html__( 'Stays', 'et_builder' ), esc_html__( 'Bounce Rate', 'et_builder' ), ), 'engagements' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Goal Views', 'et_builder' ), esc_html__( 'Goal Reads', 'et_builder' ), esc_html__( 'Engagement Rate', 'et_builder' ), ), 'conversions' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Impressions', 'et_builder' ), esc_html__( 'Conversion Goals', 'et_builder' ), esc_html__( 'Conversion Rate', 'et_builder' ), ), 'shortcode_conversions' => array( esc_html__( 'ID', 'et_builder' ), esc_html__( 'Subject', 'et_builder' ), esc_html__( 'Impressions', 'et_builder' ), esc_html__( 'Shortcode Conversions', 'et_builder' ), esc_html__( 'Conversion Rate', 'et_builder' ), ), ), // Save to Library. 'cannot_save_app_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Save Layout', 'et_builder' ), 'desc' => esc_html__( 'You cannot save layout while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), 'cannot_save_section_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Save Section', 'et_builder' ), 'desc' => esc_html__( 'You cannot save this section while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), 'cannot_save_row_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Save Row', 'et_builder' ), 'desc' => esc_html__( 'You cannot save this row while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), 'cannot_save_row_inner_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Save Row', 'et_builder' ), 'desc' => esc_html__( 'You cannot save this row while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), 'cannot_save_module_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Save Module', 'et_builder' ), 'desc' => esc_html__( 'You cannot save this module while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), // Load / Clear Layout. 'cannot_load_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Load Layout', 'et_builder' ), 'desc' => esc_html__( 'You cannot load a new layout while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), 'cannot_clear_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Clear Layout', 'et_builder' ), 'desc' => esc_html__( 'You cannot clear your layout while a split testing is running. Please end your split test before clearing your layout.', 'et_builder' ), ), // Cannot Import / Export Layout (Portability). 'cannot_import_export_layout_has_ab_testing' => array( 'title' => esc_html__( 'Can\'t Import/Export Layout', 'et_builder' ), 'desc' => esc_html__( 'You cannot import or export a layout while a split test is running. Please end your split test and then try again.', 'et_builder' ), ), // Moving Goal / Subject. 'cannot_move_module_goal_out_from_subject' => array( 'title' => esc_html__( 'Can\'t Move Goal', 'et_builder' ), 'desc' => esc_html__( 'Once set, a goal that has been placed inside a split testing subject cannot be moved outside the split testing subject. You can end your split test and start a new one if you would like to make this change.', 'et_builder' ), ), 'cannot_move_row_goal_out_from_subject' => array( 'title' => esc_html__( 'Can\'t Move Goal', 'et_builder' ), 'desc' => esc_html__( 'Once set, a goal that has been placed inside a split testing subject cannot be moved outside the split testing subject. You can end your split test and start a new one if you would like to make this change.', 'et_builder' ), ), 'cannot_move_goal_into_subject' => array( 'title' => esc_html__( 'Can\'t Move Goal', 'et_builder' ), 'desc' => esc_html__( 'A split testing goal cannot be moved inside of a split testing subject. To perform this action you must first end your split test.', 'et_builder' ), ), 'cannot_move_subject_into_goal' => array( 'title' => esc_html__( 'Can\'t Move Subject', 'et_builder' ), 'desc' => esc_html__( 'A split testing subject cannot be moved inside of a split testing goal. To perform this action you must first end your split test.', 'et_builder' ), ), // Cannot Paste Goal / Subject. 'cannot_paste_goal' => array( 'title' => esc_html__( 'Can\'t Paste Goal', 'et_builder' ), 'desc' => esc_html__( 'A split testing goal cannot be copied, cut, and pasted. To perform this action you must first end your split test.', 'et_builder' ), ), 'cannot_paste_row_has_subject_into_goal' => array( 'title' => esc_html__( 'Can\'t Paste Row', 'et_builder' ), 'desc' => esc_html__( 'Row that has split testing subject cannot be pasted inside a split testing goal. To perform this action you must first end your split test.', 'et_builder' ), ), 'cannot_paste_subject_into_goal' => array( 'title' => esc_html__( 'Can\'t Paste Subject', 'et_builder' ), 'desc' => esc_html__( 'A split testing subject cannot be pasted inside a split testing goal. To perform this action you must first end your split test.', 'et_builder' ), ), // Removing + Has Goal. 'cannot_remove_section_has_goal' => array( 'title' => esc_html__( 'Can\'t Remove Section', 'et_builder' ), 'desc' => esc_html__( 'This section cannot be removed because it contains a split testing goal. Goals cannot be deleted. You must first end your split test before performing this action.', 'et_builder' ), ), 'cannot_remove_row_has_goal' => array( 'title' => esc_html__( 'Can\'t Remove Row', 'et_builder' ), 'desc' => esc_html__( 'This row cannot be removed because it contains a split testing goal. Goals cannot be deleted. You must first end your split test before performing this action.', 'et_builder' ), ), // Removing + Has Unremovable Subjects. 'cannot_remove_section_has_unremovable_subject' => array( 'title' => esc_html__( 'Can\'t Remove Section', 'et_builder' ), 'desc' => esc_html__( 'Split testing requires at least 2 subject variations. This variation cannot be removed until additional variations have been added.', 'et_builder' ), ), 'cannot_remove_row_has_unremovable_subject' => array( 'title' => esc_html__( 'Can\'t Remove Row', 'et_builder' ), 'desc' => esc_html__( 'Split testing requires at least 2 subject variations. This variation cannot be removed until additional variations have been added', 'et_builder' ), ), // Cloning + Has Goal. 'cannot_clone_section_has_goal' => array( 'title' => esc_html__( 'Can\'t Clone Section', 'et_builder' ), 'desc' => esc_html__( 'This section cannot be duplicated because it contains a split testing goal. Goals cannot be duplicated. You must first end your split test before performing this action.', 'et_builder' ), ), 'cannot_clone_row_has_goal' => array( 'title' => esc_html__( 'Can\'t Clone Row', 'et_builder' ), 'desc' => esc_html__( 'This row cannot be duplicated because it contains a split testing goal. Goals cannot be duplicated. You must first end your split test before performing this action.', 'et_builder' ), ), // Copy + Has Goal. 'cannot_copy_section_has_goal' => array( 'title' => esc_html__( 'Can\'t Copy Section', 'et_builder' ), 'desc' => esc_html__( 'This section cannot be copied because it contains a split testing goal. Goals cannot be duplicated. You must first end your split test before performing this action.', 'et_builder' ), ), 'cannot_copy_row_has_goal' => array( 'title' => esc_html__( 'Can\'t Copy Row', 'et_builder' ), 'desc' => esc_html__( 'This row cannot be copied because it contains a split testing goal. Goals cannot be duplicated. You must first end your split test before performing this action.', 'et_builder' ), ), // Copy Goal. 'cannot_copy_goal' => array( 'title' => esc_html__( 'Can\'t Copy Goal', 'et_builder' ), 'desc' => esc_html__( 'Goal cannot be copied. You must first end your split test before performing this action.', 'et_builder' ), ), // No AB Testing Permission. 'has_no_ab_permission' => array( 'title' => esc_html__( 'Can\'t Edit Split Test', 'default' ), 'desc' => esc_html__( 'You do not have permission to edit the module, row or section in this split test.', 'et_builder' ), ), // No AB Testing Report Yet. 'no_report' => array( 'title' => esc_html__( 'Statistics are being collected', 'et_builder' ), 'desc' => esc_html__( 'Stats will be displayed upon sufficient data collection', 'et_builder' ), // 10 ), // Set Global Winner Status. 'set_global_winner_status' => array( 'title' => esc_html__( 'Set Winner Status', 'et_builder' ), 'desc' => esc_html__( 'You were using global item as split testing winner. Consequently, you have to choose between:', 'et_builder' ), 'option_1' => esc_html__( 'Save winner as global item (selected subject will be synced and your global item will be updated in the Divi Library)', 'et_builder' ), 'option_2' => esc_html__( 'Save winner as non-global item (selected subject will no longer be a global item and your changes will not modify the global item)', 'et_builder' ), 'cancel' => esc_html__( 'Save as Global Item', 'et_builder' ), 'proceed' => esc_html__( 'Save', 'et_builder' ), ), ); return apply_filters( 'et_builder_ab_settings', $ab_settings ); } /** * AJAX endpoint for builder data * * @return void */ function et_pb_ab_builder_data() { // Verify nonce. // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- `wp_verify_nonce()` does not store or display the nonce value, therefor XSS safe. if ( ! isset( $_POST['et_pb_ab_nonce'] ) || ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) { die( -1 ); } $defaults = array( 'et_pb_ab_test_id' => '', 'et_pb_ab_duration' => 'week', ); $_post = wp_parse_args( $_POST, $defaults ); $_post['et_pb_ab_test_id'] = ! empty( $_post['et_pb_ab_test_id'] ) ? intval( $_post['et_pb_ab_test_id'] ) : ''; // Verify user permission. if ( empty( $_post['et_pb_ab_test_id'] ) || ! current_user_can( 'edit_post', $_post['et_pb_ab_test_id'] ) || ! et_pb_is_allowed( 'ab_testing' ) ) { die( -1 ); } // Allowlist the duration value. $duration = in_array( $_post['et_pb_ab_duration'], et_pb_ab_get_stats_data_duration(), true ) ? $_post['et_pb_ab_duration'] : $defaults['et_pb_ab_duration']; // Get data. $output = et_pb_ab_get_stats_data( intval( $_post['et_pb_ab_test_id'] ), $duration ); // Print output. die( et_core_esc_previously( wp_json_encode( $output ) ) ); } add_action( 'wp_ajax_et_pb_ab_builder_data', 'et_pb_ab_builder_data' ); /** * Get AB Testing subject ranking data * * @param integer $post_id Post id. * * @return array */ function et_pb_ab_get_saved_subjects_ranks( $post_id ) { global $post; // Make sure that there are $post_id. if ( ! isset( $post_id ) && isset( $post->ID ) ) { $post_id = $post->ID; } // Get list of subjects. $subject_list = get_post_meta( $post_id, '_et_pb_ab_subjects', true ); $subjects_ids = explode( ',', $subject_list ); $subjects = array(); $goal_slug = et_pb_ab_get_goal_module( $post_id ); $rank_metrics = in_array( $goal_slug, et_pb_ab_get_modules_have_conversions(), true ) ? 'conversions' : 'clicks'; if ( ! empty( $subjects_ids ) ) { // Get conversion rate data. $subjects_ranks = et_pb_ab_get_subjects_ranks( $post_id, $rank_metrics, 'all' ); // Sort from high to low and mantain key association. arsort( $subjects_ranks ); // Loop saved subject ids. foreach ( $subjects_ids as $subject_id ) { $subject_key = 'subject_' . $subject_id; $subject_rank = isset( $subjects_ranks[ $subject_key ] ) ? array_search( $subjects_ranks[ $subject_key ], array_values( $subjects_ranks ), true ) + 1 : false; // Check whether current subject has saved conversion rate data or not. if ( $subject_rank ) { $subjects[ $subject_key ] = array( 'percentage' => esc_html( $subjects_ranks[ $subject_key ] . '%' ), 'rank' => esc_attr( $subject_rank ), ); } } } return $subjects; } /** * Define ranking-based subject color * * @return array */ function et_pb_ab_get_subject_rank_colors() { $subject_rank_colors = array( '#F3CB57', '#F8B852', '#F8A653', '#F88F55', '#F87356', '#F95A57', '#EA5552', '#DB514F', '#CE4441', '#BF2F2C', '#AA201C', '#920E08', '#7E0000', ); return array_map( 'et_sanitize_alpha_color', apply_filters( 'et_pb_ab_get_subject_rank_colors', $subject_rank_colors ) ); } /** * Print AB Testing subject-ranking color scheme * * @return string inline CSS styling for subject rank */ function et_pb_ab_get_subject_rank_colors_style() { $style = ''; $colors = et_pb_ab_get_subject_rank_colors(); $index = 1; foreach ( $colors as $color ) { $style .= sprintf( '.et_pb_ab_subject.rank-%1$s .et_pb_module_block, .et_pb_ab_subject.rank-%1$s.et_pb_section .et-pb-controls, .et_pb_ab_subject.rank-%1$s.et_pb_row .et-pb-controls, .et_pb_ab_subject.rank-%1$s.et_pb_module_block { background: %2$s; }', esc_html( $index ), esc_html( $color ) ); $index++; } return $style; } /** * Get subjects' ranks * * @param int $post_id post ID. * @param string $ranking_basis ranking basis. This can be any value on data's subjects_totals * view_page|read_page|view_goal|read_goal|click_goal|con_goal|clicks|reads|bounces|engagements|conversions. * @param string $duration duration of the data that is used. * @return array key = `subject_` + subject_id as key and the value as value, sorted in ascending */ function et_pb_ab_get_subjects_ranks( $post_id, $ranking_basis = 'engagements', $duration = 'week' ) { $data = et_pb_ab_get_stats_data( $post_id, $duration ); $subjects = et_pb_ab_get_subjects( $post_id, 'array', 'subject_' ); if ( isset( $data['subjects_totals'] ) && ! empty( $data['subjects_totals'] ) && ! empty( $subjects ) ) { // Pluck data. $ranks = wp_list_pluck( $data['subjects_totals'], $ranking_basis ); // Remove inactive subjects from ranks. foreach ( $ranks as $rank_key => $rank_value ) { if ( ! in_array( $rank_key, $subjects, true ) ) { unset( $ranks[ $rank_key ] ); } } // Sort rank. arsort( $ranks ); } else { $ranks = array(); } return $ranks; } /** * Get formatted stats data that is used by builder's AB Testing stats * * @param int $post_id post ID. * @param string $duration day|week|month|all duration of stats. * @param bool $time has to be in Y-m-d H:i:s format. * @param bool $force_update Whether to force stats update. * @param bool $is_cron_task Is it a cron task. * * @return array stats data */ function et_pb_ab_get_stats_data( $post_id, $duration = 'week', $time = false, $force_update = false, $is_cron_task = false ) { global $wpdb; $post_id = intval( $post_id ); $goal_slug = et_pb_ab_get_goal_module( $post_id ); $rank_metrics = in_array( $goal_slug, et_pb_ab_get_modules_have_conversions(), true ) ? 'conversions' : 'clicks'; // Get subjects. $subjects = et_pb_ab_get_subjects( $post_id, 'array', 'subject_', $is_cron_task ); $subjects_id = et_pb_ab_get_subjects( $post_id, 'array', false, $is_cron_task ); // Get cached data. $cached_data = get_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration ); // Get rank coloring scheme. $subject_rank_colors = et_pb_ab_get_subject_rank_colors(); // return cached logs if exist and if force_update == false. if ( $cached_data && ! $force_update ) { // Remove inactive subjects. if ( isset( $cached_data['subjects_id'] ) && ! empty( $cached_data['subjects_id'] ) ) { foreach ( $cached_data['subjects_id'] as $subject_id_key => $subject_id_value ) { if ( ! in_array( $subject_id_value, $subjects_id, true ) ) { unset( $cached_data['subjects_id'][ $subject_id_key ] ); } } } if ( isset( $cached_data['subjects_logs'] ) && ! empty( $cached_data['subjects_logs'] ) ) { foreach ( $cached_data['subjects_logs'] as $subject_log_id => $subject_logs ) { if ( ! in_array( $subject_log_id, $subjects, true ) ) { unset( $cached_data['subjects_logs'][ $subject_log_id ] ); } } } if ( isset( $cached_data['subjects_analysis'] ) && ! empty( $cached_data['subjects_analysis'] ) ) { foreach ( $cached_data['subjects_analysis'] as $subject_analysis_id => $subject_analysis ) { if ( ! in_array( $subject_analysis_id, $subjects, true ) ) { unset( $cached_data['subjects_analysis'][ $subject_analysis_id ] ); } } } if ( isset( $cached_data['subjects_totals'] ) && ! empty( $cached_data['subjects_totals'] ) ) { $subject_totals_index = 0; foreach ( $cached_data['subjects_totals'] as $subject_total_id => $subject_totals ) { if ( ! in_array( $subject_total_id, $subjects, true ) ) { unset( $cached_data['subjects_totals'][ $subject_total_id ] ); continue; } } // Rank by engagement. $cached_subjects_ranks = wp_list_pluck( $cached_data['subjects_totals'], $rank_metrics ); $cached_subjects_ranks_index = 0; // Sort from high to low, mantain keys. arsort( $cached_subjects_ranks ); // Push color data. foreach ( $cached_subjects_ranks as $subject_rank_id => $subject_rank_value ) { $is_empty_rank_value = 0 === $subject_rank_value; $has_subject_rank_color = isset( $subject_rank_colors[ $cached_subjects_ranks_index ] ); // If the rank value (derived from engagement) is empty, display default subject color. if ( $is_empty_rank_value ) { $cached_data['subjects_totals'][ $subject_rank_id ]['color'] = '#F3CB57'; } else { $cached_data['subjects_totals'][ $subject_rank_id ]['color'] = $has_subject_rank_color ? $subject_rank_colors[ $cached_subjects_ranks_index ] : '#7E0000'; } $cached_subjects_ranks_index++; } } return $cached_data; } $wpdb->et_divi_ab_testing_stats = $wpdb->prefix . 'et_divi_ab_testing_stats'; // do nothing if no stats table exists in current WP. if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->et_divi_ab_testing_stats'" ) ) { return false; } // Main placeholder. $event_types = et_pb_ab_get_event_types(); $analysis_types = et_pb_ab_get_analysis_types(); $analysis_formulas = et_pb_ab_get_analysis_formulas(); $time = $time ? $time : gmdate( 'Y-m-d H:i:s' ); $stats = array( 'subjects_id' => $subjects_id, 'subjects_logs' => array(), 'subjects_analysis' => array(), 'subjects_totals' => array(), 'events_totals' => array(), 'dates' => array(), ); // Get all logs in test. switch ( $duration ) { case 'all': $date_range_interval = 'week'; $query = $wpdb->prepare( "SELECT subject_id, event, YEARWEEK(record_date) AS 'date', COUNT(id) AS 'count' FROM `{$wpdb->et_divi_ab_testing_stats}` WHERE test_id = %d GROUP BY subject_id, YEARWEEK(record_date), event", $post_id ); break; case 'month': $date_range_interval = 'day'; $query = $wpdb->prepare( "SELECT subject_id, event, DATE(record_date) AS 'date', COUNT(id) AS 'count' FROM `{$wpdb->et_divi_ab_testing_stats}` WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 MONTH ) GROUP BY subject_id, DAYOFMONTH(record_date), event", $post_id, $time, $time ); break; case 'day': $date_range_interval = 'hour'; $query = $wpdb->prepare( "SELECT subject_id, event, DATE_FORMAT(record_date, %s) AS 'date', COUNT(id) AS 'count' FROM `{$wpdb->et_divi_ab_testing_stats}` WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 DAY ) GROUP BY subject_id, HOUR(record_date), event", '%Y-%m-%d %H:00', $post_id, $time, $time ); break; default: $date_range_interval = 'day'; $query = $wpdb->prepare( "SELECT subject_id, event, DATE(record_date) AS 'date', COUNT(id) AS 'count' FROM `{$wpdb->et_divi_ab_testing_stats}` WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 WEEK ) GROUP BY subject_id, DAYOFMONTH(record_date), event", $post_id, $time, $time ); break; } $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- value of $query was prepared in above switch statement. unset( $wpdb->et_divi_ab_testing_stats ); if ( ! empty( $results ) ) { // Get min and max timestamp based on query result. $min_max_date = et_pb_ab_get_min_max_timestamp( $results, $date_range_interval ); // Create default list. $date_list = et_pb_ab_get_date_range( $min_max_date['min'], $min_max_date['max'], $date_range_interval ); // Insert date list to main placeholder. $stats['dates'] = $date_list; // Format YYYYWW format on all-time stats into human-readable format (M jS). foreach ( $stats['dates'] as $date_key => $date_time ) { if ( 'all' === $duration ) { // Format weekly label. $week_in_seconds = 60 * 60 * 24 * 7; $current_time = time(); $week_start_time = strtotime( substr( $date_time, 0, 4 ) . 'W' . substr( $date_time, 4, 2 ) ); $week_end_time = $week_start_time + $week_in_seconds; // Don't let the end time pass current time. if ( $week_end_time > $current_time ) { $week_end_time = $current_time; } // Simplify the label by removing the end month when the start and end month are identical. if ( gmdate( 'M', $week_start_time ) === gmdate( 'M', $week_end_time ) ) { $stats['dates'][ $date_key ] = gmdate( 'M jS', $week_start_time ) . ' - ' . gmdate( 'jS', $week_end_time ); } else { $stats['dates'][ $date_key ] = gmdate( 'M jS', $week_start_time ) . ' - ' . gmdate( 'M jS', $week_end_time ); } } elseif ( 'day' === $duration ) { $stats['dates'][ $date_key ] = gmdate( 'H:i', strtotime( $date_time ) ); } else { $stats['dates'][ $date_key ] = gmdate( 'M jS', strtotime( $date_time ) ); } } // Fill subject logs placeholder with proper default. $stats['subjects_logs'] = array_fill_keys( $subjects, array_fill_keys( $event_types, array_fill_keys( $date_list, 0 ) ) ); // Loop query result and place into placeholder. foreach ( $results as $log ) { if ( ! in_array( $log->subject_id, $subjects_id, true ) ) { continue; } $log_date = $log->date; // Format year-week to ensure the given date is equal to the expected format. MySQl YEARWEEK() seems to output // the date in ISO-8601 format (first week of the year becomes 201753 instead of the expected 201801). if ( 'all' === $duration ) { $log_date = gmdate( 'YW', strtotime( substr( $log_date, 0, 4 ) . 'W' . substr( $log_date, 4, 2 ) ) ); } $stats['subjects_logs'][ "subject_{$log->subject_id}" ][ $log->event ][ $log_date ] = $log->count; } // Determine logs' totals and run analysis. foreach ( $stats['subjects_logs'] as $subject_log_id => $subject_log ) { // Push stats total data. foreach ( $subject_log as $log_type => $logs ) { $stats['subjects_totals'][ $subject_log_id ][ $log_type ] = array_sum( $logs ); } // Run analysis for stats' total data. foreach ( $analysis_types as $analysis_type ) { $numerator_event = $analysis_formulas[ $analysis_type ]['numerator']; $denominator_event = $analysis_formulas[ $analysis_type ]['denominator']; $numerator = isset( $stats['subjects_totals'][ $subject_log_id ][ $numerator_event ] ) ? $stats['subjects_totals'][ $subject_log_id ][ $numerator_event ] : 0; $denominator = isset( $stats['subjects_totals'][ $subject_log_id ][ $denominator_event ] ) ? $stats['subjects_totals'][ $subject_log_id ][ $denominator_event ] : 0; $analysis = 0 === $denominator ? 0 : floatval( number_format( ( $numerator / $denominator ) * 100, 2 ) ); if ( $analysis_formulas[ $analysis_type ]['inverse'] && 0 !== $numerator && 0 !== $denominator_event ) { $analysis = 100 - $analysis; } $stats['subjects_totals'][ $subject_log_id ][ $analysis_type ] = $analysis; } // Run analysis for each log date. foreach ( $date_list as $log_date ) { // Run analysis per analysis type. foreach ( $analysis_types as $analysis_type ) { $numerator_event = $analysis_formulas[ $analysis_type ]['numerator']; $denominator_event = $analysis_formulas[ $analysis_type ]['denominator']; $numerator = isset( $stats['subjects_logs'][ $subject_log_id ][ $numerator_event ][ $log_date ] ) ? intval( $stats['subjects_logs'][ $subject_log_id ][ $numerator_event ][ $log_date ] ) : 0; $denominator = isset( $stats['subjects_logs'][ $subject_log_id ][ $denominator_event ][ $log_date ] ) ? intval( $stats['subjects_logs'][ $subject_log_id ][ $denominator_event ][ $log_date ] ) : 0; $analysis = 0 === $denominator ? 0 : floatval( number_format( ( $numerator / $denominator ) * 100, 2 ) ); if ( $analysis_formulas[ $analysis_type ]['inverse'] ) { $analysis = 100 - $analysis; } $stats['subjects_analysis'][ $subject_log_id ][ $analysis_type ][ $log_date ] = $analysis; } } } // Push total events data. foreach ( $event_types as $event_type ) { $stats['events_totals'][ $event_type ] = array_sum( wp_list_pluck( $stats['subjects_totals'], $event_type ) ); } foreach ( $analysis_types as $analysis_type ) { $analysis_data = wp_list_pluck( $stats['subjects_totals'], $analysis_type ); $analysis_count = count( $analysis_data ); $stats['events_totals'][ $analysis_type ] = floatval( number_format( array_sum( $analysis_data ) / $analysis_count, 2 ) ); } // Rank by engagement. $subjects_ranks = wp_list_pluck( $stats['subjects_totals'], $rank_metrics ); $subjects_ranks_index = 0; // Sort from high to low, mantain keys. arsort( $subjects_ranks ); // Push color data. foreach ( $subjects_ranks as $subject_rank_id => $subject_rank_value ) { $is_empty_rank_value = 0 === $subject_rank_value; $has_subject_rank_color = isset( $subject_rank_colors[ $subjects_ranks_index ] ); // If the rank value (derived from engagement) is empty, display default subject color. if ( $is_empty_rank_value ) { $stats['subjects_totals'][ $subject_rank_id ]['color'] = '#F3CB57'; } else { $stats['subjects_totals'][ $subject_rank_id ]['color'] = $has_subject_rank_color ? $subject_rank_colors[ $subjects_ranks_index ] : '#7E0000'; } $subjects_ranks_index++; } // update cache. set_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration, $stats, DAY_IN_SECONDS ); } else { // remove the cache if no logs found. delete_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration ); return false; } return $stats; } /** * Outputs get data stats duration * * @return array of data */ function et_pb_ab_get_stats_data_duration() { $stats_data_duration = array( 'day', 'week', 'month', 'all', ); return apply_filters( 'et_pb_ab_get_stats_data_duration', $stats_data_duration ); } /** * Get list of AB Testing event type * * @return array of event types */ function et_pb_ab_get_event_types() { $event_types = array( 'view_page', 'read_page', 'view_goal', 'read_goal', 'click_goal', 'con_goal', 'con_short', ); return apply_filters( 'et_pb_ab_get_event_types', $event_types ); } /** * Get min and max timestamp from returned MySQL query * * @param array $query_result MySQL returned value. Expected to be array( array ( 'date' => 'YYYY-MM-DD' ) ) format. * @param string $interval day|week. * @return array using min and max key */ function et_pb_ab_get_min_max_timestamp( $query_result, $interval = 'day' ) { $output = array( 'min' => false, 'max' => false, ); // Get all available dates from logs. $dates = array_unique( wp_list_pluck( $query_result, 'date' ) ); // Sort low-to-high and reset array keys. sort( $dates ); // Get min and max dates from logs. $min_date = $dates[0]; $max_date = $dates[ ( count( $dates ) - 1 ) ]; switch ( $interval ) { case 'week': $output['min'] = strtotime( substr( $min_date, 0, 4 ) . 'W' . substr( $min_date, 4, 2 ) ); $output['max'] = strtotime( substr( $max_date, 0, 4 ) . 'W' . substr( $max_date, 4, 2 ) ); break; default: $output['min'] = strtotime( $min_date ); $output['max'] = strtotime( $max_date ); break; } return $output; } /** * Get all days between min and max dates from logs * * @param int $min_date_timestamp start date timestamp. * @param int $max_date_timestamp end date timestamp. * @param string $interval day|week interval of rage. * @return array of dates */ function et_pb_ab_get_date_range( $min_date_timestamp, $max_date_timestamp, $interval = 'day' ) { $day_timestamp = $min_date_timestamp; $full_dates = array(); switch ( $interval ) { case 'week': $date_format = 'YW'; $time_interval = '+1 week'; break; case 'hour': $date_format = 'Y-m-d H:i'; $time_interval = '+1 hour'; break; default: $date_format = 'Y-m-d'; $time_interval = '+1 day'; break; } while ( $day_timestamp <= $max_date_timestamp ) { $full_dates[] = gmdate( $date_format, $day_timestamp ); $day_timestamp = strtotime( $time_interval, $day_timestamp ); } return $full_dates; } /** * Get list of Split analysis types * * @return array analysis types */ function et_pb_ab_get_analysis_types() { $analysis_types = array( 'clicks', 'reads', 'bounces', 'engagements', 'conversions', 'shortcode_conversions', ); return apply_filters( 'et_pb_ab_get_analysis_types', $analysis_types ); } /** * Get numerator and denominator of various stats types * * @return array stats' data type formula */ function et_pb_ab_get_analysis_formulas() { $analysis_formulas = array( 'clicks' => array( 'numerator' => 'click_goal', 'denominator' => 'view_page', 'inverse' => false, ), 'reads' => array( 'numerator' => 'read_goal', 'denominator' => 'view_page', 'inverse' => false, ), 'bounces' => array( 'numerator' => 'read_page', 'denominator' => 'view_page', 'inverse' => true, ), 'engagements' => array( 'numerator' => 'read_goal', 'denominator' => 'view_goal', 'inverse' => false, ), 'conversions' => array( 'numerator' => 'con_goal', 'denominator' => 'view_page', 'inverse' => false, ), 'shortcode_conversions' => array( 'numerator' => 'con_short', 'denominator' => 'view_page', 'inverse' => false, ), ); return apply_filters( 'et_pb_ab_get_analysis_formulas', $analysis_formulas ); } /** * List modules' slug which has conversions support * * @return array slugs of modules which have conversions support */ function et_pb_ab_get_modules_have_conversions() { $modules_have_conversions = array( 'et_pb_shop', 'et_pb_contact_form', 'et_pb_signup', 'et_pb_comments', ); return apply_filters( 'et_pb_ab_get_modules_have_conversions', $modules_have_conversions ); } /** * Check whether AB Testing active on current page * * @since 4.0 Added the $post_id parameter. * * @param integer $post_id post ID. * * @return bool */ function et_is_ab_testing_active( $post_id = 0 ) { $post_id = $post_id > 0 ? $post_id : get_the_ID(); $post_id = apply_filters( 'et_is_ab_testing_active_post_id', $post_id ); $ab_testing_status = 'on' === get_post_meta( $post_id, '_et_pb_use_ab_testing', true ); $fb_enabled = function_exists( 'et_core_is_fb_enabled' ) ? et_core_is_fb_enabled() : false; if ( ! $ab_testing_status && $fb_enabled && 'publish' !== get_post_status() ) { $ab_testing_status = 'on' === get_post_meta( $post_id, '_et_pb_use_ab_testing_draft', true ); } return $ab_testing_status; } /** * Check whether AB Testing has report * * @param integer $post_id post ID. * * @return bool */ function et_pb_ab_has_report( $post_id ) { global $wpdb; if ( ! et_is_ab_testing_active() ) { return false; } $wpdb->et_divi_ab_testing_stats = $wpdb->prefix . 'et_divi_ab_testing_stats'; $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `{$wpdb->et_divi_ab_testing_stats}` WHERE test_id = %d", $post_id ) ) ? true : false; unset( $wpdb->et_divi_ab_testing_stats ); return apply_filters( 'et_pb_ab_has_report', $result, $post_id ); } /** * Check the status of the ab db version * * @return bool */ function et_pb_db_status_up_to_date() { $ab_db_settings = get_option( 'et_pb_ab_test_settings' ); return ( $ab_db_settings ) && version_compare( $ab_db_settings['db_version'], ET_PB_AB_DB_VERSION, '>=' ); } /** * Create AB Testing table needed for AB Testing feature * * @return void */ function et_pb_create_ab_tables() { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- `wp_verify_nonce()` does not store or display the nonce value, therefor XSS safe. if ( isset( $_POST['et_pb_ab_nonce'] ) && ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) { die( -1 ); } // Verify user permission. if ( ! current_user_can( 'edit_posts' ) || ! et_pb_is_allowed( 'ab_testing' ) ) { die( -1 ); } // Verify update is needed. if ( et_pb_db_status_up_to_date() ) { die( -1 ); } global $wpdb; $stats_table_name = $wpdb->prefix . 'et_divi_ab_testing_stats'; $wpdb->et_divi_ab_testing_stats = $stats_table_name; $client_subject_table_name = $wpdb->prefix . 'et_divi_ab_testing_clients'; $wpdb->et_divi_ab_testing_clients = $client_subject_table_name; /* * We'll set the default character set and collation for this table. * If we don't do this, some characters could end up being converted * to just ?'s when saved in our table. */ $charset_collate = ''; if ( ! empty( $wpdb->charset ) ) { $charset_collate = sprintf( 'DEFAULT CHARACTER SET %1$s', sanitize_text_field( $wpdb->charset ) ); } if ( ! empty( $wpdb->collate ) ) { $charset_collate .= sprintf( ' COLLATE %1$s', sanitize_text_field( $wpdb->collate ) ); } $ab_tables_queries = array(); // Remove client_id column from stats table. if ( 0 < $wpdb->query( "SHOW COLUMNS FROM `$wpdb->et_divi_ab_testing_stats` LIKE 'client_id'" ) ) { $wpdb->query( "ALTER TABLE `$wpdb->et_divi_ab_testing_stats` DROP COLUMN client_id" ); } // Remove client subject table. if ( 0 < $wpdb->query( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->et_divi_ab_testing_clients ) ) ) { $wpdb->query( "DROP TABLE $wpdb->et_divi_ab_testing_clients" ); } $ab_tables_queries[] = "CREATE TABLE $wpdb->et_divi_ab_testing_stats ( id mediumint(9) NOT NULL AUTO_INCREMENT, test_id varchar(20) NOT NULL, subject_id varchar(20) NOT NULL, record_date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, event varchar(10) NOT NULL, UNIQUE KEY id (id) ) $charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $ab_tables_queries ); $db_settings = array( 'db_version' => ET_PB_AB_DB_VERSION, ); update_option( 'et_pb_ab_test_settings', $db_settings ); // Register AB Testing cron. et_pb_create_ab_cron(); unset( $wpdb->et_divi_ab_testing_stats ); unset( $wpdb->et_divi_ab_testing_clients ); die( 'success' ); } add_action( 'wp_ajax_et_pb_create_ab_tables', 'et_pb_create_ab_tables' ); /** * Handle adding the AB testing log record via ajax * * @return void */ function et_pb_update_stats_table() { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- `wp_verify_nonce()` does not store or display the nonce value, therefor XSS safe. if ( ! isset( $_POST['et_ab_log_nonce'] ) || ! wp_verify_nonce( $_POST['et_ab_log_nonce'], 'et_ab_testing_log_nonce' ) ) { die( -1 ); } // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- Stats data will be sanitized in the `et_pb_add_stats_record()`. $stats_data_json = str_replace( '\\', '', $_POST['stats_data_array'] ); $stats_data_array = json_decode( $stats_data_json, true ); et_pb_add_stats_record( $stats_data_array ); die( 1 ); } add_action( 'wp_ajax_et_pb_update_stats_table', 'et_pb_update_stats_table' ); add_action( 'wp_ajax_nopriv_et_pb_update_stats_table', 'et_pb_update_stats_table' ); /** * List of valid AB Testing refresh interval duration * * @return array */ function et_pb_ab_refresh_interval_durations() { $refresh_interval_durations = array( 'hourly' => 'day', 'daily' => 'week', ); return apply_filters( 'et_pb_ab_refresh_interval_durations', $refresh_interval_durations ); } /** * Get refresh interval of particular AB Testing * * @param int $post_id post ID. * @param string $default default interval. * @return string interval used in particular AB Testing */ function et_pb_ab_get_refresh_interval( $post_id, $default = 'hourly' ) { $interval = get_post_meta( $post_id, '_et_pb_ab_stats_refresh_interval', true ); if ( in_array( $interval, array_keys( et_pb_ab_refresh_interval_durations() ), true ) ) { return apply_filters( 'et_pb_ab_get_refresh_interval', $interval, $post_id ); } return apply_filters( 'et_pb_ab_default_refresh_interval', $default, $post_id ); } /** * Get refresh interval duration of particular AB Testing * * @param int $post_id post ID. * @param string $default default interval duration. * @return string test's interval duration */ function et_pb_ab_get_refresh_interval_duration( $post_id, $default = 'day' ) { $durations = et_pb_ab_refresh_interval_durations(); $interval = et_pb_ab_get_refresh_interval( $post_id ); $interval_duration = isset( $durations[ $interval ] ) ? $durations[ $interval ] : $default; return apply_filters( 'et_pb_ab_get_refresh_interval_duration', $interval_duration, $post_id ); } /** * Get goal module slug of particular AB Testing * * @param int $post_id post ID. * @return string test's goal module slug */ function et_pb_ab_get_goal_module( $post_id ) { return get_post_meta( $post_id, '_et_pb_ab_goal_module', true ); } /** * Register Divi's AB Testing cron * There are 2 options - daily and hourly, so schedule 2 events * * @return void */ function et_pb_create_ab_cron() { // schedule daily event. if ( ! wp_next_scheduled( 'et_pb_ab_cron', array( 'daily' ) ) ) { wp_schedule_event( time(), 'daily', 'et_pb_ab_cron', array( 'daily' ) ); } // schedule hourly event. if ( ! wp_next_scheduled( 'et_pb_ab_cron', array( 'hourly' ) ) ) { wp_schedule_event( time(), 'hourly', 'et_pb_ab_cron', array( 'hourly' ) ); } } /** * Perform Divi's AB Testing cron * * @param string $args Interval. * * @return void */ function et_pb_ab_cron( $args ) { $all_tests = et_pb_ab_get_all_tests(); $interval = isset( $args ) ? $args : 'hourly'; if ( empty( $all_tests ) ) { return; } // update cache for each test and for each duration. foreach ( $all_tests as $test ) { $current_test_interval = et_pb_ab_get_refresh_interval( $test['test_id'] ); // determine whether or not we should update the stats for current test depending on interval parameter. if ( $current_test_interval !== $interval ) { continue; } foreach ( et_pb_ab_get_stats_data_duration() as $duration ) { et_pb_ab_get_stats_data( $test['test_id'], $duration, false, true, true ); } } } add_action( 'et_pb_ab_cron', 'et_pb_ab_cron' ); /** * Refresh testings stats. * * @param int $test_id Post id. */ function et_pb_ab_clear_cache_handler( $test_id ) { if ( ! $test_id ) { return; } foreach ( et_pb_ab_get_stats_data_duration() as $duration ) { delete_transient( 'et_pb_ab_' . $test_id . '_stats_' . $duration ); } } /** * Ajax Callback :: Refresh stats */ function et_pb_ab_clear_cache() { // Verify nonce. // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- `wp_verify_nonce()` does not store or display the nonce value, therefor XSS safe. if ( ! isset( $_POST['et_pb_ab_nonce'] ) || ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) { die( -1 ); } $test_id = ! empty( $_POST['et_pb_test_id'] ) ? intval( $_POST['et_pb_test_id'] ) : ''; // Verify user permission. if ( empty( $test_id ) || ! current_user_can( 'edit_post', $test_id ) || ! et_pb_is_allowed( 'ab_testing' ) ) { die( -1 ); } et_pb_ab_clear_cache_handler( $test_id ); // VB ask to load data to save request. if ( isset( $_POST['et_pb_ab_load_data'] ) && isset( $_POST['et_pb_test_id'] ) && isset( $_POST['et_pb_ab_duration'] ) ) { // Allowlist the duration value. $duration = in_array( sanitize_text_field( $_POST['et_pb_ab_duration'] ), et_pb_ab_get_stats_data_duration(), true ) ? sanitize_text_field( $_POST['et_pb_ab_duration'] ) : 'day'; // Get data. $output = et_pb_ab_get_stats_data( intval( $_POST['et_pb_test_id'] ), $duration ); // Print output. die( wp_json_encode( $output ) ); } die( 1 ); } add_action( 'wp_ajax_et_pb_ab_clear_cache', 'et_pb_ab_clear_cache' ); /** * Get all the test ID from db. * * @return array|bool|object|null */ function et_pb_ab_get_all_tests() { global $wpdb; $wpdb->et_divi_ab_testing_stats = $wpdb->prefix . 'et_divi_ab_testing_stats'; // do nothing if no stats table exists in current WP. if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->et_divi_ab_testing_stats'" ) ) { return false; } // construct sql query to get all the test ID from db. $sql = "SELECT DISTINCT test_id FROM `$wpdb->et_divi_ab_testing_stats`"; // cache the data from conversions table. $all_tests = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL -- unprepared SQL okay, value of $sql was prepared above. unset( $wpdb->et_divi_ab_testing_stats ); return $all_tests; } /** * Ajax callback :: Stats removal immediately after test stopped. */ function et_pb_ab_clear_stats() { // Verify nonce. // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- wp_verify_nonce does not store or display the nonce value, therefor XSS safe. if ( ! isset( $_POST['et_pb_ab_nonce'] ) || ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) { die( -1 ); } $test_id = ! empty( $_POST['et_pb_test_id'] ) ? intval( $_POST['et_pb_test_id'] ) : ''; // Verify user permission. if ( empty( $test_id ) || ! current_user_can( 'edit_post', $test_id ) || ! et_pb_is_allowed( 'ab_testing' ) ) { die( -1 ); } et_pb_ab_remove_stats( $test_id ); et_pb_ab_clear_cache_handler( $test_id ); die( 1 ); } add_action( 'wp_ajax_et_pb_ab_clear_stats', 'et_pb_ab_clear_stats' ); /** * Remove AB Testing log and clear stats cache * * @param int $test_id Post ID. * * @return void|bool */ function et_pb_ab_remove_stats( $test_id ) { global $wpdb; $test_id = intval( $test_id ); et_pb_ab_clear_cache_handler( $test_id ); $sql_args = array( $test_id, ); $wpdb->et_divi_ab_testing_stats = $wpdb->prefix . 'et_divi_ab_testing_stats'; // do nothing if no stats table exists in current WP. if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->et_divi_ab_testing_stats'" ) ) { return false; } // construct sql query to remove value from DB table. $sql = "DELETE FROM `$wpdb->et_divi_ab_testing_stats` WHERE test_id = %d"; $wpdb->query( $wpdb->prepare( $sql, $sql_args ) ); // phpcs:ignore WordPress.DB.PreparedSQL -- unprepared SQL okay, value of $sql was prepared above. unset( $wpdb->et_divi_ab_testing_stats ); } /** * Shop trigger DOM * * @return void */ function et_pb_ab_shop_trigger() { echo '<div class="et_pb_ab_shop_conversion"></div>'; } add_action( 'woocommerce_thankyou', 'et_pb_ab_shop_trigger' ); /** * Tracking shortcode * * @param array $atts User defined attributes in shortcode tag. * * @return string */ function et_pb_split_track( $atts ) { $settings = shortcode_atts( array( 'id' => '', ), $atts ); $output = sprintf( '<div class="et_pb_ab_split_track" style="display:none;" data-test_id="%1$s"></div>', esc_attr( $settings['id'] ) ); return $output; } add_shortcode( 'et_pb_split_track', 'et_pb_split_track' ); /** * Get all posts loaded for the current request that have AB testing enabled. * This includes TB layouts and the current post, if any. * * @since 4.0 * * @return integer[] */ function et_builder_ab_get_current_tests() { $layouts = et_theme_builder_get_template_layouts(); $posts = array(); $tests = array(); foreach ( $layouts as $layout ) { if ( is_array( $layout ) && $layout['override'] ) { $posts[] = $layout['id']; } } if ( is_singular() ) { $posts[] = get_the_ID(); } foreach ( $posts as $post_id ) { if ( et_pb_is_pagebuilder_used( $post_id ) && et_is_ab_testing_active( $post_id ) ) { $tests[] = array( 'post_id' => $post_id, 'test_id' => get_post_meta( $post_id, '_et_pb_ab_testing_id', true ), ); } } return $tests; } /** * Initialize AB Testing. Check whether the user has visited the page or not by checking its cookie * * @since * * @return void */ function et_pb_ab_init() { $tests = et_builder_ab_get_current_tests(); foreach ( $tests as $test ) { et_builder_ab_initialize_for_post( $test['post_id'] ); } } add_action( 'wp', 'et_pb_ab_init' ); /** * Initialize AB testing for the specified post. * * @since 4.0 * * @param integer $post_id Post id. * * @return void */ function et_builder_ab_initialize_for_post( $post_id ) { global $et_pb_ab_subject; if ( ! is_array( $et_pb_ab_subject ) ) { $et_pb_ab_subject = array(); } $ab_subjects = et_pb_ab_get_subjects( $post_id ); $ab_hash_key = defined( 'NONCE_SALT' ) ? NONCE_SALT : 'default-divi-hash-key'; $hashed_subject_id = et_pb_ab_get_visitor_cookie( $post_id, 'view_page' ); if ( $hashed_subject_id ) { // Compare subjects against hashed subject id found on cookie to verify whether cookie value is valid or not. foreach ( $ab_subjects as $ab_subject ) { // Valid subject_id is found. if ( hash_hmac( 'md5', $ab_subject, $ab_hash_key ) === $hashed_subject_id ) { $et_pb_ab_subject[ $post_id ] = $ab_subject; // no need to continue. break; } } // If no valid subject found, get the first one. if ( isset( $ab_subjects[0] ) && ! et_()->array_get( $et_pb_ab_subject, $post_id, '' ) ) { $et_pb_ab_subject[ $post_id ] = $ab_subjects[0]; } } else { // First visit. Get next subject on queue. $next_subject_index = get_post_meta( $post_id, '_et_pb_ab_next_subject', true ); // Get current subject index based on `_et_pb_ab_next_subject` post meta value. $subject_index = false !== $next_subject_index && isset( $ab_subjects[ $next_subject_index ] ) ? (int) $next_subject_index : 0; // Get current subject index. $et_pb_ab_subject[ $post_id ] = $ab_subjects[ $subject_index ]; // Hash the subject. $hashed_subject_id = hash_hmac( 'md5', $et_pb_ab_subject[ $post_id ], $ab_hash_key ); // Set cookie for returning visit. et_pb_ab_set_visitor_cookie( $post_id, 'view_page', $hashed_subject_id ); // Bump subject index and save on post meta for next visitor. et_pb_ab_increment_current_ab_module_id( $post_id ); // log the view_page event right away. $is_et_fb_enabled = function_exists( 'et_core_is_fb_enabled' ) && et_core_is_fb_enabled(); if ( ! is_admin() && ! $is_et_fb_enabled ) { et_pb_add_stats_record( array( 'test_id' => $post_id, 'subject_id' => $et_pb_ab_subject[ $post_id ], 'record_type' => 'view_page', ) ); } } } PK X�\�f=� � . builder/class-et-builder-post-feature-base.phpnu �[��� <?php /** * Feature base class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Post Based Feature Base. * * @since 4.10.0 */ class ET_Builder_Post_Feature_Base { // Only save cache if time (milliseconds) to generate it is above this threshold. const CACHE_SAVE_THRESHOLD = 15; const CACHE_META_KEY = '_et_builder_module_feature_cache'; /** * Post ID. * * @access protected * @var int */ protected $_post_id = 0; /** * Primed status. * * @access protected * @var bool */ protected $_primed = false; /** * Cache array. * * @access protected * @var array */ protected $_cache = []; /** * Total time needed to populate the cache. * * @access protected * @var float */ protected $_cache_set_time = 0; /** * Whether the feature manager is enabled. * * @access protected * @var null|bool */ protected static $_enabled = null; /** * Whether this page load is loading the cache or using existing cache. * * @access protected * @var bool */ protected $_cache_loading = false; /** * `ET_Builder_Post_Feature_Base` instance. * * @var ET_Builder_Post_Feature_Base */ private static $_instance; /** * Construct instance. */ public function __construct() { if ( self::enabled() ) { global $shortname; $this->_post_id = ET_Builder_Element::get_current_post_id(); if ( 'extra' === $shortname ) { if ( ( et_is_extra_layout_used_as_home() || et_is_extra_layout_used_as_front() ) && ! is_null( et_get_extra_home_layout_id() ) ) { $this->_post_id = et_get_extra_home_layout_id(); } elseif ( ( is_category() || is_tag() ) && ! is_null( et_get_extra_tax_layout_id() ) ) { $this->_post_id = et_get_extra_tax_layout_id(); } } $this->cache_prime(); if ( ! has_action( 'shutdown', [ $this, 'cache_save' ] ) ) { add_action( 'shutdown', [ $this, 'cache_save' ] ); } } } /** * Initialize ET_Builder_Post_Feature_Base class. */ public static function instance() { if ( ! self::$_instance ) { self::$_instance = new static(); } return self::$_instance; } /** * Purge the features cache for a given post. * * @since 4.10.0 * * @param int $post_id The post ID to purge cache from. */ public static function purge_cache( $post_id = '' ) { if ( $post_id ) { delete_post_meta( $post_id, static::CACHE_META_KEY ); } else { delete_post_meta_by_key( static::CACHE_META_KEY ); } } /** * Get the features cache for a given post. * * @since 4.10.0 * * @param int $post_id The post ID to get cache from. * @return mixed */ public static function load_cache( $post_id ) { return get_post_meta( $post_id, static::CACHE_META_KEY, true ); } /** * Tell whether we should use cache or not. * * @since 4.10.0 * * @return bool */ public static function enabled() { if ( null === self::$_enabled ) { $et_dynamic_module_framework = et_builder_dynamic_module_framework(); $enabled = et_builder_is_frontend() && 'on' === $et_dynamic_module_framework; /** * Whether Post Feature Cache should be enabled or not. * * @since ? * * @param bool $enabled. */ self::$_enabled = apply_filters( 'et_builder_post_feature_cache_enabled', $enabled ); } return self::$_enabled; } /** * Prime the cache. * * @since 4.10.0 */ public function cache_prime() { if ( empty( $this->_primed ) ) { $meta = self::load_cache( $this->_post_id ); $current_index = (string) self::_get_cache_index(); $stored_index = null; if ( isset( $meta[1] ) ) { list( $stored_index, $cache ) = $meta; $this->_cache = ( $current_index === $stored_index ) ? $cache : []; } do_action( 'et_builder_post_feature_cache_primed', $this->_cache, $this->_post_id, $current_index, $stored_index ); // determine if were loading cache, // or using previous cache values. $this->_cache_loading = empty( $this->_cache ); $this->_primed = true; } } /** * Save the cache. * * @since 4.10.0 */ public function cache_save() { if ( ! self::enabled() ) { return; } $cache_index = self::_get_cache_index(); $is_above_threshold = $this->_cache_set_time >= self::CACHE_SAVE_THRESHOLD; do_action( 'et_builder_post_feature_cache_save', $this->_cache, $this->_post_id, $this->_cache_set_time, $is_above_threshold, $cache_index ); // Only save cache if time to generate it is above a certain threshold. if ( $is_above_threshold ) { $cache = array( $cache_index, $this->_cache ); update_post_meta( $this->_post_id, static::CACHE_META_KEY, $cache ); } } /** * Get Cache Version Index Items. * * @since 4.10.0 * @access protected * @return array Cache version items. */ protected static function _get_cache_index_items() { global $wp_version; $dynamic_assets = ET_Dynamic_Assets::init(); $tb_ids = $dynamic_assets->get_theme_builder_template_ids(); $tb_data = []; foreach ( $tb_ids as $tb_id ) { $tb_post = get_post( $tb_id ); $tb_data[ $tb_id ] = $tb_post->post_modified_gmt; } // Ignore WP Editor template if current page use TB. $wpe_data = []; if ( empty( $tb_data ) ) { $wpe_ids = $dynamic_assets->get_wp_editor_template_ids(); foreach ( $wpe_ids as $wpe_id ) { $wpe_post = get_post( $wpe_id ); $wpe_data[ $wpe_id ] = $wpe_post->post_modified_gmt; } } return array( 'gph' => ET_Builder_Global_Presets_History::instance()->get_global_history_index(), 'divi' => et_get_theme_version(), 'wp' => $wp_version, 'tb' => $tb_data, 'wpe' => $wpe_data, ); } /** * Get Cache Version Index. * * @since 4.10.0 * @access protected * @return string .Cache version index. */ protected static function _get_cache_index() { return wp_json_encode( self::_get_cache_index_items() ); } /** * Get cache * * @since 4.10.0 * @param string $key Cache key. * @param string $group Cache group. */ public function cache_get( $key, $group = 'default' ) { $exists = isset( $this->_cache[ $group ] ) && isset( $this->_cache[ $group ][ $key ] ); return $exists ? $this->_cache[ $group ][ $key ] : null; } /** * Set cache * * @since 4.10.0 * @param string $key Cache key. * @param mixed $value To be cached. * @param string $group Cache group. * @param float $elapsed How much time it took to generate the value. */ public function cache_set( $key, $value, $group = 'default', $elapsed = 0 ) { // Only save truthy values into cache. if ( $value ) { $this->_cache[ $group ][ $key ] = $value; } $this->_cache_set_time += $elapsed * 1000; } /** * Check for cached value. * * First check cache if present, if not, determine * from calling the callback. * * @param string $key Name of item. * @param function $cb Callback function to perform logic. * @param string $group Cache group. * * @return bool/mixed Result. */ public function get( $key, $cb, $group = 'default' ) { if ( ! self::enabled() ) { return $cb(); } $result = $this->cache_get( $key, $group ); if ( is_null( $result ) ) { if ( $this->_cache_loading ) { // Set cache for next time. $before = microtime( true ); $result = $cb(); $elapsed = microtime( true ) - $before; $this->cache_set( $key, $result, $group, $elapsed ); } else { // No entry found in a previsouly loaded cache, // means the answer was falsey last time $cb() was checked, // as falsey values arent stored in cache. $result = false; } } return $result; } } PK X�\��X.�4 �4 builder/autoload.phpnu �[��� <?php /** * Register autoloader. * * @package Divi * @subpackage Builder * @since 4.6.2 */ /** * Autoloader for module fields. * * @param string $class The class name. */ function _et_pb_autoload_fields( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "module/field/{$class}.php"; } /** * Autoloader for module helpers. * * @param string $class The class name. */ function _et_pb_autoload_helpers( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "module/helpers/{$class}.php"; } /** * Autoloader for module motion helpers. * * @param string $class The class name. */ function _et_pb_autoload_helpers_motion( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "module/helpers/motion/{$class}.php"; } /** * Autoloader for module mask style. * * @param string $class The class name. */ function _et_pb_autoload_mask_pattern_helpers( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "feature/background-masks/{$class}.php"; } /** * Autoloader for module mask style. * * @param string $class The class name. */ function _et_pb_autoload_mask( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "feature/background-masks/mask/{$class}.php"; } /** * Autoloader for module pattern style. * * @param string $class The class name. */ function _et_pb_autoload_pattern( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "feature/background-masks/pattern/{$class}.php"; } /** * Autoloader for module types. * * @param string $class The class name. */ function _et_pb_autoload_types( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "module/type/{$class}.php"; } /** * Autoloader for woo modules. * * @param string $class The class name. */ function _et_pb_autoload_woo_modules( $class ) { if ( et_is_woocommerce_plugin_active() ) { // For multipart classnames. $class = str_replace( '_', '', $class ); require_once "module/woocommerce/{$class}.php"; } } /** * Autoloader for modules. * * @param string $class The class name. */ function _et_pb_autoload_modules( $class ) { // For multipart classnames. $class = str_replace( '_', '', $class ); if ( file_exists( ET_BUILDER_DIR . "module/{$class}.php" ) ) { require_once "module/{$class}.php"; } elseif ( 'Shop' === $class && et_is_woocommerce_plugin_active() && /* * ET_Builder_Module_Shop class moved to "woocommerce/" directory * as part of WooCommerce Modules v2. */ file_exists( ET_BUILDER_DIR . "module/woocommerce/{$class}.php" ) ) { require_once "module/woocommerce/{$class}.php"; } } /** * Autoloader for module helpers and structure elements. * * @param string $class The class name. */ function _et_pb_autoload( $class ) { if ( in_array( $class, [ 'ET_Builder_Section', 'ET_Builder_Row', 'ET_Builder_Row_Inner', 'ET_Builder_Column' ], true ) ) { require_once 'main-structure-elements.php'; } elseif ( 'ET_Builder_I18n' === $class ) { require_once 'feature/I18n.php'; } elseif ( in_array( $class, [ 'ET_Builder_Element', 'ET_Builder_Module', 'ET_Builder_Structure_Element' ], true ) ) { // This is needed for custom module that extends official module and gets registered in unexpected location. require_once ET_BUILDER_DIR . 'functions.php'; require_once 'class-et-builder-element.php'; } elseif ( 'ET_Builder_Module_Shortcode_Manager' === $class ) { require_once 'class-et-builder-module-shortcode-manager.php'; } elseif ( 'ET_Builder_Post_Feature_Base' === $class ) { require_once 'class-et-builder-post-feature-base.php'; } elseif ( 'ET_Builder_Module_Features' === $class ) { require_once 'class-et-builder-module-features.php'; } elseif ( 'ET_Builder_Module_Fields_Factory' === $class ) { require_once 'module/field/Factory.php'; } elseif ( 'ET_Builder_Module_Use_Detection' === $class ) { require_once 'class-et-builder-module-use-detection.php'; } elseif ( 'ET_Global_Settings' === $class ) { require_once 'class-et-global-settings.php'; } elseif ( 'ET_Builder_Global_Feature_Base' === $class ) { require_once 'class-et-builder-global-feature-base.php'; } elseif ( 'ET_Builder_Google_Fonts_Feature' === $class ) { require_once 'class-et-builder-google-fonts-feature.php'; } elseif ( 'ET_Builder_Dynamic_Assets_Feature' === $class ) { require_once 'class-et-builder-dynamic-assets-feature.php'; } elseif ( 'ET_Builder_Module_Field_DisplayConditions' === $class ) { require_once 'module/field/DisplayConditions.php'; } elseif ( strpos( $class, 'ET_Builder_Background_Mask' ) !== false || strpos( $class, 'ET_Builder_Background_Pattern' ) !== false ) { _et_pb_autoload_mask_pattern_helpers( str_replace( 'ET_Builder_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Mask_' ) !== false ) { _et_pb_autoload_mask( str_replace( 'ET_Builder_Mask_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Pattern_' ) !== false ) { _et_pb_autoload_pattern( str_replace( 'ET_Builder_Pattern_', '', $class ) ); } elseif ( 'ET_Builder_Woocommerce_Product_Simple_Placeholder' === $class ) { require_once 'feature/woocommerce/placeholder/WoocommerceProductSimplePlaceholder.php'; } elseif ( strpos( $class, 'ET_Builder_Module_Helper_Motion_' ) !== false ) { _et_pb_autoload_helpers_motion( str_replace( 'ET_Builder_Module_Helper_Motion_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Module_Helper_' ) !== false ) { _et_pb_autoload_helpers( str_replace( 'ET_Builder_Module_Helper_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Module_Field_' ) !== false ) { _et_pb_autoload_fields( str_replace( 'ET_Builder_Module_Field_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Module_Type_' ) !== false ) { _et_pb_autoload_types( str_replace( 'ET_Builder_Module_Type_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Module_Woocommerce_' ) !== false ) { _et_pb_autoload_woo_modules( str_replace( 'ET_Builder_Module_Woocommerce_', '', $class ) ); } elseif ( strpos( $class, 'ET_Builder_Module_' ) !== false ) { _et_pb_autoload_modules( str_replace( 'ET_Builder_Module_', '', $class ) ); } elseif ( 'ET_Item_Library_Local' === $class ) { require_once ET_CORE_PATH . '/item-library-local/ItemLibraryLocal.php'; } elseif ( 'ET_Theme_Options_Library_Local' === $class ) { require_once ET_EPANEL_DIR . '/theme-options-library/theme-options-library-local/ThemeOptionsLibraryLocal.php'; } elseif ( 'ET_Code_Snippets_Library_Local' === $class ) { require_once ET_CORE_PATH . '/code-snippets/code-snippets-library-local/CodeSnippetsLibraryLocal.php'; } elseif ( 'ET_Theme_Builder_Library_Local' === $class ) { require_once ET_BUILDER_DIR . 'frontend-builder/theme-builder/theme-builder-library-local/ThemeBuilderLibraryLocal.php'; } } spl_autoload_register( '_et_pb_autoload' ); /** * Get an instance of `ET_Builder_Module_Helper_Multi_Value`. * * @return ET_Builder_Module_Helper_Multi_Value */ function et_pb_multi_value() { return ET_Builder_Module_Helper_Multi_Value::instance(); } /** * Get an instance of `ET_Builder_Module_Helper_Overflow`. * * @return ET_Builder_Module_Helper_Overflow */ function et_pb_overflow() { return ET_Builder_Module_Helper_Overflow::get(); } /** * Get an instance of `ET_Builder_Module_Helper_Alignment`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Alignment */ function et_pb_alignment_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Alignment( $prefix ); } /** * Get an instance of `ET_Builder_Module_Helper_Height`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Height */ function et_pb_height_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Height( $prefix ); } /** * Get an instance of `ET_Builder_Module_Hover_Options`. * * @return ET_Builder_Module_Helper_Hover_Options */ function et_pb_hover_options() { return ET_Builder_Module_Helper_Hover_Options::get(); } /** * Get sticky option instance. * * @since 4.6.0 * * @return ET_Builder_Module_Helper_Sticky_Options */ function et_pb_sticky_options() { return ET_Builder_Module_Helper_Sticky_Options::get(); } /** * Get an instance of `ET_Builder_Module_Helper_Max_Height`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Max_Height */ function et_pb_max_height_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Max_Height( $prefix ); } /** * Get an instance of `ET_Builder_Module_Helper_Max_Width`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Max_Width */ function et_pb_max_width_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Max_Width( $prefix ); } /** * Get an instance of `ET_Builder_Module_Helper_Min_Height`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Min_Height */ function et_pb_min_height_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Min_Height( $prefix ); } /** * Get an instance of `ET_Builder_Module_Helper_ResponsiveOptions`. * * @return ET_Builder_Module_Helper_ResponsiveOptions */ function et_pb_responsive_options() { return ET_Builder_Module_Helper_ResponsiveOptions::instance(); } /** * Get an instance of `ET_Builder_Module_Helper_Slider`. * * @return ET_Builder_Module_Helper_Slider */ function et_pb_slider_options() { return new ET_Builder_Module_Helper_Slider(); } /** * Get an instance of `ET_Builder_Module_Transition_Options`. * * @return ET_Builder_Module_Transition_Options */ function et_pb_transition_options() { return ET_Builder_Module_Helper_Transition_Options::get(); } /** * Get an instance of `ET_Builder_Module_Helper_Width`. * * @param string $prefix The prefix string that may be added to field name. * * @return ET_Builder_Module_Helper_Width */ function et_pb_width_options( $prefix = '' ) { return new ET_Builder_Module_Helper_Width( $prefix ); } /** * Get an instance of `ET_Builder_Module_Helper_Font`. * * @return ET_Builder_Module_Helper_Font */ function et_pb_font_options() { return ET_Builder_Module_Helper_Font::instance(); } /** * Get an instance of `ET_Builder_Module_Helper_BackgroundLayout`. * * @return ET_Builder_Module_Helper_BackgroundLayout */ function et_pb_background_layout_options() { return ET_Builder_Module_Helper_BackgroundLayout::instance(); } /** * Get helper instance * * @since 4.6.0 * * @param string $helper_name Helper name. * * @return object */ function et_builder_get_helper( $helper_name ) { switch ( $helper_name ) { case 'sticky': $helper = et_pb_sticky_options(); break; case 'hover': $helper = et_pb_hover_options(); break; case 'responsive': $helper = et_pb_responsive_options(); break; default: $helper = false; break; } return $helper; } /** * Class ET_Builder_Module_Helper_MultiViewOptions wrapper * * @since 3.27.1 * * @param ET_Builder_Element|bool $module Module object. * @param array $custom_props Defined custom props data. * @param array $conditional_values Defined options conditional values. * @param array $default_values Defined options default values. * * @return ET_Builder_Module_Helper_MultiViewOptions */ function et_pb_multi_view_options( $module = false, $custom_props = array(), $conditional_values = array(), $default_values = array() ) { return new ET_Builder_Module_Helper_MultiViewOptions( $module, $custom_props, $conditional_values, $default_values ); } if ( et_is_woocommerce_plugin_active() ) { add_filter( 'et_builder_get_woo_default_columns', array( 'ET_Builder_Module_Helper_Woocommerce_Modules', 'get_columns_posts_default_value', ) ); } /** * Get an instance of `ET_Builder_Module_Helper_OptionTemplate`. * * @return ET_Builder_Module_Helper_OptionTemplate */ function et_pb_option_template() { return ET_Builder_Module_Helper_OptionTemplate::instance(); } /** * Get an instance of `ET_Builder_Module_Helper_Background`. * * @return ET_Builder_Module_Helper_Background * * @since 4.3.3 */ function et_pb_background_options() { return ET_Builder_Module_Helper_Background::instance(); } /** * Class ET_Builder_Module_Helper_Media wrapper * * @since 4.6.4 * * @return ET_Builder_Module_Helper_Media */ function et_pb_media_options() { return ET_Builder_Module_Helper_Media::instance(); } /** * Class ET_Theme_Options_Library_Local wrapper * * @since 4.21.0 * * @return ET_Theme_Options_Library_Local */ function et_pb_theme_options_library_local() { return ET_Theme_Options_Library_Local::instance(); } /** * Class ET_Code_Snippets_Library_Local wrapper * * @since 4.21.0 * * @return ET_Code_Snippets_Library_Local */ function et_pb_code_snippets_library_local() { return ET_Code_Snippets_Library_Local::instance(); } /** * Class ET_Theme_Builder_Library_Local wrapper * * @since 4.21.0 * * @return ET_Theme_Builder_Library_Local */ function et_pb_theme_builder_library_local() { return ET_Theme_Builder_Library_Local::instance(); } PK X�\�� / / 1 builder/class-et-builder-module-use-detection.phpnu �[��� <?php /** * Module Use Detection class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Handles Module Use Detection. * * @since 4.10.0 */ class ET_Builder_Module_Use_Detection { /** * Module Slugs Used. * * @access protected * @var array */ protected $_modules_used = []; /** * Module Attrs Used. * * @access protected * @var array */ protected $_module_attrs_used = []; /** * Module Attr Values Used. * * @access protected * @var array */ protected $_module_attr_values_used = []; /** * Valid Shortcode Slugs. * * @access protected * @var array */ protected $_valid_slugs = []; /** * `ET_Builder_Module_Use_Detection` instance. * * @var ET_Builder_Module_Use_Detection */ private static $_instance; /** * Construct instance. */ public function __construct() { add_filter( 'pre_do_shortcode_tag', [ $this, 'log_slug_used' ], 99, 3 ); add_action( 'wp_footer', [ $this, 'footer' ], 1000 ); add_action( 'et_builder_ready', array( $this, '_setup_valid_slugs' ), 100 ); } /** * Get instance. */ public static function instance() { if ( ! self::$_instance ) { self::$_instance = new static(); } return self::$_instance; } /** * Get valid slugs. */ public function _setup_valid_slugs() { $this->_valid_slugs = ET_Builder_Element::get_all_module_slugs(); } /** * Log the Shortcode Tag/Slug. * * @since 4.10.0 * @access public * @param mixed $override Whether to override do_shortcode return value or not. * @param string $tag Shortcode tag. * @param array $attrs Shortcode attrs. * @return mixed */ public function log_slug_used( $override, $tag, $attrs ) { $interested_attrs_and_values = apply_filters( 'et_builder_module_attrs_values_used', [ 'gutter_width', 'animation_style', 'sticky_position', 'specialty', 'use_custom_gutter', 'font_icon', 'button_icon', 'hover_icon', 'scroll_down_icon', 'social_network', 'show_in_lightbox', 'fullwidth', 'scroll_vertical_motion_enable', 'scroll_horizontal_motion_enable', 'scroll_fade_enable', 'scroll_scaling_enable', 'scroll_rotating_enable', 'scroll_blur_enable', 'show_content', ] ); /** * The "gallery" shortcode is not part of the Divi modules but is used for enqueuing MagnificPopup * when Divi Gallery is enabled under Theme Options > Enable Divi Gallery, so we need to include * it in late detection for edge cases such as shortcodes hardcoded into child themes. */ $additional_valid_slugs = apply_filters( 'et_builder_valid_module_slugs', [ 'gallery', ] ); $valid_slugs = array_unique( array_merge( $this->_valid_slugs, $additional_valid_slugs ) ); // Log the shortcode tags used. if ( in_array( $tag, $valid_slugs, true ) ) { $this->_modules_used[] = $tag; $this->_modules_used = array_unique( $this->_modules_used ); if ( ! is_null( $attrs ) && ! is_array( $attrs ) ) { $attrs = (array) $attrs; } $found_interested_attr_and_values = array_intersect( array_keys( $attrs ), $interested_attrs_and_values ); foreach ( $found_interested_attr_and_values as $key => $attr_name ) { if ( empty( $this->_module_attr_values_used[ $attr_name ] ) ) { $this->_module_attr_values_used[ $attr_name ] = []; } $this->_module_attr_values_used[ $attr_name ][] = $attrs[ $attr_name ]; $this->_module_attr_values_used[ $attr_name ] = array_unique( $this->_module_attr_values_used[ $attr_name ] ); } } return $override; } /** * Add footer actions. */ public function footer() { /** * Fires after wp_footer hook and contains unique array of * slugs of the modules that were used on the page load. * * @param array $_used_modules Module slugs used on the page load. * * @since 4.10.0 */ do_action( 'et_builder_modules_used', $this->_modules_used ); } /** * Get modules used. * * @return array List of module slugs used. * @since 4.10.0 * @access public */ public function get_modules_used() { return $this->_modules_used; } /** * Get module attrs used. * * @return array List of interested module attrs used. * @since 4.10.0 * @access public */ public function get_module_attrs_used() { return $this->_module_attrs_used; } /** * Get module attr values used. * * @return array List of interested module attrs and values used. * @since 4.10.0 * @access public */ public function get_module_attr_values_used() { return $this->_module_attr_values_used; } } ET_Builder_Module_Use_Detection::instance(); PK X�\,.۩ � , builder/class-et-builder-module-features.phpnu �[��� <?php /** * Module Features feature class. * * @package Divi * @subpackage Builder * @since 4.10.0 */ /** * Handles Builder Module Features. * * @since 4.10.0 */ class ET_Builder_Module_Features extends ET_Builder_Post_Feature_Base { const CACHE_META_KEY = '_et_builder_module_features_cache'; /** * Cache group. * * @access protected * @var array */ protected $_cache_group = []; /** * Construct instance. */ public function __construct() { parent::__construct(); // Get shortcode tag / attributes. add_filter( 'pre_do_shortcode_tag', [ $this, 'set_cache_group' ], 99, 3 ); add_filter( 'do_shortcode_tag', [ $this, 'restore_previous_cache_group' ] ); } /** * Creates a key from a shortcode tag and its attributes. * * @since 4.10.0 * @access public * @param string $tag Shortcode tag. * @param string $attrs Shortcode attributes. * * @return string */ public static function get_key( $tag, $attrs ) { $key = $tag . '_' . md5( wp_json_encode( $attrs ) ); return $key; } /** * Set cache group. * * @since 4.10.0 * @access public * @param mixed $override Whether to override do_shortcode return value or not. * @param string $tag Shortcode tag. * @param string $attrs Shortcode attributes. * @return mixed */ public function set_cache_group( $override, $tag, $attrs ) { $this->_cache_group[] = self::get_key( $tag, $attrs ); return $override; } /** * Restore previous cache group when current shortcode execution ends. * * @since 4.10.0 * @access public * @param mixed $output Shortcode content. * @return mixed */ public function restore_previous_cache_group( $output ) { // Get rid of current shortcode cache group. array_pop( $this->_cache_group ); return $output; } /** * Check for cached value. * * First check cache if present, if not, determine * from calling the callback. * * @param string $key Name of item. * @param function $cb Callback function to perform logic. * @param string $group Cache group. * * @return bool/mixed Result. */ public function get( $key, $cb, $group = 'default' ) { return parent::get( $key, $cb, end( $this->_cache_group ) ); } } PK X�\���$D D "