= 7.1. * * @var array */ private static $post__in_none = array( 0 ); /** * Constructor */ public function __construct() { // Subscription list table columns and their content add_filter( 'manage_edit-shop_subscription_columns', array( $this, 'shop_subscription_columns' ) ); add_filter( 'manage_edit-shop_subscription_sortable_columns', array( $this, 'shop_subscription_sortable_columns' ) ); add_action( 'manage_shop_subscription_posts_custom_column', array( $this, 'render_shop_subscription_columns' ), 2 ); // Bulk actions add_filter( 'bulk_actions-edit-shop_subscription', array( $this, 'remove_bulk_actions' ) ); add_action( 'admin_print_footer_scripts', array( $this, 'print_bulk_actions_script' ) ); add_action( 'load-edit.php', array( $this, 'parse_bulk_actions' ) ); add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) ); // Subscription order/filter add_filter( 'request', array( $this, 'request_query' ) ); // Subscription Search add_filter( 'get_search_query', array( $this, 'shop_subscription_search_label' ) ); add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) ); add_action( 'parse_query', array( $this, 'shop_subscription_search_custom_fields' ) ); add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) ); add_action( 'restrict_manage_posts', array( $this, 'restrict_by_product' ) ); add_action( 'restrict_manage_posts', array( $this, 'restrict_by_payment_method' ) ); add_action( 'restrict_manage_posts', array( $this, 'restrict_by_customer' ) ); add_action( 'list_table_primary_column', array( $this, 'list_table_primary_column' ), 10, 2 ); add_filter( 'post_row_actions', array( $this, 'shop_subscription_row_actions' ), 10, 2 ); } /** * Modifies the actual SQL that is needed to order by last payment date on subscriptions. Data is pulled from related * but independent posts, so subqueries are needed. That's something we can't get by filtering the request. This is hooked * in @see WCS_Admin_Post_Types::request_query function. * * @param array $pieces all the pieces of the resulting SQL once WordPress has finished parsing it * @param WP_Query $query the query object that forms the basis of the SQL * @return array modified pieces of the SQL query */ public function posts_clauses( $pieces, $query ) { global $wpdb; if ( ! is_admin() || ! isset( $query->query['post_type'] ) || 'shop_subscription' !== $query->query['post_type'] ) { return $pieces; } // Let's check whether we even have the privileges to do the things we want to do if ( $this->is_db_user_privileged() ) { $pieces = self::posts_clauses_high_performance( $pieces ); } else { $pieces = self::posts_clauses_low_performance( $pieces ); } $order = strtoupper( $query->query['order'] ); // fields and order are identical in both cases $pieces['fields'] .= ', COALESCE(lp.last_payment, o.post_date_gmt, 0) as lp'; $pieces['orderby'] = "CAST(lp AS DATETIME) {$order}"; return $pieces; } /** * Check is database user is capable of doing high performance things, such as creating temporary tables, * indexing them, and then dropping them after. * * @return bool */ public function is_db_user_privileged() { $permissions = $this->get_special_database_privileges(); return ( in_array( 'CREATE TEMPORARY TABLES', $permissions ) && in_array( 'INDEX', $permissions ) && in_array( 'DROP', $permissions ) ); } /** * Return the privileges a database user has out of CREATE TEMPORARY TABLES, INDEX and DROP. This is so we can use * these discrete values on a debug page. * * @return array */ public function get_special_database_privileges() { global $wpdb; $permissions = $wpdb->get_col( "SELECT PRIVILEGE_TYPE FROM information_schema.user_privileges WHERE GRANTEE = CONCAT( '''', REPLACE( CURRENT_USER(), '@', '''@''' ), '''' ) AND PRIVILEGE_TYPE IN ('CREATE TEMPORARY TABLES', 'INDEX', 'DROP')" ); return $permissions; } /** * Modifies the query for a slightly faster, yet still pretty slow query in case the user does not have * the necessary privileges to run * * @param $pieces * * @return mixed */ private function posts_clauses_low_performance( $pieces ) { global $wpdb; $pieces['join'] .= "LEFT JOIN (SELECT MAX( p.post_date_gmt ) as last_payment, pm.meta_value FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '_subscription_renewal' GROUP BY pm.meta_value) lp ON {$wpdb->posts}.ID = lp.meta_value LEFT JOIN {$wpdb->posts} o on {$wpdb->posts}.post_parent = o.ID"; return $pieces; } /** * Modifies the query in such a way that makes use of the CREATE TEMPORARY TABLE, DROP and INDEX * MySQL privileges. * * @param array $pieces * * @return array $pieces */ private function posts_clauses_high_performance( $pieces ) { global $wpdb; // in case multiple users sort at the same time $session = wp_get_session_token(); $table_name = substr( "{$wpdb->prefix}tmp_{$session}_lastpayment", 0, 64 ); // Let's create a temporary table, drop the previous one, because otherwise this query is hella slow $wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name}" ); $wpdb->query( "CREATE TEMPORARY TABLE {$table_name} (id INT, INDEX USING BTREE (id), last_payment DATETIME) AS SELECT pm.meta_value as id, MAX( p.post_date_gmt ) as last_payment FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '_subscription_renewal' GROUP BY pm.meta_value" ); // Magic ends here $pieces['join'] .= "LEFT JOIN {$table_name} lp ON {$wpdb->posts}.ID = lp.id LEFT JOIN {$wpdb->posts} o on {$wpdb->posts}.post_parent = o.ID"; return $pieces; } /** * Displays the dropdown for the product filter * @return string the html dropdown element */ public function restrict_by_product() { global $typenow; if ( 'shop_subscription' !== $typenow ) { return; } $product_id = ''; $product_string = ''; if ( ! empty( $_GET['_wcs_product'] ) ) { $product_id = absint( $_GET['_wcs_product'] ); $product_string = wc_get_product( $product_id )->get_formatted_name(); } WCS_Select2::render( array( 'class' => 'wc-product-search', 'name' => '_wcs_product', 'placeholder' => esc_attr__( 'Search for a product…', 'woocommerce-subscriptions' ), 'action' => 'woocommerce_json_search_products_and_variations', 'selected' => strip_tags( $product_string ), 'value' => $product_id, 'allow_clear' => 'true', ) ); } /** * Remove "edit" from the bulk actions. * * @param array $actions * @return array */ public function remove_bulk_actions( $actions ) { if ( isset( $actions['edit'] ) ) { unset( $actions['edit'] ); } return $actions; } /** * Add extra options to the bulk actions dropdown * * It's only on the All Shop Subscriptions screen. * Introducing new filter: woocommerce_subscription_bulk_actions. This has to be done through jQuery as the * 'bulk_actions' filter that WordPress has can only be used to remove bulk actions, not to add them. * * This is a filterable array where the key is the action (will become query arg), and the value is a translatable * string. The same array is used to * */ public function print_bulk_actions_script() { $post_status = ( isset( $_GET['post_status'] ) ) ? $_GET['post_status'] : ''; if ( 'shop_subscription' !== get_post_type() || in_array( $post_status, array( 'cancelled', 'trash', 'wc-expired' ) ) ) { return; } // Make it filterable in case extensions want to change this $bulk_actions = apply_filters( 'woocommerce_subscription_bulk_actions', array( 'active' => _x( 'Activate', 'an action on a subscription', 'woocommerce-subscriptions' ), 'on-hold' => _x( 'Put on-hold', 'an action on a subscription', 'woocommerce-subscriptions' ), 'cancelled' => _x( 'Cancel', 'an action on a subscription', 'woocommerce-subscriptions' ), ) ); // No need to display certain bulk actions if we know all the subscriptions on the page have that status already switch ( $post_status ) { case 'wc-active' : unset( $bulk_actions['active'] ); break; case 'wc-on-hold' : unset( $bulk_actions['on-hold'] ); break; } ?> 'shop_subscription', $report_action => true, 'ids' => join( ',', $subscription_ids ), 'error_count' => 0, ); foreach ( $subscription_ids as $subscription_id ) { $subscription = wcs_get_subscription( $subscription_id ); $order_note = _x( 'Subscription status changed by bulk edit:', 'Used in order note. Reason why status changed.', 'woocommerce-subscriptions' ); try { if ( 'cancelled' == $action ) { $subscription->cancel_order( $order_note ); } else { $subscription->update_status( $new_status, $order_note, true ); } // Fire the action hooks switch ( $action ) { case 'active' : case 'on-hold' : case 'cancelled' : case 'trash' : do_action( 'woocommerce_admin_changed_subscription_to_' . $action, $subscription_id ); break; } $changed++; } catch ( Exception $e ) { $sendback_args['error'] = urlencode( $e->getMessage() ); $sendback_args['error_count']++; } } $sendback_args['changed'] = $changed; $sendback = add_query_arg( $sendback_args, wp_get_referer() ? wp_get_referer() : '' ); wp_safe_redirect( esc_url_raw( $sendback ) ); exit(); } /** * Show confirmation message that subscription status was changed */ public function bulk_admin_notices() { global $post_type, $pagenow; // Bail out if not on shop order list page if ( 'edit.php' !== $pagenow || 'shop_subscription' !== $post_type ) { return; } $subscription_statuses = wcs_get_subscription_statuses(); // Check if any status changes happened foreach ( $subscription_statuses as $slug => $name ) { if ( isset( $_REQUEST[ 'marked_' . str_replace( 'wc-', '', $slug ) ] ) ) { $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; // translators: placeholder is the number of subscriptions updated $message = sprintf( _n( '%s subscription status changed.', '%s subscription statuses changed.', $number, 'woocommerce-subscriptions' ), number_format_i18n( $number ) ); echo '

' . esc_html( $message ) . '

'; if ( ! empty( $_REQUEST['error_count'] ) ) { $error_msg = isset( $_REQUEST['error'] ) ? stripslashes( $_REQUEST['error'] ) : ''; $error_count = isset( $_REQUEST['error_count'] ) ? absint( $_REQUEST['error_count'] ) : 0; // translators: 1$: is the number of subscriptions not updated, 2$: is the error message $message = sprintf( _n( '%1$s subscription could not be updated: %2$s', '%1$s subscriptions could not be updated: %2$s', $error_count, 'woocommerce-subscriptions' ), number_format_i18n( $error_count ), $error_msg ); echo '

' . esc_html( $message ) . '

'; } $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'error_count', 'marked_active' ), $_SERVER['REQUEST_URI'] ); break; } } } /** * Define custom columns for subscription * * Column names that have a corresponding `WC_Order` column use the `order_` prefix here * to take advantage of core WooCommerce assets, like JS/CSS. * * @param array $existing_columns * @return array */ public function shop_subscription_columns( $existing_columns ) { $columns = array( 'cb' => '', 'status' => __( 'Status', 'woocommerce-subscriptions' ), 'order_title' => __( 'Subscription', 'woocommerce-subscriptions' ), 'order_items' => __( 'Items', 'woocommerce-subscriptions' ), 'recurring_total' => __( 'Total', 'woocommerce-subscriptions' ), 'start_date' => __( 'Start Date', 'woocommerce-subscriptions' ), 'trial_end_date' => __( 'Trial End', 'woocommerce-subscriptions' ), 'next_payment_date' => __( 'Next Payment', 'woocommerce-subscriptions' ), 'last_payment_date' => __( 'Last Order Date', 'woocommerce-subscriptions' ), // Keep deprecated 'last_payment_date' key for backward compatibility 'end_date' => __( 'End Date', 'woocommerce-subscriptions' ), 'orders' => _x( 'Orders', 'number of orders linked to a subscription', 'woocommerce-subscriptions' ), ); return $columns; } /** * Output custom columns for subscriptions * @param string $column */ public function render_shop_subscription_columns( $column ) { global $post, $the_subscription, $wp_list_table; if ( empty( $the_subscription ) || $the_subscription->get_id() != $post->ID ) { $the_subscription = wcs_get_subscription( $post->ID ); } $column_content = ''; switch ( $column ) { case 'status' : // The status label $column_content = sprintf( '%s', sanitize_title( $the_subscription->get_status() ), wcs_get_subscription_status_name( $the_subscription->get_status() ), wcs_get_subscription_status_name( $the_subscription->get_status() ) ); $post_type_object = get_post_type_object( $post->post_type ); $actions = array(); $action_url = add_query_arg( array( 'post' => $the_subscription->get_id(), '_wpnonce' => wp_create_nonce( 'bulk-posts' ), ) ); if ( isset( $_REQUEST['status'] ) ) { $action_url = add_query_arg( array( 'status' => $_REQUEST['status'] ), $action_url ); } $all_statuses = array( 'active' => __( 'Reactivate', 'woocommerce-subscriptions' ), 'on-hold' => __( 'Suspend', 'woocommerce-subscriptions' ), 'cancelled' => _x( 'Cancel', 'an action on a subscription', 'woocommerce-subscriptions' ), 'trash' => __( 'Trash', 'woocommerce-subscriptions' ), 'deleted' => __( 'Delete Permanently', 'woocommerce-subscriptions' ), ); foreach ( $all_statuses as $status => $label ) { if ( $the_subscription->can_be_updated_to( $status ) ) { if ( in_array( $status, array( 'trash', 'deleted' ) ) ) { if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) { if ( 'trash' == $post->post_status ) { $actions['untrash'] = '' . __( 'Restore', 'woocommerce-subscriptions' ) . ''; } elseif ( EMPTY_TRASH_DAYS ) { $actions['trash'] = '' . __( 'Trash', 'woocommerce-subscriptions' ) . ''; } if ( 'trash' == $post->post_status || ! EMPTY_TRASH_DAYS ) { $actions['delete'] = '' . __( 'Delete Permanently', 'woocommerce-subscriptions' ) . ''; } } } else { if ( 'pending-cancel' === $the_subscription->get_status() ) { $label = __( 'Cancel Now', 'woocommerce-subscriptions' ); } $actions[ $status ] = sprintf( '%s', add_query_arg( 'action', $status, $action_url ), $label ); } } } if ( 'pending' === $the_subscription->get_status() ) { unset( $actions['active'] ); unset( $actions['trash'] ); } elseif ( ! in_array( $the_subscription->get_status(), array( 'cancelled', 'pending-cancel', 'expired', 'switched', 'suspended' ) ) ) { unset( $actions['trash'] ); } $actions = apply_filters( 'woocommerce_subscription_list_table_actions', $actions, $the_subscription ); $column_content .= $wp_list_table->row_actions( $actions ); $column_content = apply_filters( 'woocommerce_subscription_list_table_column_status_content', $column_content, $the_subscription, $actions ); break; case 'order_title' : $customer_tip = ''; if ( $address = $the_subscription->get_formatted_billing_address() ) { $customer_tip .= _x( 'Billing:', 'meaning billing address', 'woocommerce-subscriptions' ) . ' ' . esc_html( $address ); } if ( $the_subscription->get_billing_email() ) { // translators: placeholder is customer's billing email $customer_tip .= '

' . sprintf( __( 'Email: %s', 'woocommerce-subscriptions' ), esc_attr( $the_subscription->get_billing_email() ) ); } if ( $the_subscription->get_billing_phone() ) { // translators: placeholder is customer's billing phone number $customer_tip .= '

' . sprintf( __( 'Tel: %s', 'woocommerce-subscriptions' ), esc_html( $the_subscription->get_billing_phone() ) ); } if ( ! empty( $customer_tip ) ) { echo '
'; } // This is to stop PHP from complaining $username = ''; if ( $the_subscription->get_user_id() && ( false !== ( $user_info = get_userdata( $the_subscription->get_user_id() ) ) ) ) { $username = ''; if ( $the_subscription->get_billing_first_name() || $the_subscription->get_billing_last_name() ) { $username .= esc_html( ucfirst( $the_subscription->get_billing_first_name() ) . ' ' . ucfirst( $the_subscription->get_billing_last_name() ) ); } elseif ( $user_info->first_name || $user_info->last_name ) { $username .= esc_html( ucfirst( $user_info->first_name ) . ' ' . ucfirst( $user_info->last_name ) ); } else { $username .= esc_html( ucfirst( $user_info->display_name ) ); } $username .= ''; } elseif ( $the_subscription->get_billing_first_name() || $the_subscription->get_billing_last_name() ) { $username = trim( $the_subscription->get_billing_first_name() . ' ' . $the_subscription->get_billing_last_name() ); } // translators: $1: is opening link, $2: is subscription order number, $3: is closing link tag, $4: is user's name $column_content = sprintf( _x( '%1$s#%2$s%3$s for %4$s', 'Subscription title on admin table. (e.g.: #211 for John Doe)', 'woocommerce-subscriptions' ), '', '' . esc_attr( $the_subscription->get_order_number() ) . '', '', $username ); $column_content .= '
'; $column_content .= ''; break; case 'order_items' : // Display either the item name or item count with a collapsed list of items $subscription_items = $the_subscription->get_items(); switch ( count( $subscription_items ) ) { case 0 : $column_content .= '–'; break; case 1 : foreach ( $subscription_items as $item ) { $column_content .= self::get_item_display( $item, $the_subscription ); } break; default : $column_content .= '' . esc_html( apply_filters( 'woocommerce_admin_order_item_count', sprintf( _n( '%d item', '%d items', $the_subscription->get_item_count(), 'woocommerce-subscriptions' ), $the_subscription->get_item_count() ), $the_subscription ) ) . ''; $column_content .= ''; foreach ( $subscription_items as $item ) { $column_content .= self::get_item_display( $item, $the_subscription, 'row' ); } $column_content .= '
'; break; } break; case 'recurring_total' : $column_content .= esc_html( strip_tags( $the_subscription->get_formatted_order_total() ) ); // translators: placeholder is the display name of a payment gateway a subscription was paid by $column_content .= '' . esc_html( sprintf( __( 'Via %s', 'woocommerce-subscriptions' ), $the_subscription->get_payment_method_to_display() ) ) . ''; break; case 'start_date': case 'trial_end_date': case 'next_payment_date': case 'last_payment_date': case 'end_date': $column_content = self::get_date_column_content( $the_subscription, $column ); break; case 'orders' : $column_content .= $this->get_related_orders_link( $the_subscription ); break; } echo wp_kses( apply_filters( 'woocommerce_subscription_list_table_column_content', $column_content, $the_subscription, $column ), array( 'a' => array( 'class' => array(), 'href' => array(), 'data-tip' => array(), 'title' => array() ), 'time' => array( 'class' => array(), 'title' => array() ), 'mark' => array( 'class' => array(), 'data-tip' => array() ), 'small' => array( 'class' => array() ), 'table' => array( 'class' => array(), 'cellspacing' => array(), 'cellpadding' => array() ), 'tr' => array( 'class' => array() ), 'td' => array( 'class' => array() ), 'div' => array( 'class' => array(), 'data-tip' => array() ), 'br' => array(), 'strong' => array(), 'span' => array( 'class' => array(), 'data-tip' => array() ), 'p' => array( 'class' => array() ), 'button' => array( 'type' => array(), 'class' => array() ) ) ); } /** * Return the content for a date column on the Edit Subscription screen * * @param WC_Subscription $subscription * @param string $column * @return string * @since 2.3.0 */ public static function get_date_column_content( $subscription, $column ) { $date_type_map = array( 'start_date' => 'date_created', 'last_payment_date' => 'last_order_date_created' ); $date_type = array_key_exists( $column, $date_type_map ) ? $date_type_map[ $column ] : $column; if ( 0 == $subscription->get_time( $date_type, 'gmt' ) ) { $column_content = '-'; } else { $column_content = sprintf( '', esc_attr( $column ), esc_attr( date( __( 'Y/m/d g:i:s A', 'woocommerce-subscriptions' ) , $subscription->get_time( $date_type, 'site' ) ) ), esc_html( $subscription->get_date_to_display( $date_type ) ) ); if ( 'next_payment_date' == $column && $subscription->payment_method_supports( 'gateway_scheduled_payments' ) && ! $subscription->is_manual() && $subscription->has_status( 'active' ) ) { $column_content .= '
'; } } return $column_content; } /** * Make columns sortable * * @param array $columns * @return array */ public function shop_subscription_sortable_columns( $columns ) { $sortable_columns = array( 'order_title' => 'ID', 'recurring_total' => 'order_total', 'start_date' => 'date', 'trial_end_date' => 'trial_end_date', 'next_payment_date' => 'next_payment_date', 'last_payment_date' => 'last_payment_date', 'end_date' => 'end_date', ); return wp_parse_args( $sortable_columns, $columns ); } /** * Search custom fields as well as content. * * @access public * @param WP_Query $wp * @return void */ public function shop_subscription_search_custom_fields( $wp ) { global $pagenow, $wpdb; if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_subscription' !== $wp->query_vars['post_type'] ) { return; } $post_ids = wcs_subscription_search( $_GET['s'] ); if ( ! empty( $post_ids ) ) { // Remove s - we don't want to search order name unset( $wp->query_vars['s'] ); // so we know we're doing this $wp->query_vars['shop_subscription_search'] = true; // Search by found posts $wp->query_vars['post__in'] = $post_ids; } } /** * Change the label when searching orders. * * @access public * @param mixed $query * @return string */ public function shop_subscription_search_label( $query ) { global $pagenow, $typenow; if ( 'edit.php' !== $pagenow ) { return $query; } if ( 'shop_subscription' !== $typenow ) { return $query; } if ( ! get_query_var( 'shop_subscription_search' ) ) { return $query; } return wp_unslash( $_GET['s'] ); } /** * Query vars for custom searches. * * @access public * @param mixed $public_query_vars * @return array */ public function add_custom_query_var( $public_query_vars ) { $public_query_vars[] = 'sku'; $public_query_vars[] = 'shop_subscription_search'; return $public_query_vars; } /** * Filters and sorting handler * * @param array $vars * @return array */ public function request_query( $vars ) { global $typenow; if ( 'shop_subscription' === $typenow ) { // Filter the orders by the posted customer. if ( isset( $_GET['_customer_user'] ) && $_GET['_customer_user'] > 0 ) { $subscription_ids = WCS_Customer_Store::instance()->get_users_subscription_ids( absint( $_GET['_customer_user'] ) ); $vars = self::set_post__in_query_var( $vars, $subscription_ids ); } if ( isset( $_GET['_wcs_product'] ) && $_GET['_wcs_product'] > 0 ) { $subscription_ids = wcs_get_subscriptions_for_product( $_GET['_wcs_product'] ); $subscription_ids = array_keys( $subscription_ids ); $vars = self::set_post__in_query_var( $vars, $subscription_ids ); } // If we've using the 'none' flag for the post__in query var, there's no need to apply other query filters, as we're going to return no subscriptions anyway if ( isset( $vars['post__in'] ) && self::$post__in_none === $vars['post__in'] ) { return $vars; } if ( ! empty( $_GET['_payment_method'] ) ) { $payment_gateway_filter = ( 'none' == $_GET['_payment_method'] ) ? '' : $_GET['_payment_method']; $query_vars = array( 'post_type' => 'shop_subscription', 'posts_per_page' => -1, 'post_status' => 'any', 'fields' => 'ids', 'meta_query' => array( array( 'key' => '_payment_method', 'value' => $payment_gateway_filter, ), ), ); // If there are already set post restrictions (post__in) apply them to this query if ( isset( $vars['post__in'] ) ) { $query_vars['post__in'] = $vars['post__in']; } $subscription_ids = get_posts( $query_vars ); if ( ! empty( $subscription_ids ) ) { $vars['post__in'] = $subscription_ids; } else { $vars['post__in'] = self::$post__in_none; } } // Sorting if ( isset( $vars['orderby'] ) ) { switch ( $vars['orderby'] ) { case 'order_total' : $vars = array_merge( $vars, array( 'meta_key' => '_order_total', 'orderby' => 'meta_value_num', ) ); break; case 'last_payment_date' : add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10, 2 ); break; case 'trial_end_date' : case 'next_payment_date' : case 'end_date' : $vars = array_merge( $vars, array( 'meta_key' => sprintf( '_schedule_%s', str_replace( '_date', '', $vars['orderby'] ) ), 'meta_type' => 'DATETIME', 'orderby' => 'meta_value', ) ); break; } } // Status if ( ! isset( $vars['post_status'] ) ) { $vars['post_status'] = array_keys( wcs_get_subscription_statuses() ); } } return $vars; } /** * Set the 'post__in' query var with a given set of post ids. * * There are a few special conditions for handling the post__in value. Namely: * - if there are no matching post_ids, the value should be array( 0 ), not an empty array() * - if there are existing IDs in post__in, we only want to retun posts with an ID in both * the existing set and the new set * * While this method is public, it should not be used as it will eventually be deprecated and * it's only made publicly available for other Subscriptions methods until Subscriptions * requires WC 3.0, and can rely on using methods in the data store rather than a hack like * pulling this for use outside of the admin context. * * @param array $query_vars * @param array $post_ids * @return array */ public static function set_post__in_query_var( $query_vars, $post_ids ) { if ( empty( $post_ids ) ) { // No posts for this user $query_vars['post__in'] = self::$post__in_none; } elseif ( ! isset( $query_vars['post__in'] ) ) { // No other posts limitations, include all of these posts $query_vars['post__in'] = $post_ids; } elseif ( self::$post__in_none !== $query_vars['post__in'] ) { // Existing post limitation, we only want to include existing IDs that are also in this new set of IDs $intersecting_post_ids = array_intersect( $query_vars['post__in'], $post_ids ); $query_vars['post__in'] = empty( $intersecting_post_ids ) ? self::$post__in_none : $intersecting_post_ids; } return $query_vars; } /** * Change messages when a post type is updated. * * @param array $messages * @return array */ public function post_updated_messages( $messages ) { global $post, $post_ID; $messages['shop_subscription'] = array( 0 => '', // Unused. Messages start at index 1. 1 => __( 'Subscription updated.', 'woocommerce-subscriptions' ), 2 => __( 'Custom field updated.', 'woocommerce-subscriptions' ), 3 => __( 'Custom field deleted.', 'woocommerce-subscriptions' ), 4 => __( 'Subscription updated.', 'woocommerce-subscriptions' ), // translators: placeholder is previous post title 5 => isset( $_GET['revision'] ) ? sprintf( _x( 'Subscription restored to revision from %s', 'used in post updated messages', 'woocommerce-subscriptions' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, 6 => __( 'Subscription updated.', 'woocommerce-subscriptions' ), 7 => __( 'Subscription saved.', 'woocommerce-subscriptions' ), 8 => __( 'Subscription submitted.', 'woocommerce-subscriptions' ), // translators: php date string 9 => sprintf( __( 'Subscription scheduled for: %1$s.', 'woocommerce-subscriptions' ), '' . date_i18n( _x( 'M j, Y @ G:i', 'used in "Subscription scheduled for "', 'woocommerce-subscriptions' ), wcs_date_to_time( $post->post_date ) ) . '' ), 10 => __( 'Subscription draft updated.', 'woocommerce-subscriptions' ), ); return $messages; } /** * Returns a clickable link that takes you to a collection of orders relating to the subscription. * * @uses self::get_related_orders() * @since 2.0 * @return string the link string */ public function get_related_orders_link( $the_subscription ) { return sprintf( '%s', admin_url( 'edit.php?post_status=all&post_type=shop_order&_subscription_related_orders=' . absint( $the_subscription->get_id() ) ), count( $the_subscription->get_related_orders() ) ); } /** * Displays the dropdown for the payment method filter. * * @since 2.0 */ public static function restrict_by_payment_method() { global $typenow; if ( 'shop_subscription' !== $typenow ) { return; } $selected_gateway_id = ( ! empty( $_GET['_payment_method'] ) ) ? $_GET['_payment_method'] : ''; ?> post_type ) { $actions = array(); } return $actions; } /** * Get the HTML for an order item to display on the Subscription list table. * * @param array $actions * @param object $post * @return array */ protected static function get_item_display( $item, $the_subscription, $element = 'div' ) { $_product = apply_filters( 'woocommerce_order_item_product', $the_subscription->get_product_from_item( $item ), $item ); $item_meta_html = self::get_item_meta_html( $item, $_product ); if ( 'div' === $element ) { $item_html = self::get_item_display_div( $item, self::get_item_name_html( $item, $_product ), $item_meta_html ); } else { $item_html = self::get_item_display_row( $item, self::get_item_name_html( $item, $_product, 'do_not_include_quantity' ), $item_meta_html ); } return $item_html; } /** * Get the HTML for order item meta to display on the Subscription list table. * * @param WC_Order_Item $item * @param WC_Product $product * @return string */ protected static function get_item_meta_html( $item, $_product ) { if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) { $item_meta = wcs_get_order_item_meta( $item, $_product ); $item_meta_html = $item_meta->display( true, true ); } else { $item_meta_html = wc_display_item_meta( $item, array( 'before' => '', 'after' => '', 'separator' => '', 'echo' => false, ) ); } return $item_meta_html; } /** * Get the HTML for order item meta to display on the Subscription list table. * * @param WC_Order_Item $item * @param WC_Product $product * @return string */ protected static function get_item_name_html( $item, $_product, $include_quantity = 'include_quantity' ) { $item_quantity = absint( $item['qty'] ); $item_name = ''; if ( wc_product_sku_enabled() && $_product && $_product->get_sku() ) { $item_name .= $_product->get_sku() . ' - '; } $item_name .= apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false ); $item_name = esc_html( $item_name ); if ( 'include_quantity' === $include_quantity && $item_quantity > 1 ) { $item_name = sprintf( '%s × %s', absint( $item_quantity ), $item_name ); } if ( $_product ) { $item_name = sprintf( '%s', get_edit_post_link( ( $_product->is_type( 'variation' ) ) ? wcs_get_objects_property( $_product, 'parent_id' ) : $_product->get_id() ), $item_name ); } return $item_name; } /** * Get the HTML for order item to display on the Subscription list table using a div element * as the wrapper, which is done for subscriptions with a single line item. * * @param array $actions * @param object $post * @return array */ protected static function get_item_display_div( $item, $item_name, $item_meta_html ) { $item_html = '
'; $item_html .= wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) ); if ( $item_meta_html ) { $item_html .= wcs_help_tip( $item_meta_html ); } $item_html .= '
'; return $item_html; } /** * Get the HTML for order item to display on the Subscription list table using a table element * as the wrapper, which is done for subscriptions with multilpe line items. * * @param array $actions * @param object $post * @return array */ protected static function get_item_display_row( $item, $item_name, $item_meta_html ) { ob_start(); ?> array( 'href' => array() ) ) ); if ( $item_meta_html ) { echo wcs_help_tip( $item_meta_html ); } ?> display_name, absint( $user->ID ), $user->user_email ); } ?>