PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.3.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.3.0
2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 All 65 releases
← All changes | includes/Admin/views/subscription-list.php +112 -164 1.10.0 → 2.3.0 View file →
@@ -11,28 +11,43 @@
11 11
12 12 if ( ! isset( $date_filter ) ) {
13 13 $date_filter = '';
14 14 }
15 +if ( ! isset( $renewal_due ) ) {
16 + $renewal_due = 0;
17 +}
15 18
16 -$filters_active = ! empty( $status ) || ! empty( $date_filter ) || ! empty( $search );
17 -$months = array();
19 +$filters_active = ! empty( $status ) || ! empty( $date_filter ) || ! empty( $search ) || ! empty( $renewal_due );
20 +
21 +// The last twelve of the store's months, counted back from the 1st: stepping
22 +// back from today skips a month on the 29th–31st (31 Oct βˆ’ 1 month is 1 Oct).
23 +// The store's timezone, because the list filters on the local post date.
24 +$months = array();
25 +$this_month = ( new DateTimeImmutable( 'now', wp_timezone() ) )->modify( 'first day of this month' )->setTime( 0, 0 );
18 26 for ( $i = 0; $i < 12; $i++ ) {
19 27 $month = strtotime( "-$i month" );
20 - $months[ date( 'Y-m', $month ) ] = date( 'F Y', $month );
28 + $months[ gmdate( 'Y-m', $month ) ] = gmdate( 'F Y', $month );
21 29 }
22 30 ?>
23 -<div class="wp-subscription-admin-content list-page subscrpt-subs-list">
31 +<div class="wp-subscription-admin-content list-page">
24 32
25 - <!-- Page header -->
26 - <div style="margin-bottom:20px;">
27 - <h1 style="font-size:1.375rem;font-weight:700;color:var(--wpsubs-text);margin:0 0 6px;line-height:1.2;"><?php esc_html_e( 'Subscriptions', 'subscription' ); ?></h1>
28 - <p style="font-size:13px;color:var(--wpsubs-text-muted);margin:0 0 12px;line-height:1.5;"><?php esc_html_e( 'Manage all your WooCommerce subscriptions.', 'subscription' ); ?></p>
29 - <div style="border-top:1px dashed #d0d3d7;"></div>
30 - </div>
33 + <?php
34 + // Getting started ("Welcome to WPSubscription") card hidden for now β€” re-enable later.
35 + // require __DIR__ . '/subscription-gsc.php';
36 + ?>
31 37
38 + <?php
39 + wpsubs_render_page_header(
40 + array(
41 + 'title' => __( 'Subscriptions', 'subscription' ),
42 + 'description' => __( 'Manage all your WooCommerce subscriptions.', 'subscription' ),
43 + )
44 + );
45 + ?>
46 +
32 47 <form method="post" id="subscriptions-form">
33 48 <?php wp_nonce_field( 'subscrpt_list_action' ); ?>
34 - <input type="hidden" name="page" value="wp-subscription" />
49 + <input type="hidden" name="page" value="wp-subscription-list" />
35 50
36 51 <!-- Toolbar -->
37 52 <div class="wpsubs-toolbar">
38 53
@@ -47,12 +62,20 @@
47 62 'value' => 'active',
48 63 'label' => __( 'Active', 'subscription' ),
49 64 ),
50 65 array(
66 + 'value' => 'on_hold',
67 + 'label' => __( 'On Hold', 'subscription' ),
68 + ),
69 + array(
51 70 'value' => 'pending',
52 71 'label' => __( 'Pending', 'subscription' ),
53 72 ),
54 73 array(
74 + 'value' => 'pe_cancelled',
75 + 'label' => __( 'Pending Cancellation', 'subscription' ),
76 + ),
77 + array(
55 78 'value' => 'cancelled',
56 79 'label' => __( 'Cancelled', 'subscription' ),
57 80 ),
58 81 array(
@@ -59,10 +82,10 @@
59 82 'value' => 'expired',
60 83 'label' => __( 'Expired', 'subscription' ),
61 84 ),
62 85 array(
63 - 'value' => 'draft',
64 - 'label' => __( 'Draft', 'subscription' ),
86 + 'value' => 'completed',
87 + 'label' => __( 'Completed', 'subscription' ),
65 88 ),
66 89 array(
67 90 'value' => 'trash',
68 91 'label' => __( 'Trash', 'subscription' ),
@@ -89,8 +112,32 @@
89 112 )
90 113 );
91 114 ?>
92 115
116 + <?php
117 + // Next-renewal window. The Overview's "Renewals due" figure opens the
118 + // list with 7 chosen; a window reached by URL is offered too, so the
119 + // dropdown always shows what is applied.
120 + $renewal_windows = array_unique( array_merge( array( 7, 14, 30 ), $renewal_due ? array( $renewal_due ) : array() ) );
121 + sort( $renewal_windows );
122 + $renewal_options = array();
123 + foreach ( $renewal_windows as $days ) {
124 + $renewal_options[] = array(
125 + 'value' => (string) $days,
126 + /* translators: %d: number of days ahead. */
127 + 'label' => sprintf( _n( 'Due in %d day', 'Due in %d days', $days, 'subscription' ), $days ),
128 + );
129 + }
130 + wpsubs_render_adv_select(
131 + array(
132 + 'name' => 'renewal_due',
133 + 'placeholder' => __( 'All Renewals', 'subscription' ),
134 + 'value' => $renewal_due ? (string) $renewal_due : '',
135 + 'options' => $renewal_options,
136 + )
137 + );
138 + ?>
139 +
93 140 <div class="wpsubs-search">
94 141 <div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l">
95 142 <svg class="wpsubs-input-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z"/></svg>
96 143 <input type="search" name="s" value="<?php echo esc_attr( $search ); ?>" placeholder="<?php esc_attr_e( 'Search subscriptions...', 'subscription' ); ?>" class="wpsubs-input" />
@@ -101,9 +148,9 @@
101 148 <?php esc_html_e( 'Filter', 'subscription' ); ?>
102 149 </button>
103 150
104 151 <?php if ( $filters_active ) : ?>
105 - <a href="<?php echo esc_url( remove_query_arg( array( 'subscrpt_status', 'date_filter', 's', 'title', 'paged' ) ) ); ?>" class="wpsubs-btn wpsubs-btn--outline">
152 + <a href="<?php echo esc_url( remove_query_arg( array( 'subscrpt_status', 'date_filter', 's', 'title', 'paged', 'renewal_due' ) ) ); ?>" class="wpsubs-btn wpsubs-btn--outline">
106 153 <?php esc_html_e( 'Clear', 'subscription' ); ?>
107 154 </a>
108 155 <?php endif; ?>
109 156
@@ -108,34 +155,18 @@
108 155 <?php endif; ?>
109 156
110 157 <div class="wpsubs-toolbar__spacer"></div>
111 158
159 + <?php // Page size and bulk actions wrap as one right-aligned group, never one without the other. ?>
160 + <div style="display:flex;flex-wrap:wrap;gap:8px;margin-inline-start:auto;">
112 161 <?php
113 162 $current_per_page = isset( $_GET['per_page'] ) ? intval( wp_unslash( $_GET['per_page'] ) ) : 20;
114 - wpsubs_render_adv_select(
163 + wpsubs_render_per_page_select(
115 164 array(
116 - 'name' => 'per_page',
117 - 'value' => (string) $current_per_page,
118 - 'options' => array(
119 - array(
120 - 'value' => '10',
121 - 'label' => __( '10 / page', 'subscription' ),
122 - ),
123 - array(
124 - 'value' => '20',
125 - 'label' => __( '20 / page', 'subscription' ),
126 - ),
127 - array(
128 - 'value' => '50',
129 - 'label' => __( '50 / page', 'subscription' ),
130 - ),
131 - array(
132 - 'value' => '100',
133 - 'label' => __( '100 / page', 'subscription' ),
134 - ),
135 - ),
136 - 'align' => 'right',
137 - 'id' => 'wpsubs-per-page-select',
165 + 'name' => 'per_page',
166 + 'value' => (string) $current_per_page,
167 + 'align' => 'right',
168 + 'id' => 'wpsubs-per-page-select',
138 169 )
139 170 );
140 171 ?>
141 172
@@ -177,14 +208,15 @@
177 208 <button type="submit" name="bulk_action" value="1" id="wpsubs-bulk-action-submit" style="display:none;" aria-hidden="true"></button>
178 209
179 210 <?php
180 211 if ( 'trash' === $status && ! empty( $subscriptions ) ) :
181 - $empty_trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=clean_trash&sub_id=all' ), 'wpsubs_action_clean_trash' );
212 + $empty_trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=clean_trash&sub_id=all' ), 'wpsubs_action_clean_trash' );
182 213 ?>
183 214 <a href="<?php echo esc_url( $empty_trash_url ); ?>" class="wpsubs-btn wpsubs-btn--danger" onclick="return confirm('<?php esc_attr_e( 'Permanently delete all trash items? This cannot be undone.', 'subscription' ); ?>')">
184 215 <?php esc_html_e( 'Empty Trash', 'subscription' ); ?>
185 216 </a>
186 217 <?php endif; ?>
218 + </div>
187 219
188 220 </div><!-- /.wpsubs-toolbar -->
189 221
190 222 <!-- Table card -->
@@ -230,26 +262,29 @@
230 262 $customer_url = $customer_id ? admin_url( 'user-edit.php?user_id=' . $customer_id ) : '';
231 263 $customer_email = $order ? $order->get_billing_email() : '';
232 264
233 265 $start_date = $subscription_data['start_date'] ? strtotime( $subscription_data['start_date'] ) : 0;
234 - $renewal_date = $subscription_data['next_date'] ? strtotime( $subscription_data['next_date'] ) : 0;
266 + $renewal_date = $subscription_data['next_date'] && SpringDevs\Subscription\Illuminate\Helper::has_next_payment( $subscrpt_status ) ? strtotime( $subscription_data['next_date'] ) : 0;
235 267
236 268 $is_trash = 'trash' === $subscription->post_status;
237 269 $is_grace_period = isset( $subscription_data['grace_period'] );
238 270 $grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0;
239 271
240 - // Amount + timing
241 - $quantity = $order_item ? (int) $order_item->get_quantity() : 1;
242 - $price = '' !== ( $subscription_data['price'] ?? '' )
243 - ? (float) $subscription_data['price'] * max( 1, $quantity )
244 - : '';
245 - $timing_per = $subscription_data['schedule']['timing_per'] ?? '';
246 - $timing_option = $subscription_data['schedule']['timing_option'] ?? '';
247 - $timing_label = '';
272 + // Amount + timing. Net of any discount that carries into renewals.
273 + $quantity = $order_item ? (int) $order_item->get_quantity() : 1;
274 + $amount_totals = SpringDevs\Subscription\Illuminate\Helper::get_subscription_display_totals( $subscription_id, $order_item );
275 + $price = '' !== ( $subscription_data['price'] ?? '' ) ? (float) $amount_totals['total'] : '';
276 + $price_full = (float) $amount_totals['full_excl'] + $amount_totals['tax'] + $amount_totals['discount_tax'];
277 + $has_discount = (bool) $amount_totals['has_discount'];
278 + $price_per_unit = '' !== $price ? $price / max( 1, $quantity ) : '';
279 + $timing_per = $subscription_data['schedule']['timing_per'] ?? '';
280 + $timing_option = $subscription_data['schedule']['timing_option'] ?? '';
281 + $timing_label = '';
248 282 if ( $timing_per && $timing_option ) {
283 + $timing_unit = \SpringDevs\Subscription\Illuminate\Helper::get_typos( $timing_per, $timing_option );
249 284 $timing_label = ( (int) $timing_per > 1 )
250 - ? $timing_per . ' ' . $timing_option . 's'
251 - : $timing_option;
285 + ? $timing_per . ' ' . $timing_unit
286 + : $timing_unit;
252 287 }
253 288
254 289 // Avatar
255 290 $name_parts = array_values( array_filter( explode( ' ', trim( $customer ) ) ) );
@@ -263,12 +298,12 @@
263 298 $color_slot = ord( strtolower( $initials[0] ?? 'a' ) ) % 8;
264 299
265 300 // Action URLs
266 301 $nonce_action = 'wpsubs_action_' . $subscription->ID;
267 - $view_url = get_edit_post_link( $subscription->ID );
268 - $trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=trash&sub_id=' . $subscription->ID ), $nonce_action );
269 - $delete_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=delete&sub_id=' . $subscription->ID ), $nonce_action );
270 - $restore_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=restore&sub_id=' . $subscription->ID ), $nonce_action );
302 + $view_url = admin_url( 'admin.php?page=wp-subscription-details&id=' . $subscription->ID );
303 + $trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=trash&sub_id=' . $subscription->ID ), $nonce_action );
304 + $delete_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=delete&sub_id=' . $subscription->ID ), $nonce_action );
305 + $restore_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=restore&sub_id=' . $subscription->ID ), $nonce_action );
271 306
272 307 // Status badge
273 308 $badge_mod_map = array(
274 309 'active' => 'active',
@@ -275,8 +310,9 @@
275 310 'pending' => 'pending',
276 311 'pe_cancelled' => 'pending-cancel',
277 312 'cancelled' => 'cancelled',
278 313 'expired' => 'expired',
314 + 'completed' => 'completed',
279 315 'draft' => 'draft',
280 316 'trash' => 'trash',
281 317 );
282 318 $badge_mod = $badge_mod_map[ $subscrpt_status ] ?? 'expired';
@@ -297,9 +333,9 @@
297 333 <td style="white-space:nowrap;">
298 334 <span class="wpsubs-badge wpsubs-badge--<?php echo esc_attr( $badge_mod ); ?>">
299 335 <?php echo esc_html( $verbose_status ); ?>
300 336 <?php if ( $is_grace_period && $grace_remaining > 0 ) : ?>
301 - <span class="dashicons dashicons-warning" style="font-size:11px;width:11px;height:11px;color:#d97706;" title="<?php echo esc_attr( sprintf( __( '%d days remaining in grace period', 'subscription' ), $grace_remaining ) ); ?>"></span>
337 + <span class="dashicons dashicons-warning" style="font-size:11px;width:11px;height:11px;color:#d97706;" title="<?php echo esc_attr( sprintf( /* translators: %d: number of days left in the grace period. */ __( '%d days remaining in grace period', 'subscription' ), $grace_remaining ) ); ?>"></span>
302 338 <?php endif; ?>
303 339 </span>
304 340 </td>
305 341
@@ -331,10 +367,13 @@
331 367 <!-- Subscription amount + timing -->
332 368 <td style="white-space:nowrap;">
333 369 <?php if ( '' !== $price ) : ?>
334 370 <span class="wpsubs-cell-title" style="font-variant-numeric:tabular-nums;"><?php echo wp_kses_post( wc_price( $price ) ); ?></span>
371 + <?php if ( $has_discount ) : ?>
372 + <span class="wpsubs-cell-id"><del aria-hidden="true"><?php echo wp_kses_post( wc_price( $price_full ) ); ?></del></span>
373 + <?php endif; ?>
335 374 <?php if ( $quantity > 1 ) : ?>
336 - <span class="wpsubs-cell-id"><?php echo wp_kses_post( wc_price( (float) $subscription_data['price'] ) ); ?> &times; <?php echo (int) $quantity; ?></span>
375 + <span class="wpsubs-cell-id"><?php echo wp_kses_post( wc_price( (float) $price_per_unit ) ); ?> &times; <?php echo (int) $quantity; ?></span>
337 376 <?php endif; ?>
338 377 <?php if ( $timing_label ) : ?>
339 378 <span class="wpsubs-cell-id">/ <?php echo esc_html( $timing_label ); ?></span>
340 379 <?php endif; ?>
@@ -345,10 +384,10 @@
345 384
346 385 <!-- Start date -->
347 386 <td class="wpsubs-col--nowrap">
348 387 <?php if ( $start_date ) : ?>
349 - <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( 'n/j/Y', $start_date ) ); ?></span>
350 - <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( 'g:i a', $start_date ) ); ?></span>
388 + <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $start_date ) ); ?></span>
389 + <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $start_date ) ); ?></span>
351 390 <?php else : ?>
352 391 <span class="wpsubs-cell--muted">&#8212;</span>
353 392 <?php endif; ?>
354 393 </td>
@@ -355,10 +394,10 @@
355 394
356 395 <!-- Renewal date -->
357 396 <td class="wpsubs-col--nowrap">
358 397 <?php if ( $renewal_date ) : ?>
359 - <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( 'n/j/Y', $renewal_date ) ); ?></span>
360 - <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( 'g:i a', $renewal_date ) ); ?></span>
398 + <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $renewal_date ) ); ?></span>
399 + <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $renewal_date ) ); ?></span>
361 400 <?php else : ?>
362 401 <span class="wpsubs-cell--muted">&#8212;</span>
363 402 <?php endif; ?>
364 403 </td>
@@ -398,114 +437,23 @@
398 437 </table>
399 438
400 439 <!-- Pagination inside table card -->
401 440 <?php
402 - $start_item = $total > 0 ? ( ( $paged - 1 ) * $per_page ) + 1 : 0;
403 - $end_item = min( $paged * $per_page, $total );
404 - $base_url = remove_query_arg( 'paged' );
405 - $total_pages = max( 1, $max_num_pages );
406 -
407 - // Build page range: first, last, current Β± 1 neighbours, ellipsis for gaps.
408 - $nearby = array();
409 - for ( $i = 1; $i <= $total_pages; $i++ ) {
410 - if ( $i === 1 || $i === $total_pages || abs( $i - $paged ) <= 1 ) {
411 - $nearby[] = $i;
412 - }
413 - }
414 - $page_range = array();
415 - $prev_page = null;
416 - foreach ( $nearby as $p ) {
417 - if ( null !== $prev_page ) {
418 - $gap = $p - $prev_page;
419 - if ( $gap === 2 ) {
420 - $page_range[] = $prev_page + 1; // single hidden page β€” show it directly
421 - } elseif ( $gap > 2 ) {
422 - $page_range[] = null; // ellipsis
423 - }
424 - }
425 - $page_range[] = $p;
426 - $prev_page = $p;
427 - }
441 + wpsubs_render_pager(
442 + array(
443 + 'current' => (int) $paged,
444 + 'total' => max( 1, (int) $max_num_pages ),
445 + 'info' => true,
446 + 'per_page' => (int) $per_page,
447 + 'item_count' => (int) $total,
448 + 'base_url' => remove_query_arg( 'paged' ),
449 + 'link_mode' => 'url',
450 + // translators: %1$s = first item number, %2$s = last item number, %3$s = total items.
451 + 'info_format' => __( 'Showing %1$s–%2$s of %3$s subscriptions', 'subscription' ),
452 + 'context' => 'subscription-list',
453 + )
454 + );
428 455 ?>
429 - <div class="wpsubs-pagination">
430 - <span class="wpsubs-pagination__info">
431 - <?php
432 - echo esc_html(
433 - sprintf(
434 - /* translators: 1: first item number, 2: last item number, 3: total count */
435 - __( 'Showing %1$s–%2$s of %3$s subscriptions', 'subscription' ),
436 - number_format_i18n( $start_item ),
437 - number_format_i18n( $end_item ),
438 - number_format_i18n( $total )
439 - )
440 - );
441 - ?>
442 - </span>
443 - <div class="wpsubs-pagination__nav">
444 -
445 - <?php // Previous button ?>
446 - <?php if ( $paged > 1 ) : ?>
447 - <a href="
448 - <?php
449 - echo esc_url(
450 - add_query_arg(
451 - array(
452 - 'paged' => $paged - 1,
453 - 'per_page' => $per_page,
454 - ),
455 - $base_url
456 - )
457 - );
458 - ?>
459 - " class="wpsubs-pagination__btn" aria-label="<?php esc_attr_e( 'Previous page', 'subscription' ); ?>">&#8249;</a>
460 - <?php else : ?>
461 - <span class="wpsubs-pagination__btn wpsubs-pagination__btn--disabled" aria-hidden="true">&#8249;</span>
462 - <?php endif; ?>
463 -
464 - <?php // Numbered pages + ellipsis ?>
465 - <?php foreach ( $page_range as $p ) : ?>
466 - <?php if ( null === $p ) : ?>
467 - <span class="wpsubs-pagination__ellipsis" aria-hidden="true">…</span>
468 - <?php elseif ( $p === $paged ) : ?>
469 - <span class="wpsubs-pagination__btn wpsubs-pagination__btn--active" aria-current="page"><?php echo (int) $p; ?></span>
470 - <?php else : ?>
471 - <a href="
472 - <?php
473 - echo esc_url(
474 - add_query_arg(
475 - array(
476 - 'paged' => $p,
477 - 'per_page' => $per_page,
478 - ),
479 - $base_url
480 - )
481 - );
482 - ?>
483 - " class="wpsubs-pagination__btn"><?php echo (int) $p; ?></a>
484 - <?php endif; ?>
485 - <?php endforeach; ?>
486 -
487 - <?php // Next button ?>
488 - <?php if ( $paged < $total_pages ) : ?>
489 - <a href="
490 - <?php
491 - echo esc_url(
492 - add_query_arg(
493 - array(
494 - 'paged' => $paged + 1,
495 - 'per_page' => $per_page,
496 - ),
497 - $base_url
498 - )
499 - );
500 - ?>
501 - " class="wpsubs-pagination__btn" aria-label="<?php esc_attr_e( 'Next page', 'subscription' ); ?>">&#8250;</a>
502 - <?php else : ?>
503 - <span class="wpsubs-pagination__btn wpsubs-pagination__btn--disabled" aria-hidden="true">&#8250;</span>
504 - <?php endif; ?>
505 -
506 - </div>
507 - </div>
508 456
509 457 </div><!-- /.wpsubs-table-card -->
510 458
511 459 </form>