2021-12-07 11:08:05 +00:00
< ? php
/**
* All WooCommerce modules specific functions . php stuff goes here
*
* @ package Divi
* @ subpackage Builder
* @ since 3.29
*/
/**
* Define required constants .
*/
if ( ! defined ( 'ET_BUILDER_WC_PRODUCT_LONG_DESC_META_KEY' ) ) {
// Post meta key to retrieve/save Long description metabox content.
define ( 'ET_BUILDER_WC_PRODUCT_LONG_DESC_META_KEY' , '_et_pb_old_content' );
}
if ( ! defined ( 'ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY' ) ) {
// Post meta key to retrieve/save Long description metabox content.
define ( 'ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY' , '_et_pb_product_page_layout' );
}
if ( ! defined ( 'ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY' ) ) {
// Post meta key to track Product page content status changes.
define ( 'ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY' , '_et_pb_woo_page_content_status' );
}
2021-12-20 18:06:11 +00:00
/**
* Handles Shipping calculator Update button click .
*
* `wc-form-handler` handles shipping calculator update ONLY when WooCommerce shortcode is used .
* Hence , Cart Total ' s shipping calculator update is handled this way .
*
* @ since 4.14 . 3
*/
function et_builder_handle_shipping_calculator_update_btn_click () {
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verification is handled by WooCommerce plugin.
if ( ! isset ( $_POST [ 'woocommerce-shipping-calculator-nonce' ] ) ) {
return ;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verification is handled by WooCommerce plugin.
if ( ! isset ( $_POST [ '_wp_http_referer' ] ) ) {
return ;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verification is handled by WooCommerce plugin.
$referrer = esc_url_raw ( $_POST [ '_wp_http_referer' ] );
$referrer_page_id = url_to_postid ( $referrer );
$cart_page_id = wc_get_page_id ( 'cart' );
if ( $cart_page_id !== $referrer_page_id ) {
return ;
}
$post_content = get_post_field ( 'post_content' , $referrer_page_id );
if ( has_shortcode ( $post_content , 'woocommerce_cart' ) ) {
return ;
}
if ( ( ! class_exists ( 'WC_Shortcodes' ) ) ||
( ! method_exists ( 'WC_Shortcodes' , 'cart' ) ) ) {
return ;
}
WC_Shortcodes :: cart ();
}
/**
* Identify whether Woo v2 should replace content on Cart & Checkout pages .
*
* @ param string $shortcode Post content . Builder converts empty string to shortcode string .
*
* @ since 4.14 . 0
*
* @ return bool
*/
function et_builder_wc_should_replace_content ( $shortcode ) {
$default_shortcodes = array ( 'et_pb_section' , 'et_pb_row' , 'et_pb_column' , 'et_pb_text' , 'woocommerce_cart' , 'woocommerce_checkout' );
$should_replace_content = true ;
// Get all shortcodes on the page.
preg_match_all ( '@\[([^<>&/\[\]\x00-\x20=]++)@' , $shortcode , $matches );
$matched_shortcodes = $matches [ 1 ];
foreach ( $matched_shortcodes as $shortcode ) {
// If a shortcode exists that is not a default shortcode, don't replace content. The user has already built a custom page.
if ( ! in_array ( $shortcode , $default_shortcodes , true ) ) {
$should_replace_content = false ;
break ;
}
}
return $should_replace_content ;
}
/**
* Stop redirecting to Cart page when enabling builder on Checkout page .
*
* @ since 4.14 . 0
*
* @ link https :// github . com / elegantthemes / Divi / issues / 23873
*
* @ param bool $flag Flag .
*
* @ return bool
*/
function et_builder_stop_cart_redirect_while_enabling_builder ( $flag ) {
/*
* Don ' t need to check if the current page is Checkout page since this filter
* `woocommerce_checkout_redirect_empty_cart` only fires if the
* current page is a Checkout page .
*/
$post_id = get_the_ID ();
if ( is_array ( $_GET ) && isset ( $_GET [ 'et_fb' ] ) && '1' === $_GET [ 'et_fb' ] ) {
$is_builder_activation_request = true ;
} else {
// Verify if the request is a valid Builder activation request.
$is_builder_activation_request = et_core_security_check (
'' ,
" et_fb_activation_nonce_ { $post_id } " ,
'et_fb_activation_nonce' ,
'_REQUEST' ,
false
);
}
return $is_builder_activation_request ? false : $flag ;
}
/**
* Message to be displayed in Checkout Payment Info module in VB mode .
*
* So styling the Notice becomes easier .
*
* @ since 4.14 . 0
*
* @ return string
*/
function et_builder_wc_no_available_payment_methods_message () {
// Fallback.
$message = esc_html__ ( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.' );
if ( ! function_exists ( 'WC' ) ) {
return $message ;
}
if ( ! isset ( WC () -> customer ) && ! method_exists ( WC () -> customer , 'get_billing_country' ) ) {
return $message ;
}
$message = WC () -> customer -> get_billing_country ()
? esc_html__ ( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.' , 'et_builder' )
: esc_html__ ( 'Please fill in your details above to see available payment methods.' , 'et_builder' );
return apply_filters (
'woocommerce_no_available_payment_methods_message' ,
$message
);
}
/**
* Output the cart shipping calculator .
*
* @ param string $button_text Text for the shipping calculation toggle .
*/
function et_builder_woocommerce_shipping_calculator ( $button_text = '' ) {
wp_enqueue_script ( 'wc-country-select' );
wc_get_template (
'cart/shipping-calculator.php' ,
array (
'button_text' => $button_text ,
)
);
}
/**
* Gets the Checkout modules notice to be displayed on non - checkout pages .
*
* @ since 4.14 . 0
*
* @ used - by et_fb_get_static_backend_helpers ()
*
* @ return string
*/
function et_builder_wc_get_non_checkout_page_notice () {
return esc_html__ ( 'This module will not function properly on the front end of your website because this is not the assigned Checkout page.' , 'et_builder' );
}
/**
* Gets the Checkout notice to be displayed on Checkout Payment Info module .
*
* @ since 4.14 . 0
*
* @ param string $woocommerce_ship_to_destination Default `shipping` .
*
* @ used - by et_fb_get_static_backend_helpers ()
*
* @ return string
*/
function et_builder_wc_get_checkout_notice ( $woocommerce_ship_to_destination = 'shipping' ) {
$settings_modal_notice = '' ;
if ( 'billing_only' === $woocommerce_ship_to_destination ) {
$settings_modal_notice = wp_kses (
__ ( '<strong>Woo Billing Address Module</strong> must be added to this page to allow users to submit orders.' , 'et_builder' ),
array ( 'strong' => array () )
);
} else {
$settings_modal_notice = wp_kses (
__ ( '<strong>Woo Billing Address Module</strong> and <strong>Woo Shipping Address Module</strong> must be added to this page to allow users to submit orders.' , 'et_builder' ),
array ( 'strong' => array () )
);
}
return $settings_modal_notice ;
}
/**
* Stop WooCommerce from redirecting Checkout page to Cart when the cart is empty .
*
* Divi Builder stops redirection only for logged - in admins .
*
* @ since 4.14 . 0
*/
function et_builder_wc_template_redirect () {
$checkout_page_id = wc_get_page_id ( 'checkout' );
$post = get_post ( $checkout_page_id );
if ( ! ( $post instanceof WP_Post ) ) {
return ;
}
$is_checkout_page = $checkout_page_id === $post -> ID ;
if ( ! $is_checkout_page ) {
return ;
}
if ( ! et_core_is_fb_enabled () ) {
return ;
}
if ( ! is_user_logged_in () || ! current_user_can ( 'manage_options' ) ) {
return ;
}
$has_wc_shortcode = has_shortcode ( $post -> post_content , 'et_pb_section' );
if ( ! $has_wc_shortcode ) {
return ;
}
add_filter ( 'woocommerce_checkout_redirect_empty_cart' , '__return_false' );
}
/**
* Sets the meta to indicate that the Divi content has been modified .
*
* This avoids setting the default WooCommerce Modules layout more than once .
*
* @ link https :// github . com / elegantthemes / Divi / issues / 16420
*
* @ since 4.14 . 0
*
* @ param int $post_id Post ID .
*/
function et_builder_wc_set_page_content_status ( $post_id ) {
if ( 0 === absint ( $post_id ) ) {
return ;
}
/**
* The ID page of the Checkout page set in WooCommerce Settings page .
*
* WooCommerce — Settings — Advanced — Checkout page
*/
$checkout_page_id = wc_get_page_id ( 'checkout' );
/**
* The ID page of the Cart page set in WooCommerce Settings page .
*
* WooCommerce — Settings — Advanced — Cart page
*/
$cart_page_id = wc_get_page_id ( 'cart' );
$is_cart = $post_id === $cart_page_id ;
$is_checkout = $post_id === $checkout_page_id ;
$is_product = 'product' === get_post_type ( $post_id );
// Take action only on Product, Cart and Checkout pages. Bail early otherwise.
if ( ! ( $is_product || $is_cart || $is_checkout ) ) {
return ;
}
$modified_status = 'modified' ;
$is_content_status_modified = get_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY , true ) === $modified_status ;
if ( $is_content_status_modified ) {
return ;
}
update_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY , $modified_status );
}
/**
* Gets the prefilled Cart Page content built using Divi Woo Modules .
*
* @ since 4.14 . 0
*
* @ return string
*/
function et_builder_wc_get_prefilled_cart_page_content () {
$page_title = '[et_pb_post_title meta="off" featured_image="off"][/et_pb_post_title]' ;
// Gets Parent theme's info in case child theme is used.
if ( 'Extra' === et_core_get_theme_info ( 'Name' ) ) {
$page_title = '' ;
}
return '
[ et_pb_section ]
[ et_pb_row ]
[ et_pb_column type = " 4_4 " ]
' . $page_title . '
[ et_pb_wc_cart_notice page_type = " cart " ][ / et_pb_wc_cart_notice ]
[ et_pb_wc_cart_products ][ / et_pb_wc_cart_products ]
[ / et_pb_column ]
[ / et_pb_row ]
[ et_pb_row column_structure = " 1_2,1_2 " ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_cross_sells ][ / et_pb_wc_cross_sells ]
[ / et_pb_column ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_cart_totals ][ / et_pb_wc_cart_totals ]
[ / et_pb_column ]
[ / et_pb_row ]
[ / et_pb_section ]
' ;
}
/**
* Gets the prefilled Checkout Page content built using Divi Woo Modules .
*
* @ since 4.14 . 0
* @ return string
*/
function et_builder_wc_get_prefilled_checkout_page_content () {
$page_title = '[et_pb_post_title meta="off" featured_image="off"][/et_pb_post_title]' ;
// Use `et_core_get_theme_info` to get Parent theme's info even when a child theme is used.
if ( 'Extra' === et_core_get_theme_info ( 'Name' ) ) {
$page_title = '' ;
}
return '
[ et_pb_section ]
[ et_pb_row custom_padding = " ||0%||false|false " ]
[ et_pb_column type = " 4_4 " ]
' . $page_title . '
[ et_pb_wc_cart_notice page_type = " checkout " ][ / et_pb_wc_cart_notice ]
[ / et_pb_column ]
[ / et_pb_row ]
[ et_pb_row column_structure = " 1_2,1_2 " ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_checkout_billing ][ / et_pb_wc_checkout_billing ]
[ / et_pb_column ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_checkout_shipping ][ / et_pb_wc_checkout_shipping ]
[ et_pb_wc_checkout_additional_info ][ / et_pb_wc_checkout_additional_info ]
[ / et_pb_column ]
[ / et_pb_row ]
[ et_pb_row ]
[ et_pb_column type = " 4_4 " ]
[ et_pb_wc_checkout_order_details ][ / et_pb_wc_checkout_order_details ]
[ et_pb_wc_checkout_payment_info ][ / et_pb_wc_checkout_payment_info ]
[ / et_pb_column ]
[ / et_pb_row ]
[ / et_pb_section ]
' ;
}
/**
* Sets the pre - filled Divi Woo Pages layout content .
*
* The following are the three types of WooCommerce pages that have pre - filled content .
*
* 1. WooCommerce Product page
* 2. WooCommerce Cart page
* 3. WooCommerce Checkout page
*
* @ param string $maybe_shortcode_content May be shortcode content .
* @ param int $post_id Post ID .
*
* @ return string
*/
function et_builder_wc_set_prefilled_page_content ( $maybe_shortcode_content , $post_id ) {
$post = get_post ( absint ( $post_id ) );
if ( ! $post ) {
return $maybe_shortcode_content ;
}
/**
* The ID page of the Checkout page set in WooCommerce Settings page .
*
* WooCommerce — Settings — Advanced — Checkout page
*/
$checkout_page_id = wc_get_page_id ( 'checkout' );
/**
* The ID page of the Cart page set in WooCommerce Settings page .
*
* WooCommerce — Settings — Advanced — Cart page
*/
$cart_page_id = wc_get_page_id ( 'cart' );
$is_cart = $post_id === $cart_page_id ;
$is_checkout = $post_id === $checkout_page_id ;
$is_product = ( $post instanceof WP_Post ) && 'product' === $post -> post_type ;
// Bail early when none of the conditions are met.
if ( ! ( $is_product || $is_checkout || $is_cart ) ) {
return $maybe_shortcode_content ;
}
// Bail early if the Page already has initial content set.
$is_content_status_modified = 'modified' === get_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY , true );
if ( $is_content_status_modified ) {
return $maybe_shortcode_content ;
}
$should_replace_content = true ;
if ( $is_cart || $is_checkout ) {
$should_replace_content = et_builder_wc_should_replace_content ( $maybe_shortcode_content );
}
if ( $is_cart && $should_replace_content ) {
return et_builder_wc_get_prefilled_cart_page_content ();
} elseif ( $is_checkout && $should_replace_content ) {
return et_builder_wc_get_prefilled_checkout_page_content ();
} elseif ( $is_product ) {
$args = array ();
$product_page_layout = et_builder_wc_get_product_layout ( $post_id );
/*
* When FALSE , this means the Product doesn ' t use Builder at all ;
* Or the Product has been using the Builder before WooCommerce Modules QF launched .
*/
if ( ! $product_page_layout ) {
$product_page_layout = et_get_option (
'et_pb_woocommerce_page_layout' ,
'et_build_from_scratch'
);
}
// Load default content.
if ( 'et_default_layout' === $product_page_layout ) {
return $maybe_shortcode_content ;
}
$has_et_builder_shortcode = has_shortcode ( $maybe_shortcode_content , 'et_pb_section' );
$is_layout_type_build_from_scratch = 'et_build_from_scratch' === $product_page_layout ;
if ( $has_et_builder_shortcode && $is_layout_type_build_from_scratch ) {
$args [ 'existing_shortcode' ] = $maybe_shortcode_content ;
}
return et_builder_wc_get_prefilled_product_page_content ( $args );
}
return $maybe_shortcode_content ;
}
2021-12-07 11:08:05 +00:00
/**
* Returning < img > string for default image placeholder
*
2021-12-20 18:06:11 +00:00
* @ since 4.14 . 0 Added $mode param .
2021-12-07 11:08:05 +00:00
* @ since 4.0 . 10
*
2021-12-20 18:06:11 +00:00
* @ param string $mode Default ET_BUILDER_PLACEHOLDER_LANDSCAPE_IMAGE_DATA . Either Landscape or
* Portrait image mode .
*
2021-12-07 11:08:05 +00:00
* @ return string
*/
2021-12-20 18:06:11 +00:00
function et_builder_wc_placeholder_img ( $mode = 'portrait' ) {
$allowed_list = array (
'portrait' => ET_BUILDER_PLACEHOLDER_PORTRAIT_VARIATION_IMAGE_DATA ,
'landscape' => ET_BUILDER_PLACEHOLDER_LANDSCAPE_IMAGE_DATA ,
);
if ( ! in_array ( $mode , array_keys ( $allowed_list ), true ) ) {
$mode = 'portrait' ;
}
2021-12-07 11:08:05 +00:00
return sprintf (
'<img src="%1$s" alt="2$s" />' ,
2021-12-20 18:06:11 +00:00
et_core_esc_attr ( 'placeholder' , $allowed_list [ $mode ] ),
2021-12-07 11:08:05 +00:00
esc_attr__ ( 'Product image' , 'et_builder' )
);
}
/**
* Gets the Product Content options .
*
* This array is used in Divi Page Settings metabox and in Divi Theme Options ⟶ Builder ⟶ Post Type integration .
*
* @ since 3.29
*
* @ param string $translation_context Translation Context to indicate if translation origins from Divi Theme or
* from the Builder . Optional . Default 'et_builder' .
*
* @ return array
*/
function et_builder_wc_get_page_layouts ( $translation_context = 'et_builder' ) {
switch ( $translation_context ) {
case 'Divi' :
$product_page_layouts = array (
'et_build_from_scratch' => esc_html__ ( 'Build From Scratch' , 'Divi' ),
'et_default_layout' => esc_html__ ( 'Default' , 'Divi' ),
);
break ;
default :
$product_page_layouts = array (
'et_build_from_scratch' => esc_html__ ( 'Build From Scratch' , 'et_builder' ),
'et_default_layout' => et_builder_i18n ( 'Default' ),
);
break ;
}
return $product_page_layouts ;
}
/**
* Adds WooCommerce Module settings to the Builder settings .
*
* Adding in the Builder Settings tab will ensure that the field is available in Extra Theme and
* Divi Builder Plugin .
*
* @ since 4.0 . 3 Hide Product Content layout settings Divi Builder Plugin options .
* @ since 3.29
*
* @ param array $builder_settings_fields Builder settings fields .
*
* @ return array
*/
function et_builder_wc_add_settings ( $builder_settings_fields ) {
// Bail early to hide WooCommerce Settings tab under the Builder tab.
// If $fields['tab_slug'] is not equal to the tab slug (i.e. woocommerce_page_layout) then WooCommerce settings tab won't be displayed.
// {@see ET_Builder_Settings::_get_builder_settings_in_epanel_format}.
if ( ! et_is_woocommerce_plugin_active () ) {
return $builder_settings_fields ;
}
$fields = array (
'et_pb_woocommerce_product_layout' => array (
'type' => 'select' ,
'id' => 'et_pb_woocommerce_product_layout' ,
'index' => - 1 ,
'label' => esc_html__ ( 'Product Layout' , 'et_builder' ),
'description' => esc_html__ ( 'Here you can choose Product Page Layout for WooCommerce.' , 'et_builder' ),
'options' => array (
'et_right_sidebar' => esc_html__ ( 'Right Sidebar' , 'et_builder' ),
'et_left_sidebar' => esc_html__ ( 'Left Sidebar' , 'et_builder' ),
'et_no_sidebar' => esc_html__ ( 'No Sidebar' , 'et_builder' ),
'et_full_width_page' => esc_html__ ( 'Fullwidth' , 'et_builder' ),
),
'default' => 'et_right_sidebar' ,
'validation_type' => 'simple_text' ,
'et_save_values' => true ,
'tab_slug' => 'post_type_integration' ,
'toggle_slug' => 'performance' ,
),
'et_pb_woocommerce_page_layout' => array (
'type' => 'select' ,
'id' => 'et_pb_woocommerce_product_page_layout' ,
'index' => - 1 ,
'label' => esc_html__ ( 'Product Content' , 'et_builder' ),
'description' => esc_html__ ( '"Build From Scratch" loads a pre-built WooCommerce page layout, with which you build on when the Divi Builder is enabled. "Default" option lets you use default WooCommerce page layout.' , 'et_builder' ),
'options' => et_builder_wc_get_page_layouts (),
'default' => 'et_build_from_scratch' ,
'validation_type' => 'simple_text' ,
'et_save_values' => true ,
'tab_slug' => 'post_type_integration' ,
'toggle_slug' => 'performance' ,
),
);
// Hide setting in DBP : https://github.com/elegantthemes/Divi/issues/17378.
if ( et_is_builder_plugin_active () ) {
unset ( $fields [ 'et_pb_woocommerce_product_layout' ] );
}
return array_merge ( $builder_settings_fields , $fields );
}
/**
* Gets the pre - built layout for WooCommerce product pages .
*
* @ since 3.29
*
* @ param array $args {
* Additional args .
*
* @ type string $existing_shortcode Existing builder shortcode .
* }
*
* @ return string
*/
2021-12-20 18:06:11 +00:00
function et_builder_wc_get_prefilled_product_page_content ( $args = array () ) {
2021-12-07 11:08:05 +00:00
/**
* Filters the Top section Background in the default WooCommerce Modules layout .
*
* @ param string $color Default empty .
*/
$et_builder_wc_initial_top_section_bg = apply_filters ( 'et_builder_wc_initial_top_section_bg' , '' );
$content = '
[ et_pb_section custom_padding = " 0px||||false|false " background_color = " ' . esc_attr( $et_builder_wc_initial_top_section_bg ) . ' " ]
[ et_pb_row width = " 100% " custom_padding = " 0px||0px||false|false " ]
[ et_pb_column type = " 4_4 " ]
[ et_pb_wc_breadcrumb ][ / et_pb_wc_breadcrumb ]
[ et_pb_wc_cart_notice ][ / et_pb_wc_cart_notice ]
[ / et_pb_column ]
[ / et_pb_row ]
[ et_pb_row custom_padding = " 0px||||false|false " width = " 100% " ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_images ][ / et_pb_wc_images ]
[ / et_pb_column ]
[ et_pb_column type = " 1_2 " ]
[ et_pb_wc_title ][ / et_pb_wc_title ]
[ et_pb_wc_rating ][ / et_pb_wc_rating ]
[ et_pb_wc_price ][ / et_pb_wc_price ]
[ et_pb_wc_description ][ / et_pb_wc_description ]
[ et_pb_wc_add_to_cart form_field_text_align = " center " ][ / et_pb_wc_add_to_cart ]
[ et_pb_wc_meta ][ / et_pb_wc_meta ]
[ / et_pb_column ]
[ / et_pb_row ]
[ et_pb_row width = " 100% " ]
[ et_pb_column type = " 4_4 " ]
[ et_pb_wc_tabs ]
[ / et_pb_wc_tabs ]
[ et_pb_wc_upsells columns_number = " 3 " ][ / et_pb_wc_upsells ]
[ et_pb_wc_related_products columns_number = " 3 " ][ / et_pb_wc_related_products ]
[ / et_pb_column ]
[ / et_pb_row ]
[ / et_pb_section ] ' ;
if ( ! empty ( $args [ 'existing_shortcode' ] ) ) {
return $content . $args [ 'existing_shortcode' ];
}
return $content ;
}
/**
* Gets the Product layout for a given Post ID .
*
* @ since 3.29
*
* @ param int $post_id Post Id .
*
* @ return string The return value will be one of the values from
* { @ see et_builder_wc_get_page_layouts ()} when the Post ID is valid .
* Empty string otherwise .
*/
function et_builder_wc_get_product_layout ( $post_id ) {
$post = get_post ( $post_id );
if ( ! $post ) {
return false ;
}
return get_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY , true );
}
/**
* Sets the pre - built layout for WooCommerce product pages .
*
* @ param string $maybe_shortcode_content Post content .
* @ param int $post_id Post id .
*
* @ return string
*/
function et_builder_wc_set_initial_content ( $maybe_shortcode_content , $post_id ) {
$post = get_post ( absint ( $post_id ) );
$args = array ();
if ( ! ( $post instanceof WP_Post ) || 'product' !== $post -> post_type ) {
return $maybe_shortcode_content ;
}
// $post_id is a valid Product ID by now.
$product_page_layout = et_builder_wc_get_product_layout ( $post_id );
/*
* When FALSE , this means the Product doesn ' t use Builder at all ;
* Or the Product has been using the Builder before WooCommerce Modules QF launched .
*/
if ( ! $product_page_layout ) {
$product_page_layout = et_get_option (
'et_pb_woocommerce_page_layout' ,
'et_build_from_scratch'
);
}
$is_product_content_modified = 'modified' === get_post_meta (
$post_id ,
ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY ,
true
);
// Content was already saved or default content should be loaded.
if ( $is_product_content_modified || 'et_default_layout' === $product_page_layout ) {
return $maybe_shortcode_content ;
}
if ( has_shortcode ( $maybe_shortcode_content , 'et_pb_section' ) && 'et_build_from_scratch' === $product_page_layout && ! empty ( $maybe_shortcode_content ) ) {
$args [ 'existing_shortcode' ] = $maybe_shortcode_content ;
}
2021-12-20 18:06:11 +00:00
return et_builder_wc_get_prefilled_product_page_content ( $args );
2021-12-07 11:08:05 +00:00
}
/**
* Saves the WooCommerce long description metabox content .
*
* The content is stored as post meta w / the key `_et_pb_old_content` .
*
* @ param int $post_id Post id .
* @ param WP_Post $post Post Object .
* @ param array $request The $_POST Request variables .
*
* @ since 3.29
*/
function et_builder_wc_long_description_metabox_save ( $post_id , $post , $request ) {
if ( ! isset ( $request [ 'et_bfb_long_description_nonce' ] ) ) {
return ;
}
if ( current_user_can ( 'edit_posts' , $post_id ) && et_core_security_check ( 'edit_posts' , 'et_bfb_long_description_nonce' , '_et_bfb_long_description_nonce' , '_POST' , false )
) {
return ;
}
if ( 'product' !== $post -> post_type ) {
return ;
}
if ( ! isset ( $request [ 'et_builder_wc_product_long_description' ] ) ) {
return ;
}
$long_desc_content = $request [ 'et_builder_wc_product_long_description' ];
$is_updated = update_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_LONG_DESC_META_KEY , wp_kses_post ( $long_desc_content ) );
}
/**
* Output Callback for Product long description metabox .
*
* @ since 3.29
*
* @ param WP_Post $post Post .
*/
function et_builder_wc_long_description_metabox_render ( $post ) {
$settings = array (
'textarea_name' => 'et_builder_wc_product_long_description' ,
'quicktags' => array ( 'buttons' => 'em,strong,link' ),
'tinymce' => array (
'theme_advanced_buttons1' => 'bold,italic,strikethrough,separator,bullist,numlist,separator,blockquote,separator,justifyleft,justifycenter,justifyright,separator,link,unlink,separator,undo,redo,separator' ,
'theme_advanced_buttons2' => '' ,
),
'editor_css' => '<style>#wp-et_builder_wc_product_long_description-editor-container .wp-editor-area{height:175px; width:100%;}</style>' ,
);
// Since we use $post_id in more than one place, use a variable.
$post_id = $post -> ID ;
// Long description metabox content. Default Empty.
$long_desc_content = get_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_LONG_DESC_META_KEY , true );
$long_desc_content = ! empty ( $long_desc_content ) ? $long_desc_content : '' ;
/**
* Filters the wp_editor settings used in the Long description metabox .
*
* @ param array $settings WP Editor settings .
*
* @ since 3.29
*/
$settings = apply_filters ( 'et_builder_wc_product_long_description_editor_settings' , $settings );
wp_nonce_field ( '_et_bfb_long_description_nonce' , 'et_bfb_long_description_nonce' );
wp_editor (
$long_desc_content ,
'et_builder_wc_product_long_description' ,
$settings
);
}
/**
* Adds the Long description metabox to Product post type .
*
* @ since 3.29
*
* @ param WP_Post $post WP Post .
*/
function et_builder_wc_long_description_metabox_register ( $post ) {
if ( 'on' !== get_post_meta ( $post -> ID , '_et_pb_use_builder' , true ) ) {
return ;
}
add_meta_box (
'et_builder_wc_product_long_description_metabox' ,
__ ( 'Product long description' , 'et_builder' ),
'et_builder_wc_long_description_metabox_render' ,
'product' ,
'normal'
);
}
/**
* Determine if WooCommerce ' s $product global need to be overwritten or not .
* IMPORTANT : make sure to reset it later
*
* @ since 3.29
*
* @ param string $product_id Post id .
*
* @ return bool
*/
function et_builder_wc_need_overwrite_global ( $product_id = 'current' ) {
$is_current_product_page = 'current' === $product_id ;
// There are three situation which requires global value overwrite: initial builder
// ajax request, computed callback jax request (all ajax request has faulty global variable),
// and if `product` attribute is not current page's product id (ie Woo Tabs being used
// on non `product` CPT).
$need_overwrite_global = ! $is_current_product_page
|| et_fb_is_builder_ajax ()
|| et_fb_is_computed_callback_ajax ();
return $need_overwrite_global ;
}
2021-12-20 18:06:11 +00:00
/**
* Gets the Product ID .
*
* @ since 4.14 . 0
*
* @ param array $args Module props .
*
* @ return int $product_id
*/
function et_builder_wc_get_product_id ( $args ) {
$maybe_product_id = et_ () -> array_get ( $args , 'product' , 'latest' );
$is_latest_product = 'latest' === $maybe_product_id ;
$is_current_product_page = 'current' === $maybe_product_id ;
if ( $is_latest_product ) {
// Dynamic filter's product_id need to be translated into correct id.
$product_id = ET_Builder_Module_Helper_Woocommerce_Modules :: get_product_id ( $maybe_product_id );
} elseif ( $is_current_product_page && wp_doing_ajax () && class_exists ( 'ET_Builder_Element' ) ) {
/*
* $product global doesn ' t exist in ajax request ; thus get the fallback post id
* this is likely happen in computed callback ajax request .
*/
$product_id = ET_Builder_Element :: get_current_post_id ();
} else {
// Besides two situation above, $product_id is current $args['product'].
if ( false !== get_post_status ( $maybe_product_id ) ) {
$product_id = $maybe_product_id ;
} else {
// Fallback to Latest product if saved product ID doesn't exist.
$product_id = ET_Builder_Module_Helper_Woocommerce_Modules :: get_product_id ( 'latest' );
}
}
return $product_id ;
}
2021-12-07 11:08:05 +00:00
/**
* Helper to render module template for module ' s front end and computed callback output
*
* @ since 3.29
*
* @ param string $function_name Rendering method name .
* @ param array $args Method arguments .
* @ param array $overwrite List of global variables to overwrites e . g $product , $post and $wp_query .
*
* @ return string
*/
function et_builder_wc_render_module_template ( $function_name , $args = array (), $overwrite = array ( 'product' ) ) {
// Shouldn't be fired in Backend to not break the BB loading.
if ( is_admin () && ! wp_doing_ajax () ) {
return ;
}
// Check if passed function name is allowlisted or not.
$allowlisted_functions = array (
'the_title' ,
'woocommerce_breadcrumb' ,
'woocommerce_template_single_price' ,
'woocommerce_template_single_add_to_cart' ,
'woocommerce_product_additional_information_tab' ,
'woocommerce_template_single_meta' ,
'woocommerce_template_single_rating' ,
'woocommerce_show_product_images' ,
'wc_get_stock_html' ,
'wc_print_notices' ,
'wc_print_notice' ,
'woocommerce_output_related_products' ,
'woocommerce_upsell_display' ,
2021-12-20 18:06:11 +00:00
'woocommerce_checkout_login_form' ,
'wc_cart_empty_template' ,
'woocommerce_output_all_notices' ,
2021-12-07 11:08:05 +00:00
);
if ( ! in_array ( $function_name , $allowlisted_functions , true ) ) {
return '' ;
}
// phpcs:disable WordPress.WP.GlobalVariablesOverride -- Overwrite global variables when rendering templates which are restored before this function exist.
global $product , $post , $wp_query ;
$defaults = array (
'product' => 'current' ,
);
$args = wp_parse_args ( $args , $defaults );
$overwrite_global = et_builder_wc_need_overwrite_global ( $args [ 'product' ] );
$overwrite_product = in_array ( 'product' , $overwrite , true );
$overwrite_post = in_array ( 'post' , $overwrite , true );
$overwrite_wp_query = in_array ( 'wp_query' , $overwrite , true );
$is_tb = et_builder_tb_enabled ();
if ( $is_tb ) {
// global object needs to be set before output rendering. This needs to be performed on each
// module template rendering instead of once for all module template rendering because some
// module's template rendering uses `wp_reset_postdata()` which resets global query.
et_theme_builder_wc_set_global_objects ();
} elseif ( $overwrite_global ) {
2021-12-20 18:06:11 +00:00
$product_id = et_builder_wc_get_product_id ( $args );
2021-12-07 11:08:05 +00:00
if ( 'product' !== get_post_type ( $product_id ) ) {
// We are in a Theme Builder layout and the current post is not a product - use the latest one instead.
$products = new WP_Query (
array (
'post_type' => 'product' ,
'post_status' => 'publish' ,
'posts_per_page' => 1 ,
'no_found_rows' => true ,
)
);
if ( ! $products -> have_posts () ) {
return '' ;
}
$product_id = $products -> posts [ 0 ] -> ID ;
}
// Overwrite product.
if ( $overwrite_product ) {
$original_product = $product ;
$product = wc_get_product ( $product_id );
}
// Overwrite post.
if ( $overwrite_post ) {
$original_post = $post ;
$post = get_post ( $product_id );
}
// Overwrite wp_query.
if ( $overwrite_wp_query ) {
$original_wp_query = $wp_query ;
$wp_query = new WP_Query ( array ( 'p' => $product_id ) );
}
}
ob_start ();
switch ( $function_name ) {
case 'woocommerce_breadcrumb' :
$breadcrumb_separator = et_ () -> array_get ( $args , 'breadcrumb_separator' , '' );
$breadcrumb_separator = str_replace ( '”' , '' , $breadcrumb_separator );
woocommerce_breadcrumb (
array (
'delimiter' => ' ' . $breadcrumb_separator . ' ' ,
'home' => et_ () -> array_get ( $args , 'breadcrumb_home_text' , '' ),
)
);
break ;
case 'woocommerce_show_product_images' :
// WC Images module needs to modify global variable's property. Thus it is performed
// here instead at module's class since the $product global might be modified.
$gallery_ids = $product -> get_gallery_image_ids ();
$image_id = $product -> get_image_id ();
$show_image = 'on' === $args [ 'show_product_image' ];
$show_gallery = 'on' === $args [ 'show_product_gallery' ];
$show_sale_badge = 'on' === $args [ 'show_sale_badge' ];
// If featured image is disabled, replace it with first gallery image's id (if gallery
// is enabled) or replaced it with empty string (if gallery is disabled as well).
if ( ! $show_image ) {
if ( $show_gallery && isset ( $gallery_ids [ 0 ] ) ) {
$product -> set_image_id ( $gallery_ids [ 0 ] );
// Remove first image from the gallery because it'll be added as thumbnail and will be duplicated.
unset ( $gallery_ids [ 0 ] );
$product -> set_gallery_image_ids ( $gallery_ids );
} else {
$product -> set_image_id ( '' );
}
}
// Replaced gallery image ids with empty array.
if ( ! $show_gallery ) {
$product -> set_gallery_image_ids ( array () );
}
if ( $show_sale_badge && function_exists ( 'woocommerce_show_product_sale_flash' ) ) {
woocommerce_show_product_sale_flash ();
}
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
call_user_func ( $function_name );
// Reset product's actual featured image id.
if ( ! $show_image ) {
$product -> set_image_id ( $image_id );
}
// Reset product's actual gallery image id.
if ( ! $show_gallery ) {
$product -> set_gallery_image_ids ( $gallery_ids );
}
break ;
case 'wc_get_stock_html' :
echo wc_get_stock_html ( $product ); // phpcs:ignore WordPress.Security.EscapeOutput -- `wc_get_stock_html` include woocommerce's `single-product/stock.php` template.
break ;
case 'wc_print_notice' :
2021-12-20 18:06:11 +00:00
$message = et_ () -> array_get ( $args , 'wc_cart_message' , '' );
2021-12-07 11:08:05 +00:00
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
2021-12-20 18:06:11 +00:00
call_user_func ( $function_name , $message );
2021-12-07 11:08:05 +00:00
break ;
case 'wc_print_notices' :
// Save existing notices to restore them as many times as we need.
$et_wc_cached_notices = WC () -> session -> get ( 'wc_notices' , array () );
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
call_user_func ( $function_name );
// Restore notices which were removed after wc_print_notices() executed to render multiple modules on page.
if ( ! empty ( $et_wc_cached_notices ) && empty ( WC () -> session -> get ( 'wc_notices' , array () ) ) ) {
WC () -> session -> set ( 'wc_notices' , $et_wc_cached_notices );
}
break ;
2021-12-20 18:06:11 +00:00
case 'woocommerce_checkout_login_form' :
if ( function_exists ( 'woocommerce_checkout_login_form' ) ) {
woocommerce_checkout_login_form ();
}
if ( function_exists ( 'woocommerce_checkout_coupon_form' ) ) {
woocommerce_checkout_coupon_form ();
}
$is_builder = et_ () -> array_get ( $args , 'is_builder' , false );
if ( $is_builder ) {
ET_Builder_Module_Woocommerce_Cart_Notice :: output_coupon_error_message ();
}
break ;
2021-12-07 11:08:05 +00:00
case 'woocommerce_upsell_display' :
$order = isset ( $args [ 'order' ] ) ? $args [ 'order' ] : '' ;
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
call_user_func ( $function_name , '' , '' , '' , $order );
break ;
2021-12-20 18:06:11 +00:00
case 'wc_cart_empty_template' :
wc_get_template ( 'cart/cart-empty.php' );
break ;
case 'woocommerce_output_all_notices' :
// Save existing notices to restore them as many times as we need.
$et_wc_cached_notices = WC () -> session -> get ( 'wc_notices' , array () );
if ( function_exists ( $function_name ) ) {
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Using for consistency.
call_user_func ( $function_name );
}
// Restore notices which were removed after wc_print_notices() executed to render multiple modules on page.
if ( ! empty ( $et_wc_cached_notices ) && empty ( WC () -> session -> get ( 'wc_notices' , array () ) ) ) {
WC () -> session -> set ( 'wc_notices' , $et_wc_cached_notices );
}
break ;
2021-12-07 11:08:05 +00:00
default :
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
call_user_func ( $function_name );
}
$output = ob_get_clean ();
// Reset original product variable to global $product.
if ( $is_tb ) {
et_theme_builder_wc_reset_global_objects ();
} elseif ( $overwrite_global ) {
// Reset $product global.
if ( $overwrite_product ) {
$product = $original_product ;
}
// Reset post.
if ( $overwrite_post ) {
$post = $original_post ;
}
// Reset wp_query.
if ( $overwrite_wp_query ) {
$wp_query = $original_wp_query ;
}
// phpcs:enable WordPress.WP.GlobalVariablesOverride -- Enable global variable override check.
}
return $output ;
}
/**
* Renders the content .
*
* Rendering the content will enable Divi Builder to take over the entire
* post content area .
*
* @ since 3.29
*/
function et_builder_wc_product_render_layout () {
do_action ( 'et_builder_wc_product_before_render_layout' );
the_content ();
do_action ( 'et_builder_wc_product_after_render_layout' );
}
/**
* Force WooCommerce to load default template over theme 's custom template when builder' s
* et_builder_from_scratch is used to prevent unexpected custom layout which makes builder
* experience inconsistent
*
* @ since 3.29
*
* @ param string $template Path to template file .
* @ param string $slug Template slug .
* @ param string $name Template name .
*
* @ return string
*/
function et_builder_wc_override_template_part ( $template , $slug , $name ) {
// Only force load default `content-single-product.php` template.
$is_content_single_product = 'content' === $slug && 'single-product' === $name ;
return $is_content_single_product ? WC () -> plugin_path () . " /templates/ { $slug } - { $name } .php " : $template ;
}
/**
* Disable all default WooCommerce single layout hooks .
*
* @ since 4.0 . 10
*/
function et_builder_wc_disable_default_layout () {
// To remove a hook, the $function_to_remove and $priority arguments must match
// with which the hook was added.
remove_action (
'woocommerce_before_main_content' ,
'woocommerce_breadcrumb' ,
20
);
remove_action (
'woocommerce_before_single_product_summary' ,
'woocommerce_show_product_sale_flash' ,
10
);
remove_action (
'woocommerce_before_single_product_summary' ,
'woocommerce_show_product_images' ,
20
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_title' ,
5
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_rating' ,
10
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_price' ,
10
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_excerpt' ,
20
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_add_to_cart' ,
30
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_meta' ,
40
);
remove_action (
'woocommerce_single_product_summary' ,
'woocommerce_template_single_sharing' ,
50
);
remove_action (
'woocommerce_after_single_product_summary' ,
'woocommerce_output_product_data_tabs' ,
10
);
remove_action (
'woocommerce_after_single_product_summary' ,
'woocommerce_upsell_display' ,
15
);
remove_action (
'woocommerce_after_single_product_summary' ,
'woocommerce_output_related_products' ,
20
);
}
/**
* Overrides the default WooCommerce layout .
*
* @ see woocommerce / includes / wc - template - functions . php
*
* @ since 3.29
*/
function et_builder_wc_override_default_layout () {
if ( ! is_singular ( 'product' ) ) {
return ;
}
// global $post won't be available with `after_setup_theme` hook and hence `wp` hook is used.
global $post ;
if ( ! et_pb_is_pagebuilder_used ( $post -> ID ) ) {
return ;
}
$product_page_layout = et_builder_wc_get_product_layout ( $post -> ID );
$is_product_content_modified = 'modified' === get_post_meta ( $post -> ID , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY , true );
$is_preview_loading = is_preview ();
// BFB was enabled but page content wasn't saved yet. Load default layout on FE.
if ( 'et_build_from_scratch' === $product_page_layout && ! $is_product_content_modified && ! $is_preview_loading ) {
return ;
}
/*
* The `has_shortcode()` check does not work here . Hence solving the need using `strpos()` .
*
* The WHY behind the check is explained in the following issue .
* @ see https :// github . com / elegantthemes / Divi / issues / 16155
*/
if ( ! $product_page_layout && ! et_core_is_fb_enabled () || ( $product_page_layout && 'et_build_from_scratch' !== $product_page_layout )
) {
return ;
}
// Force use WooCommerce's default template if current theme is not Divi or Extra (handling
// possible custom template on DBP / Child Theme).
if ( ! in_array ( wp_get_theme () -> get ( 'Name' ), array ( 'Divi' , 'Extra' ), true ) ) {
add_filter ( 'wc_get_template_part' , 'et_builder_wc_override_template_part' , 10 , 3 );
}
et_builder_wc_disable_default_layout ();
do_action ( 'et_builder_wc_product_before_render_layout_registration' );
// Add render content on product page.
add_action ( 'woocommerce_after_single_product_summary' , 'et_builder_wc_product_render_layout' , 5 );
}
/**
* Skips setting default content on Product post type during Builder activation .
*
* Otherwise , the description would be shown in both Product Tabs and at the end of the
* default WooCommerce layout set at
*
2021-12-20 18:06:11 +00:00
* @ see et_builder_wc_get_prefilled_product_page_content ()
2021-12-07 11:08:05 +00:00
*
* @ since 3.29
*
* @ param bool $flag Whether to skips the content activation .
* @ param WP_Post $post Post .
*
* @ return bool
*/
function et_builder_wc_skip_initial_content ( $flag , $post ) {
if ( ! ( $post instanceof WP_Post ) ) {
return $flag ;
}
if ( 'product' !== $post -> post_type ) {
return $flag ;
}
return true ;
}
/**
* Determine whether given content has WooCommerce module inside it or not
*
* @ since 4.0 Added ET_Builder_Element class exists check .
* @ since 3.29
*
* @ param string $content Content .
*
* @ return bool
*/
function et_builder_has_woocommerce_module ( $content = '' ) {
if ( ! class_exists ( 'ET_Builder_Element' ) ) {
return false ;
}
$has_woocommerce_module = false ;
$woocommerce_modules = ET_Builder_Element :: get_woocommerce_modules ();
foreach ( $woocommerce_modules as $module ) {
if ( has_shortcode ( $content , $module ) ) {
$has_woocommerce_module = true ;
// Stop the loop once any shortcode is found.
break ;
}
}
return apply_filters ( 'et_builder_has_woocommerce_module' , $has_woocommerce_module );
}
/**
* Check if current global $post uses builder / layout block , not `product` CPT , and contains
* WooCommerce module inside it . This check is needed because WooCommerce by default only adds
* scripts and style to `product` CPT while WooCommerce Modules can be used at any CPT
*
* @ since 3.29
* @ since 4.1 . 0 check if layout block is used instead of builder
*
* @ since bool
*/
function et_builder_wc_is_non_product_post_type () {
global $post ;
if ( $post && 'product' === $post -> post_type ) {
return false ;
}
$types = et_theme_builder_get_layout_post_types ();
$layouts = et_theme_builder_get_template_layouts ();
foreach ( $types as $type ) {
if ( ! isset ( $layouts [ $type ] ) ) {
continue ;
}
if ( $layouts [ $type ][ 'override' ] && et_builder_has_woocommerce_module ( get_post_field ( 'post_content' , $layouts [ $type ][ 'id' ] ) ) ) {
return true ;
}
}
// If no post found, bail early.
if ( ! $post ) {
return false ;
}
$is_builder_used = et_pb_is_pagebuilder_used ( $post -> ID );
$is_layout_block_used = has_block ( 'divi/layout' , $post -> post_content );
// If no builder or layout block used, bail early.
if ( ! $is_builder_used && ! $is_layout_block_used ) {
return false ;
}
$has_wc_module = et_builder_has_woocommerce_module ( $post -> post_content );
if ( ( $is_builder_used || $is_layout_block_used ) && $has_wc_module ) {
return true ;
}
return false ;
}
/**
* Load WooCommerce related scripts . This function basically redo what
* `WC_Frontend_Scripts::load_scripts()` does without the `product` CPT limitation .
*
* Once more WooCommerce Modules are added ( checkout , account , etc ), revisit this method and
* compare it against `WC_Frontend_Scripts::load_scripts()` . Some of the script queues are
* removed here because there is currently no WooCommerce module equivalent of them .
*
* @ since 3.29
* @ since 4.3 . 3 Loads WC scripts on Shop , Product Category & Product Tags archives .
* @ since 4.9 . 11 Avoid invalid argument supplied for foreach () warning .
*/
function et_builder_wc_load_scripts () {
global $post ;
2021-12-20 18:06:11 +00:00
$is_shop = function_exists ( 'is_shop' ) && is_shop ();
$is_checkout = function_exists ( 'is_checkout' ) && is_checkout ();
2021-12-07 11:08:05 +00:00
// is_product_taxonomy() is not returning TRUE for Category & Tags.
// Hence we check Category & Tag archives individually.
$is_product_category = function_exists ( 'is_product_category' ) && is_product_category ();
$is_product_tag = function_exists ( 'is_product_tag' ) && is_product_tag ();
// If current page is not non-`product` CPT which using builder, stop early.
if ( ( ! et_builder_wc_is_non_product_post_type ()
|| ! class_exists ( 'WC_Frontend_Scripts' ) )
&& function_exists ( 'et_fb_enabled' )
&& ! et_core_is_fb_enabled ()
&& ! $is_shop
&& ! $is_product_category
&& ! $is_product_tag
2021-12-20 18:06:11 +00:00
&& ! $is_checkout
2021-12-07 11:08:05 +00:00
) {
return ;
}
// Simply enqueue the scripts; All of them have been registered.
if ( 'yes' === get_option ( 'woocommerce_enable_ajax_add_to_cart' ) ) {
wp_enqueue_script ( 'wc-add-to-cart' );
}
if ( current_theme_supports ( 'wc-product-gallery-zoom' ) ) {
wp_enqueue_script ( 'zoom' );
}
if ( current_theme_supports ( 'wc-product-gallery-slider' ) ) {
wp_enqueue_script ( 'flexslider' );
}
if ( current_theme_supports ( 'wc-product-gallery-lightbox' ) ) {
wp_enqueue_script ( 'photoswipe-ui-default' );
wp_enqueue_style ( 'photoswipe-default-skin' );
add_action ( 'wp_footer' , 'woocommerce_photoswipe' );
}
wp_enqueue_script ( 'wc-single-product' );
if ( 'geolocation_ajax' === get_option ( 'woocommerce_default_customer_address' ) ) {
$ua = strtolower ( wc_get_user_agent () ); // Exclude common bots from geolocation by user agent.
if ( ! strstr ( $ua , 'bot' ) && ! strstr ( $ua , 'spider' ) && ! strstr ( $ua , 'crawl' ) ) {
wp_enqueue_script ( 'wc-geolocation' );
}
}
wp_enqueue_script ( 'woocommerce' );
wp_enqueue_script ( 'wc-cart-fragments' );
2021-12-20 18:06:11 +00:00
wp_enqueue_script ( 'wc-checkout' );
wp_enqueue_script ( 'select2' );
wp_enqueue_script ( 'selectWoo' );
wp_enqueue_style ( 'select2' );
2021-12-07 11:08:05 +00:00
// Enqueue style.
$wc_styles = WC_Frontend_Scripts :: get_styles ();
/*
* Since $wc_styles is passed in to `woocommerce_enqueue_styles` filter ,
* ensure that the value is array .
*
* @ see https :// github . com / elegantthemes / divi - builder / issues / 1268
*/
if ( ! is_array ( $wc_styles ) ) {
return ;
}
foreach ( $wc_styles as $style_handle => $wc_style ) {
if ( ! isset ( $wc_style [ 'has_rtl' ] ) ) {
$wc_style [ 'has_rtl' ] = false ;
}
wp_enqueue_style ( $style_handle , $wc_style [ 'src' ], $wc_style [ 'deps' ], $wc_style [ 'version' ], $wc_style [ 'media' ], $wc_style [ 'has_rtl' ] );
}
}
/**
* Add WooCommerce body class name on non `product` CPT builder page
*
* @ param array $classes CSS class names .
*
* @ return array
* @ since 3.29
*/
function et_builder_wc_add_body_class ( $classes ) {
if ( et_builder_wc_is_non_product_post_type () ) {
$classes [] = 'woocommerce' ;
$classes [] = 'woocommerce-page' ;
}
return $classes ;
}
/**
* Add product class name on inner content wrapper page on non `product` CPT builder page with woocommerce modules
* And on Product posts
*
* @ param array $classes Product class names .
*
* @ return array
* @ since 3.29
*/
function et_builder_wc_add_inner_content_class ( $classes ) {
// The class is required on any post with woocommerce modules and on product pages.
if ( et_builder_wc_is_non_product_post_type () || is_product () ) {
$classes [] = 'product' ;
}
return $classes ;
}
/**
* Add WooCommerce class names on Divi Shop Page ( not WooCommerce Shop ) .
*
* @ since 4.0 . 7
*
* @ param array $classes Array of Classes .
*
* @ return array
*/
function et_builder_wc_add_outer_content_class ( $classes ) {
$body_classes = get_body_class ();
// Add Class only to WooCommerce Shop page if built using Divi (i.e. Divi Shop page).
if ( ! ( function_exists ( 'is_shop' ) && is_shop () ) ) {
return $classes ;
}
// Add Class only when the WooCommerce Shop page is built using Divi.
if ( ! et_builder_wc_is_non_product_post_type () ) {
return $classes ;
}
// Precautionary check: $body_classes should always be an array.
if ( ! is_array ( $body_classes ) ) {
return $classes ;
}
// Add Class only when the <body> tag does not contain them.
$woocommerce_classes = array ( 'woocommerce' , 'woocommerce-page' );
$common_classes = array_intersect (
$body_classes ,
array (
'woocommerce' ,
'woocommerce-page' ,
)
);
if ( is_array ( $common_classes ) && count ( $woocommerce_classes ) === count ( $common_classes ) ) {
return $classes ;
}
// Precautionary check: $classes should always be an array.
if ( ! is_array ( $classes ) ) {
return $classes ;
}
$classes [] = 'woocommerce' ;
$classes [] = 'woocommerce-page' ;
return $classes ;
}
/**
* Sets the Product page layout post meta on two occurrences .
*
* They are 1 ) On WP Admin Publish / Update post 2 ) On VB Save .
*
2021-12-20 18:06:11 +00:00
* @ since 4.14 . 0 Remove ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY meta key on non - product post types .
* Also move `since` section above `param` section .
2021-12-07 11:08:05 +00:00
* @ since 3.29
2021-12-20 18:06:11 +00:00
*
* @ param int $post_id Post ID .
2021-12-07 11:08:05 +00:00
*/
function et_builder_set_product_page_layout_meta ( $post_id ) {
$post = get_post ( $post_id );
if ( ! $post ) {
return ;
}
/*
* The Product page layout post meta adds no meaning to the Post when the Builder is not used .
* Hence the meta key / value is removed , when the Builder is turned off .
*/
if ( ! et_pb_is_pagebuilder_used ( $post_id ) ) {
delete_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY );
return ;
}
2021-12-20 18:06:11 +00:00
// The meta key is to be used only on Product post types.
// Hence remove the meta if exists on other post types.
$is_non_product_post_type = 'product' !== $post -> post_type ;
if ( $is_non_product_post_type ) {
// Returns FALSE when no meta key is found.
delete_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY );
return ;
}
2021-12-07 11:08:05 +00:00
// Do not update Product page layout post meta when it contains a value.
$product_page_layout = get_post_meta (
$post_id ,
ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY ,
true
);
if ( $product_page_layout ) {
return ;
}
$product_page_layout = et_get_option (
'et_pb_woocommerce_page_layout' ,
'et_build_from_scratch'
);
update_post_meta (
$post_id ,
ET_BUILDER_WC_PRODUCT_PAGE_LAYOUT_META_KEY ,
sanitize_text_field ( $product_page_layout )
);
}
/**
* Sets the Product content status as modified during VB save .
*
* This avoids setting the default WooCommerce Modules layout more than once .
*
* @ link https :// github . com / elegantthemes / Divi / issues / 16420
*
* @ param int $post_id Post ID .
*/
function et_builder_set_product_content_status ( $post_id ) {
if ( 0 === absint ( $post_id ) ) {
return ;
}
if ( 'product' !== get_post_type ( $post_id ) || 'modified' === get_post_meta (
$post_id ,
ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY ,
true
) ) {
return ;
}
update_post_meta ( $post_id , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY , 'modified' );
}
/**
* Gets Woocommerce Tabs for the given Product ID .
*
* @ since 4.4 . 2
*/
function et_builder_get_woocommerce_tabs () {
// Nonce verification.
et_core_security_check ( 'edit_posts' , 'et_builder_get_woocommerce_tabs' , 'nonce' );
$_ = et_ ();
$product_id = $_ -> array_get ( $_POST , 'product' , 0 );
if ( null === $product_id || ! et_is_woocommerce_plugin_active () ) {
wp_send_json_error ();
}
// Allow Latest Product ID which is a string 'latest'.
// `This Product` tabs are defined in et_fb_current_page_params().
if ( ! in_array ( $product_id , array ( 'current' , 'latest' ), true ) && 0 === absint ( $product_id ) ) {
wp_send_json_error ();
}
$tabs = ET_Builder_Module_Woocommerce_Tabs :: get_tabs ( array ( 'product' => $product_id ) );
wp_send_json_success ( $tabs );
}
/**
* Returns alternative hook to make Woo Extra Product Options display fields in FE when TB is
* enabled .
*
* - The Woo Extra Product Options addon does not display the extra fields on the FE .
* - This is because the original hook i . e . `woocommerce_before_single_product` in the plugin
* is not triggered when TB is enabled .
* - Hence return a suitable hook that is fired for all types of Products i . e . Simple , Variable ,
* etc .
*
* @ param string $hook Hook name .
*
* @ return string WooCommerce Hook that is being fired on TB enabled Product pages .
* @ see WEPOF_Product_Options_Frontend :: define_public_hooks ()
*
* @ since 4.0 . 9
*/
function et_builder_trigger_extra_product_options ( $hook ) {
return 'woocommerce_before_add_to_cart_form' ;
}
/**
* Strip Builder shortcodes to avoid nested parsing .
*
* @ see https :// github . com / elegantthemes / Divi / issues / 18682
*
* @ param string $content Post content .
*
* @ since 4.3 . 3
*
* @ return string
*/
function et_builder_avoid_nested_shortcode_parsing ( $content ) {
// Strip shortcodes only on non-builder pages that contain Builder shortcodes.
if ( et_pb_is_pagebuilder_used ( get_the_ID () ) ) {
return $content ;
}
// WooCommerce layout loads when builder is not enabled.
// So strip builder shortcodes from Post content.
if ( function_exists ( 'is_product' ) && is_product () ) {
return et_strip_shortcodes ( $content );
}
// Strip builder shortcodes from non-product pages.
// Only Tabs shortcode is checked since that causes nested rendering.
if ( has_shortcode ( $content , 'et_pb_wc_tabs' ) ) {
return et_strip_shortcodes ( $content );
}
return $content ;
}
/**
* Parses Product description to
*
* - converts any [ embed ][ / embed ] shortcode to its respective HTML .
* - strips `et_` shortcodes to avoid nested rendering in Woo Tabs module .
* - adds < p > tag to keep the paragraph sanity .
* - runs other shortcodes if any using do_shortcode .
*
* @ since 4.4 . 1
*
* @ param string $description Product description i . e . Post content .
*
* @ return string
*/
function et_builder_wc_parse_description ( $description ) {
if ( ! is_string ( $description ) ) {
return $description ;
}
global $wp_embed ;
$parsed_description = et_strip_shortcodes ( $description );
$parsed_description = $wp_embed -> run_shortcode ( $parsed_description );
$parsed_description = do_shortcode ( $parsed_description );
$parsed_description = wpautop ( $parsed_description );
return $parsed_description ;
}
2021-12-20 18:06:11 +00:00
/**
* Deletes ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY when Builder is OFF .
*
* The deletion allows switching between Divi Builder and the GB builder smoothly .
*
* @ link https :// github . com / elegantthemes / Divi / issues / 22477
*
* @ since 4.14 . 0
*
* @ param WP_Post $post Post Object .
*/
function et_builder_wc_delete_post_meta ( $post ) {
if ( ! ( $post instanceof WP_Post ) ) {
return ;
}
if ( et_pb_is_pagebuilder_used ( $post -> ID ) ) {
return ;
}
delete_post_meta ( $post -> ID , ET_BUILDER_WC_PRODUCT_PAGE_CONTENT_STATUS_META_KEY );
}
2021-12-07 11:08:05 +00:00
/**
* Entry point for the woocommerce - modules . php file .
*
* @ since 3.29
*/
function et_builder_wc_init () {
// global $post won't be available with `after_setup_theme` hook and hence `wp` hook is used.
add_action ( 'wp' , 'et_builder_wc_override_default_layout' );
// Add WooCommerce class names on non-`product` CPT which uses builder.
add_filter ( 'body_class' , 'et_builder_wc_add_body_class' );
add_filter ( 'et_builder_inner_content_class' , 'et_builder_wc_add_inner_content_class' );
add_filter ( 'et_builder_outer_content_class' , 'et_builder_wc_add_outer_content_class' );
// Load WooCommerce related scripts.
add_action ( 'wp_enqueue_scripts' , 'et_builder_wc_load_scripts' , 15 );
add_filter (
'et_builder_skip_content_activation' ,
'et_builder_wc_skip_initial_content' ,
10 ,
2
);
// Show Product Content dropdown settings under
// Divi Theme Options ⟶ Builder ⟶ Post TYpe Integration.
add_filter ( 'et_builder_settings_definitions' , 'et_builder_wc_add_settings' );
/**
* Adds the metabox only to Product post type .
*
* This is achieved using the post type hook - add_meta_boxes_ { post_type } .
*
* @ see https :// codex . wordpress . org / Plugin_API / Action_Reference / add_meta_boxes
*
* @ since 3.29
*/
add_action ( 'add_meta_boxes_product' , 'et_builder_wc_long_description_metabox_register' );
// Saves the long description metabox data.
// Since `et_pb_metabox_settings_save_details()` already uses `save_post` hook
// to save `_et_pb_old_content` post meta,
// we use this additional hook `et_pb_old_content_updated`.
add_action ( 'et_pb_old_content_updated' , 'et_builder_wc_long_description_metabox_save' , 10 , 3 );
2021-12-20 18:06:11 +00:00
/*
* 01. Sets the initial Content when `Use Divi Builder` button is clicked
* in the Admin dashboard .
* 02. Sets the initial Content when `Enable Visual Builder` is clicked .
*/
2021-12-07 11:08:05 +00:00
add_filter (
'et_fb_load_raw_post_content' ,
2021-12-20 18:06:11 +00:00
'et_builder_wc_set_prefilled_page_content' ,
2021-12-07 11:08:05 +00:00
10 ,
2
);
add_action ( 'et_save_post' , 'et_builder_set_product_page_layout_meta' );
/*
* Set the Product modified status as modified upon save to make sure default layout is not
* loaded more than one time .
*
* @ see https :// github . com / elegantthemes / Divi / issues / 16420
*/
2021-12-20 18:06:11 +00:00
add_action ( 'et_update_post' , 'et_builder_wc_set_page_content_status' );
2021-12-07 11:08:05 +00:00
/*
* Handle get Woocommerce tabs AJAX call initiated by Tabs checkbox in settings modal .
*/
add_action ( 'wp_ajax_et_builder_get_woocommerce_tabs' , 'et_builder_get_woocommerce_tabs' );
/*
* Fix Woo Extra Product Options addon compatibility .
* @ see https :// github . com / elegantthemes / Divi / issues / 17909
*/
add_filter ( 'thwepof_hook_name_before_single_product' , 'et_builder_trigger_extra_product_options' );
/*
* Fix nested parsing on non - builder product pages w / shortcode content .
* @ see https :// github . com / elegantthemes / Divi / issues / 18682
*/
add_filter ( 'the_content' , 'et_builder_avoid_nested_shortcode_parsing' );
add_filter ( 'et_builder_wc_description' , 'et_builder_wc_parse_description' );
2021-12-20 18:06:11 +00:00
add_filter ( 'template_redirect' , 'et_builder_wc_template_redirect' , 9 );
/*
* Delete `_et_pb_woo_page_content_status` post meta when Divi Builder is off
* when using GB editor .
*
* The latest value of `_et_pb_use_builder` post meta is only available in
* `rest_after_insert_page` and NOT in `rest_insert_page` hook .
*
* This action is documented in
* wp - includes / rest - api / endpoints / class - wp - rest - posts - controller . php
*/
add_action ( 'rest_after_insert_page' , 'et_builder_wc_delete_post_meta' );
add_filter ( 'woocommerce_checkout_redirect_empty_cart' , 'et_builder_stop_cart_redirect_while_enabling_builder' );
/*
* `wp_loaded` is used intentionally because
* `get_cart()` should not be called before wp_loaded hook .
*/
add_action (
'wp_loaded' ,
'et_builder_handle_shipping_calculator_update_btn_click'
);
2021-12-07 11:08:05 +00:00
/*
* In the case of dynamic module framework ' s shortcode manager
* we need to fire this hook on its own ,
*/
if ( ! et_builder_should_load_all_module_data () ) {
add_action (
'et_builder_module_lazy_shortcodes_registered' ,
[
'ET_Builder_Module_Woocommerce_Cart_Notice' ,
'disable_default_notice' ,
]
);
2021-12-20 18:06:11 +00:00
add_action (
'et_builder_module_lazy_shortcodes_registered' ,
[
'ET_Builder_Module_Woocommerce_Checkout_Additional_Info' ,
'maybe_invoke_woocommerce_hooks' ,
]
);
2021-12-07 11:08:05 +00:00
}
}
et_builder_wc_init ();