PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.9.6
Subscriptions for WooCommerce with Stripe Recurring Payments v1.9.6
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 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Admin / Subscriptions.php

Subscriptions.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.9.6, at includes/Admin/Subscriptions.php

1,034 lines 34.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SpringDevs\Subscription\Admin;
4
5 use SpringDevs\Subscription\Illuminate\Action;
6 use SpringDevs\Subscription\Illuminate\Helper;
7
8 // HPOS: This file is compatible with WooCommerce High-Performance Order Storage (HPOS).
9 // All WooCommerce order data is accessed via WooCommerce CRUD methods (wc_get_order, etc.).
10 // All direct post meta access is for subscription data only, not WooCommerce order data.
11 // If you add new order data access, use WooCommerce CRUD for HPOS compatibility.
12
13 /**
14 * Subscriptions class
15 *
16 * @package SpringDevs\Subscription\Admin
17 */
18 class Subscriptions {
19
20
21 /**
22 * Initialize the class.
23 */
24 public function __construct() {
25 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
26 add_filter( 'post_row_actions', array( $this, 'post_row_actions' ) );
27 add_filter( 'manage_subscrpt_order_posts_columns', array( $this, 'add_custom_columns' ) );
28 add_action( 'manage_subscrpt_order_posts_custom_column', array( $this, 'add_custom_columns_data' ), 10, 2 );
29 add_action( 'add_meta_boxes', array( $this, 'create_meta_boxes' ) );
30 add_action( 'admin_head-post.php', array( $this, 'some_styles' ) );
31 add_action( 'admin_head-post-new.php', array( $this, 'some_styles' ) );
32 add_action( 'admin_footer-post.php', array( $this, 'some_scripts' ) );
33 add_action( 'admin_footer-post-new.php', array( $this, 'some_scripts' ) );
34 add_action( 'save_post', array( $this, 'save_subscrpt_order' ) );
35 add_filter( 'woocommerce_order_item_get_formatted_meta_data', array( $this, 'remove_order_meta' ), 10, 1 );
36 add_filter( 'bulk_actions-edit-subscrpt_order', array( $this, 'remove_bulk_actions' ) );
37 add_action( 'restrict_manage_posts', array( $this, 'add_subscription_filter_select' ) );
38 add_action( 'admin_menu', array( $this, 'add_overview_submenu' ), 40 );
39 add_action( 'edit_form_after_title', array( $this, 'display_subscription_details_section' ) );
40
41 add_action( 'admin_head-post.php', [ $this, 'add_back_to_list_button' ] );
42 }
43
44 /**
45 * Remove 'Edit` and 'Trash' from bulk actions.
46 *
47 * @param array $actions Action list.
48 *
49 * @return array
50 */
51 public function remove_bulk_actions( $actions ) {
52 unset( $actions['edit'] );
53 unset( $actions['trash'] );
54
55 return $actions;
56 }
57
58 /**
59 * Hide order meta key from custom fields.
60 *
61 * @param array $formatted_meta Data with key-value.
62 *
63 * @return array
64 */
65 public function remove_order_meta( $formatted_meta ): array {
66 $temp_metas = array();
67 foreach ( $formatted_meta as $key => $meta ) {
68 if ( isset( $meta->key ) && '_renew_subscrpt' !== $meta->key ) {
69 $temp_metas[ $key ] = $meta;
70 }
71 }
72
73 return $temp_metas;
74 }
75
76 /**
77 * Enqueue admin assets.
78 *
79 * @return void
80 */
81 public function enqueue_scripts() {
82 wp_enqueue_style( 'subscrpt_admin_css' );
83 wp_enqueue_style( 'subscrpt_status_css' );
84 }
85
86 /**
87 * Remove default post actions.
88 *
89 * @param array $actions Actions.
90 *
91 * @return array
92 */
93 public function post_row_actions( $actions ) {
94 global $current_screen;
95 if ( 'subscrpt_order' !== $current_screen->post_type ) {
96 return $actions;
97 }
98 unset( $actions['inline hide-if-no-js'] );
99 unset( $actions['view'] );
100 unset( $actions['trash'] );
101 unset( $actions['edit'] );
102
103 return $actions;
104 }
105
106 /**
107 * Register custom columns.
108 *
109 * @param array $columns Columns.
110 *
111 * @return array
112 */
113 public function add_custom_columns( $columns ) {
114 $columns['subscrpt_start_date'] = __( 'Start Date', 'subscription' );
115 $columns['subscrpt_customer'] = __( 'Customer', 'subscription' );
116 $columns['subscrpt_next_date'] = __( 'Next Date', 'subscription' );
117 $columns['subscrpt_status'] = __( 'Status', 'subscription' );
118 unset( $columns['date'] );
119 unset( $columns['cb'] );
120
121 return $columns;
122 }
123
124 /**
125 * Display column data.
126 *
127 * @param string $column Column.
128 * @param int $post_id Post Id.
129 *
130 * @return void
131 */
132 public function add_custom_columns_data( $column, $post_id ) {
133 // HPOS: Safe. Only retrieves WooCommerce order via CRUD, and subscription meta via post meta.
134 $order_id = get_post_meta( $post_id, '_subscrpt_order_id', true ); // HPOS: Only subscription meta, not order meta.
135 $order = wc_get_order( $order_id ); // HPOS: Safe, uses WooCommerce CRUD.
136 if ( $order ) {
137 if ( 'subscrpt_start_date' === $column ) {
138 $start_date = get_post_meta( $post_id, '_subscrpt_start_date', true );
139 echo ! empty( $start_date ) ? esc_html( wp_date( 'F d, Y', $start_date ) ) : '-';
140 } elseif ( 'subscrpt_customer' === $column ) {
141 ?>
142 <?php echo wp_kses_post( $order->get_formatted_billing_full_name() ); ?>
143 <br />
144 <a href="mailto:<?php echo wp_kses_post( $order->get_billing_email() ); ?>"><?php echo wp_kses_post( $order->get_billing_email() ); ?></a>
145 <br />
146 <?php if ( ! empty( $order->get_billing_phone() ) ) : ?>
147 Phone : <a
148 href="tel:<?php echo esc_js( $order->get_billing_phone() ); ?>"><?php echo esc_js( $order->get_billing_phone() ); ?></a>
149 <?php endif; ?>
150 <?php
151 } elseif ( 'subscrpt_next_date' === $column ) {
152 $next_date = get_post_meta( $post_id, '_subscrpt_next_date', true );
153 echo ! empty( $next_date ) ? esc_html( wp_date( 'F d, Y', $next_date ) ) : '-';
154 } elseif ( 'subscrpt_status' === $column ) {
155 $status_obj = get_post_status_object( get_post_status( $post_id ) );
156 ?>
157 <span
158 class="subscrpt-<?php echo esc_html( $status_obj->name ); ?>"><?php echo esc_html( $status_obj->label ); ?></span>
159 <?php
160 }
161 } else {
162 esc_html_e( 'Order not found !!', 'subscription' );
163 }
164 }
165
166 /**
167 * Create meta boxes for admin subscriptions.
168 */
169 public function create_meta_boxes() {
170 remove_meta_box( 'submitdiv', 'subscrpt_order', 'side' );
171 add_meta_box(
172 'subscrpt_order_save_post',
173 __( 'Subscription Action', 'subscription' ),
174 array( $this, 'subscrpt_order_save_post' ),
175 'subscrpt_order',
176 'side',
177 'default'
178 );
179
180 add_meta_box(
181 'subscrpt_customer_info',
182 __( 'Customer Details', 'subscription' ),
183 array( $this, 'customer_info' ),
184 'subscrpt_order',
185 'side',
186 'default'
187 );
188
189 // Removed the redundant subscription info meta box as it's now shown prominently above
190
191 add_meta_box(
192 'subscrpt_order_history',
193 __( 'Related Orders', 'subscription' ),
194 array( $this, 'order_histories' ),
195 'subscrpt_order',
196 'normal',
197 'default'
198 );
199
200 add_meta_box(
201 'subscrpt_order_activities',
202 __( 'Subscription Activities', 'subscription' ),
203 array( $this, 'order_activities' ),
204 'subscrpt_order',
205 'normal',
206 'default'
207 );
208 }
209
210 /**
211 * Display Order Histories.
212 *
213 * @param \WP_Post $post Post Object.
214 *
215 * @return void
216 */
217 public function order_histories( $post ) {
218 $subscription_id = $post->ID;
219 global $wpdb;
220 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
221
222 // @phpcs:ignore
223 $order_histories = $wpdb->get_results(
224 $wpdb->prepare(
225 'SELECT * FROM %i WHERE subscription_id=%d ORDER BY id DESC',
226 array(
227 $table_name,
228 $subscription_id,
229 )
230 )
231 );
232
233 include 'views/order-history.php';
234 }
235
236 /**
237 * Display Order Activities
238 *
239 * @param \WP_Post $post Post Object.
240 *
241 * @return void
242 */
243 public function order_activities( $post ) {
244 if ( function_exists( 'subscrpt_pro_activated' ) ) :
245 if ( subscrpt_pro_activated() ) :
246 do_action( 'subscrpt_order_activities', $post->ID );
247 else :
248 ?>
249 <div class="wp-subscription-admin-box wp-subscription-upgrade-pro-banner" style="margin-bottom:18px;display:flex;align-items:center;gap:18px;justify-content:space-between;background:linear-gradient(90deg,#38bdf8 0%,#6366f1 100%);border-radius:10px;padding:22px 28px;box-shadow:0 2px 12px rgba(56,189,248,0.08);color:#fff;">
250 <div style="flex:1;">
251 <div style="display:flex;align-items:center;gap:14px;">
252 <span style="font-size:2.2em;line-height:1;">🚀</span>
253 <span style="font-family:Georgia,serif;font-size:1.25em;font-weight:bold;">Upgrade to WP Subscription Pro</span>
254 </div>
255 <div style="margin-top:8px;font-size:1.08em;max-width:500px;opacity:0.95;">
256 Unlock advanced features, automation, and priority support. Take your subscription business to the next level!
257 </div>
258 </div>
259 <div style="flex-shrink:0;">
260 <a href="https://wpsubscription.co/" target="_blank" class="button button-primary" style="background:#fff;color:#6366f1;font-weight:600;font-size:1.08em;padding:12px 28px;border:none;box-shadow:0 2px 8px rgba(99,102,241,0.10);border-radius:6px;">Upgrade to Pro</a>
261 </div>
262 </div>
263 <?php
264 endif;
265 endif;
266 }
267
268 /**
269 * Save subscription HTML.
270 */
271 public function subscrpt_order_save_post() {
272 $actions_data = array(
273 'active' => array(
274 'label' => __( 'Activate Subscription', 'subscription' ),
275 'value' => 'active',
276 ),
277 'pending' => array(
278 'label' => __( 'Pending Subscription', 'subscription' ),
279 'value' => 'pending',
280 ),
281 'expire' => array(
282 'label' => __( 'Expire Subscription', 'subscription' ),
283 'value' => 'expired',
284 ),
285 'pe_cancelled' => array(
286 'label' => __( 'Pending Cancel Subscription', 'subscription' ),
287 'value' => 'pe_cancelled',
288 ),
289 'cancelled' => array(
290 'label' => __( 'Cancel Subscription', 'subscription' ),
291 'value' => 'cancelled',
292 ),
293 );
294
295 $map_actions = array(
296 'pending' => array( 'active', 'cancelled' ),
297 'active' => array( 'pe_cancelled', 'cancelled' ),
298 'pe_cancelled' => array( 'active', 'cancelled' ),
299 'cancelled' => array( 'active' ),
300 'expired' => array(),
301 );
302
303 $status = get_post_status( get_the_ID() );
304 $actions = $map_actions[ $status ];
305
306 include 'views/subscription-save-meta.php';
307 }
308
309 /**
310 * Display Customer Info
311 *
312 * @param \WP_Post $post Post Object.
313 *
314 * @return void
315 */
316 public function customer_info( $post ) {
317 $order_id = get_post_meta( $post->ID, '_subscrpt_order_id', true );
318 $order = wc_get_order( $order_id );
319 if ( ! $order ) {
320 return;
321 }
322 include 'views/subscription-customer.php';
323 }
324
325 /**
326 * Display subscription info.
327 *
328 * @return void
329 */
330 public function subscrpt_order_info() {
331 $order_id = get_post_meta( get_the_ID(), '_subscrpt_order_id', true );
332 $order_item_id = get_post_meta( get_the_ID(), '_subscrpt_order_item_id', true );
333 $trial = get_post_meta( get_the_ID(), '_subscrpt_trial', true );
334 $start_date = get_post_meta( get_the_ID(), '_subscrpt_start_date', true );
335 $next_date = get_post_meta( get_the_ID(), '_subscrpt_next_date', true );
336 $trial_start_date = get_post_meta( get_the_ID(), '_subscrpt_trial_started', true );
337 $trial_end_date = get_post_meta( get_the_ID(), '_subscrpt_trial_ended', true );
338 $trial_mode = get_post_meta( get_the_ID(), '_subscrpt_trial_mode', true );
339 $order = wc_get_order( $order_id );
340 if ( ! $order ) {
341 return;
342 }
343 $order_item = $order->get_item( $order_item_id );
344
345 $product_name = $order_item->get_name();
346 $product_link = get_the_permalink( $order_item->get_product_id() );
347
348 // Get payment information
349 $product_id = $order_item->get_product_id(); // get_post_meta( get_the_ID(), '_subscrpt_product_id', true );
350 $max_payments = subscrpt_get_max_payments( get_the_ID() ) ?: 0;
351 $payments_made = subscrpt_count_payments_made( get_the_ID() );
352
353 $rows = array(
354 'product' => array(
355 'label' => __( 'Product', 'subscription' ),
356 'value' => '<a href="' . esc_html( $product_link ) . '" target="_blank">' . esc_html( $product_name ) . '</a>',
357 ),
358 'cost' => array(
359 'label' => __( 'Cost', 'subscription' ),
360 'value' => Helper::format_price_with_order_item( get_post_meta( get_the_ID(), '_subscrpt_price', true ), $order_item->get_id() ),
361 ),
362 'quantity' => array(
363 'label' => __( 'Qty', 'subscription' ),
364 'value' => "x{$order_item->get_quantity()}",
365 ),
366 );
367
368 // Add payment information if max_payments is set and not unlimited
369 if ( ! empty( $max_payments ) && $max_payments > 0 ) {
370 $rows['total_payments'] = array(
371 'label' => __( 'Total Payments', 'subscription' ),
372 'value' => esc_html( $payments_made ) . ' / ' . esc_html( $max_payments ),
373 );
374 }
375
376 $rows += array(
377 'start_date' => array(
378 'label' => __( 'Started date', 'subscription' ),
379 'value' => ! empty( $start_date ) ? wp_date( 'F d, Y', $trial && $trial_start_date ? $trial_start_date : $start_date ) : '-',
380 ),
381 'next_date' => array(
382 'label' => __( 'Payment due date', 'subscription' ),
383 'value' => ! empty( $next_date ) ? wp_date( 'F d, Y', $trial && $trial_end_date && 'on' === $trial_mode ? $trial_end_date : ( $next_date ?? '-' ) ) : '-',
384 ),
385 'status' => array(
386 'label' => __( 'Status', 'subscription' ),
387 'value' => '<span class="subscrpt-' . get_post_status() . '">' . get_post_status_object( get_post_status() )->label . '</span>',
388 ),
389 'payment_method' => array(
390 'label' => __( 'Payment Method', 'subscription' ),
391 'value' => empty( $order->get_payment_method_title() ) ? '-' : $order->get_payment_method_title(),
392 ),
393 'billing_address' => array(
394 'label' => __( 'Billing', 'subscription' ),
395 'value' => $order->get_formatted_billing_address() ? $order->get_formatted_billing_address() : __( 'No billing address set.', 'subscription' ),
396 ),
397 'shipping_address' => array(
398 'label' => __( 'Shipping', 'subscription' ),
399 'value' => $order->get_formatted_shipping_address() ? $order->get_formatted_shipping_address() : __( 'No shipping address set.', 'subscription' ),
400 ),
401 );
402 if ( $trial ) {
403 $rows = array_slice( $rows, 0, 3, true ) + array(
404 'trial' => array(
405 'label' => __( 'Trial', 'subscription' ),
406 'value' => $trial,
407 ),
408 'trial_period' => array(
409 'label' => __( 'Trial Period', 'subscription' ),
410 'value' => ( $trial_start_date && $trial_end_date ? ' [ ' . wp_date( 'F d, Y', $trial_start_date ) . ' - ' . wp_date( 'F d, Y', $trial_end_date ) . ' ] ' : __( 'Trial isn\'t activated yet! ', 'subscription' ) ),
411 ),
412 ) + array_slice( $rows, 3, count( $rows ) - 1, true );
413 }
414
415 if ( class_exists( 'WC_Stripe' ) && 'stripe' === $order->get_payment_method() ) {
416 $is_auto_renew = get_post_meta( get_the_ID(), '_subscrpt_auto_renew', true );
417 $new_rows = array();
418 foreach ( $rows as $key => $value ) {
419 $new_rows[ $key ] = $value;
420 if ( 'payment_method' === $key ) {
421 $new_rows['stripe_auto_renewal'] = array(
422 'label' => __( 'Stripe Auto Renewal', 'subscription' ),
423 'value' => '0' !== $is_auto_renew ? 'On' : 'Off',
424 );
425 }
426 }
427
428 $rows = $new_rows;
429 }
430
431 $rows = apply_filters( 'subscrpt_admin_info_rows', $rows, get_the_ID(), $order );
432
433 include 'views/subscription-info.php';
434 }
435
436 /**
437 * Display a prominent subscription details section.
438 *
439 * @param \WP_Post $post The post object.
440 *
441 * @return void
442 */
443 public function display_subscription_details_section( $post ) {
444 // Only display for subscription post type
445 if ( 'subscrpt_order' !== $post->post_type ) {
446 return;
447 }
448
449 $subscription_id = $post->ID;
450 $subscription_data = Helper::get_subscription_data( $subscription_id );
451
452 $order_id = $subscription_data['order']['order_id'] ?? 0;
453 $order = $order_id ? wc_get_order( $order_id ) : null;
454
455 $order_item_id = $subscription_data['order']['order_item_id'] ?? 0;
456 $order_item = $order ? $order->get_item( $order_item_id ) : null;
457
458 if ( ! $order || ! $order_item ) {
459 return;
460 }
461
462 $product_id = $subscription_data['product']['product_id'] ?? 0;
463 $max_payments = ! empty( subscrpt_get_max_payments( $subscription_id ) ) ? subscrpt_get_max_payments( $subscription_id ) : 0;
464 $payments_made = subscrpt_count_payments_made( $subscription_id );
465
466 // Get subscription details
467 $product = wc_get_product( $product_id );
468 $subscrpt_type = $subscription_data['schedule']['timing_option'] ?? '';
469 $subscrpt_time = $subscription_data['schedule']['timing_per'] ?? '';
470
471 $trial_type = $subscription_data['trial']['timing_option'] ?? 'days';
472 $trial_time = $subscription_data['trial']['timing_per'] ?? 0;
473
474 $signup_fee = $subscription_data['signup_fee'] ?? '';
475
476 $cost = $subscription_data['price'] ?? 0;
477 $cost = empty( $cost ) ? $order_item->get_total() : $cost;
478
479 $price_excl_tax = (float) $order_item->get_total();
480 $tax_amount = (float) $order_item->get_total_tax();
481 if ( $tax_amount > 0 ) {
482 $cost = $price_excl_tax + $tax_amount;
483 $cost = number_format( (float) $cost, 2, '.', '' );
484 }
485
486 $subscrpt_status = $subscription_data['status'] ?? '';
487 $verbose_status = Helper::get_verbose_status( $subscrpt_status );
488
489 $started_date = $subscription_data['start_date'] ?? '';
490 $started_date = ! empty( $started_date ) ? wp_date( 'F j, Y - g:i A', strtotime( $started_date ) ) : '-';
491
492 $next_payment_date = $subscription_data['next_date'] ?? '';
493 $next_payment_date = ! empty( $next_payment_date ) ? wp_date( 'F j, Y - g:i A', strtotime( $next_payment_date ) ) : '-';
494
495 $is_grace_period = isset( $subscription_data['grace_period'] );
496 $grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0;
497 $grace_end_date = $subscription_data['grace_period']['end_date'] ?? '';
498 $grace_end_date = ! empty( $grace_end_date ) ? wp_date( 'F j, Y - g:i A', strtotime( $grace_end_date ) ) : '';
499
500 ?>
501 <div class="wp-subscription-details-section">
502 <h2 style="margin: 0 0 20px 0; padding: 0; border-bottom: 1px solid #ddd; padding-bottom: 12px;">
503 <?php
504 $text = sprintf(
505 // translators: Subscription ID.
506 __( 'Subscription #%d details', 'subscription' ),
507 $post->ID
508 );
509 echo esc_html( $text );
510 ?>
511 </h2>
512
513 <div class="subscription-details-grid">
514 <!-- Primary Information -->
515 <div class="details-group primary">
516 <h3><?php esc_html_e( 'General', 'subscription' ); ?></h3>
517 <table>
518 <tr>
519 <th><?php esc_html_e( 'Status', 'subscription' ); ?></th>
520 <td>
521 <?php if ( $is_grace_period && $grace_remaining > 0 ) : ?>
522 <span class="subscrpt-active grace-active">
523 Active
524
525 <?php
526 $grace_remaining_text = sprintf(
527 // translators: Number of days remaining in grace period.
528 __( '%d days remaining!', 'subscription' ),
529 $grace_remaining
530 );
531 ?>
532 <span class="grace-icon" data-tooltip="<?php echo esc_attr( $grace_remaining_text ); ?>">
533 <span class="dashicons dashicons-warning"></span>
534 </span>
535 </span>
536 <?php else : ?>
537 <span class="subscrpt-<?php echo esc_attr( strtolower( $subscrpt_status ) ); ?>">
538 <?php echo esc_html( $verbose_status ); ?>
539 </span>
540 <?php endif; ?>
541 </td>
542 </tr>
543 <tr>
544 <th><?php esc_html_e( 'Product', 'subscription' ); ?></th>
545 <td><a href="<?php echo esc_url( get_the_permalink( $order_item->get_product_id() ) ); ?>" target="_blank"><?php echo esc_html( $order_item->get_name() ); ?></a></td>
546 </tr>
547 </table>
548 </div>
549
550 <!-- Subscription Terms -->
551 <div class="details-group">
552 <h3><?php esc_html_e( 'Subscription Terms', 'subscription' ); ?></h3>
553 <table>
554 <tr>
555 <th><?php esc_html_e( 'Billing', 'subscription' ); ?></th>
556 <td>
557 <?php echo wp_kses_post( wc_price( $cost ) ); ?> /
558 <?php
559 echo esc_html( $subscrpt_time > 1 ? $subscrpt_time . '-' : '' );
560 ?>
561 <?php
562 echo esc_html( $subscrpt_type );
563 ?>
564 </td>
565 </tr>
566
567 <?php if ( $signup_fee ) : ?>
568 <tr>
569 <th><?php esc_html_e( 'Signup Fee', 'subscription' ); ?></th>
570 <td><?php echo wp_kses_post( wc_price( $signup_fee ) ); ?></td>
571 </tr>
572 <?php endif; ?>
573
574 <?php if ( $trial_time ) : ?>
575 <tr>
576 <th><?php esc_html_e( 'Free Trial', 'subscription' ); ?></th>
577 <td><?php echo esc_html( $trial_time . ' ' . $trial_type ); ?></td>
578 </tr>
579 <?php endif; ?>
580
581 <?php if ( ! empty( $max_payments ) && $max_payments > 0 ) : ?>
582 <tr>
583 <th><?php esc_html_e( 'Total Payments', 'subscription' ); ?></th>
584 <td><strong><?php echo esc_html( $payments_made . ' / ' . $max_payments ); ?></strong></td>
585 </tr>
586 <?php endif; ?>
587 </table>
588 </div>
589
590 <!-- Schedule Dates -->
591 <div class="details-group">
592 <h3><?php esc_html_e( 'Schedule Dates', 'subscription' ); ?></h3>
593 <table>
594 <tr>
595 <th><?php esc_html_e( 'Started', 'subscription' ); ?></th>
596 <td><?php echo esc_html( $started_date ); ?></td>
597 </tr>
598 <tr>
599 <th><?php esc_html_e( 'Next Payment', 'subscription' ); ?></th>
600 <td><?php echo esc_html( $next_payment_date ); ?></td>
601 </tr>
602 </table>
603 </div>
604
605 <!-- Grace Period Info -->
606 <?php if ( $is_grace_period ) : ?>
607 <div class="details-group">
608 <h3><?php esc_html_e( 'Grace Period', 'subscription' ); ?></h3>
609 <table>
610 <tr>
611 <th><?php esc_html_e( 'Remaining', 'subscription' ); ?></th>
612 <td><?php echo esc_html( $grace_remaining . ' days' ); ?></td>
613 </tr>
614 <tr>
615 <th><?php esc_html_e( 'End Date', 'subscription' ); ?></th>
616 <td><?php echo esc_html( $grace_end_date ); ?></td>
617 </tr>
618 </table>
619 </div>
620 <?php endif; ?>
621 </div>
622 </div>
623 <?php
624 }
625
626 /**
627 * Include some styles.
628 *
629 * @return void
630 */
631 public function some_styles() {
632 global $post;
633 if ( 'subscrpt_order' === $post->post_type ) :
634 ?>
635 <style>
636 .submitbox {
637 display: flex;
638 justify-content: space-around;
639 }
640
641 .subscrpt_sub_box {
642 display: grid;
643 line-height: 2;
644 }
645
646 /* Hide WordPress title area margin */
647 #poststuff #post-body.columns-2 {
648 margin-right: 300px;
649 }
650
651 /* Clean, minimal details section */
652 .wp-subscription-details-section {
653 margin: 20px 0 30px 0;
654 padding: 20px;
655 border: 1px solid #ddd;
656 background: #fff;
657 }
658
659 /* Grid layout for organized sections */
660 .subscription-details-grid {
661 display: grid;
662 grid-template-columns: 1fr 1fr;
663 gap: 30px;
664 margin-top: 20px;
665 }
666
667 @media (max-width: 782px) {
668 .subscription-details-grid {
669 grid-template-columns: 1fr;
670 }
671 }
672
673 /* Details group styling */
674 .details-group h3 {
675 margin: 0 0 12px 0;
676 padding: 0 0 8px 0;
677 border-bottom: 1px solid #eee;
678 font-size: 14px;
679 font-weight: 600;
680 color: #555;
681 text-transform: uppercase;
682 letter-spacing: 0.5px;
683 }
684
685 .details-group.primary h3 {
686 color: #333;
687 }
688
689 .details-group table {
690 width: 100%;
691 border-collapse: collapse;
692 }
693
694 .details-group table th {
695 text-align: left;
696 padding: 8px 12px 8px 0;
697 font-weight: 500;
698 color: #666;
699 width: 40%;
700 vertical-align: top;
701 font-size: 13px;
702 }
703
704 .details-group table td {
705 padding: 8px 0;
706 color: #333;
707 font-size: 13px;
708 line-height: 1.4;
709 }
710
711 .details-group table tr {
712 border-bottom: 1px solid #f5f5f5;
713 }
714
715 .details-group table tr:last-child {
716 border-bottom: none;
717 }
718
719 /* Simple status badge */
720 .status-badge {
721 display: inline-block;
722 padding: 2px 8px;
723 border-radius: 3px;
724 font-size: 12px;
725 font-weight: 500;
726 text-transform: capitalize;
727 background: #f5f5f5;
728 color: #666;
729 border: 1px solid #ddd;
730 }
731
732 /* Clean links */
733 .details-group a {
734 color: #0073aa;
735 text-decoration: none;
736 }
737
738 .details-group a:hover {
739 text-decoration: underline;
740 }
741
742 /* Meta box styling improvements */
743 .postbox {
744 border: 1px solid #ddd;
745 }
746
747 .postbox .hndle {
748 border-bottom: 1px solid #eee;
749 background: #fafafa;
750 }
751
752 #subscrpt_order_history .inside table,
753 #subscrpt_order_activities .inside table {
754 border: none;
755 }
756 </style>
757 <?php
758 endif;
759 }
760
761 /**
762 * Disable changes popup.
763 *
764 * @return void
765 */
766 public function some_scripts() {
767 global $post;
768 if ( 'subscrpt_order' === $post->post_type ) :
769 ?>
770 <script>
771 jQuery(document).ready(function() {
772 jQuery(window).off('beforeunload', null);
773 });
774 </script>
775 <?php
776 endif;
777 }
778
779 public function save_subscrpt_order( $post_id ) {
780 if ( wp_is_post_revision( $post_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! isset( $_POST['subscrpt_order_action'] ) ) {
781 return;
782 }
783
784 // Verify nonce for security.
785 if ( ! isset( $_POST['subscrpt_order_action_nonce_field'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['subscrpt_order_action_nonce_field'] ) ), 'subscrpt_order_action_nonce' ) ) {
786 return;
787 }
788
789 // Check permissions.
790 if ( ! current_user_can( 'edit_post', $post_id ) ) {
791 return;
792 }
793 remove_all_actions( 'save_post' );
794
795 $action = sanitize_text_field( wp_unslash( $_POST['subscrpt_order_action'] ) );
796 $old_status = get_post_status( $post_id );
797
798 wp_update_post(
799 array(
800 'ID' => $post_id,
801 'post_status' => $action,
802 )
803 );
804
805 if ( $old_status !== $action ) {
806 $old_status_object = get_post_status_object( $old_status );
807 $new_status_object = get_post_status_object( $action );
808 WC()->mailer();
809 do_action( 'subscrpt_status_changed_admin_email_notification', $post_id, $old_status_object->label, $new_status_object->label );
810 }
811
812 $order_id = get_post_meta( $post_id, '_subscrpt_order_id', true );
813 if ( 'active' === $action ) {
814 $order = wc_get_order( $order_id );
815 $order->update_status( 'completed' );
816 Action::status( $action, $post_id );
817 } else {
818 Action::status( $action, $post_id );
819 }
820 }
821
822 public function add_subscription_filter_select() {
823 // Implementation of add_subscription_filter_select method
824 }
825
826 public function add_overview_submenu() {
827 // Remove and re-add submenu to ensure Overview is first
828 remove_submenu_page( 'edit.php?post_type=subscrpt_order', 'edit.php?post_type=subscrpt_order' );
829 add_submenu_page(
830 'edit.php?post_type=subscrpt_order',
831 __( 'Overview', 'subscription' ),
832 __( 'Overview', 'subscription' ),
833 'manage_options',
834 'subscription_overview',
835 array( $this, 'render_overview_page' ),
836 0
837 );
838 add_submenu_page(
839 'edit.php?post_type=subscrpt_order',
840 __( 'All Subscriptions', 'subscription' ),
841 __( 'All Subscriptions', 'subscription' ),
842 'manage_options',
843 'edit.php?post_type=subscrpt_order',
844 '',
845 1
846 );
847 if ( ! class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) {
848 add_submenu_page(
849 'edit.php?post_type=subscrpt_order',
850 __( 'Go Pro', 'subscription' ),
851 __( 'Go Pro', 'subscription' ),
852 'manage_options',
853 'wp_subscription_go_pro',
854 array( $this, 'render_go_pro_page' ),
855 99
856 );
857 }
858 }
859
860 public function render_overview_page() {
861 ?>
862 <div class="wrap wpsubscription-overview" style="max-width:1100px;margin:40px auto 0 auto;">
863 <div class="wpsubscription-overview-card" style="background:#fff;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,0.06);padding:40px 32px 32px 32px;">
864 <div class="wpsubscription-overview-top" style="display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:start;margin-bottom:40px;">
865 <div class="wpsubscription-overview-info" style="display:flex;flex-direction:column;gap:18px;">
866 <h1 style="margin-bottom:0.2em;"><?php esc_html_e( 'WP Subscription Overview', 'subscription' ); ?></h1>
867 <p class="product-desc" style="font-size:1.15em;line-height:1.6;max-width:500px;">
868 <?php esc_html_e( 'WP Subscription is the most seamless and reliable WooCommerce subscription solution for store owners looking to grow recurring revenue. Easily manage recurring payments, automate renewals, and delight your customers with flexible plans.', 'subscription' ); ?>
869 </p>
870 <div class="wpsubscription-links" style="display:flex;gap:12px;flex-wrap:wrap;">
871 <a href="https://docs.converslabs.com/en" target="_blank" class="button button-secondary"><?php esc_html_e( 'Documentation', 'subscription' ); ?></a>
872 <a href="https://wpsubscription.co/" target="_blank" class="button button-secondary"><?php esc_html_e( 'Website', 'subscription' ); ?></a>
873 </div>
874 </div>
875 <div class="promo-video" style="text-align:center;">
876 <iframe width="420" height="236" src="https://www.youtube.com/embed/2e6o5p0M7L4" title="WP Subscription Promo" frameborder="0" allowfullscreen style="max-width:100%;border-radius:8px;"></iframe>
877 </div>
878 </div>
879
880 <div class="wpsubscription-what-section" style="margin-bottom:40px;">
881 <h2><?php esc_html_e( 'What does Subscriptions for WooCommerce do?', 'subscription' ); ?></h2>
882 <p style="font-size:1.08em;max-width:900px;line-height:1.7;">
883 <?php esc_html_e( 'Subscriptions for WooCommerce enables you to create and manage recurring payment products and services with ease. Automate renewals, offer flexible billing schedules, and provide your customers with a seamless subscription experience. Whether you sell digital content, physical goods, or memberships, WP Subscription gives you the tools to grow your recurring revenue.', 'subscription' ); ?>
884 </p>
885 </div>
886
887 <h2 style="margin-top:2em;"><?php esc_html_e( 'Highlights', 'subscription' ); ?></h2>
888 <div class="wpsubscription-features-grid">
889 <div class="feature-box"><span class="dashicons dashicons-admin-generic"></span><h3>Easy Setup</h3><p>Get started in minutes with our intuitive onboarding wizard.</p></div>
890 <div class="feature-box"><span class="dashicons dashicons-money"></span><h3>Multiple Gateways</h3><p>Support for Stripe, PayPal, and Paddle out of the box.</p></div>
891 <div class="feature-box"><span class="dashicons dashicons-schedule"></span><h3>Flexible Plans</h3><p>Create and manage various subscription types and delivery schedules.</p></div>
892 <div class="feature-box"><span class="dashicons dashicons-chart-line"></span><h3>Comprehensive Dashboard</h3><p>Monitor and manage all subscriptions in one place.</p></div>
893 </div>
894 </div>
895 </div>
896 <style>
897 .wpsubscription-overview .promo-video { text-align:center; }
898 .wpsubscription-features-grid {
899 display: grid;
900 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
901 gap: 24px;
902 margin-top: 32px;
903 }
904 .feature-box {
905 background: #fff;
906 border-radius: 10px;
907 box-shadow: 0 2px 8px rgba(0,0,0,0.06);
908 padding: 24px 18px 18px 18px;
909 text-align: center;
910 transition: transform 0.2s, box-shadow 0.2s;
911 will-change: transform;
912 }
913 .feature-box:hover {
914 transform: translateY(-6px) scale(1.03);
915 box-shadow: 0 6px 24px rgba(0,0,0,0.10);
916 }
917 .feature-box .dashicons {
918 font-size: 2.2em;
919 color: #7f54b3;
920 margin-bottom: 10px;
921 display: block;
922 }
923 .feature-box h3 {
924 margin: 12px 0 8px 0;
925 font-size: 1.15em;
926 }
927 .feature-box p {
928 color: #555;
929 font-size: 1em;
930 margin: 0;
931 }
932 </style>
933 <?php
934 }
935
936 public function render_go_pro_page() {
937 if ( class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) {
938 echo '<div class="notice notice-info" style="margin:40px auto;max-width:700px;text-align:center;font-size:1.2em;">Pro is already active.</div>';
939 return;
940 }
941 ?>
942 <div class="wrap wpsubscription-go-pro" style="max-width:900px;margin:40px auto 0 auto;">
943 <div class="wpsubscription-go-pro-card" style="background:#fff;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,0.06);padding:40px 32px 32px 32px;">
944 <h1 style="margin-bottom:0.5em;"><?php esc_html_e( 'Upgrade to WP Subscription Pro', 'subscription' ); ?></h1>
945 <p style="font-size:1.12em;max-width:600px;line-height:1.6;">
946 <?php esc_html_e( 'Unlock the full power of subscriptions for WooCommerce. Get advanced features, priority support, and more ways to grow your recurring revenue.', 'subscription' ); ?>
947 </p>
948 <table class="wpsubscription-compare-table" style="width:100%;margin:32px 0 40px 0;border-collapse:separate;border-spacing:0;box-shadow:0 1px 4px rgba(0,0,0,0.04);background:#fafbfc;border-radius:8px;overflow:hidden;">
949 <thead>
950 <tr style="background:#f8f9fa;">
951 <th style="padding:18px 12px 18px 24px;font-size:1.08em;text-align:left;border:none;"></th>
952 <th style="padding:18px 12px;font-size:1.08em;text-align:center;border:none;">Free</th>
953 <th style="padding:18px 12px;font-size:1.08em;text-align:center;border:none;color:#7f54b3;">Pro</th>
954 </tr>
955 </thead>
956 <tbody>
957 <tr>
958 <td style="padding:16px 12px 16px 24px;">Simple subscription products</td>
959 <td style="text-align:center;">✔️</td>
960 <td style="text-align:center;">✔️</td>
961 </tr>
962 <tr style="background:#f6f7f7;">
963 <td style="padding:16px 12px 16px 24px;">Automated recurring billing</td>
964 <td style="text-align:center;">✔️</td>
965 <td style="text-align:center;">✔️</td>
966 </tr>
967 <tr>
968 <td style="padding:16px 12px 16px 24px;">Multiple payment gateways</td>
969 <td style="text-align:center;">✔️</td>
970 <td style="text-align:center;">✔️</td>
971 </tr>
972 <tr style="background:#f6f7f7;">
973 <td style="padding:16px 12px 16px 24px;">Customer self-service portal</td>
974 <td style="text-align:center;">✔️</td>
975 <td style="text-align:center;">✔️</td>
976 </tr>
977 <tr>
978 <td style="padding:16px 12px 16px 24px;">Priority support</td>
979 <td style="text-align:center;"></td>
980 <td style="text-align:center;">✔️</td>
981 </tr>
982 <tr style="background:#f6f7f7;">
983 <td style="padding:16px 12px 16px 24px;">Advanced reporting & analytics</td>
984 <td style="text-align:center;"></td>
985 <td style="text-align:center;">✔️</td>
986 </tr>
987 <tr>
988 <td style="padding:16px 12px 16px 24px;">Variable product support</td>
989 <td style="text-align:center;"></td>
990 <td style="text-align:center;font-weight:600;color:#43a047;">✔️</td>
991 </tr>
992 </tbody>
993 </table>
994 <div style="text-align:center;margin-top:24px;">
995 <a href="https://wpsubscription.co/" target="_blank" class="button button-primary button-hero" style="font-size:1.2em;padding:16px 40px 16px 40px;background:#7f54b3;border:none;box-shadow:0 2px 8px rgba(127,84,179,0.10);">
996 <?php esc_html_e( 'Upgrade to Pro', 'subscription' ); ?>
997 </a>
998 </div>
999 </div>
1000 </div>
1001 <?php
1002 }
1003
1004 /**
1005 * Add "Back to list" button next to the post title.
1006 */
1007 public function add_back_to_list_button() {
1008 global $post;
1009
1010 // Only apply on edit subscription screen
1011 if ( ! isset( $post ) || $post->post_type !== 'subscrpt_order' ) {
1012 return;
1013 }
1014
1015 $list_url = admin_url( 'admin.php?page=wp-subscription' );
1016 ?>
1017 <script type="text/javascript">
1018 document.addEventListener('DOMContentLoaded', function() {
1019 const heading = document.querySelector('.wrap .wp-heading-inline');
1020 if (heading) {
1021 const iconLink = document.createElement('a');
1022 iconLink.href = '<?php echo esc_url( $list_url ); ?>';
1023 iconLink.innerHTML = '<span class="dashicons dashicons-arrow-left-alt2" style="font-size:28px;height:28px;width:28px;"></span>';
1024 iconLink.style.cssText = "text-decoration:none;color:#555;display:inline-flex;align-items:center;margin-right:2px;transform:translateY(20%);box-shadow:none;";
1025 iconLink.title = '<?php echo esc_html_e( 'Back to subscriptions list.', 'subscription' ); ?>';
1026
1027 heading.insertAdjacentElement('beforebegin', iconLink);
1028 }
1029 });
1030 </script>
1031 <?php
1032 }
1033 }
1034