Products screen. add_filter( 'product_type_selector', array( __CLASS__, 'add_downloadable_and_virtual_filters' ) ); add_filter( 'request', array( __CLASS__, 'modify_downloadable_and_virtual_product_queries' ), 11 ); // Add subscription pricing fields on edit product page add_action( 'woocommerce_product_options_general_product_data', __CLASS__ . '::subscription_pricing_fields' ); // Add listener to clear our own transients when WooCommerce -> Clear Transients is // triggered from the admin panel add_action( 'woocommerce_page_wc-status', __CLASS__ . '::clear_subscriptions_transients' ); // Add subscription shipping options on edit product page add_action( 'woocommerce_product_options_shipping', __CLASS__ . '::subscription_shipping_fields' ); // And also on the variations section add_action( 'woocommerce_product_after_variable_attributes', __CLASS__ . '::variable_subscription_pricing_fields', 10, 3 ); // Add bulk edit actions for variable subscription products add_action( 'woocommerce_variable_product_bulk_edit_actions', __CLASS__ . '::variable_subscription_bulk_edit_actions', 10 ); // Save subscription meta when a subscription product is changed via bulk edit add_action( 'woocommerce_product_bulk_edit_save', __CLASS__ . '::bulk_edit_save_subscription_meta', 10 ); // Save subscription meta only when a subscription product is saved, can't run on the "'woocommerce_process_product_meta_' . $product_type" action because we need to override some WC defaults add_action( 'save_post', __CLASS__ . '::save_subscription_meta', 11 ); add_action( 'save_post', __CLASS__ . '::save_variable_subscription_meta', 11 ); // Save variable subscription meta add_action( 'woocommerce_process_product_meta_variable-subscription', __CLASS__ . '::process_product_meta_variable_subscription' ); add_action( 'woocommerce_save_product_variation', __CLASS__ . '::save_product_variation', 20, 2 ); add_action( 'woocommerce_subscription_pre_update_status', __CLASS__ . '::check_customer_is_set', 10, 3 ); add_action( 'product_variation_linked', __CLASS__ . '::set_variation_meta_defaults_on_bulk_add' ); add_filter( 'woocommerce_settings_tabs_array', __CLASS__ . '::add_subscription_settings_tab', 50 ); add_action( 'woocommerce_settings_subscriptions', __CLASS__ . '::subscription_settings_page' ); add_action( 'woocommerce_update_options_' . self::$tab_name, __CLASS__ . '::update_subscription_settings' ); add_filter( 'manage_users_columns', __CLASS__ . '::add_user_columns', 11, 1 ); add_filter( 'manage_users_custom_column', __CLASS__ . '::user_column_values', 11, 3 ); // Fire after default to avoid being broken by plugins #doing_it_wrong add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_styles_scripts' ); add_action( 'woocommerce_admin_field_informational', __CLASS__ . '::add_informational_admin_field' ); add_filter( 'posts_where', array( __CLASS__, 'filter_orders' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_orders_and_subscriptions_from_list' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_paid_subscription_orders_for_user' ) ); add_action( 'admin_notices', __CLASS__ . '::display_renewal_filter_notice' ); add_shortcode( 'subscriptions', __CLASS__ . '::do_subscriptions_shortcode' ); add_filter( 'set-screen-option', __CLASS__ . '::set_manage_subscriptions_screen_option', 10, 3 ); add_filter( 'woocommerce_payment_gateways_setting_columns', array( __CLASS__, 'payment_gateways_renewal_column' ) ); add_action( 'woocommerce_payment_gateways_setting_column_renewals', array( __CLASS__, 'payment_gateways_renewal_support' ) ); // Do not display formatted order total on the Edit Order administration screen add_filter( 'woocommerce_get_formatted_order_total', __CLASS__ . '::maybe_remove_formatted_order_total_filter', 0, 2 ); add_action( 'woocommerce_payment_gateways_settings', __CLASS__ . '::add_recurring_payment_gateway_information', 10 , 1 ); // Change text for when order items cannot be edited add_action( 'woocommerce_admin_order_totals_after_refunded', __CLASS__ . '::maybe_attach_gettext_callback', 10, 1 ); // Unhook gettext callback to prevent extra call impact add_action( 'woocommerce_order_item_add_action_buttons', __CLASS__ . '::maybe_unattach_gettext_callback', 10, 1 ); // Add a reminder on the enable guest checkout setting that subscriptions still require an account add_filter( 'woocommerce_payment_gateways_settings', array( __CLASS__, 'add_guest_checkout_setting_note' ), 10, 1 ); add_filter( 'woocommerce_account_settings', array( __CLASS__, 'add_guest_checkout_setting_note' ), 10, 1 ); } /** * Clear all transients data we have when the WooCommerce::Tools::Clear Transients action is * triggered. * * @since 2.1.1 * * @return null */ public static function clear_subscriptions_transients() { global $wpdb; if ( empty( $_GET['action'] ) || empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) ) { return; } if ( wc_clean( $_GET['action'] ) === 'clear_transients' ) { $transients_to_delete = array( 'wc_report_subscription_by_product', 'wc_report_subscription_by_customer', 'wc_report_subscription_events_by_date', 'wcs_report_subscription_by_product', 'wcs_report_subscription_by_customer', 'wcs_report_subscription_events_by_date', 'wcs_report_upcoming_recurring_revenue', ); // Get all related order and subscription ranges transients $results = $wpdb->get_col( "SELECT DISTINCT `option_name` FROM `$wpdb->options` WHERE `option_name` LIKE '%wcs-related-orders-to-%' OR `option_name` LIKE '%wcs-sub-ranges-%'" ); foreach ( $results as $column ) { $name = explode( 'transient_', $column, 2 ); $transients_to_delete[] = $name[1]; } foreach ( $transients_to_delete as $transient_to_delete ) { delete_transient( $transient_to_delete ); } } } /** * Add the 'subscriptions' product type to the WooCommerce product type select box. * * @param array Array of Product types & their labels, excluding the Subscription product type. * @return array Array of Product types & their labels, including the Subscription product type. * @since 1.0 */ public static function add_subscription_products_to_select( $product_types ) { $product_types['subscription'] = __( 'Simple subscription', 'woocommerce-subscriptions' ); $product_types['variable-subscription'] = __( 'Variable subscription', 'woocommerce-subscriptions' ); return $product_types; } /** * Add options for downloadable and virtual subscription products to the product type selector on the WooCommerce products screen. * * @param array $product_types * @return array * @since 2.5.1 */ public static function add_downloadable_and_virtual_filters( $product_types ) { global $typenow; if ( ! is_admin() || ! doing_action( 'restrict_manage_posts' ) || 'product' !== $typenow ) { return $product_types; } $product_options = array_reverse( array( 'downloadable_subscription' => ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Downloadable', 'woocommerce-subscriptions' ), 'virtual_subscription' => ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Virtual', 'woocommerce-subscriptions' ), ) ); foreach ( $product_options as $key => $label ) { $product_types = wcs_array_insert_after( 'subscription', $product_types, $key, $label ); } return $product_types; } /** * Modifies the main query on the WooCommerce products screen to correctly handle filtering by virtual and downloadable * product types. * * @param array $query_vars * @return array $query_vars * @since 2.5.1 */ public static function modify_downloadable_and_virtual_product_queries( $query_vars) { global $pagenow, $typenow; if ( ! is_admin() || 'edit.php' !== $pagenow || 'product' !== $typenow ) { return $query_vars; } $current_product_type = isset( $_REQUEST['product_type'] ) ? wc_clean( wp_unslash( $_REQUEST['product_type'] ) ) : false; if ( ! $current_product_type ) { return $query_vars; } if ( in_array( $current_product_type, array( 'downloadable', 'virtual' ) ) && ! isset( $query_vars['tax_query'] ) ) { // Do not include subscriptions when the default "Downloadable" or "Virtual" query for simple products is being executed. $query_vars['tax_query'] = array( array( 'taxonomy' => 'product_type', 'terms' => array( 'subscription' ), 'field' => 'slug', 'operator' => 'NOT IN', ), ); } elseif ( in_array( $current_product_type, array( 'downloadable_subscription', 'virtual_subscription' ) ) ) { // Limit query to subscription products when the "Downloadable" or "Virtual" choices under "Simple Subscription" are being used. $query_vars['meta_value'] = 'yes'; $query_vars['meta_key'] = '_' . str_replace( '_subscription', '', $current_product_type ); $query_vars['product_type'] = 'subscription'; } return $query_vars; } /** * Output the subscription specific pricing fields on the "Edit Product" admin page. * * @since 1.0 */ public static function subscription_pricing_fields() { global $post; $chosen_price = get_post_meta( $post->ID, '_subscription_price', true ); $chosen_interval = get_post_meta( $post->ID, '_subscription_period_interval', true ); $chosen_trial_length = WC_Subscriptions_Product::get_trial_length( $post->ID ); $chosen_trial_period = WC_Subscriptions_Product::get_trial_period( $post->ID ); $price_tooltip = __( 'Choose the subscription price, billing interval and period.', 'woocommerce-subscriptions' ); // translators: placeholder is trial period validation message if passed an invalid value (e.g. "Trial period can not exceed 4 weeks") $trial_tooltip = sprintf( _x( 'An optional period of time to wait before charging the first recurring payment. Any sign up fee will still be charged at the outset of the subscription. %s', 'Trial period field tooltip on Edit Product administration screen', 'woocommerce-subscriptions' ), self::get_trial_period_validation_message() ); // Set month as the default billing period if ( ! $chosen_period = get_post_meta( $post->ID, '_subscription_period', true ) ) { $chosen_period = 'month'; } echo ''; echo '