echo '<div class="woocommerce-error">' . esc_html__('Invalid Subscription.', 'woocommerce-subscriptions') . ' <a href="' . esc_url(wc_get_page_permalink('myaccount')) . '" class="wc-forward">' . esc_html__('My Account', 'woocommerce-subscriptions') . '</a>' . '</div>'; return; } $subscription = wcs_get_subscription($wp->query_vars['view-subscription']); } wc_print_notices(); ?> <table class="shop_table subscription_details"> <tr> <td><?php esc_html_e('Status', 'woocommerce-subscriptions'); ?> </td> <td><?php echo esc_html(wcs_get_subscription_status_name($subscription->get_status())); ?> </td> </tr> <tr> <td><?php esc_html_e('Start Date', 'woocommerce-subscriptions'); ?> </td> <td><?php echo esc_html($subscription->get_date_to_display('start')); ?> </td> </tr> <?php foreach (array('last_payment' => _x('Last Payment Date', 'admin subscription table header', 'woocommerce-subscriptions'), 'next_payment' => _x('Next Payment Date', 'admin subscription table header', 'woocommerce-subscriptions'), 'end' => _x('End Date', 'admin subscription table header', 'woocommerce-subscriptions'), 'trial end' => _x('Trial End Date', 'admin subscription table header', 'woocommerce-subscriptions')) as $date_type => $date_title) {
/** * Checks if the user's current request to change the status of their subscription is valid. * * @since 2.0 */ public static function validate_request($user_id, $subscription, $new_status, $wpnonce = '') { $subscription = !is_object($subscription) ? wcs_get_subscription($subscription) : $subscription; if (!wcs_is_subscription($subscription)) { WC_Subscriptions::add_notice(__('That subscription does not exist. Please contact us if you need assistance.', 'woocommerce-subscriptions'), 'error'); return false; } elseif (!empty($wpnonce) && wp_verify_nonce($wpnonce, $subscription->id . $subscription->get_status()) === false) { WC_Subscriptions::add_notice(__('Security error. Please contact us if you need assistance.', 'woocommerce-subscriptions'), 'error'); return false; } elseif (!user_can($user_id, 'edit_shop_subscription_status', $subscription->id)) { WC_Subscriptions::add_notice(__('That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions'), 'error'); return false; } elseif (!$subscription->can_be_updated_to($new_status)) { // translators: placeholder is subscription's new status, translated WC_Subscriptions::add_notice(sprintf(__('That subscription can not be changed to %s. Please contact us if you need assistance.', 'woocommerce-subscriptions'), wcs_get_subscription_status_name($new_status)), 'error'); return false; } return true; }
/** * 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->id != $post->ID) { $the_subscription = wcs_get_subscription($post->ID); } $column_content = ''; switch ($column) { case 'status': // The status label $column_content = sprintf('<mark class="%s tips" data-tip="%s">%s</mark>', 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->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'] = '<a title="' . esc_attr(__('Restore this item from the Trash', 'woocommerce-subscriptions')) . '" href="' . wp_nonce_url(admin_url(sprintf($post_type_object->_edit_link . '&action=untrash', $post->ID)), 'untrash-post_' . $post->ID) . '">' . __('Restore', 'woocommerce-subscriptions') . '</a>'; } elseif (EMPTY_TRASH_DAYS) { $actions['trash'] = '<a class="submitdelete" title="' . esc_attr(__('Move this item to the Trash', 'woocommerce-subscriptions')) . '" href="' . get_delete_post_link($post->ID) . '">' . __('Trash', 'woocommerce-subscriptions') . '</a>'; } if ('trash' == $post->post_status || !EMPTY_TRASH_DAYS) { $actions['delete'] = '<a class="submitdelete" title="' . esc_attr(__('Delete this item permanently', 'woocommerce-subscriptions')) . '" href="' . get_delete_post_link($post->ID, '', true) . '">' . __('Delete Permanently', 'woocommerce-subscriptions') . '</a>'; } } } else { if ('pending-cancel' === $the_subscription->get_status()) { $label = __('Cancel Now', 'woocommerce-subscriptions'); } $actions[$status] = sprintf('<a href="%s">%s</a>', 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->billing_email) { // translators: placeholder is customer's billing email $customer_tip .= '<br/><br/>' . sprintf(__('Email: %s', 'woocommerce-subscriptions'), esc_attr($the_subscription->billing_email)); } if ($the_subscription->billing_phone) { // translators: placeholder is customer's billing phone number $customer_tip .= '<br/><br/>' . sprintf(__('Tel: %s', 'woocommerce-subscriptions'), esc_html($the_subscription->billing_phone)); } if (!empty($customer_tip)) { echo '<div class="tips" data-tip="' . esc_attr($customer_tip) . '">'; } // 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 = '******' . absint($user_info->ID) . '">'; if ($the_subscription->billing_first_name || $the_subscription->billing_last_name) { $username .= esc_html(ucfirst($the_subscription->billing_first_name) . ' ' . ucfirst($the_subscription->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 .= '</a>'; } elseif ($the_subscription->billing_first_name || $the_subscription->billing_last_name) { $username = trim($the_subscription->billing_first_name . ' ' . $the_subscription->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'), '<a href="' . esc_url(admin_url('post.php?post=' . absint($post->ID) . '&action=edit')) . '">', '<strong>' . esc_attr($the_subscription->get_order_number()) . '</strong>', '</a>', $username); $column_content .= '</div>'; $column_content .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'woocommerce-subscriptions') . '</span></button>'; 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) { $_product = apply_filters('woocommerce_order_item_product', $the_subscription->get_product_from_item($item), $item); $item_meta = wcs_get_order_item_meta($item, $_product); $item_meta_html = $item_meta->display(true, true); $item_quantity = absint($item['qty']); $item_name = ''; if (wc_product_sku_enabled() && $_product && $_product->get_sku()) { $item_name .= $_product->get_sku() . ' - '; } $item_name .= $item['name']; $item_name = apply_filters('woocommerce_order_item_name', $item_name, $item); $item_name = esc_html($item_name); if ($item_quantity > 1) { $item_name = sprintf('%s × %s', absint($item_quantity), $item_name); } if ($_product) { $item_name = sprintf('<a href="%s">%s</a>', get_edit_post_link($_product->id), $item_name); } ob_start(); ?> <div class="order-item"> <?php echo wp_kses($item_name, array('a' => array('href' => array()))); ?> <?php if ($item_meta_html) { ?> <a class="tips" href="#" data-tip="<?php echo esc_attr($item_meta_html); ?> ">[?]</a> <?php } ?> </div> <?php $column_content .= ob_get_clean(); } break; default: $column_content .= '<a href="#" class="show_order_items">' . 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)) . '</a>'; $column_content .= '<table class="order_items" cellspacing="0">'; foreach ($the_subscription->get_items() as $item) { $_product = apply_filters('woocommerce_order_item_product', $the_subscription->get_product_from_item($item), $item); $item_meta = wcs_get_order_item_meta($item, $_product); $item_meta_html = $item_meta->display(true, true); ob_start(); ?> <tr class="<?php echo esc_attr(apply_filters('woocommerce_admin_order_item_class', '', $item)); ?> "> <td class="qty"><?php echo absint($item['qty']); ?> </td> <td class="name"> <?php if (wc_product_sku_enabled() && $_product && $_product->get_sku()) { echo esc_html($_product->get_sku()) . ' - '; } echo esc_html(apply_filters('woocommerce_order_item_name', $item['name'], $item)); if ($item_meta_html) { ?> <a class="tips" href="#" data-tip="<?php echo esc_attr($item_meta_html); ?> ">[?]</a> <?php } ?> </td> </tr> <?php $column_content .= ob_get_clean(); } $column_content .= '</table>'; 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 .= '<small class="meta">' . esc_html(sprintf(__('Via %s', 'woocommerce-subscriptions'), $the_subscription->get_payment_method_to_display())) . '</small>'; break; case 'start_date': case 'trial_end_date': case 'next_payment_date': case 'last_payment_date': case 'end_date': if (0 == $the_subscription->get_time($column, 'gmt')) { $column_content .= '-'; } else { $column_content .= sprintf('<time class="%s" title="%s">%s</time>', esc_attr($column), esc_attr(date(__('Y/m/d g:i:s A', 'woocommerce-subscriptions'), $the_subscription->get_time($column, 'site'))), esc_html($the_subscription->get_date_to_display($column))); if ('next_payment_date' == $column && $the_subscription->payment_method_supports('gateway_scheduled_payments') && !$the_subscription->is_manual() && $the_subscription->has_status('active')) { $column_content .= '<div class="woocommerce-help-tip" data-tip="' . esc_attr__('This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.', 'woocommerce-subscriptions') . '"></div>'; } } $column_content = $column_content; 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()), 'p' => array('class' => array()), 'button' => array('type' => array(), 'class' => array()))); }
/** * Updates status of the subscription * * @param string $new_status Status to change the order to. No internal wc- prefix is required. * @param string $note (default: '') Optional note to add */ public function update_status($new_status, $note = '', $manual = false) { if (!$this->id) { return; } // Standardise status names. $new_status = 'wc-' === substr($new_status, 0, 3) ? substr($new_status, 3) : $new_status; $new_status_key = 'wc-' . $new_status; $old_status = $this->get_status(); $old_status_key = $this->post_status; if ($new_status !== $old_status || !in_array($this->post_status, array_keys(wcs_get_subscription_statuses()))) { // Only update is possible if (!$this->can_be_updated_to($new_status)) { $message = sprintf(__('Unable to change subscription status to "%s".', 'woocommerce-subscriptions'), $new_status); $this->add_order_note($message); do_action('woocommerce_subscription_unable_to_update_status', $this, $new_status, $old_status); // Let plugins handle it if they tried to change to an invalid status throw new Exception($message); } try { wp_update_post(array('ID' => $this->id, 'post_status' => $new_status_key)); $this->post_status = $new_status_key; switch ($new_status) { case 'pending': // Nothing to do here break; case 'pending-cancel': $end_date = $this->calculate_date('end_of_prepaid_term'); // If there is no future payment and no expiration date set, the customer has no prepaid term (this shouldn't be possible as only active subscriptions can be set to pending cancellation and an active subscription always has either an end date or next payment) if (0 == $end_date) { $end_date = current_time('mysql', true); } $this->delete_date('trial_end'); $this->delete_date('next_payment'); $this->update_dates(array('end' => $end_date)); break; case 'completed': // core WC order status mapped internally to avoid exceptions // core WC order status mapped internally to avoid exceptions case 'active': // Recalculate and set next payment date $next_payment = $this->get_time('next_payment'); if ($next_payment < gmdate('U')) { // also accounts for a $next_payment of 0, meaning it's not set $next_payment = $this->calculate_date('next_payment'); if ($next_payment > 0) { $this->update_dates(array('next_payment' => $next_payment)); } } // Trial end date and end/expiration date don't change at all - they should be set when the subscription is first created wcs_make_user_active($this->customer_user); break; case 'failed': // core WC order status mapped internally to avoid exceptions // core WC order status mapped internally to avoid exceptions case 'on-hold': // Record date of suspension - 'post_modified' column? $this->update_suspension_count($this->suspension_count + 1); wcs_maybe_make_user_inactive($this->customer_user); break; case 'cancelled': case 'switched': case 'expired': $this->delete_date('trial_end'); $this->delete_date('next_payment'); $this->update_dates(array('end' => current_time('mysql', true))); wcs_maybe_make_user_inactive($this->customer_user); break; } $this->add_order_note(trim($note . ' ' . sprintf(__('Status changed from %s to %s.', 'woocommerce-subscriptions'), wcs_get_subscription_status_name($old_status), wcs_get_subscription_status_name($new_status))), 0, $manual); // dynamic hooks for convenience do_action('woocommerce_subscription_status_' . $new_status, $this); do_action('woocommerce_subscription_status_' . $old_status . '_to_' . $new_status, $this); // Trigger a hook with params we want do_action('woocommerce_subscription_status_updated', $this, $new_status, $old_status); // Trigger a hook with params matching WooCommerce's 'woocommerce_order_status_changed' hook so functions attached to it can be attached easily to subscription status changes do_action('woocommerce_subscription_status_changed', $this->id, $old_status, $new_status); } catch (Exception $e) { // Make sure the old status is restored wp_update_post(array('ID' => $this->id, 'post_status' => $old_status_key)); $this->post_status = $old_status_key; $this->add_order_note(sprintf(__('Unable to change subscription status to "%s".', 'woocommerce-subscriptions'), $new_status)); do_action('woocommerce_subscription_unable_to_update_status', $this, $new_status, $old_status); throw $e; } } }