$default_label = esc_html__( 'Give Me A Choice', 'et_builder' );
'default' => $default_label,
foreach ( et_builder_page_creation_options() as $key => $option ) {
if ( ! et_()->array_get( $option, 'setting' ) ) {
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 ) ) {
$value = $value_as_index ? $option['setting']['value_index'] : $key;
$label = et_()->array_get( $option, 'setting.label', $option['titleText'] );
$settings[ $value ] = $label;
* Return an app preferences.
function et_fb_app_preferences_settings() {
$app_preferences = array(
'settings_bar_location' => array(
'builder_animation' => array(
'builder_display_modal_settings' => array(
'builder_enable_dummy_content' => array(
'hover' => esc_html__( 'Hover Mode', 'et_builder' ),
'click' => esc_html__( 'Click Mode', 'et_builder' ),
'grid' => esc_html__( 'Grid Mode', 'et_builder' ),
'default' => et_builder_bfb_enabled() ? 'wireframe' : 'desktop',
'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(
'history_intervals' => 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(
'options' => et_builder_page_creation_settings(),
'quick_actions_always_start_with' => array(
'quick_actions_show_recent_queries' => array(
'quick_actions_recent_queries' => array(
'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(
// Re: "width/height": responsive dimensions presume portrait orientation.
'responsive_tablet_width' => array(
'responsive_tablet_height' => array(
'responsive_phone_width' => array(
'responsive_phone_height' => array(
'responsive_minimum_width' => array(
'responsive_maximum_width' => array(
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.
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' ) );
* Return app preferences.
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(), 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
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() ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Overriding global post is safe as original $post has been restored at the end.
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.
$post = $old_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Restoring original global $post data.
* Get the category taxonomy associated with a given post type.
* @param string $post_type 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 ) ) {
$cache[ $post_type ] = reset( $matches );
return $cache[ $post_type ];
$cache[ $post_type ] = false;
return $cache[ $post_type ];
* 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 = ( isset( $_SERVER['HTTP_HOST'] ) && isset( $_SERVER['REQUEST_URI'] ) ) ? ( is_ssl() ? 'https://' : 'http://' ) . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] ) : '';
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 );
$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' );
// 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' );
$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';