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();
}
?>
_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 '