get_displayed_subtotal() ), $cart ); if ( $cart->get_subtotal_tax() > 0 ) { if ( $cart->display_prices_including_tax() && ! wc_prices_include_tax() ) { $subtotal_html .= ' ' . WC()->countries->inc_tax_or_vat() . ''; } elseif ( ! $cart->display_prices_including_tax() && wc_prices_include_tax() ) { $subtotal_html .= ' ' . WC()->countries->ex_tax_or_vat() . ''; } } echo wp_kses_post( $subtotal_html ); } /** * Get recurring shipping methods. */ function wcs_cart_totals_shipping_html() { $initial_packages = WC()->shipping->get_packages(); $show_package_details = count( WC()->cart->recurring_carts ) > 1; $show_package_name = true; $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', array() ); // Create new subscriptions for each subscription product in the cart (that is not a renewal) foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) { // This ensures we get the correct package IDs (these are filtered by WC_Subscriptions_Cart). WC_Subscriptions_Cart::set_calculation_type( 'recurring_total' ); WC_Subscriptions_Cart::set_recurring_cart_key( $recurring_cart_key ); WC_Subscriptions_Cart::set_cached_recurring_cart( $recurring_cart ); // Allow third parties to filter whether the recurring cart has a shipment. $cart_has_next_shipment = apply_filters( 'woocommerce_subscriptions_cart_has_next_shipment', 0 !== $recurring_cart->next_payment_date, $recurring_cart ); // Create shipping packages for each subscription item if ( $cart_has_next_shipment && WC_Subscriptions_Cart::cart_contains_subscriptions_needing_shipping( $recurring_cart ) ) { foreach ( $recurring_cart->get_shipping_packages() as $recurring_cart_package_key => $recurring_cart_package ) { $package_index = isset( $recurring_cart_package['package_index'] ) ? $recurring_cart_package['package_index'] : 0; $product_names = array(); $package = WC()->shipping->calculate_shipping_for_package( $recurring_cart_package ); if ( $show_package_details ) { foreach ( $package['contents'] as $item_id => $values ) { $product_names[] = $values['data']->get_title() . ' ×' . $values['quantity']; } $package_details = implode( ', ', $product_names ); } else { $package_details = ''; } $chosen_initial_method = isset( $chosen_shipping_methods[ $package_index ] ) ? $chosen_shipping_methods[ $package_index ] : ''; if ( isset( $chosen_shipping_methods[ $recurring_cart_package_key ] ) ) { $chosen_recurring_method = $chosen_shipping_methods[ $recurring_cart_package_key ]; } elseif ( in_array( $chosen_initial_method, $package['rates'], true ) ) { $chosen_recurring_method = $chosen_initial_method; } else { $chosen_recurring_method = empty( $package['rates'] ) ? '' : current( $package['rates'] )->id; } $shipping_selection_displayed = false; $only_one_shipping_option = count( $package['rates'] ) === 1; $recurring_rates_match_initial_rates = isset( $package['rates'][ $chosen_initial_method ] ) && isset( $initial_packages[ $package_index ] ) && $package['rates'] == $initial_packages[ $package_index ]['rates']; // phpcs:ignore WordPress.PHP.StrictComparisons if ( $only_one_shipping_option || ( $recurring_rates_match_initial_rates && apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart ) ) ) { $shipping_method = ( 1 === count( $package['rates'] ) ) ? current( $package['rates'] ) : $package['rates'][ $chosen_initial_method ]; // packages match, display shipping amounts only ?> label ) ); ?> ' . wp_kses_post( $package_details ) . '

'; ?> $package, 'available_methods' => $package['rates'], 'show_package_details' => $show_package_details, 'package_details' => $package_details, 'package_name' => $package_name, 'index' => $recurring_cart_package_key, 'chosen_method' => $chosen_recurring_method, 'recurring_cart_key' => $recurring_cart_key, 'recurring_cart' => $recurring_cart, ), '', WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'templates/' ) ); $show_package_name = false; } do_action( 'woocommerce_subscriptions_after_recurring_shipping_rates', $recurring_cart_package_key, $recurring_cart_package, $recurring_cart, $chosen_recurring_method, $shipping_selection_displayed ); } } WC_Subscriptions_Cart::set_calculation_type( 'none' ); WC_Subscriptions_Cart::set_recurring_cart_key( 'none' ); } } /** * Display a recurring shipping method's input element, either as a hidden element if there is only one shipping method, * or a radio select box when there is more than one available method. * * @param string $shipping_method_index * @param object $shipping_method * @param string $chosen_method * @param string $input_type */ function wcs_cart_print_shipping_input( $shipping_method_index, $shipping_method, $chosen_method = '', $input_type = 'hidden' ) { if ( 'radio' === $input_type ) { $checked = checked( $shipping_method->id, $chosen_method, false ); } else { // Make sure we only output safe input types $input_type = 'hidden'; $checked = ''; } printf( '', esc_attr( $input_type ), esc_attr( $shipping_method_index ), esc_attr( sanitize_title( $shipping_method->id ) ), esc_attr( $shipping_method->id ), esc_attr( $checked ) ); } /** * Prints a hidden element which indicates that the shipping method for a given recurring cart is inherited from the initial cart. * * In frontend scripts, this hidden element's data properties are used to link initial cart shipping method changes to the * corresponding hidden recurring cart shipping method input element. * * @param string $shipping_method_index The shipping method index for the recurring cart. eg 2023_08_11_monthly_0. */ function wcs_cart_print_inherit_shipping_flag( $shipping_method_index ) { // Split the string using the underscore character $parts = explode( '_', $shipping_method_index ); // Extract the recurring cart key and package index $recurring_cart_key = implode( '_', array_slice( $parts, 0, -1 ) ); $package_index = end( $parts ); printf( '', esc_attr( $shipping_method_index ), esc_attr( $package_index ), esc_attr( $recurring_cart_key ) ); } /** * Display a recurring shipping methods price & name as a label * * @param WC_Shipping_Rate $method The shipping method rate object. * @return string The recurring shipping method price html. */ function wcs_cart_totals_shipping_method( $method, $cart ) { // Backwards compatibility for third-parties who passed WC_Shipping_Method or std object types. if ( ! is_a( $method, 'WC_Shipping_Rate' ) ) { wcs_deprecated_argument( __METHOD__, '3.0.2', 'The $method param must be a WC_Shipping_Rate object.' ); $label = $method->label . ': ' . wcs_cart_totals_shipping_method_price_label( $method, $cart ); return apply_filters( 'wcs_cart_totals_shipping_method', $label, $method, $cart ); } $method_id = is_callable( array( $method, 'get_method_id' ) ) ? $method->get_method_id() : $method->method_id; // WC 3.2 compat. get_method_id() was introduced in 3.2.0. $label = $method->get_label(); $has_cost = 0 < $method->cost; $hide_cost = ! $has_cost && in_array( $method_id, array( 'free_shipping', 'local_pickup' ), true ); if ( $has_cost && ! $hide_cost ) { $label .= ': ' . wcs_cart_totals_shipping_method_price_label( $method, $cart ); } return apply_filters( 'wcs_cart_totals_shipping_method', $label, $method, $cart ); } /** * Display a recurring shipping methods price * * @param object $method * @return string */ function wcs_cart_totals_shipping_method_price_label( $method, $cart ) { $price_label = ''; if ( 0 < $method->cost ) { $display_prices_include_tax = wcs_is_woocommerce_pre( '3.3' ) ? ( 'incl' === WC()->cart->tax_display_cart ) : WC()->cart->display_prices_including_tax(); if ( ! $display_prices_include_tax ) { $price_label .= wcs_cart_price_string( $method->cost, $cart ); if ( $method->get_shipping_tax() > 0 && $cart->prices_include_tax ) { $price_label .= ' ' . WC()->countries->ex_tax_or_vat() . ''; } } else { $price_label .= wcs_cart_price_string( $method->cost + $method->get_shipping_tax(), $cart ); if ( $method->get_shipping_tax() > 0 && ! $cart->prices_include_tax ) { $price_label .= ' ' . WC()->countries->inc_tax_or_vat() . ''; } } } elseif ( ! empty( $cart->recurring_cart_key ) ) { $price_label .= _x( 'Free', 'shipping method price', 'woocommerce-subscriptions' ); } return apply_filters( 'wcs_cart_totals_shipping_method_price_label', $price_label, $method, $cart ); } /** * Display recurring taxes total * * @access public * @return void */ function wcs_cart_totals_taxes_total_html( $cart ) { $value = apply_filters( 'woocommerce_cart_totals_taxes_total_html', $cart->get_taxes_total() ); echo wp_kses_post( apply_filters( 'wcs_cart_totals_taxes_total_html', wcs_cart_price_string( $value, $cart ), $cart ) ); } /** * Display the remove link for a coupon. * * @access public * * @param WC_Coupon $coupon */ function wcs_cart_coupon_remove_link_html( $coupon ) { $html = '' . __( '[Remove]', 'woocommerce-subscriptions' ) . ''; echo wp_kses( $html, array_replace_recursive( wp_kses_allowed_html( 'post' ), array( 'a' => array( 'data-coupon' => true ) ) ) ); } /** * Display a recurring coupon's value. * * @see wc_cart_totals_coupon_html() * * @access public * * @param string|WC_Coupon $coupon * @param WC_Cart $cart */ function wcs_cart_totals_coupon_html( $coupon, $cart ) { if ( is_string( $coupon ) ) { $coupon = new WC_Coupon( $coupon ); } $value = array(); if ( $amount = $cart->get_coupon_discount_amount( wcs_get_coupon_property( $coupon, 'code' ), $cart->display_cart_ex_tax ) ) { $discount_html = '-' . wc_price( $amount ); } else { $discount_html = ''; } $value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon ); if ( wcs_get_coupon_property( $coupon, 'enable_free_shipping' ) ) { $value[] = __( 'Free shipping coupon', 'woocommerce-subscriptions' ); } // get rid of empty array elements $value = implode( ', ', array_filter( $value ) ); // Apply filters. $html = apply_filters( 'wcs_cart_totals_coupon_html', $value, $coupon, $cart ); $html = apply_filters( 'woocommerce_cart_totals_coupon_html', $html, $coupon, $discount_html ); echo wp_kses( $html, array_replace_recursive( wp_kses_allowed_html( 'post' ), array( 'a' => array( 'data-coupon' => true ) ) ) ); } /** * Gets recurring total html including inc tax if needed. * * @param WC_Cart $cart The cart to display the total for. */ function wcs_cart_totals_order_total_html( $cart ) { $order_total_html = '' . $cart->get_total() . ' '; $tax_total_html = ''; $display_prices_include_tax = wcs_is_woocommerce_pre( '3.3' ) ? ( 'incl' === $cart->tax_display_cart ) : $cart->display_prices_including_tax(); // If prices are tax inclusive, show taxes here if ( wc_tax_enabled() && $display_prices_include_tax ) { $tax_string_array = array(); $cart_taxes = $cart->get_tax_totals(); if ( get_option( 'woocommerce_tax_total_display' ) === 'itemized' ) { foreach ( $cart_taxes as $tax ) { $tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label ); } } elseif ( ! empty( $cart_taxes ) ) { $tax_string_array[] = sprintf( '%s %s', wc_price( $cart->get_taxes_total( true, true ) ), WC()->countries->tax_or_vat() ); } if ( ! empty( $tax_string_array ) ) { // translators: placeholder is price string, denotes tax included in cart/order total $tax_total_html = ' ' . sprintf( _x( '(includes %s)', 'includes tax', 'woocommerce-subscriptions' ), implode( ', ', $tax_string_array ) ) . ''; } } // Apply WooCommerce core filter $order_total_html = apply_filters( 'woocommerce_cart_totals_order_total_html', $order_total_html ); echo wp_kses_post( apply_filters( 'wcs_cart_totals_order_total_html', wcs_cart_price_string( $order_total_html, $cart ) . $tax_total_html, $cart ) ); } /** * Return a formatted price string for a given cart object * * @access public * @return string */ function wcs_cart_price_string( $recurring_amount, $cart ) { return wcs_price_string( apply_filters( 'woocommerce_cart_subscription_string_details', array( 'recurring_amount' => $recurring_amount, // Schedule details 'subscription_interval' => wcs_cart_pluck( $cart, 'subscription_period_interval' ), 'subscription_period' => wcs_cart_pluck( $cart, 'subscription_period', '' ), 'subscription_length' => wcs_cart_pluck( $cart, 'subscription_length' ), ), $cart ) ); } /** * Return a given piece of meta data from the cart * * The data can exist on the cart object, a cart item, or product data on a cart item. * The first piece of data with a matching key (in that order) will be returned if it * is found, otherwise, the value specified with $default, will be returned. * * @access public * @return string */ function wcs_cart_pluck( $cart, $field, $default = 0 ) { $value = $default; if ( isset( $cart->$field ) ) { $value = $cart->$field; } else { foreach ( $cart->get_cart() as $cart_item ) { if ( isset( $cart_item[ $field ] ) ) { $value = $cart_item[ $field ]; } else { $value = WC_Subscriptions_Product::get_meta_data( $cart_item['data'], $field, $default ); } } } return $value; } /** * Append the first renewal payment date to a string (which is the order total HTML string by default) * * @access public * @return string */ function wcs_add_cart_first_renewal_payment_date( $order_total_html, $cart ) { if ( 0 !== $cart->next_payment_date ) { $first_renewal_date = date_i18n( wc_date_format(), wcs_date_to_time( get_date_from_gmt( $cart->next_payment_date ) ) ); // translators: placeholder is a date $order_total_html .= '
' . sprintf( __( 'First renewal: %s', 'woocommerce-subscriptions' ), $first_renewal_date ) . '
'; } return $order_total_html; } add_filter( 'wcs_cart_totals_order_total_html', 'wcs_add_cart_first_renewal_payment_date', 10, 2 ); /** * Return the cart item name for specific cart item * * @access public * @return string */ function wcs_get_cart_item_name( $cart_item, $include = array() ) { $include = wp_parse_args( $include, array( 'attributes' => false, ) ); $cart_item_name = $cart_item['data']->get_title(); if ( $include['attributes'] ) { $attributes_string = WC()->cart->get_item_data( $cart_item, true ); $attributes_string = implode( ', ', array_filter( explode( "\n", $attributes_string ) ) ); if ( ! empty( $attributes_string ) ) { $cart_item_name = sprintf( '%s (%s)', $cart_item_name, $attributes_string ); } } return $cart_item_name; } /** * Allows protected products to be renewed. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0 */ function wcs_allow_protected_products_to_renew() { remove_filter( 'woocommerce_add_to_cart_validation', 'wc_protected_product_add_to_cart' ); } /** * Restores protected products from being added to the cart. * @see wcs_allow_protected_products_to_renew * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0 */ function wcs_disallow_protected_product_add_to_cart_validation() { add_filter( 'woocommerce_add_to_cart_validation', 'wc_protected_product_add_to_cart', 10, 2 ); } /** * Gets all the cart items linked to a given subscription order type. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.1.0 * * @param string $order_type The order type to get cart items for. Can be 'parent', 'renewal', 'resubscribe', 'switch'. * @return array[] An array of cart items which are linked to an order or subscription by the order type relationship. */ function wcs_get_order_type_cart_items( $order_type ) { $cart_items = array(); if ( ! in_array( $order_type, array( 'parent', 'renewal', 'resubscribe', 'switch' ) ) ) { wcs_doing_it_wrong( __METHOD__, 'The parameter must be a valid subscription order type "parent", "renewal", "resubscribe", "switch".', '3.1.0' ); return $cart_items; } if ( empty( WC()->cart->cart_contents ) ) { return $cart_items; } $order_type_cart_key = 'parent' === $order_type ? 'subscription_initial_payment' : "subscription_$order_type"; foreach ( WC()->cart->cart_contents as $cart_item_key => $cart_item ) { if ( isset( $cart_item[ $order_type_cart_key ] ) ) { $cart_items[ $cart_item_key ] = $cart_item; } } return $cart_items; }