: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
'quick_actions_recent_category' => array(
'modal_preference' => 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(
'modal_fullscreen' => array(
'modal_dimension_width' => array(
'modal_dimension_height' => array(
'modal_position_x' => array(
'modal_position_y' => array(
'toolbar_click' => array(
'toolbar_desktop' => array(
'toolbar_hover' => array(
'toolbar_phone' => array(
'toolbar_tablet' => array(
'toolbar_wireframe' => array(
'lv_modal_dimension_height' => array(
'lv_modal_dimension_width' => array(
'lv_modal_position_x' => array(
'lv_modal_position_y' => array(
return apply_filters( 'et_fb_app_preferences_defaults', $app_preferences );
function et_fb_unsynced_preferences() {
* Filters the preferences list which should not be synced between Visual Builder and Backend Visual Builder.
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' ) );
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_' : '';
$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() ) ) {
$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 );
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
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;
* 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()
function et_fb_woocommerce_tabs() {
if ( ! isset( $product ) && et_is_woocommerce_plugin_active() ) {
$product = ET_Builder_Module_Helper_Woocommerce_Modules::get_product( 'latest' );
$post = get_post( $product->get_id() );
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() );
foreach ( $tabs as $name => $tab ) {
$options[ $name ] = array(
'label' => $tab['title'],
// Reset global $product.
* Get the category taxonomy associated with a given post type.
* @param string $post_type
function et_builder_get_category_taxonomy( $post_type ) {
return 'project_category';
if ( isset( $cache[ $post_type ] ) ) {
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 ) ) {
return $cache[ $post_type ] = reset( $matches );
return $cache[ $post_type ] = false;
* Retrieve a post's category terms as a list with specified format.
* @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 = '' ) {
$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
function et_fb_current_page_params() {
global $post, $authordata, $paged;
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
if ( empty( $authordata ) && isset( $post->post_author ) ) {
$authordata = get_userdata( $post->post_author ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited
$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 ( $comment_count === 0 ) {
$comment_count_text = __( 'No Comments', 'et_builder' );
} elseif ( $comment_count === 1 ) {
$comment_count_text = __( '1 Comment', 'et_builder' );
$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' );
$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' );
$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 );
'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,
'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 ) : '',
'blockId' => ET_GB_Block_Layout::is_layout_block_preview() ? sanitize_title( et_()->array_get( $_GET, 'blockId', '' ) ) : '',
'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(),
return apply_filters( 'et_fb_current_page_params', $current_page );
function et_pb_process_computed_property() {
if ( !isset( $_POST['et_pb_process_computed_property_nonce'] ) || !wp_verify_nonce( $_POST['et_pb_process_computed_property_nonce'], 'et_pb_process_computed_property_nonce' ) ) {
if ( ! current_user_can( 'edit_posts' ) ) {
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( json_encode( null ) );
$utils = ET_Core_Data_Utils::instance();
$depends_on = $_POST['depends_on'];
$conditional_tags = $_POST['conditional_tags'];
$current_page = $_POST['current_page'];
$conditional_tags = array_intersect_key( $conditional_tags, et_fb_conditional_tag_params() );
$current_page = array_intersect_key( $current_page, et_fb_current_page_params() );
$conditional_tags = $utils->sanitize_text_fields( $conditional_tags );
$current_page = $utils->sanitize_text_fields( $current_page );
if ( empty( $current_page['id'] ) || ! current_user_can( 'edit_post', $current_page['id'] ) ) {
// $_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 ) );
$sanitized_value = sanitize_text_field( $value );
$depends_on[ sanitize_text_field( $key ) ] = $sanitized_value;
$module_slug = sanitize_text_field( $_POST['module_type'] );
$post_type = sanitize_text_field( $_POST['post_type'] );
$computed_property = sanitize_text_field( $_POST['computed_property'] );
// get all fields for module
$fields = ET_Builder_Element::get_module_fields( $post_type, $module_slug );
// make sure only valid fields are being passed through
$depends_on = array_intersect_key( $depends_on, $fields );
// computed property field
$field = $fields[ $computed_property ];
$callback = $field['computed_callback'];
if ( is_callable( $callback ) ) {
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
die( json_encode( call_user_func( $callback, $depends_on, $conditional_tags, $current_page ) ) );
add_action( 'wp_ajax_et_pb_process_computed_property', 'et_pb_process_computed_property' );
function et_fb_process_to_shortcode( $object, $options = array(), $library_item_type = '', $escape_content_slashes = true ) {
$options = wp_parse_args( $options, array(
'force_valid_slugs' => false,
'apply_global_presets' => false,
$global_presets_manager = ET_Builder_Global_Presets_Settings::instance();
// do not proceed if $object is empty
if ( empty( $object ) ) {
$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' ) ) ) {
$excluded_elements = array();
switch ( $library_item_type ) {
$excluded_elements = array( 'et_pb_section', 'et_pb_row', 'et_pb_column' );
$excluded_elements = array( 'et_pb_section' );
foreach ( $object as $item ) {
// do not proceed if $item is empty
while ( in_array( $item['type'], $excluded_elements ) ) {
$item = $item['content'][0];
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.