: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* WooCommerce Module Helper
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
if ( et_is_woocommerce_plugin_active() ) {
* Class ET_Builder_Module_Helper_Woocommerce_Modules
* Shared code between all Woo Modules.
class ET_Builder_Module_Helper_Woocommerce_Modules {
* Returns TRUE if the Product attribute value is valid.
* Valid values are Product Ids, `current` and `latest`.
* @param string $maybe_product_id
public static function is_product_attr_valid( $maybe_product_id ) {
if ( empty( $maybe_product_id ) ) {
if ( absint( $maybe_product_id ) === 0
&& ! in_array( $maybe_product_id, array( 'current', 'latest' ) ) ) {
* Gets the Product Id by the given Product prop value.
* @param string $valid_product_attr
public static function get_product_id_by_prop( $valid_product_attr ) {
if ( ! self::is_product_attr_valid( $valid_product_attr ) ) {
if ( 'current' === $valid_product_attr ) {
$current_post_id = ET_Builder_Element::get_current_post_id();
if ( et_theme_builder_is_layout_post_type( get_post_type( $current_post_id ) ) ) {
// We want to use the latest product when we are editing a TB layout.
$valid_product_attr = 'latest';
if ( ! in_array( $valid_product_attr, array(
) ) && false === get_post_status( $valid_product_attr ) ) {
$valid_product_attr = 'latest';
if ( 'current' === $valid_product_attr ) {
$product_id = ET_Builder_Element::get_current_post_id();
} else if ( 'latest' === $valid_product_attr ) {
'post_status' => array( 'publish', 'private' ),
$products = wc_get_products( $args );
if ( ! empty( $products ) ) {
$product_id = $products[0]->get_id();
} else if ( is_numeric( $valid_product_attr ) && 'product' !== get_post_type( $valid_product_attr ) ) {
// There is a condition that $valid_product_attr value passed here is not the product ID.
// For example when you set product breadcrumb as Blurb Title when building layout in TB.
// So we get the most recent product ID in date descending order.
$query = new WC_Product_Query( array(
'status' => array( 'publish' ),
$products = $query->get_products();
if ( $products && ! empty( $products[0] ) ) {
$product_id = absint( $products[0] );
$product_id = absint( $valid_product_attr );
$product_id = absint( $valid_product_attr );
* Gets the Product (WC_Product) by the value stored in the Product attribute.
* @param string $maybe_product_id The Value stored in the Product attribute using VB.
* @return false|WC_Product
public static function get_product( $maybe_product_id ) {
$product_id = self::get_product_id_by_prop( $maybe_product_id );
* No need to check `wc_get_product()` exists since this Class is defined only when
$product = wc_get_product( $product_id );
if ( empty( $product ) ) {
* @param string $maybe_product_id The Value stored in the Product attribute using VB.
* @return int WP_Product ID.
public static function get_product_id( $maybe_product_id ) {
$product = self::get_product( $maybe_product_id );
return $product->get_id();
* Get reusable WooCommerce field definition
* @param string $name Field template name.
* @param array $attrs Attribute that need to be inserted into field definition.
* @param array $unset Attribute that need to be removed from field definition.
public static function get_field( $name, $attrs = array(), $unset = array() ) {
'label' => esc_html__( 'Product', 'et_builder' ),
'type' => 'select_product',
'option_category' => 'basic_option',
'description' => esc_html__( 'Here you can select the Product.', 'et_builder' ),
'toggle_slug' => 'main_content',
'displayRecent' => false,
'post_type' => 'product',
'computed_affects' => array(
'label' => esc_html__( 'Filter By', 'et_builder' ),
'option_category' => 'configuration',
'newest' => esc_html__( 'Newest', 'et_builder' ),
'toggle_slug' => 'main_content',
'description' => esc_html__( 'Here you can filter the Products.', 'et_builder' ),
'computed_affects' => array(
'label' => esc_html__( 'Product Count', 'et_builder' ),
'option_category' => 'configuration',
'description' => esc_html__( 'Define the number of products that should be displayed per page.', 'et_builder' ),
'computed_affects' => array(
'toggle_slug' => 'main_content',
'label' => esc_html__( 'Column Layout', 'et_builder' ),
'option_category' => 'layout',
'6' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '6' ) ),
'5' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '5' ) ),
'4' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '4' ) ),
'3' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '3' ) ),
'2' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '2' ) ),
'1' => esc_html__( '1 Column', 'et_builder' ),
'description' => esc_html__( 'Choose how many columns to display.', 'et_builder' ),
'computed_affects' => array(
'toggle_slug' => 'main_content',
'label' => esc_html__( 'Order', 'et_builder' ),
'option_category' => 'configuration',
'default' => esc_html__( 'Default Sorting', 'et_builder' ),
'menu_order' => esc_html__( 'Sort by Menu Order', 'et_builder' ),
'popularity' => esc_html__( 'Sort By Popularity', 'et_builder' ),
'date' => esc_html__( 'Sort By Date: Oldest To Newest', 'et_builder' ),
'date-desc' => esc_html__( 'Sort By Date: Newest To Oldest', 'et_builder' ),
'price' => esc_html__( 'Sort By Price: Low To High', 'et_builder' ),
'price-desc' => esc_html__( 'Sort By Price: High To Low', 'et_builder' ),
'default_on_front' => 'default',
'description' => esc_html__( 'Choose how your products should be ordered.', 'et_builder' ),
'computed_affects' => array(
'toggle_slug' => 'main_content',
// Added custom attribute(s).
if ( ! empty( $attrs ) ) {
$field = wp_parse_args( $attrs, $field );
// Remove default attribute(s).
if ( ! empty( $unset ) ) {
foreach ( $unset as $unset_attr ) {
unset( $field[ $unset_attr ] );
* Gets the Reviews title.
* @param WC_Product $product The Product Post.
public static function get_reviews_title( $product ) {
if ( ! ( $product instanceof WC_Product ) ) {
$count = $product->get_review_count();
$reviews_title = sprintf(
esc_html( _n( '%1$s review for %2$s', '%1$s reviews for %2$s', $count, 'et_builder' ) ),
'<span>' . $product->get_title() . '</span>'
$reviews_title = esc_html__( 'Reviews', 'et_builder' );
* Gets the Reviews comment form.
* @param WC_Product $product The Product Post.
* @param WP_Comment[] $comments Array of Comment objects.
public static function get_reviews_comment_form( $product, $comments ) {
$has_reviews = empty( $comments ) ? false : true;
<?php if ( get_option( 'woocommerce_review_rating_verification_required' ) === 'no' ||
wc_customer_bought_product( '', get_current_user_id(), $product->get_id() ) ) : ?>
<div id="review_form_wrapper">
$commenter = wp_get_current_commenter();
'title_reply' => $has_reviews ? esc_html__( 'Add a review', 'et_builder' ) : sprintf( esc_html__( 'Be the first to review “%s”', 'woocommerce' ), get_the_title( $product->get_id() ) ),
'title_reply_to' => esc_html__( 'Leave a Reply to %s', 'et_builder' ),
'title_reply_before' => '<span id="reply-title" class="comment-reply-title">',
'title_reply_after' => '</span>',
'comment_notes_after' => '',
'author' => '<p class="comment-form-author">' . '<label for="author">' . esc_html__( 'Name', 'woocommerce' ) . ' <span class="required">*</span></label> ' .
'<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" required /></p>',
'email' => '<p class="comment-form-email"><label for="email">' . esc_html__( 'Email', 'woocommerce' ) . ' <span class="required">*</span></label> ' .
'<input id="email" name="email" type="email" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30" required /></p>',
'label_submit' => esc_html__( 'Submit', 'et_builder' ),
'submit_button' => '<button name="%1$s" type="submit" id="%2$s" class="et_pb_button %3$s" />%4$s</button>',
if ( $account_page_url = wc_get_page_permalink( 'myaccount' ) ) {
/* translators: %s opening and closing link tags respectively */
$comment_form['must_log_in'] = '<p class="must-log-in">' . sprintf( esc_html__( 'You must be %1$slogged in%2$s to post a review.', 'woocommerce' ), '<a href="' . esc_url( $account_page_url ) . '">', '</a>' ) . '</p>';
if ( get_option( 'woocommerce_enable_review_rating' ) === 'yes' ) {
$comment_form['comment_field'] = '<div class="comment-form-rating"><label for="rating">' . esc_html__( 'Your rating', 'et_builder' ) . '</label><select name="rating" id="rating" required>
<option value="">' . esc_html__( 'Rate…', 'et_builder' ) . '</option>
<option value="5">' . esc_html__( 'Perfect', 'et_builder' ) . '</option>
<option value="4">' . esc_html__( 'Good', 'et_builder' ) . '</option>
<option value="3">' . esc_html__( 'Average', 'et_builder' ) . '</option>
<option value="2">' . esc_html__( 'Not that bad', 'et_builder' ) . '</option>
<option value="1">' . esc_html__( 'Very poor', 'et_builder' ) . '</option>
$comment_form['comment_field'] .= '<p class="comment-form-comment"><label for="comment">' . esc_html__( 'Your review', 'et_builder' ) . ' <span class="required">*</span></label><textarea id="comment" name="comment" cols="45" rows="8" required></textarea></p>';
apply_filters( 'woocommerce_product_review_comment_form_args', $comment_form ),
<p class="woocommerce-verification-required"><?php esc_html_e( 'Only logged in customers who have purchased this product may leave a review.', 'woocommerce' ); ?></p>
* Gets the formatted weight markup for the given Product Id.
* @param int $product_id Product Id.
public static function get_weight_formatted( $product_id ) {
$product = self::get_product( $product_id );
return ( $product->has_weight() ) ? wc_format_weight( $product->get_weight() ) : $markup;
* Gets the formatted dimension markup for the given Product Id.
* @param int $product_id Product Id.
public static function get_dimensions_formatted( $product_id ) {
$product = self::get_product( $product_id );
return ( $product->has_dimensions() ) ? wc_format_dimensions( $product->get_dimensions( false ) ) : $markup;
* Filters the $outer_wrapper_attrs.
* Adds 'data-background-layout' and 'data-background-layout-hover' attributes if needed.
* @param array $outer_wrapper_attrs Key value pairs of outer wrapper attributes.
* @param ET_Builder_Element $this_class Module's class.
* @return array filtered $outer_wrapper_attrs.
public static function maybe_add_background_layout_data( $outer_wrapper_attrs, $this_class ) {
$background_layout = et_()->array_get( $this_class->props, 'background_layout', '' );
$background_layout_hover = et_pb_hover_options()->get_value( 'background_layout', $this_class->props, 'light' );
$background_layout_hover_enabled = et_pb_hover_options()->is_enabled( 'background_layout', $this_class->props );
if ( $background_layout_hover_enabled ) {
$outer_wrapper_attrs['data-background-layout'] = esc_attr( $background_layout );
$outer_wrapper_attrs['data-background-layout-hover'] = esc_attr( $background_layout_hover );
return $outer_wrapper_attrs;
* Processes the Background Layout options for Woocommerce Modules.
* Adds Background Layout related classes.
* Adds Filter for $outer_wrapper_attrs, so required data attributes can be added for specific modules.
* @param string $render_slug Module's render slug.
* @param ET_Builder_Element $this_class Module's class.
public static function process_background_layout_data( $render_slug, $this_class ) {
$background_layout = et_()->array_get( $this_class->props, 'background_layout', '' );
$background_layout_values = et_pb_responsive_options()->get_property_values( $this_class->props, 'background_layout' );
$background_layout_tablet = et_()->array_get( $background_layout_values, 'tablet', '' );
$background_layout_phone = et_()->array_get( $background_layout_values, 'phone', '' );
$this_class->add_classname( "et_pb_bg_layout_{$background_layout}" );
if ( ! empty( $background_layout_tablet ) ) {
$this_class->add_classname( "et_pb_bg_layout_{$background_layout_tablet}_tablet" );
if ( ! empty( $background_layout_phone ) ) {
$this_class->add_classname( "et_pb_bg_layout_{$background_layout_phone}_phone" );
add_filter( "et_builder_module_{$render_slug}_outer_wrapper_attrs", array( 'ET_Builder_Module_Helper_Woocommerce_Modules', 'maybe_add_background_layout_data' ), 10, 2 );
* Processes the Button Icon options for Woocommerce Modules.
* Adds et_pb_woo_custom_button_icon class if needed.
* Adds Filter for $outer_wrapper_attrs, so required button icon attributes can be added for specific modules.
* @param string $render_slug Module's render slug.
* @param ET_Builder_Element $this_class Module's class.
public static function process_custom_button_icons( $render_slug, $this_class ) {
$button_custom = $this_class->props['custom_button'];
$custom_icon_values = et_()->array_get( $this_class->props, 'button_icon', '' );
// Exit if no custom styles or icons defined for button.
if ( 'on' !== $button_custom || '' === $custom_icon_values ) {
$this_class->add_classname( 'et_pb_woo_custom_button_icon' );
add_filter( "et_builder_module_{$render_slug}_outer_wrapper_attrs", array( 'ET_Builder_Module_Helper_Woocommerce_Modules', 'add_custom_button_icons' ), 10, 2 );