is_purchasable() && $this->is_in_stock() ) { $text = WC_Subscriptions_Product::get_add_to_cart_text(); } else { $text = parent::add_to_cart_text(); // translated "Read More" } return apply_filters( 'woocommerce_product_single_add_to_cart_text', $text, $this ); } /** * Returns the price in html format. * * @access public * @param string $price (default: '') * @return string */ public function get_price_html( $price = '' ) { $prices = $this->get_variation_prices( true ); $min_price_variation_id = $this->get_meta( '_min_price_variation_id' ); if ( empty( $min_price_variation_id ) ) { WC_Subscriptions_Product::variable_subscription_product_sync( $this ); $min_price_variation_id = $this->get_meta( '_min_price_variation_id' ); } if ( empty( $prices['price'] ) || ! $min_price_variation_id ) { return apply_filters( 'woocommerce_variable_empty_price_html', '', $this ); } $tax_display_mode = get_option( 'woocommerce_tax_display_shop' ); $price = WC_Subscriptions_Product::get_price( $min_price_variation_id ); $price = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $this, array( 'price' => $price ) ) : wcs_get_price_excluding_tax( $this, array( 'price' => $price ) ); $price = $this->get_price_prefix( $prices ) . wc_price( $price ) . $this->get_price_suffix(); $price = apply_filters( 'woocommerce_variable_price_html', $price, $this ); $price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) ); return apply_filters( 'woocommerce_variable_subscription_price_html', apply_filters( 'woocommerce_get_price_html', $price, $this ), $this ); } /** * Checks if the store manager has requested the current product be limited to one purchase * per customer, and if so, checks whether the customer already has an active subscription to * the product. * * @access public * @return bool */ function is_purchasable() { $purchasable = WCS_Limiter::is_purchasable( parent::is_purchasable(), $this ); return apply_filters( 'woocommerce_subscription_is_purchasable', $purchasable, $this ); } /** * Checks the product type to see if it is either this product's type or the parent's * product type. * * @access public * @param mixed $type Array or string of types * @return bool */ public function is_type( $type ) { if ( 'variable' == $type || ( is_array( $type ) && in_array( 'variable', $type ) ) ) { return true; } else { return parent::is_type( $type ); } } /** * Sort an associative array of $variation_id => $price pairs in order of min and max prices. * * @param array $prices Associative array of $variation_id => $price pairs * @return array */ protected function sort_variation_prices( $prices ) { // If we don't have any prices, there's nothing to sort. if ( empty( $prices ) ) { return $prices; } $prices_hash = md5( json_encode( $prices ) ); if ( empty( $this->sorted_variation_prices[ $prices_hash ] ) ) { $min_max_variation_data = $this->get_min_and_max_variation_data( array_keys( $prices ) ); $min_variation_id = $min_max_variation_data['min']['variation_id']; $max_variation_id = $min_max_variation_data['max']['variation_id']; // Reorder the variable price arrays to reflect the min and max values so that WooCommerce will find them in the correct order $min_price = $prices[ $min_variation_id ]; $max_price = $prices[ $max_variation_id ]; unset( $prices[ $min_variation_id ] ); unset( $prices[ $max_variation_id ] ); // Prepend the minimum variation and append the maximum variation $prices = array( $min_variation_id => $min_price ) + $prices; $prices += array( $max_variation_id => $max_price ); $this->sorted_variation_prices[ $prices_hash ] = $prices; } return $this->sorted_variation_prices[ $prices_hash ]; } /** * Set the product's min and max variation data. * * @param array $min_and_max_data The min and max variation data returned by @see wcs_get_min_max_variation_data(). Optional. * @param array $variation_ids The child variation IDs. Optional. By default this value be generated by @see WC_Product_Variable->get_visible_children(). * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0 */ public function set_min_and_max_variation_data( $min_and_max_data = array(), $variation_ids = array() ) { if ( empty( $variation_ids ) ) { $variation_ids = $this->get_visible_children(); } if ( empty( $min_and_max_data ) ) { $min_and_max_data = wcs_get_min_max_variation_data( $this, $variation_ids ); } $this->add_meta_data( '_min_max_variation_data', $min_and_max_data, true ); $this->add_meta_data( '_min_max_variation_ids_hash', $this->get_variation_ids_hash( $variation_ids ), true ); do_action( 'wc_subscriptions_min_and_max_variation_data_set', $this, $min_and_max_data, $variation_ids ); } /** * Get the min and max variation data. * * This is a wrapper for @see wcs_get_min_max_variation_data() but to avoid calling * that resource intensive function multiple times per request, check the value * stored in meta or cached in memory before calling that function. * * @param array $variation_ids An array of variation IDs. * @return array The variable product's min and max variation data. * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0 */ public function get_min_and_max_variation_data( $variation_ids ) { $variation_ids_hash = $this->get_variation_ids_hash( $variation_ids ); if ( $variation_ids_hash === $this->get_meta( '_min_max_variation_ids_hash', true ) ) { $min_and_max_variation_data = $this->get_meta( '_min_max_variation_data', true ); } elseif ( ! empty( $this->min_max_variation_data[ $variation_ids_hash ] ) ) { $min_and_max_variation_data = $this->min_max_variation_data[ $variation_ids_hash ]; } else { $min_and_max_variation_data = wcs_get_min_max_variation_data( $this, $variation_ids ); $this->min_max_variation_data[ $variation_ids_hash ] = $min_and_max_variation_data; } return $min_and_max_variation_data; } /** * Generate a unique hash from an array of variation IDs. * * @param array $variation_ids * @return string */ protected static function get_variation_ids_hash( $variation_ids ) { // Sort the variation IDs so the hash isn't different for the same array of IDs sort( $variation_ids ); return md5( json_encode( $variation_ids ) ); } /* Deprecated Functions */ /** * Return the sign-up fee for this product * * @return string */ public function get_sign_up_fee() { wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Product::get_sign_up_fee( $this )' ); return WC_Subscriptions_Product::get_sign_up_fee( $this ); } /** * Returns the sign up fee (including tax) by filtering the products price used in * @see WC_Product::get_price_including_tax( $qty ) * * @return string */ public function get_sign_up_fee_including_tax( $qty = 1 ) { wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_including_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' ); return wcs_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ), ) ); } /** * Returns the sign up fee (excluding tax) by filtering the products price used in * @see WC_Product::get_price_excluding_tax( $qty ) * * @return string */ public function get_sign_up_fee_excluding_tax( $qty = 1 ) { wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_excluding_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' ); return wcs_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ), ) ); } /** * Use WC core add-to-cart handlers for subscription products. * * @param string $handler The name of the handler to use when adding product to the cart * @param WC_Product $product */ public function add_to_cart_handler( $handler, $product ) { wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Cart::add_to_cart_handler( $handler, $product )' ); return WC_Subscriptions_Cart::add_to_cart_handler( $handler, $product ); } /** * Sync variable product prices with the children lowest/highest prices. * * @param int $product_id The ID of the product to sync. * * @return void */ public function variable_product_sync( $product_id = 0 ) { wcs_deprecated_function( __METHOD__, '2.2,0', 'WC_Subscriptions_Product::variable_subscription_product_sync( $this )' ); if ( empty( $product_id ) ) { $product_id = $this->get_id(); } // Sync prices with children self::sync( $product_id ); } /** * Get the suffix to display before prices. * * @return string */ protected function get_price_prefix( $prices ) { $child_variation_ids = array_keys( $prices['price'] ); $min_max_variation_data = $this->get_min_and_max_variation_data( $child_variation_ids ); // Are the subscription details of all variations identical? if ( $min_max_variation_data['identical'] ) { $prefix = ''; } else { $prefix = wcs_get_price_html_from_text( $this ); } return $prefix; } /** * Gets an array of available variations. * * @param string $return Optional. The format to return the results in. Can be 'array' to return an array of variation data or 'objects' for the product objects. Default 'array'. * @return array|WC_Product_Subscription_Variation[] */ public function get_available_variations( $return = 'array' ) { $available_variations = parent::get_available_variations( $return ); // Add the variation first payment date if data is being prepared for the add to cart form. if ( 'array' === $return ) { foreach ( $available_variations as $index => $variation_data ) { // Add the product's synced first payment date to the variation data if applicable. if ( isset( $variation_data['variation_id'] ) ) { $available_variations[ $index ]['first_payment_html'] = WC_Subscriptions_Synchroniser::get_products_first_payment_date( wc_get_product( $variation_data['variation_id'] ) ); } } } return $available_variations; } }