, array( 'checkout' => WC()->checkout(), 'available_gateways' => $available_gateways, 'order_button_text' => apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ), ) ); } } if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) { /** * Output the Coupon form for the checkout. */ function woocommerce_checkout_coupon_form() { if ( is_user_logged_in() || WC()->checkout()->is_registration_enabled() || ! WC()->checkout()->is_registration_required() ) { wc_get_template( 'checkout/form-coupon.php', array( 'checkout' => WC()->checkout(), ) ); } } } if ( ! function_exists( 'woocommerce_products_will_display' ) ) { /** * Check if we will be showing products or not (and not sub-categories only). * * @return bool */ function woocommerce_products_will_display() { $display_type = woocommerce_get_loop_display_mode(); return 0 < wc_get_loop_prop( 'total', 0 ) && 'subcategories' !== $display_type; } } if ( ! function_exists( 'woocommerce_get_loop_display_mode' ) ) { /** * See what is going to display in the loop. * * @since 3.3.0 * @return string Either products, subcategories, or both, based on current page. */ function woocommerce_get_loop_display_mode() { // Only return products when filtering things. if ( wc_get_loop_prop( 'is_search' ) || wc_get_loop_prop( 'is_filtered' ) ) { return 'products'; } $parent_id = 0; $display_type = ''; if ( is_shop() ) { $display_type = get_option( 'woocommerce_shop_page_display', '' ); } elseif ( is_product_category() ) { $parent_id = get_queried_object_id(); $display_type = get_term_meta( $parent_id, 'display_type', true ); $display_type = '' === $display_type ? get_option( 'woocommerce_category_archive_display', '' ) : $display_type; } if ( ( ! is_shop() || 'subcategories' !== $display_type ) && 1 < wc_get_loop_prop( 'current_page' ) ) { return 'products'; } // Ensure valid value. if ( '' === $display_type || ! in_array( $display_type, array( 'products', 'subcategories', 'both' ), true ) ) { $display_type = 'products'; } // If we're showing categories, ensure we actually have something to show. if ( in_array( $display_type, array( 'subcategories', 'both' ), true ) ) { $subcategories = woocommerce_get_product_subcategories( $parent_id ); if ( empty( $subcategories ) ) { $display_type = 'products'; } } return $display_type; } } if ( ! function_exists( 'woocommerce_maybe_show_product_subcategories' ) ) { /** * Maybe display categories before, or instead of, a product loop. * * @since 3.3.0 * @param string $loop_html HTML. * @return string */ function woocommerce_maybe_show_product_subcategories( $loop_html = '' ) { if ( wc_get_loop_prop( 'is_shortcode' ) && ! WC_Template_Loader::in_content_filter() ) { return $loop_html; } $display_type = woocommerce_get_loop_display_mode(); // If displaying categories, append to the loop. if ( 'subcategories' === $display_type || 'both' === $display_type ) { ob_start(); woocommerce_output_product_categories( array( 'parent_id' => is_product_category() ? get_queried_object_id() : 0, ) ); $loop_html .= ob_get_clean(); if ( 'subcategories' === $display_type ) { wc_set_loop_prop( 'total', 0 ); // This removes pagination and products from display for themes not using wc_get_loop_prop in their product loops. @todo Remove in future major version. global $wp_query; if ( $wp_query->is_main_query() ) { $wp_query->post_count = 0; $wp_query->max_num_pages = 0; } } } return $loop_html; } } if ( ! function_exists( 'woocommerce_output_product_categories' ) ) { /** * Display product sub categories as thumbnails. * * This is a replacement for woocommerce_product_subcategories which also does some logic * based on the loop. This function however just outputs when called. * * @since 3.3.1 * @param array $args Arguments. * @return boolean */ function woocommerce_output_product_categories( $args = array() ) { $args = wp_parse_args( $args, array( 'before' => apply_filters( 'woocommerce_before_output_product_categories', '' ), 'after' => apply_filters( 'woocommerce_after_output_product_categories', '' ), 'parent_id' => 0, ) ); $product_categories = woocommerce_get_product_subcategories( $args['parent_id'] ); if ( ! $product_categories ) { return false; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['before']; foreach ( $product_categories as $category ) { wc_get_template( 'content-product_cat.php', array( 'category' => $category, ) ); } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['after']; return true; } } if ( ! function_exists( 'woocommerce_get_product_subcategories' ) ) { /** * Get (and cache) product subcategories. * * @param int $parent_id Get subcategories of this ID. * @return array */ function woocommerce_get_product_subcategories( $parent_id = 0 ) { $parent_id = absint( $parent_id ); $cache_key = apply_filters( 'woocommerce_get_product_subcategories_cache_key', 'product-category-hierarchy-' . $parent_id, $parent_id ); $product_categories = $cache_key ? wp_cache_get( $cache_key, 'product_cat' ) : false; if ( false === $product_categories ) { // NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work. $product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array( 'parent' => $parent_id, 'hide_empty' => 0, 'hierarchical' => 1, 'taxonomy' => 'product_cat', 'pad_counts' => 1, ) ) ); if ( $cache_key ) { wp_cache_set( $cache_key, $product_categories, 'product_cat' ); } } if ( apply_filters( 'woocommerce_product_subcategories_hide_empty', true ) ) { $product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' ); } return $product_categories; } } if ( ! function_exists( 'woocommerce_subcategory_thumbnail' ) ) { /** * Show subcategory thumbnails. * * @param mixed $category Category. */ function woocommerce_subcategory_thumbnail( $category ) { $small_thumbnail_size = apply_filters( 'subcategory_archive_thumbnail_size', 'woocommerce_thumbnail' ); $dimensions = wc_get_image_size( $small_thumbnail_size ); $thumbnail_id = get_term_meta( $category->term_id, 'thumbnail_id', true ); if ( $thumbnail_id ) { $image = wp_get_attachment_image_src( $thumbnail_id, $small_thumbnail_size ); $image = $image[0]; $image_srcset = function_exists( 'wp_get_attachment_image_srcset' ) ? wp_get_attachment_image_srcset( $thumbnail_id, $small_thumbnail_size ) : false; $image_sizes = function_exists( 'wp_get_attachment_image_sizes' ) ? wp_get_attachment_image_sizes( $thumbnail_id, $small_thumbnail_size ) : false; } else { $image = wc_placeholder_img_src(); $image_srcset = false; $image_sizes = false; } if ( $image ) { // Prevent esc_url from breaking spaces in urls for image embeds. // Ref: https://core.trac.wordpress.org/ticket/23605. $image = str_replace( ' ', '%20', $image ); // Add responsive image markup if available. if ( $image_srcset && $image_sizes ) { echo '' . esc_attr( $category->name ) . ''; } else { echo '' . esc_attr( $category->name ) . ''; } } } } if ( ! function_exists( 'woocommerce_order_details_table' ) ) { /** * Displays order details in a table. * * @param mixed $order_id Order ID. */ function woocommerce_order_details_table( $order_id ) { if ( ! $order_id ) { return; } $order = wc_get_order( $order_id ); if ( ! $order ) { return; } $template = 'order/order-details.php'; if ( FeaturesUtil::feature_is_enabled( 'fulfillments' ) ) { $fulfillment_data_store = wc_get_container()->get( FulfillmentsDataStore::class ); $fulfillments = $fulfillment_data_store->read_fulfillments( WC_Order::class, $order_id ); if ( ! empty( $fulfillments ) ) { $template = 'order/order-details-fulfillments.php'; } } wc_get_template( $template, array( 'order_id' => $order_id, /** * Determines if the order downloads table should be shown (in the context of the order details * template). * * By default, this is true if the order has at least one dowloadable items and download is permitted * (which is partly determined by the order status). For special cases, though, this can be overridden * and the downloads table can be forced to render (or forced not to render). * * @since 8.5.0 * * @param bool $show_downloads If the downloads table should be shown. * @param WC_Order $order The related order. */ 'show_downloads' => apply_filters( 'woocommerce_order_downloads_table_show_downloads', ( $order->has_downloadable_item() && $order->is_download_permitted() ), $order ), ) ); } } if ( ! function_exists( 'woocommerce_order_downloads_table' ) ) { /** * Displays order downloads in a table. * * @since 3.2.0 * @param array $downloads Downloads. */ function woocommerce_order_downloads_table( $downloads ) { if ( ! $downloads ) { return; } wc_get_template( 'order/order-downloads.php', array( 'downloads' => $downloads, ) ); } } if ( ! function_exists( 'woocommerce_order_again_button' ) ) { /** * Display an 'order again' button on the view order page. * * @param object $order Order. */ function woocommerce_order_again_button( $order ) { /** * Filter the valid order statuses for reordering. * * @since 3.5.0 * * @param array $statuses_for_reordering Array of valid order statuses for reordering. */ $statuses_for_reordering = apply_filters( 'woocommerce_valid_order_statuses_for_order_again', array( OrderStatus::COMPLETED ) ); if ( ! $order || ! $order->has_status( $statuses_for_reordering ) || ! is_user_logged_in() ) { return; } wc_get_template( 'order/order-again.php', array( 'order' => $order, 'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '', 'order_again_url' => wp_nonce_url( add_query_arg( 'order_again', $order->get_id(), wc_get_cart_url() ), 'woocommerce-order_again' ), ) ); } } /** Forms */ if ( ! function_exists( 'woocommerce_form_field' ) ) { /** * Outputs a checkout/address form field. * * @param string $key Key. * @param mixed $args Arguments. * @param string $value (default: null). * @return string */ function woocommerce_form_field( $key, $args, $value = null ) { $defaults = array( 'type' => 'text', 'label' => '', 'description' => '', 'placeholder' => '', 'maxlength' => false, 'minlength' => false, 'required' => false, 'autocomplete' => false, 'id' => $key, 'class' => array(), 'label_class' => array(), 'input_class' => array(), 'return' => false, 'options' => array(), 'custom_attributes' => array(), 'validate' => array(), 'default' => '', 'autofocus' => '', 'priority' => '', 'unchecked_value' => null, 'checked_value' => '1', ); $args = wp_parse_args( $args, $defaults ); $args = apply_filters( 'woocommerce_form_field_args', $args, $key, $value ); if ( is_string( $args['class'] ) ) { $args['class'] = array( $args['class'] ); } if ( is_string( $args['label_class'] ) ) { $args['label_class'] = array( $args['label_class'] ); } if ( is_null( $value ) ) { $value = $args['default']; } // Custom attribute handling. $custom_attributes = array(); $args['custom_attributes'] = array_filter( (array) $args['custom_attributes'], 'strlen' ); if ( $args['required'] ) { // hidden inputs are the only kind of inputs that don't need an `aria-required` attribute. // checkboxes apply the `custom_attributes` to the label - we need to apply the attribute on the input itself, instead. if ( ! in_array( $args['type'], array( 'hidden', 'checkbox' ), true ) ) { $args['custom_attributes']['aria-required'] = 'true'; $args['label_class'][] = 'required_field'; } $args['class'][] = 'validate-required'; $required_indicator = ' '; } else { $required_indicator = ' (' . esc_html__( 'optional', 'woocommerce' ) . ')'; } if ( $args['maxlength'] ) { $args['custom_attributes']['maxlength'] = absint( $args['maxlength'] ); } if ( $args['minlength'] ) { $args['custom_attributes']['minlength'] = absint( $args['minlength'] ); } if ( ! empty( $args['autocomplete'] ) ) { $args['custom_attributes']['autocomplete'] = $args['autocomplete']; } if ( true === $args['autofocus'] ) { $args['custom_attributes']['autofocus'] = 'autofocus'; } if ( $args['description'] ) { $args['custom_attributes']['aria-describedby'] = $args['id'] . '-description'; } if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) { foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) { $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"'; } } if ( ! empty( $args['validate'] ) ) { foreach ( $args['validate'] as $validate ) { $args['class'][] = 'validate-' . $validate; } } $field = ''; $label_id = $args['id']; $sort = $args['priority'] ? $args['priority'] : ''; $field_container = '

%3$s

'; switch ( $args['type'] ) { case 'country': $countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries(); if ( 1 === count( $countries ) ) { $field .= '' . current( array_values( $countries ) ) . ''; $field .= ''; } else { $data_label = ! empty( $args['label'] ) ? 'data-label="' . esc_attr( $args['label'] ) . '"' : ''; $field = ''; $field .= ''; } break; case 'state': /* Get country this state field is representing */ $for_country = isset( $args['country'] ) ? $args['country'] : WC()->checkout->get_value( 'billing_state' === $key ? 'billing_country' : 'shipping_country' ); $states = WC()->countries->get_states( $for_country ); if ( is_array( $states ) && empty( $states ) ) { $field_container = ''; $field .= ''; } elseif ( ! is_null( $for_country ) && is_array( $states ) ) { $data_label = ! empty( $args['label'] ) ? 'data-label="' . esc_attr( $args['label'] ) . '"' : ''; $field .= ''; } else { $field .= ''; } break; case 'textarea': $field .= ''; break; case 'checkbox': $field = ''; break; case 'text': case 'password': case 'datetime': case 'datetime-local': case 'date': case 'month': case 'time': case 'week': case 'number': case 'email': case 'url': case 'tel': $field .= ''; break; case 'hidden': $field .= ''; break; case 'select': $options = ''; if ( ! empty( $args['options'] ) ) { foreach ( $args['options'] as $option_key => $option_text ) { if ( '' === $option_key ) { // A blank option is the proper way to set a placeholder. If one is supplied we make sure the placeholder key is set for selectWoo. if ( empty( $args['placeholder'] ) ) { $args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' ); } $custom_attributes[] = 'data-allow_clear="true"'; } $options .= ''; } $field .= ''; } break; case 'radio': $label_id .= '_' . current( array_keys( $args['options'] ) ); if ( ! empty( $args['options'] ) ) { foreach ( $args['options'] as $option_key => $option_text ) { $field .= ''; $field .= ''; } } break; } if ( ! empty( $field ) ) { $field_html = ''; if ( $args['label'] && 'checkbox' !== $args['type'] ) { $field_html .= ''; } $field_html .= '' . $field; if ( $args['description'] ) { $field_html .= ''; } $field_html .= ''; $container_class = esc_attr( implode( ' ', $args['class'] ) ); $container_id = esc_attr( $args['id'] ) . '_field'; $field = sprintf( $field_container, $container_class, $container_id, $field_html ); } /** * Filter by type. */ $field = apply_filters( 'woocommerce_form_field_' . $args['type'], $field, $key, $args, $value ); /** * General filter on form fields. * * @since 3.4.0 */ $field = apply_filters( 'woocommerce_form_field', $field, $key, $args, $value ); if ( $args['return'] ) { return $field; } else { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $field; } } } if ( ! function_exists( 'get_product_search_form' ) ) { /** * Display product search form. * * Will first attempt to locate the product-searchform.php file in either the child or. * the parent, then load it. If it doesn't exist, then the default search form. * will be displayed. * * The default searchform uses html5. * * @param bool $echo (default: true). * @return string */ function get_product_search_form( $echo = true ) { global $product_search_form_index; ob_start(); if ( empty( $product_search_form_index ) ) { $product_search_form_index = 0; } do_action( 'pre_get_product_search_form' ); wc_get_template( 'product-searchform.php', array( 'index' => $product_search_form_index++, ) ); $form = apply_filters( 'get_product_search_form', ob_get_clean() ); if ( ! $echo ) { return $form; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $form; } } if ( ! function_exists( 'woocommerce_output_auth_header' ) ) { /** * Output the Auth header. */ function woocommerce_output_auth_header() { wc_get_template( 'auth/header.php' ); } } if ( ! function_exists( 'woocommerce_output_auth_footer' ) ) { /** * Output the Auth footer. */ function woocommerce_output_auth_footer() { wc_get_template( 'auth/footer.php' ); } } if ( ! function_exists( 'woocommerce_single_variation' ) ) { /** * Output placeholders for the single variation. */ function woocommerce_single_variation() { echo ''; } } if ( ! function_exists( 'woocommerce_single_variation_add_to_cart_button' ) ) { /** * Output the add to cart button for variations. */ function woocommerce_single_variation_add_to_cart_button() { wc_get_template( 'single-product/add-to-cart/variation-add-to-cart-button.php' ); } } if ( ! function_exists( 'wc_dropdown_variation_attribute_options' ) ) { /** * Output a list of variation attributes for use in the cart forms. * * @param array $args Arguments. * @since 2.4.0 */ function wc_dropdown_variation_attribute_options( $args = array() ) { $args = wp_parse_args( apply_filters( 'woocommerce_dropdown_variation_attribute_options_args', $args ), array( 'options' => false, 'attribute' => false, 'product' => false, 'selected' => false, 'required' => false, 'name' => '', 'aria-label' => false, 'id' => '', 'class' => '', 'show_option_none' => __( 'Choose an option', 'woocommerce' ), ) ); // Get selected value. if ( false === $args['selected'] && $args['attribute'] && $args['product'] instanceof WC_Product ) { $selected_key = 'attribute_' . sanitize_title( $args['attribute'] ); // phpcs:disable WordPress.Security.NonceVerification.Recommended $args['selected'] = isset( $_REQUEST[ $selected_key ] ) ? wc_clean( wp_unslash( $_REQUEST[ $selected_key ] ) ) : $args['product']->get_variation_default_attribute( $args['attribute'] ); // phpcs:enable WordPress.Security.NonceVerification.Recommended } $options = $args['options']; $product = $args['product']; $attribute = $args['attribute']; $name = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute ); $id = $args['id'] ? $args['id'] : sanitize_title( $attribute ); $class = $args['class']; $required = (bool) $args['required']; $show_option_none = (bool) $args['show_option_none']; $show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __( 'Choose an option', 'woocommerce' ); // We'll do our best to hide the placeholder, but we'll need to show something when resetting options. if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) { $attributes = $product->get_variation_attributes(); $options = $attributes[ $attribute ]; } $html = ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args ); } } if ( ! function_exists( 'woocommerce_account_content' ) ) { /** * My Account content output. */ function woocommerce_account_content() { global $wp; if ( ! empty( $wp->query_vars ) ) { foreach ( $wp->query_vars as $key => $value ) { // Ignore pagename param. if ( 'pagename' === $key ) { continue; } if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) { do_action( 'woocommerce_account_' . $key . '_endpoint', $value ); return; } } } // No endpoint found? Default to dashboard. wc_get_template( 'myaccount/dashboard.php', array( 'current_user' => get_user_by( 'id', get_current_user_id() ), ) ); } } if ( ! function_exists( 'woocommerce_account_navigation' ) ) { /** * My Account navigation template. */ function woocommerce_account_navigation() { wc_get_template( 'myaccount/navigation.php' ); } } if ( ! function_exists( 'woocommerce_account_orders' ) ) { /** * My Account > Orders template. * * @param int $current_page Current page number. */ function woocommerce_account_orders( $current_page ) { $current_page = empty( $current_page ) ? 1 : absint( $current_page ); $customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array( 'customer' => get_current_user_id(), 'page' => $current_page, 'paginate' => true, ) ) ); wc_get_template( 'myaccount/orders.php', array( 'current_page' => absint( $current_page ), 'customer_orders' => $customer_orders, 'has_orders' => 0 < $customer_orders->total, 'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '', ) ); } } if ( ! function_exists( 'woocommerce_account_view_order' ) ) { /** * My Account > View order template. * * @param int $order_id Order ID. */ function woocommerce_account_view_order( $order_id ) { WC_Shortcode_My_Account::view_order( absint( $order_id ) ); } } if ( ! function_exists( 'woocommerce_account_downloads' ) ) { /** * My Account > Downloads template. */ function woocommerce_account_downloads() { wc_get_template( 'myaccount/downloads.php' ); } } if ( ! function_exists( 'woocommerce_account_edit_address' ) ) { /** * My Account > Edit address template. * * @param string $type Type of address; 'billing' or 'shipping'. */ function woocommerce_account_edit_address( $type ) { $type = wc_edit_address_i18n( sanitize_title( $type ), true ); WC_Shortcode_My_Account::edit_address( $type ); } } if ( ! function_exists( 'woocommerce_account_payment_methods' ) ) { /** * My Account > Downloads template. */ function woocommerce_account_payment_methods() { wc_get_template( 'myaccount/payment-methods.php' ); } } if ( ! function_exists( 'woocommerce_account_add_payment_method' ) ) { /** * My Account > Add payment method template. */ function woocommerce_account_add_payment_method() { WC_Shortcode_My_Account::add_payment_method(); } } if ( ! function_exists( 'woocommerce_account_edit_account' ) ) { /** * My Account > Edit account template. */ function woocommerce_account_edit_account() { WC_Shortcode_My_Account::edit_account(); } } if ( ! function_exists( 'wc_no_products_found' ) ) { /** * Handles the loop when no products were found/no product exist. */ function wc_no_products_found() { if ( ! function_exists( 'wc_print_notice' ) ) { // wc_print_notice() is used in our default template, so this likely means this function was called out of // context. We include the notice functions here to avoid a fatal error. wc_doing_it_wrong( __FUNCTION__, 'Function should only be used during frontend requests.', '9.8.0' ); return; } wc_get_template( 'loop/no-products-found.php' ); } } if ( ! function_exists( 'wc_get_email_order_items' ) ) { /** * Get HTML for the order items to be shown in emails. * * @param WC_Order $order Order object. * @param array $args Arguments. * * @since 3.0.0 * @return string */ function wc_get_email_order_items( $order, $args = array() ) { ob_start(); $email_improvements_enabled = FeaturesUtil::feature_is_enabled( 'email_improvements' ); $image_size = $email_improvements_enabled ? 48 : 32; $defaults = array( 'show_sku' => false, 'show_image' => $email_improvements_enabled, 'image_size' => array( $image_size, $image_size ), 'plain_text' => false, 'sent_to_admin' => false, ); $args = wp_parse_args( $args, $defaults ); $template = $args['plain_text'] ? 'emails/plain/email-order-items.php' : 'emails/email-order-items.php'; wc_get_template( $template, apply_filters( 'woocommerce_email_order_items_args', array( 'order' => $order, 'items' => $order->get_items(), 'show_download_links' => $order->is_download_permitted() && ! $args['sent_to_admin'], 'show_sku' => $args['show_sku'], 'show_purchase_note' => $order->is_paid() && ! $args['sent_to_admin'], 'show_image' => $args['show_image'], 'image_size' => $args['image_size'], 'plain_text' => $args['plain_text'], 'sent_to_admin' => $args['sent_to_admin'], ) ) ); return apply_filters( 'woocommerce_email_order_items_table', ob_get_clean(), $order ); } } if ( ! function_exists( 'wc_get_email_fulfillment_items' ) ) { /** * Get HTML for the order items to be shown in emails. * * @param WC_Order $order Order object. * @param Fulfillment $fulfillment Fulfillment object. * @param array $args Arguments. * * @since 3.0.0 * @return string */ function wc_get_email_fulfillment_items( $order, $fulfillment, $args = array() ) { ob_start(); $email_improvements_enabled = FeaturesUtil::feature_is_enabled( 'email_improvements' ); $image_size = $email_improvements_enabled ? 48 : 32; $defaults = array( 'show_sku' => false, 'show_image' => $email_improvements_enabled, 'image_size' => array( $image_size, $image_size ), 'plain_text' => false, 'sent_to_admin' => false, ); $args = wp_parse_args( $args, $defaults ); $template = $args['plain_text'] ? 'emails/plain/email-fulfillment-items.php' : 'emails/email-fulfillment-items.php'; $fulfillment_items = $fulfillment->get_items(); if ( empty( $fulfillment_items ) ) { // If there are no fulfillment items, we return an empty string. return ''; } $order_items = $order->get_items(); if ( empty( $order_items ) ) { // If there are no order items, we return an empty string. return ''; } $order_items_filtered = array(); foreach ( $fulfillment_items as $fulfillment_item ) { // Filter order items to only include those that are part of the fulfillment. foreach ( $order_items as $order_item ) { if ( $order_item->get_id() === $fulfillment_item['item_id'] ) { if ( method_exists( $order_item, 'get_subtotal' ) && method_exists( $order_item, 'set_subtotal' ) && method_exists( $order_item, 'get_quantity' ) ) { $order_item->set_subtotal( $order_item->get_subtotal() * $fulfillment_item['qty'] / $order_item->get_quantity() ); } $order_items_filtered[] = (object) array( 'item_id' => $order_item->get_id(), 'qty' => $fulfillment_item['qty'], 'item' => $order_item, ); break; } } } wc_get_template( $template, /** * Filter to modify the arguments for the email fulfillment items. * * @since 10.1.0 * * @param array $args The arguments for the email fulfillment items. */ apply_filters( 'woocommerce_email_fulfillment_items_args', array( 'order' => $order, 'fulfillment' => $fulfillment, 'items' => $order_items_filtered, 'show_download_links' => $order->is_download_permitted() && ! $args['sent_to_admin'], 'show_sku' => $args['show_sku'], 'show_purchase_note' => $order->is_paid() && ! $args['sent_to_admin'], 'show_image' => $args['show_image'], 'image_size' => $args['image_size'], 'plain_text' => $args['plain_text'], 'sent_to_admin' => $args['sent_to_admin'], ) ) ); /** * Filter to modify the email fulfillment items table HTML. * * @since 10.1.0 * * @param string $html The HTML output of the fulfillment items table. * @param WC_Order $order The order object. * @param Fulfillment $fulfillment The fulfillment object. */ return apply_filters( 'woocommerce_get_email_fulfillment_items_table', ob_get_clean(), $order, $fulfillment ); } } if ( ! function_exists( 'wc_display_item_meta' ) ) { /** * Display item meta data. * * @since 3.0.0 * @param WC_Order_Item $item Order Item. * @param array $args Arguments. * @return string|void */ function wc_display_item_meta( $item, $args = array() ) { $strings = array(); $html = ''; $args = wp_parse_args( $args, array( 'before' => '', 'separator' => '
  • ', 'echo' => true, 'autop' => false, 'label_before' => '', 'label_after' => ': ', ) ); foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) { $value = $args['autop'] ? wp_kses_post( $meta->display_value ) : wp_kses_post( make_clickable( trim( $meta->display_value ) ) ); $strings[] = $args['label_before'] . wp_kses_post( $meta->display_key ) . $args['label_after'] . $value; } if ( $strings ) { $html = $args['before'] . implode( $args['separator'], $strings ) . $args['after']; } $html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args ); if ( $args['echo'] ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $html; } else { return $html; } } } if ( ! function_exists( 'wc_display_item_downloads' ) ) { /** * Display item download links. * * @since 3.0.0 * @param WC_Order_Item $item Order Item. * @param array $args Arguments. * @return string|void */ function wc_display_item_downloads( $item, $args = array() ) { $strings = array(); $html = ''; $args = wp_parse_args( $args, array( 'before' => '', 'separator' => '
  • ', 'echo' => true, 'show_url' => false, ) ); $downloads = is_object( $item ) && $item->is_type( 'line_item' ) ? $item->get_item_downloads() : array(); if ( ! empty( $downloads ) ) { $i = 0; foreach ( $downloads as $file ) { ++$i; if ( $args['show_url'] ) { $strings[] = '' . esc_html( $file['name'] ) . ': ' . esc_html( $file['download_url'] ); } else { /* translators: %d: downloads count */ $prefix = count( $downloads ) > 1 ? sprintf( __( 'Download %d', 'woocommerce' ), $i ) : __( 'Download', 'woocommerce' ); $strings[] = '' . $prefix . ': ' . esc_html( $file['name'] ) . ''; } } } if ( $strings ) { $html = $args['before'] . implode( $args['separator'], $strings ) . $args['after']; } $html = apply_filters( 'woocommerce_display_item_downloads', $html, $item, $args ); if ( $args['echo'] ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $html; } else { return $html; } } } if ( ! function_exists( 'woocommerce_photoswipe' ) ) { /** * Get the shop sidebar template. */ function woocommerce_photoswipe() { if ( current_theme_supports( 'wc-product-gallery-lightbox' ) ) { wc_get_template( 'single-product/photoswipe.php' ); } } } /** * Outputs a list of product attributes for a product. * * @since 3.0.0 * @param WC_Product $product Product Object. */ function wc_display_product_attributes( $product ) { $product_attributes = array(); // Display weight and dimensions before attribute list. $display_dimensions = apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() ); if ( $display_dimensions && $product->has_weight() ) { $product_attributes['weight'] = array( 'label' => __( 'Weight', 'woocommerce' ), 'value' => wc_format_weight( $product->get_weight() ), ); } if ( $display_dimensions && $product->has_dimensions() ) { $product_attributes['dimensions'] = array( 'label' => __( 'Dimensions', 'woocommerce' ), 'value' => wc_format_dimensions( $product->get_dimensions( false ) ), ); } // Add product attributes to list. $attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' ); foreach ( $attributes as $attribute ) { $values = array(); if ( $attribute->is_taxonomy() ) { $attribute_taxonomy = $attribute->get_taxonomy_object(); $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) ); foreach ( $attribute_values as $attribute_value ) { $value_name = esc_html( $attribute_value->name ); if ( $attribute_taxonomy->attribute_public ) { $values[] = ''; } else { $values[] = $value_name; } } } else { $values = $attribute->get_options(); foreach ( $values as &$value ) { $value = make_clickable( esc_html( $value ) ); } } $product_attributes[ 'attribute_' . sanitize_title_with_dashes( $attribute->get_name() ) ] = array( 'label' => wc_attribute_label( $attribute->get_name() ), 'value' => apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ), ); } /** * Hook: woocommerce_display_product_attributes. * * @since 3.6.0. * @param array $product_attributes Array of attributes to display; label, value. * @param WC_Product $product Showing attributes for this product. */ $product_attributes = apply_filters( 'woocommerce_display_product_attributes', $product_attributes, $product ); wc_get_template( 'single-product/product-attributes.php', array( 'product_attributes' => $product_attributes, // Legacy params. 'product' => $product, 'attributes' => $attributes, 'display_dimensions' => $display_dimensions, ) ); } /** * Get HTML to show product stock. * * @since 3.0.0 * @param WC_Product $product Product Object. * @return string */ function wc_get_stock_html( $product ) { $html = ''; $availability = $product->get_availability(); if ( ! empty( $availability['availability'] ) ) { ob_start(); wc_get_template( 'single-product/stock.php', array( 'product' => $product, 'class' => $availability['class'], 'availability' => $availability['availability'], ) ); $html = ob_get_clean(); } if ( has_filter( 'woocommerce_stock_html' ) ) { wc_deprecated_function( 'The woocommerce_stock_html filter', '', 'woocommerce_get_stock_html' ); $html = apply_filters( 'woocommerce_stock_html', $html, $availability['availability'], $product ); } return apply_filters( 'woocommerce_get_stock_html', $html, $product ); } /** * Get HTML for ratings. * * @since 3.0.0 * @param float $rating Rating being shown. * @param int $count Total number of ratings. * @return string */ function wc_get_rating_html( $rating, $count = 0 ) { $html = ''; if ( 0 < $rating ) { /* translators: %s: rating */ $label = sprintf( __( 'Rated %s out of 5', 'woocommerce' ), $rating ); $html = ''; } return apply_filters( 'woocommerce_product_get_rating_html', $html, $rating, $count ); } /** * Get HTML for star rating. * * @since 3.1.0 * @param float $rating Rating being shown. * @param int $count Total number of ratings. * @return string */ function wc_get_star_rating_html( $rating, $count = 0 ) { $html = ''; if ( 0 < $count ) { /* translators: 1: rating 2: rating count */ $html .= sprintf( _n( 'Rated %1$s out of 5 based on %2$s customer rating', 'Rated %1$s out of 5 based on %2$s customer ratings', $count, 'woocommerce' ), '' . esc_html( $rating ) . '', '' . esc_html( $count ) . '' ); } else { /* translators: %s: rating */ $html .= sprintf( esc_html__( 'Rated %s out of 5', 'woocommerce' ), '' . esc_html( $rating ) . '' ); } $html .= ''; return apply_filters( 'woocommerce_get_star_rating_html', $html, $rating, $count ); } /** * Returns a 'from' prefix if you want to show where prices start at. * * @since 3.0.0 * @return string */ function wc_get_price_html_from_text() { return apply_filters( 'woocommerce_get_price_html_from_text', '' . _x( 'From:', 'min_price', 'woocommerce' ) . ' ' ); } /** * Get the redirect URL after logging out. Defaults to the my account page. * * @since 9.3.0 * @return string */ function wc_get_logout_redirect_url() { /** * Filters the logout redirect URL. * * @since 2.6.9 * @param string $logout_url Logout URL. * @return string */ return apply_filters( 'woocommerce_logout_default_redirect_url', wc_get_page_permalink( 'myaccount' ) ); } /** * Get logout link. * * @since 2.6.9 * @param string $redirect Redirect URL. * @return string */ function wc_logout_url( $redirect = '' ) { return wp_logout_url( $redirect ? $redirect : wc_get_logout_redirect_url() ); } /** * Show notice if cart is empty. * * @since 3.1.0 */ function wc_empty_cart_message() { $notice = wc_print_notice( wp_kses_post( /** * Filter empty cart message text. * * @since 3.1.0 * @param string $message Default empty cart message. * @return string */ apply_filters( 'wc_empty_cart_message', __( 'Your cart is currently empty.', 'woocommerce' ) ) ), 'notice', array(), true ); // This adds the cart-empty classname to the notice to preserve backwards compatibility (for styling purposes etc). $notice = str_replace( 'class="woocommerce-info"', 'class="cart-empty woocommerce-info"', $notice ); // Return the notice within a consistent wrapper element. This is targeted by some scripts such as cart.js. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
    ' . $notice . '
    '; } /** * Disable search engines indexing core, dynamic, cart/checkout pages. * * @todo Deprecated this function after dropping support for WP 5.6. * @since 3.2.0 */ function wc_page_noindex() { // wp_no_robots is deprecated since WP 5.7. if ( function_exists( 'wp_robots_no_robots' ) ) { return; } if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) { wp_no_robots(); } } add_action( 'wp_head', 'wc_page_noindex' ); /** * Disable search engines indexing core, dynamic, cart/checkout pages. * Uses "wp_robots" filter introduced in WP 5.7. * * @since 5.0.0 * @param array $robots Associative array of robots directives. * @return array Filtered robots directives. */ function wc_page_no_robots( $robots ) { if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) { return wp_robots_no_robots( $robots ); } return $robots; } add_filter( 'wp_robots', 'wc_page_no_robots' ); /** * Get a slug identifying the current theme. * * @since 3.3.0 * @return string */ function wc_get_theme_slug_for_templates() { return apply_filters( 'woocommerce_theme_slug_for_templates', get_option( 'template' ) ); } /** * Gets and formats a list of cart item data + variations for display on the frontend. * * @since 3.3.0 * @param array $cart_item Cart item object. * @param bool $flat Should the data be returned flat or in a list. * @return string */ function wc_get_formatted_cart_item_data( $cart_item, $flat = false ) { $item_data = array(); // Variation values are shown only if they are not found in the title as of 3.0. // This is because variation titles display the attributes. if ( $cart_item['data']->is_type( ProductType::VARIATION ) && is_array( $cart_item['variation'] ) ) { foreach ( $cart_item['variation'] as $name => $value ) { $taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', urldecode( $name ) ) ); if ( taxonomy_exists( $taxonomy ) ) { // If this is a term slug, get the term's nice name. $term = get_term_by( 'slug', $value, $taxonomy ); if ( ! is_wp_error( $term ) && $term && $term->name ) { $value = $term->name; } $label = wc_attribute_label( $taxonomy ); } else { // If this is a custom option slug, get the options name. $value = apply_filters( 'woocommerce_variation_option_name', $value, null, $taxonomy, $cart_item['data'] ); $label = wc_attribute_label( str_replace( 'attribute_', '', $name ), $cart_item['data'] ); } // Check the nicename against the title. if ( '' === $value || wc_is_attribute_in_product_name( $value, $cart_item['data']->get_name() ) ) { continue; } $item_data[] = array( 'key' => $label, 'value' => $value, ); } } // Filter item data to allow 3rd parties to add more to the array. $item_data = apply_filters( 'woocommerce_get_item_data', $item_data, $cart_item ); if ( is_array( $item_data ) ) { // Format item data ready to display. foreach ( $item_data as $key => $data ) { // Set hidden to true to not display meta on cart. if ( ! empty( $data['hidden'] ) ) { unset( $item_data[ $key ] ); continue; } $item_data[ $key ]['key'] = ! empty( $data['key'] ) ? $data['key'] : $data['name']; $item_data[ $key ]['display'] = ! empty( $data['display'] ) ? $data['display'] : $data['value']; } // Output flat or in list format. if ( count( $item_data ) > 0 ) { ob_start(); if ( $flat ) { foreach ( $item_data as $data ) { echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['display'] ) . "\n"; } } else { wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) ); } return ob_get_clean(); } } return ''; } /** * Gets the url to remove an item from the cart. * * @since 3.3.0 * @param string $cart_item_key contains the id of the cart item. * @return string url to page */ function wc_get_cart_remove_url( $cart_item_key ) { $cart_page_url = wc_get_cart_url(); return apply_filters( 'woocommerce_get_remove_url', $cart_page_url ? wp_nonce_url( add_query_arg( 'remove_item', $cart_item_key, $cart_page_url ), 'woocommerce-cart' ) : '' ); } /** * Gets the url to re-add an item into the cart. * * @since 3.3.0 * @param string $cart_item_key Cart item key to undo. * @return string url to page */ function wc_get_cart_undo_url( $cart_item_key ) { $cart_page_url = wc_get_cart_url(); $query_args = array( 'undo_item' => $cart_item_key, ); return apply_filters( 'woocommerce_get_undo_url', $cart_page_url ? wp_nonce_url( add_query_arg( $query_args, $cart_page_url ), 'woocommerce-cart' ) : '', $cart_item_key ); } /** * Outputs all queued notices on WC pages. * * @since 3.5.0 */ function woocommerce_output_all_notices() { if ( ! function_exists( 'wc_print_notices' ) ) { wc_doing_it_wrong( __FUNCTION__, 'Function should only be used during frontend requests.', '9.8.0' ); return; } echo '
    '; wc_print_notices(); echo '
    '; } /** * Display pay buttons HTML. * * @since 3.9.0 */ function wc_get_pay_buttons() { $supported_gateways = array(); $available_gateways = WC()->payment_gateways()->get_available_payment_gateways(); foreach ( $available_gateways as $gateway ) { if ( $gateway->supports( PaymentGatewayFeature::PAY_BUTTON ) ) { $supported_gateways[] = $gateway->get_pay_button_id(); } } if ( ! $supported_gateways ) { return; } echo '
    '; foreach ( $supported_gateways as $pay_button_id ) { printf( '
    ', esc_attr( $pay_button_id ) ); } echo '
    '; } /** * Update the product archive title to the title of the shop page. Fallback to * 'Shop' if the shop page doesn't exist. * * @param string $post_type_name Post type 'name' label. * @param string $post_type Post type. * * @return string */ function wc_update_product_archive_title( $post_type_name, $post_type ) { if ( is_shop() && 'product' === $post_type ) { $shop_page_title = get_the_title( wc_get_page_id( 'shop' ) ); if ( $shop_page_title ) { return $shop_page_title; } return __( 'Shop', 'woocommerce' ); } return $post_type_name; } add_filter( 'post_type_archive_title', 'wc_update_product_archive_title', 10, 2 ); // phpcs:enable Generic.Commenting.Todo.TaskFound /** * Set the version of the hooked blocks in the database. Used when WC is installed for the first time. * * @since 9.2.0 * * @return void */ function wc_set_hooked_blocks_version() { // Only set the version if the current theme is a block theme. if ( ! wp_is_block_theme() && ! current_theme_supports( 'block-template-parts' ) ) { return; } $option_name = 'woocommerce_hooked_blocks_version'; if ( get_option( $option_name ) ) { return; } add_option( $option_name, WC()->stable_version() ); } /** * Attach functions that listen to theme switches. * * @since 9.7.0 * * @param string $old_name Old theme name. * @param \WP_Theme $old_theme Instance of the old theme. * @return void */ function wc_after_switch_theme( $old_name, $old_theme ) { wc_set_hooked_blocks_version_on_theme_switch( $old_name, $old_theme ); wc_update_store_notice_visible_on_theme_switch( $old_name, $old_theme ); } /** * Update the Store Notice visibility when switching themes: * - When switching from a classic theme to a block theme, disable the Store Notice. * - When switching from a block theme to a classic theme, re-enable the Store Notice * only if it was enabled last time there was a switchi from a classic theme to a block theme. * * @since 9.7.0 * * @param string $old_name Old theme name. * @param \WP_Theme $old_theme Instance of the old theme. * @return void */ function wc_update_store_notice_visible_on_theme_switch( $old_name, $old_theme ) { $enable_store_notice_in_classic_theme_option = 'woocommerce_enable_store_notice_in_classic_theme'; $is_store_notice_active_option = 'woocommerce_demo_store'; if ( ! $old_theme->is_block_theme() && wp_is_block_theme() ) { /* * When switching from a classic theme to a block theme, check if the store notice is active, * if it is, disable it but set an option to re-enable it when switching back to a classic theme. */ if ( is_store_notice_showing() ) { update_option( $is_store_notice_active_option, wc_bool_to_string( false ) ); add_option( $enable_store_notice_in_classic_theme_option, wc_bool_to_string( true ) ); } } elseif ( $old_theme->is_block_theme() && ! wp_is_block_theme() ) { /* * When switching from a block theme to a clasic theme, check if we have set the option to * re-enable the store notice. If so, re-enable it. */ $enable_store_notice_in_classic_theme = wc_string_to_bool( get_option( $enable_store_notice_in_classic_theme_option, 'no' ) ); if ( $enable_store_notice_in_classic_theme ) { update_option( $is_store_notice_active_option, wc_bool_to_string( true ) ); delete_option( $enable_store_notice_in_classic_theme_option ); } } } /** * If the user switches from a classic to a block theme and they haven't already got a woocommerce_hooked_blocks_version, * set the version of the hooked blocks in the database, or as "no" to disable all block hooks then set as the latest WC version. * * @since 9.2.0 * * @param string $old_name Old theme name. * @param \WP_Theme $old_theme Instance of the old theme. * @return void */ function wc_set_hooked_blocks_version_on_theme_switch( $old_name, $old_theme ) { $option_name = 'woocommerce_hooked_blocks_version'; $option_value = get_option( $option_name, false ); // Sites with the option value set to "no" have already been migrated, and block hooks have been disabled. Checking explicitly for false to avoid setting the option again. if ( ! $old_theme->is_block_theme() && ( wp_is_block_theme() || current_theme_supports( 'block-template-parts' ) ) && false === $option_value ) { add_option( $option_name, WC()->stable_version() ); } } /** * Add aria-label to pagination numbers. * * @param string $html HTML output. * @param array $args An array of arguments. See paginate_links() * for information on accepted arguments. * * @return string */ function wc_add_aria_label_to_pagination_numbers( $html, $args ) { $p = new WP_HTML_Tag_Processor( $html ); $n = 1; $page_text = __( 'Page', 'woocommerce' ); while ( $p->next_tag( array( 'class_name' => 'page-numbers' ) ) ) { if ( $p->has_class( 'prev' ) || $p->has_class( 'next' ) || ( 'SPAN' !== $p->get_tag() && 'A' !== $p->get_tag() ) ) { continue; } if ( $p->has_class( 'current' ) ) { $n = $args['current']; } if ( $p->has_class( 'dots' ) ) { if ( $args['current'] - $args['mid_size'] > $n ) { $n = $args['current'] - $args['mid_size'] - 1; } else { $n = $args['total'] - $args['end_size']; } ++$n; continue; } $p->set_attribute( 'aria-label', $page_text . ' ' . number_format_i18n( (int) $n ) ); ++$n; } $html = $p->get_updated_html(); return $html; } add_filter( 'paginate_links_output', 'wc_add_aria_label_to_pagination_numbers', 10, 2 ); /** * Get the quantity input args. * * Note, when autocomplete is enabled in firefox, it will overwrite actual value with what user entered last. So we default to off. * See @link https://github.com/woocommerce/woocommerce/issues/30733. * * @param array $args The arguments. * @param \WC_Product|null $product The product. * * @return array */ function wc_get_quantity_input_args( $args, $product = null ) { // phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment, WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle $defaults = array( 'input_id' => uniqid( 'quantity_' ), 'input_name' => 'quantity', 'classes' => apply_filters( 'woocommerce_quantity_input_classes', array( 'input-text', 'qty', 'text' ), $product ), 'pattern' => apply_filters( 'woocommerce_quantity_input_pattern', wc_is_stock_amount_integer() ? '[0-9]*' : '' ), 'inputmode' => apply_filters( 'woocommerce_quantity_input_inputmode', wc_is_stock_amount_integer() ? 'numeric' : 'decimal' ), 'placeholder' => apply_filters( 'woocommerce_quantity_input_placeholder', '', $product ), 'autocomplete' => apply_filters( 'woocommerce_quantity_input_autocomplete', 'off', $product ), 'readonly' => false, ); if ( $product ) { $defaults['min_value'] = $product->get_min_purchase_quantity(); $defaults['max_value'] = $product->get_max_purchase_quantity(); $defaults['step'] = $product->get_purchase_quantity_step(); $defaults['product_name'] = $product->get_title(); } else { $defaults['min_value'] = apply_filters( 'woocommerce_quantity_input_min', 1, $product ); $defaults['max_value'] = apply_filters( 'woocommerce_quantity_input_max', -1, $product ); $defaults['step'] = apply_filters( 'woocommerce_quantity_input_step', 1, $product ); $defaults['product_name'] = ''; } // phpcs:enable WooCommerce.Commenting.CommentHooks.MissingHookComment, WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle /** * Filters all quantity input args. * * @since 2.5.0 * @param array $args The arguments. * @param \WC_Product|null $product The product. * * @return array */ $args = apply_filters( 'woocommerce_quantity_input_args', wp_parse_args( $args, $defaults ), $product ); // Apply correction to min/max args - min cannot be lower than 0. $args['min_value'] = max( $args['min_value'], 0 ); $args['max_value'] = 0 < $args['max_value'] ? $args['max_value'] : ''; // Max cannot be lower than min if defined. if ( '' !== $args['max_value'] && $args['max_value'] < $args['min_value'] ) { $args['max_value'] = $args['min_value']; } // Default value should be the min value unless defined. $args['input_value'] = isset( $args['input_value'] ) ? $args['input_value'] : $defaults['min_value']; /** * The input type attribute will generally be 'number' unless the quantity cannot be changed, in which case * it will be set to 'hidden'. An exception is made for non-hidden readonly inputs: in this case we set the * type to 'text' (this prevents most browsers from rendering increment/decrement arrows, which are useless * and/or confusing in this context). */ $type = $args['min_value'] > 0 && $args['min_value'] === $args['max_value'] ? 'hidden' : 'number'; $type = $args['readonly'] && 'hidden' !== $type ? 'text' : $type; /** * Controls the quantity input's type attribute. * * @since 7.4.0 * * @param string $type A valid input type attribute value, usually 'number' or 'hidden'. */ $args['type'] = apply_filters( 'woocommerce_quantity_input_type', $type ); return $args; } Không tìm thấy trang - Green Pharma

    Không tìm thấy.