ents->get( $id ); if ( ! is_object( $document ) ) { return false; } $fragments = []; $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, $this->get_fragments_handler( $fragments ) ); return ! empty( $fragments ) ? $fragments : false; } /** * Get Fragments Handler. * * @since 3.7.0 * * @param array $fragments * @return void */ public function get_fragments_handler( array &$fragments ) { return function ( $element ) use ( &$fragments ) { if ( ! isset( $element['widgetType'] ) ) { return; } $fragment_data = $this->get_fragment_data( $element ); $total_fragments = count( $fragment_data ) / 2; for ( $i = 0; $i < $total_fragments; $i++ ) { $fragments[ $fragment_data['selector'][ $i ] ] = $fragment_data['html'][ $i ]; } }; } /** * Empty Cart Fragments * * When the Cart is emptied, the selected 'Empty Cart Template' needs to be added as an item * in the WooCommerce `$fragments` array, so that WC will push the custom Template content into the DOM. * This is done to prevent the need for a page refresh after the cart is cleared. * * @since 3.7.0 * * @param array $fragments * @return array */ public function empty_cart_fragments( $fragments ) { // Only do this when the cart is empty. if ( WC()->cart->get_cart_contents_count() !== 0 ) { return $fragments; } $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); if ( is_object( $document ) ) { $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { if ( isset( $element['widgetType'] ) && 'woocommerce-cart' === $element['widgetType'] && ( isset( $element['settings']['additional_template_switch'] ) && 'active' === $element['settings']['additional_template_switch'] ) && ( isset( $element['settings']['additional_template_select'] ) && 0 < $element['settings']['additional_template_select'] ) ) { $fragments[ 'div.elementor-element-' . $element['id'] . ' .elementor-widget-container' ] = '
' . do_shortcode( '[elementor-template id="' . $element['settings']['additional_template_select'] . '"]' ) . '
'; } } ); } return $fragments; } public function maybe_init_cart() { $has_cart = is_a( WC()->cart, 'WC_Cart' ); if ( ! $has_cart ) { $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' ); WC()->session = new $session_class(); WC()->session->init(); WC()->cart = new \WC_Cart(); WC()->customer = new \WC_Customer( get_current_user_id(), true ); } } public function localized_settings_frontend( $settings ) { $has_cart = is_a( WC()->cart, 'WC_Cart' ); if ( $has_cart ) { $settings['woocommerce']['menu_cart'] = [ 'cart_page_url' => wc_get_cart_url(), 'checkout_page_url' => wc_get_checkout_url(), 'fragments_nonce' => wp_create_nonce( self::MENU_CART_FRAGMENTS_ACTION ), ]; } return $settings; } public function theme_template_include( $need_override_location, $location ) { if ( is_product() && 'single' === $location ) { $need_override_location = true; } return $need_override_location; } public function add_loop_recommended_widgets( $config, $post_id ) { if ( ! $this->is_source_set_to_products( $post_id ) ) { return $config; } $config = $this->add_woocommerce_widgets_to_recommended( $config ); return $this->hide_woocommerce_widgets_in_loop_document( $config ); } /** * Add plugin path to wc template search path. * Based on: https://www.skyverge.com/blog/override-woocommerce-template-file-within-a-plugin/ * @param $template * @param $template_name * @param $template_path * * @return string */ public function woocommerce_locate_template( $template, $template_name, $template_path ) { if ( self::TEMPLATE_MINI_CART !== $template_name ) { return $template; } if ( ! $this->use_mini_cart_template ) { return $template; } $plugin_path = plugin_dir_path( __DIR__ ) . 'woocommerce/wc-templates/'; if ( file_exists( $plugin_path . $template_name ) ) { $template = $plugin_path . $template_name; } return $template; } /** * WooCommerce/WordPress widget(s), some of the widgets have css classes that used by final selectors. * before this filter, all those widgets were warped by `.elementor-widget-container` without chain original widget * classes, now they will be warped by div with the original css classes. * * @param array $default_widget_args * @param \Elementor\Widget_WordPress $widget * * @return array $default_widget_args */ public function woocommerce_wordpress_widget_css_class( $default_widget_args, $widget ) { $widget_instance = $widget->get_widget_instance(); if ( ! empty( $widget_instance->widget_cssclass ) ) { $default_widget_args['before_widget'] .= '
'; $default_widget_args['after_widget'] .= '
'; } return $default_widget_args; } public function register_admin_fields( Settings $settings ) { if ( ! API::is_licence_has_feature( static::MENU_CART_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { return; } $settings->add_section( Settings::TAB_INTEGRATIONS, 'woocommerce', [ 'callback' => function() { echo '

' . esc_html__( 'WooCommerce', 'elementor-pro' ) . '

'; }, 'fields' => [ self::OPTION_NAME_USE_MINI_CART => [ 'label' => esc_html__( 'Mini Cart Template', 'elementor-pro' ), 'field_args' => [ 'type' => 'select', 'std' => 'initial', 'options' => [ 'initial' => '', // Relevant until either menu-cart widget is used or option is explicitly set to 'no'. 'no' => esc_html__( 'Disable', 'elementor-pro' ), 'yes' => esc_html__( 'Enable', 'elementor-pro' ), ], 'desc' => esc_html__( 'Set to `Disable` in order to use your Theme\'s or WooCommerce\'s mini-cart template instead of Elementor\'s.', 'elementor-pro' ), ], ], ], ] ); } /** * Load Widget Before WooCommerce Ajax. * * When outputting the complex WooCommerce shortcodes (which we use in our widgets) e.g. Checkout, Cart, etc. WC * immediately does more ajax calls and retrieves updated html fragments based on the data in the forms that may * be autofilled by the current user's browser e.g. the Payment section holding the "Place order" button. * * This function runs before these ajax calls. Using the `elementorPageId` and `elementorWidgetId` querystring * appended to the forms `_wp_http_referer` url field, or the referer page ID, it loads the relevant Elementor widget. * The rendered Elementor widget replaces the default WooCommerce template used to refresh WooCommerce elements in the page. * * This is necessary for example in the Checkout Payment section where we modify the Terms & Conditions text * using settings from the widget or when updating shipping methods on the Cart. * * @since 3.5.0 */ public function load_widget_before_wc_ajax() { // Make sure is a WooCommerce ajax call. $wc_ajax = ProUtils::_unstable_get_super_global_value( $_GET, 'wc-ajax' ); if ( ! $wc_ajax ) { return; } // Only handle relevant WC AJAX calls if ( ! in_array( $wc_ajax, [ 'update_order_review', 'update_shipping_method' ], true ) ) { return; } // Security checks. switch ( $wc_ajax ) { case 'update_order_review': check_ajax_referer( 'update-order-review', 'security' ); break; case 'update_shipping_method': check_ajax_referer( 'update-shipping-method', 'security' ); break; } $page_id = false; $widget_id = false; // Try to get the `$page_id` and `$widget_id` we added as a query string to `_wp_http_referer` in `post_data`. // This is only available when a form is submitted. $raw_post_data = ProUtils::_unstable_get_super_global_value( $_POST, 'post_data' ); if ( $raw_post_data ) { $raw_post_data = html_entity_decode( $raw_post_data ); parse_str( $raw_post_data, $post_data ); if ( isset( $post_data['_wp_http_referer'] ) ) { $wp_http_referer = wp_unslash( $post_data['_wp_http_referer'] ); $wp_http_referer_query_string = wp_parse_url( $wp_http_referer, PHP_URL_QUERY ); if ( ! empty( $wp_http_referer_query_string ) ) { parse_str( $wp_http_referer_query_string, $wp_http_referer_query_string ); if ( isset( $wp_http_referer_query_string['elementorPageId'] ) ) { $page_id = $wp_http_referer_query_string['elementorPageId']; } if ( isset( $wp_http_referer_query_string['elementorWidgetId'] ) ) { $widget_id = $wp_http_referer_query_string['elementorWidgetId']; } } } } if ( ! $page_id ) { $page_id = url_to_postid( wp_get_referer() ); } // Bail if no `$page_id`. if ( ! $page_id ) { return; } // Get Elementor document from `$page_id`. $document = Plugin::elementor()->documents->get_doc_for_frontend( $page_id ); // Bail if not Elementor page. if ( ! $document ) { return; } // Setup $page_id as the WP global $post, so is available to our widgets. $post = get_post( $page_id, OBJECT ); setup_postdata( $post ); $widget_data = false; if ( $widget_id ) { // If we did manage to pass `$widget_id` to this ajax call we get the widget data by its ID. $widget_data = Utils::find_element_recursive( $document->get_elements_data(), $widget_id ); } else { // If we didn't manage to pass `$widget_id` to this ajax call we use this alternate method and get the first // of the type of widget used on the WC endpoint pages responsible for these ajax calls - cart or checkout widget. $woocommerce_widgets = [ 'woocommerce-cart', 'woocommerce-checkout-page' ]; $document_data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $document_data, function( $element ) use ( $woocommerce_widgets, &$widget_data ) { if ( $widget_data && ( ! isset( $element['widgetType'] ) || ! in_array( $element['widgetType'], $woocommerce_widgets, true ) ) ) { return; } $widget_data = $element; } ); } // If we found a widget then run `add_render_hooks()` widget method. if ( $widget_data ) { $widget_instance = Plugin::elementor()->elements_manager->create_element_instance( $widget_data ); if ( method_exists( $widget_instance, 'add_render_hooks' ) ) { $widget_instance->add_render_hooks(); } } } /** * Elementor Woocommerce Checkout Login User * * Handle the Ajax call for the custom login form on the Checkout Widget * * @since 3.5.0 */ public function elementor_woocommerce_checkout_login_user() { if ( is_user_logged_in() ) { wp_logout(); } $error = false; $error_message = ''; if ( ! wp_verify_nonce( ProUtils::_unstable_get_super_global_value( $_POST, 'nonce' ), 'woocommerce-login' ) ) { $error = true; $error_message = sprintf( /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ esc_html__( '%1$sError:%2$s The nonce security check didn’t pass. Please reload the page and try again. You may want to try clearing your browser cache as a last attempt.', 'elementor-pro' ), '', '' ); } else { $info = [ 'user_login' => trim( ProUtils::_unstable_get_super_global_value( $_POST, 'username' ) ), 'user_password' => $_POST['password'] ?? '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, A password should not be sanitized. 'remember' => ProUtils::_unstable_get_super_global_value( $_POST, 'remember' ), ]; $user_signon = wp_signon( $info, false ); if ( is_wp_error( $user_signon ) ) { $error = true; $error_message = $user_signon->get_error_message(); } } if ( $error ) { wc_add_notice( $error_message, 'error' ); $response = [ 'logged_in' => false, 'message' => wc_print_notices( true ), ]; } else { $response = [ 'logged_in' => true ]; } echo wp_json_encode( $response ); wp_die(); } /** * Register Ajax Actions. * * Registers ajax action used by the Editor js. * * @since 3.5.0 * * @param Ajax $ajax */ public function register_ajax_actions( Ajax $ajax ) { // `woocommerce_update_page_option` is called in the editor save-show-modal.js. $ajax->register_ajax_action( 'pro_woocommerce_update_page_option', [ $this, 'update_page_option' ] ); $ajax->register_ajax_action( 'pro_woocommerce_mock_notices', [ $this, 'woocommerce_mock_notices' ] ); } /** * @throws \Exception */ public function woocommerce_mock_notices( $data ) { $document = ProUtils::_unstable_get_document_for_edit( $data['editor_post_id'] ); if ( in_array( 'wc_error', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s', esc_html__( 'This is how an error notice would look.', 'elementor-pro' ), esc_html__( 'Here\'s a link', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'error' ); } if ( in_array( 'wc_message', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s %3$s', esc_html__( 'Button', 'elementor-pro' ), esc_html__( 'This is what a WooCommerce message notice looks like.', 'elementor-pro' ), esc_html__( 'Here\'s a link', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'success' ); } if ( in_array( 'wc_info', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s', esc_html__( 'Button', 'elementor-pro' ), esc_html__( 'This is how WooCommerce provides an info notice.', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'notice' ); } return '
' . wc_print_notices( true ) . '
'; } /** * Update Page Option. * * Ajax action can be used to update any WooCommerce option. * * @since 3.5.0 * * @param array $data */ public function update_page_option( $data ) { $is_admin = current_user_can( 'manage_options' ); $is_shop_manager = current_user_can( 'manage_woocommerce' ); $is_allowed = $is_admin || $is_shop_manager; if ( ! $is_allowed ) { return new \WP_Error( 401 ); } $allowed_options = [ 'woocommerce_checkout_page_id', 'woocommerce_cart_page_id', 'woocommerce_myaccount_page_id', 'elementor_woocommerce_purchase_summary_page_id', ]; $option_name = $data['option_name']; $post_id = absint( $data['editor_post_id'] ); if ( ! in_array( $option_name, $allowed_options, true ) ) { return new \WP_Error( 400 ); } update_option( $option_name, $post_id ); } public function init_site_settings( \Elementor\Core\Kits\Documents\Kit $kit ) { $kit->register_tab( 'settings-woocommerce', \ElementorPro\Modules\Woocommerce\Settings\Settings_Woocommerce::class ); } public function add_products_type_to_template_popup( $form ) { $this->add_products_to_options( $form, '_elementor_source' ); } public function add_products_type_to_loop_settings_query( $form ) { $this->add_products_to_options( $form, 'source' ); } public function e_cart_count_fragments( $fragments ) { $product_count = WC()->cart->get_cart_contents_count(); $fragments['.elementor-menu-cart__toggle_button span.elementor-button-text'] = '' . WC()->cart->get_cart_subtotal() . ''; $fragments['.elementor-menu-cart__toggle_button span.elementor-button-icon-qty'] = '' . $product_count . ''; return $fragments; } /** * @param $form * @param $control_name * @return void */ protected function add_products_to_options( $form, $control_name ) { if ( empty( $form ) ) { return; } $controls = $form->get_controls( $control_name ); if ( ! $controls || ! isset( $controls['options'] ) ) { return; } $options = $controls['options']; $options[ self::LOOP_PRODUCT_SKIN_ID ] = esc_html__( 'Products', 'elementor-pro' ); $form->update_control($control_name, [ 'options' => $options, ]); } /** * Add Update Kit Settings Hooks * * Add hooks that update the corresponding kit setting when the WooCommerce option is updated. */ public function add_update_kit_settings_hooks() { add_action( 'update_option_woocommerce_cart_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_cart_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_checkout_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_checkout_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_myaccount_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_myaccount_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_terms_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_terms_page_id', $value ); }, 10, 2 ); } /** * Elementor WC My Account Logout * * Programatically log out if $_REQUEST['elementor_wc_logout'] is set. * The $_REQUEST variables we have generated a custom logout URL for in the My Account menu. * * @since 3.5.0 */ public function elementor_wc_my_account_logout() { $elementor_wc_logout = ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_wc_logout' ); $nonce = ProUtils::_unstable_get_super_global_value( $_REQUEST, '_wpnonce' ); if ( $elementor_wc_logout && $nonce && wp_verify_nonce( $nonce, 'customer-logout' ) ) { wp_logout(); // Log the user out Programatically. wp_safe_redirect( esc_url( ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_my_account_redirect' ) ) ); // Redirect back to the widget page. exit; } } /** * Add Localize Data * * Makes `woocommercePages` available with the page name and the associated post ID for use with the various * widgets site settings modal. * * @param $settings * @return array */ public function add_localize_data( $settings ) { $settings['woocommerce']['woocommercePages'] = [ 'checkout' => wc_get_page_id( 'checkout' ), 'cart' => wc_get_page_id( 'cart' ), 'myaccount' => wc_get_page_id( 'myaccount' ), 'purchase_summary' => get_option( 'elementor_woocommerce_purchase_summary_page_id' ), ]; return $settings; } /** * Localize Added To Cart On Product Single * * WooCommerce doesn't trigger `added_to_cart` event on its products single page which is required for us to * automatically open our Menu Cart if the settings is chosen. We make the `productAddedToCart` setting * available that we can use in the Menu Cart js to check if a product has just been added. * * @since 3.5.0 */ public function localize_added_to_cart_on_product_single() { add_filter( 'elementor_pro/frontend/localize_settings', function ( $settings ) { $settings['woocommerce']['productAddedToCart'] = true; return $settings; } ); } public function e_notices_body_classes( $classes ) { if ( $this->should_load_wc_notices_styles() ) { foreach ( $this->get_styled_notice_elements() as $notice_element ) { $classes[] = 'e-' . str_replace( '_', '-', $notice_element ) . '-notice'; } } return $classes; } public function get_styled_notice_elements() { if ( empty( $this->woocommerce_notices_elements ) ) { $kit = Plugin::elementor()->kits_manager->get_active_kit_for_frontend(); $this->woocommerce_notices_elements = $kit->get_settings_for_display( 'woocommerce_notices_elements' ); } return ! empty( $this->woocommerce_notices_elements ) ? $this->woocommerce_notices_elements : []; } public function custom_gutenberg_woocommerce_notice() { $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'elementor-gutenberg-woocommerce-notice', ELEMENTOR_PRO_URL . '/assets/js/gutenberg-woocommerce-notice' . $min_suffix . '.js', [ 'wp-blocks' ], ELEMENTOR_PRO_VERSION, false ); wp_set_script_translations( 'elementor-gutenberg-woocommerce-notice', 'elementor-pro' ); } public function e_notices_css() { if ( ! $this->should_load_wc_notices_styles() ) { return false; } wp_enqueue_style( 'e-woocommerce-notices', ELEMENTOR_PRO_URL . 'assets/css/woocommerce-notices.min.css', [], ELEMENTOR_PRO_VERSION ); } public function get_order_received_endpoint_url( $url, $endpoint, $value ) { $order_received_endpoint = get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ); if ( $order_received_endpoint === $endpoint ) { $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); $order = wc_get_order( $value ); if ( $woocommerce_purchase_summary_page_id && $order ) { $url = trailingslashit( trailingslashit( trailingslashit( get_permalink( $woocommerce_purchase_summary_page_id ) ) . $order_received_endpoint ) . $order->get_id() ); } } return $url; } public function maybe_define_woocommerce_checkout() { $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); if ( $woocommerce_purchase_summary_page_id && intval( $woocommerce_purchase_summary_page_id ) === get_queried_object_id() ) { if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { define( 'WOOCOMMERCE_CHECKOUT', true ); } } } /** * Products Query Sources Fragments. * * Since we introduced additional query sources to the Products Widget, * some of these query sources can now be used outside of the Single Product template. * * For example the Related Products and Cross-Sells. * * But now we'll need to make those sections also update when the Cart is updated. So * we'll do this by creating fragments for each of these. * * @since 3.7.0 * * @param array $fragments * * @return array */ public function products_query_sources_fragments( $fragments ) { if ( WC()->cart->get_cart_contents_count() !== 0 ) { $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); if ( is_object( $document ) ) { $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { if ( isset( $element['widgetType'] ) && 'woocommerce-products' === $element['widgetType'] ) { $settings = $element['settings']; if ( isset( $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ] ) ) { $query_type = $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ]; $query_types_to_check = [ 'related_products', 'upsells', 'cross_sells' ]; if ( in_array( $query_type, $query_types_to_check, true ) ) { switch ( $query_type ) { case 'related_products': $content = self::get_products_related_content( $settings ); break; case 'upsells': $content = self::get_upsells_content( $settings ); break; case 'cross_sells': $content = self::get_cross_sells_content( $settings ); break; default: $content = null; } if ( $content ) { $fragments[ 'div.elementor-element-' . $element['id'] . ' div.elementor-widget-container' ] = '
' . $content . '
'; } } } } } ); } } else { $fragments['div.elementor-widget-container .woocommerce .cross-sells'] = '
'; $fragments['div.elementor-widget-container .woocommerce section.up-sells'] = '
'; } return $fragments; } /** * Get Products Related Content. * * A function to return content for the 'related' products query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_products_related_content( $settings ) { global $product; $product = wc_get_product(); if ( ! $product ) { return; } return self::get_product_widget_content( $settings, 'related_products', 'woocommerce_product_related_products_heading', 'products_related_title_text' ); } /** * Get Upsells Content. * * A function to return content for the 'upsell' query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_upsells_content( $settings ) { return self::get_product_widget_content( $settings, 'upsells', 'woocommerce_product_upsells_products_heading', 'products_upsells_title_text' ); } /** * Get Cross Sells Content. * * A function to return content for the 'cross_sells' query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_cross_sells_content( $settings ) { return self::get_product_widget_content( $settings, 'cross_sells', 'woocommerce_product_cross_sells_products_heading', 'products_cross_sells_title_text' ); } /** * Print Woocommerce Shipping Message * * Format the shipping messages that will be displayed on the Cart and Checkout Widgets. * This will add extra classes to those messages so that we can target certain messages * with certain style controls. * * @since 3.5.0 * * @param string $html the original HTML from WC * @param string $classes the classes we will surround $html with * @return string the final formatted HTML that will be rendered */ private function print_woocommerce_shipping_message( $html, $classes ) { return '' . $html . ''; } /** * Should load WC Notices Styles * * Determine if we should load the WooCommerce notices CSS. * It should only load: * - When we are in the Editor, regardless if any notices have been activated. * - If WooCoomerce is active. * - When we are on the front end, if at least one notice is activated. * * It should not load in WP Admin. * * @return boolean */ private function should_load_wc_notices_styles() { if ( ! API::is_licence_has_feature( static::SITE_SETTINGS_NOTICES_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { return false; } $woocommerce_active = in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ); $is_editor = ProUtils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); // Editor checks. if ( $woocommerce_active && $is_editor ) { return true; } // Front end checks. if ( 0 < count( $this->get_styled_notice_elements() ) // At least one notice has been activated. && $woocommerce_active // WooCommerce is active. && ( ! is_admin() || $is_editor ) // We are not in WP Admin. ) { return true; } return false; } /** * Get Product Widget Content. * * A general function to create markup for the new query types in the Products widget. * * @since 3.7.0 * @access private * * @param array $settings The widget settings. * @param string $type The query type to create content for. * @param string $title_hook The hook name to filter in the widget title. * @param string $title_key The control ID for the section title. * * @return mixed The content or false */ private static function get_product_widget_content( $settings, $type, $title_hook, $title_key = '' ) { add_filter( $title_hook, function ( $heading ) use ( $settings, $title_key ) { $title_text = isset( $settings[ $title_key ] ) ? $settings[ $title_key ] : ''; if ( ! empty( $title_text ) ) { return $title_text; } return $heading; }, 10, 1 ); ob_start(); $args = self::parse_product_widget_args( $settings, $type ); if ( 'related_products' === $type ) { woocommerce_related_products( $args ); } elseif ( 'upsells' === $type ) { woocommerce_upsell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); } else { /** * We need to wrap this content in the 'woocommerce' class for the layout to have the correct styling. * Because this will only be used as a separate widget on the Cart page, * the normal 'woocommerce' div from the cart widget will be closed before this content. */ echo '
'; woocommerce_cross_sell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); echo '
'; } $products_html = ob_get_clean(); remove_filter( $title_hook, function() {}, 10 ); if ( $products_html ) { $products_html = str_replace( '