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 / Menu.php

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

698 lines 26.5 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 /**
6 * Menu class
7 *
8 * @package SpringDevs\Subscription\Admin
9 */
10 class Menu {
11
12 /**
13 * Initialize the class
14 */
15 public function __construct() {
16 add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
17 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
18 add_action( 'wp_ajax_subscrpt_bulk_action', array( $this, 'handle_bulk_action_ajax' ) );
19 }
20
21 /**
22 * Enqueue admin assets
23 */
24 public function enqueue_admin_assets() {
25 wp_enqueue_style(
26 'wp-subscription-admin',
27 SUBSCRPT_ASSETS . '/css/admin.css',
28 array(),
29 SUBSCRPT_VERSION
30 );
31
32 // Enqueue admin JavaScript for subscription list functionality
33 wp_enqueue_script(
34 'sdevs_subscription_admin',
35 SUBSCRPT_ASSETS . '/js/admin.js',
36 array( 'jquery' ),
37 SUBSCRPT_VERSION,
38 true
39 );
40
41 // Localize script for AJAX
42 wp_localize_script(
43 'sdevs_subscription_admin',
44 'wp_subscription_ajax',
45 array(
46 'nonce' => wp_create_nonce( 'subscrpt_bulk_action_nonce' ),
47 'ajaxurl' => admin_url( 'admin-ajax.php' ),
48 )
49 );
50 }
51
52 /**
53 * Create Subscriptions Menu.
54 */
55 public function create_admin_menu() {
56 $parent_slug = 'wp-subscription';
57 // Determine if the menu is active
58 $is_active = isset( $_GET['page'] ) && strpos( sanitize_text_field( wp_unslash( $_GET['page'] ) ), 'wp-subscription' ) === 0;
59 $icon_url = $is_active
60 ? SUBSCRPT_ASSETS . '/images/icons/subscription-20.png'
61 : SUBSCRPT_ASSETS . '/images/icons/subscription-20-gray.png';
62 // Main menu
63 add_menu_page(
64 __( 'WP Subscription', 'subscription' ),
65 __( 'WP Subscription', 'subscription' ),
66 'manage_woocommerce',
67 $parent_slug,
68 array( $this, 'render_subscriptions_page' ),
69 $icon_url,
70 40
71 );
72
73 // Subscriptions List
74 add_submenu_page(
75 $parent_slug,
76 __( 'Subscriptions', 'subscription' ),
77 __( 'Subscriptions', 'subscription' ),
78 'manage_woocommerce',
79 $parent_slug,
80 array( $this, 'render_subscriptions_page' )
81 );
82
83 // Stats Overview
84 add_submenu_page(
85 $parent_slug,
86 __( 'Reports', 'subscription' ),
87 __( 'Reports', 'subscription' ),
88 'manage_woocommerce',
89 'wp-subscription-stats',
90 array( $this, 'render_stats_page' )
91 );
92
93 /*
94 ! For god's sake, check existing code before implementing!
95 // Settings
96 add_submenu_page(
97 $parent_slug,
98 __( 'Settings', 'subscription' ),
99 __( 'Settings', 'subscription' ),
100 'manage_woocommerce',
101 'wp-subscription-settings',
102 array( $this, 'render_settings_page' )
103 );
104 */
105
106 // Support
107 add_submenu_page(
108 $parent_slug,
109 __( 'Support', 'subscription' ),
110 __( 'Support', 'subscription' ),
111 'manage_woocommerce',
112 'wp-subscription-support',
113 array( $this, 'render_support_page' )
114 );
115
116 // Add WP Subscription link under WooCommerce menu
117 add_submenu_page(
118 'woocommerce',
119 __( 'WP Subscription', 'subscription' ),
120 __( 'WP Subscription', 'subscription' ),
121 'manage_woocommerce',
122 'wp-subscription',
123 array( $this, 'render_subscriptions_page' )
124 );
125 }
126 /**
127 * Render the admin header
128 */
129 public function render_admin_footer() {
130 ?>
131 <div style="text-align:center;margin:38px 0 0 0;font-size:14px;color:#888;">
132 Made with <span style="color:#e25555;font-size:1.1em;"></span> by the WP Subscription Team
133 <div style="margin-top:6px;">
134 <a href="https://wpsubscription.co/contact" target="_blank" style="color:#2563eb;text-decoration:none;">Support</a>
135 &nbsp;/&nbsp;
136 <a href="https://docs.converslabs.com/en" target="_blank" style="color:#2563eb;text-decoration:none;">Docs</a>
137 </div>
138 </div>
139 <?php
140 }
141 /**
142 * Render the admin header
143 */
144 public function render_admin_header() {
145 // Get current page slug
146 $current = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : 'wp-subscription';
147 $menu_items = [
148 [
149 'slug' => 'wp-subscription',
150 'label' => __( 'Subscriptions', 'subscription' ),
151 'url' => admin_url( 'admin.php?page=wp-subscription' ),
152 ],
153 [
154 'slug' => 'wp-subscription-stats',
155 'label' => __( 'Reports', 'subscription' ),
156 'url' => admin_url( 'admin.php?page=wp-subscription-stats' ),
157 ],
158 [
159 'slug' => 'wp-subscription-settings',
160 'label' => __( 'Settings', 'subscription' ),
161 'url' => admin_url( 'admin.php?page=wp-subscription-settings' ),
162 ],
163 ];
164 // Allow pro plugin to inject menu items
165 $menu_items = apply_filters( 'subscrpt_admin_header_menu_items', $menu_items, $current );
166 $menu_items = array_merge(
167 $menu_items,
168 [
169 [
170 'slug' => 'wp-subscription-support',
171 'label' => __( 'Support', 'subscription' ),
172 'url' => admin_url( 'admin.php?page=wp-subscription-support' ),
173 ],
174 ]
175 );
176 ?>
177 <div class="wp-subscription-admin-header">
178 <div style="width:1240px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;">
179 <div class="wp-subscription-admin-header-left" style="display:flex;align-items:center;gap:14px;">
180 <img style="height:30px;" src="<?php echo esc_url( SUBSCRPT_ASSETS . '/images/logo-title.svg' ); ?>" alt="WP Subscription" class="wp-subscription-logo">
181 <nav class="wp-subscription-admin-header-menu">
182 <?php foreach ( $menu_items as $item ) : ?>
183 <a href="<?php echo esc_url( $item['url'] ); ?>" class="<?php echo ( $current === $item['slug'] ) ? 'current' : ''; ?>">
184 <?php echo esc_html( $item['label'] ); ?>
185 </a>
186 <?php endforeach; ?>
187 </nav>
188 </div>
189 <div class="wp-subscription-admin-header-right">
190 <?php if ( ! class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) : ?>
191 <a target="_blank" href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro" class="wp-subscription-upgrade-btn"><?php esc_html_e( 'Upgrade to Pro', 'subscription' ); ?></a>
192 <?php endif; ?>
193 </div>
194 </div>
195 </div>
196 <?php
197 }
198
199 /**
200 * Render Subscriptions page
201 */
202 public function render_subscriptions_page() {
203 $this->render_admin_header();
204
205 // Handle filters
206 $status = isset( $_GET['subscrpt_status'] ) ? sanitize_text_field( wp_unslash( $_GET['subscrpt_status'] ) ) : '';
207 $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
208 $date_filter = isset( $_GET['date_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['date_filter'] ) ) : '';
209 $per_page = isset( $_GET['per_page'] ) ? max( 1, intval( $_GET['per_page'] ) ) : 20;
210 $paged = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
211
212 // Handle form submissions (both filters and bulk actions)
213 $request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';
214 if ( 'POST' === $request_method ) {
215 // Verify nonce before processing any POST data.
216 $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
217 if ( ! wp_verify_nonce( $nonce, 'subscrpt_list_action' ) ) {
218 wp_die( esc_html__( 'Security check failed.', 'subscription' ) );
219 }
220 // Handle bulk actions
221 if ( isset( $_POST['bulk_action'] ) || isset( $_POST['bulk_action2'] ) ) {
222 $bulk_action = isset( $_POST['bulk_action'] ) ? sanitize_text_field( wp_unslash( $_POST['bulk_action'] ) ) : sanitize_text_field( wp_unslash( $_POST['bulk_action2'] ?? '' ) );
223 $action = isset( $_POST['action'] ) ? sanitize_text_field( wp_unslash( $_POST['action'] ) ) : sanitize_text_field( wp_unslash( $_POST['action2'] ?? '' ) );
224
225 if ( $bulk_action && $action && $action !== '-1' && isset( $_POST['subscription_ids'] ) && is_array( $_POST['subscription_ids'] ) ) {
226 $subscription_ids = array_map( 'intval', $_POST['subscription_ids'] );
227
228 if ( $action === 'trash' ) {
229 foreach ( $subscription_ids as $sub_id ) {
230 wp_trash_post( $sub_id );
231 }
232 } elseif ( $action === 'restore' ) {
233 foreach ( $subscription_ids as $sub_id ) {
234 wp_untrash_post( $sub_id );
235 }
236 } elseif ( $action === 'delete' ) {
237 foreach ( $subscription_ids as $sub_id ) {
238 wp_delete_post( $sub_id, true );
239 }
240 }
241
242 wp_safe_redirect( admin_url( 'admin.php?page=wp-subscription' ) );
243 exit;
244 }
245 }
246
247 // Handle filter form submission
248 if ( isset( $_POST['filter_action'] ) ) {
249 $filter_params = array();
250
251 if ( ! empty( $_POST['subscrpt_status'] ) ) {
252 $filter_params['subscrpt_status'] = sanitize_text_field( wp_unslash( $_POST['subscrpt_status'] ) );
253 }
254 if ( ! empty( $_POST['date_filter'] ) ) {
255 $filter_params['date_filter'] = sanitize_text_field( wp_unslash( $_POST['date_filter'] ) );
256 }
257 if ( ! empty( $_POST['s'] ) ) {
258 $filter_params['s'] = sanitize_text_field( wp_unslash( $_POST['s'] ) );
259 }
260 if ( ! empty( $_POST['per_page'] ) ) {
261 $filter_params['per_page'] = intval( $_POST['per_page'] );
262 }
263
264 $redirect_url = add_query_arg( $filter_params, admin_url( 'admin.php?page=wp-subscription' ) );
265 wp_safe_redirect( $redirect_url );
266 exit;
267 }
268 }
269
270 // Handle individual actions
271 if ( isset( $_GET['action'] ) && ! empty( $_GET['sub_id'] ) ) {
272 $sub_id = intval( $_GET['sub_id'] );
273 $action = sanitize_text_field( wp_unslash( $_GET['action'] ) );
274 $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
275
276 // Clean trash action.
277 if ( $action === 'clean_trash' ) {
278 // Verify nonce for security.
279 $nonce_action = 'wpsubs_action_clean_trash';
280 if ( ! wp_verify_nonce( $nonce, $nonce_action ) ) {
281 echo '<div class="notice notice-error"><p>' . esc_html__( 'Security check failed. Please try again.', 'subscription' ) . '</p></div>';
282 wp_die();
283 }
284
285 // Clean all trash items.
286 $trash_posts = get_posts(
287 [
288 'post_type' => 'subscrpt_order',
289 'post_status' => 'trash',
290 'numberposts' => -1,
291 'fields' => 'ids',
292 ]
293 );
294
295 foreach ( $trash_posts as $trash_id ) {
296 wp_delete_post( $trash_id, true );
297 }
298
299 wp_safe_redirect( admin_url( 'admin.php?page=wp-subscription&subscrpt_status=trash' ) );
300 exit;
301 } else {
302 // For other actions, verify nonce with subscription ID.
303 $nonce_action = 'wpsubs_action_' . $sub_id;
304 if ( ! wp_verify_nonce( $nonce, $nonce_action ) ) {
305 echo '<div class="notice notice-error"><p>' . esc_html__( 'Security check failed. Please try again.', 'subscription' ) . '</p></div>';
306 wp_die();
307 }
308
309 $redirect_url = admin_url( 'admin.php?page=wp-subscription' );
310
311 switch ( $action ) {
312 case 'duplicate':
313 $post = get_post( $sub_id );
314 if ( $post && $post->post_type === 'subscrpt_order' ) {
315 $new_post = [
316 'post_title' => $post->post_title . ' (Copy)',
317 'post_content' => $post->post_content,
318 'post_status' => 'draft',
319 'post_type' => 'subscrpt_order',
320 ];
321 $new_id = wp_insert_post( $new_post );
322 if ( $new_id ) {
323 $meta = get_post_meta( $sub_id );
324 foreach ( $meta as $key => $values ) {
325 foreach ( $values as $value ) {
326 add_post_meta( $new_id, $key, maybe_unserialize( $value ) );
327 }
328 }
329 }
330 }
331 break;
332 case 'trash':
333 wp_trash_post( $sub_id );
334 break;
335 case 'restore':
336 wp_untrash_post( $sub_id );
337 break;
338 case 'delete':
339 wp_delete_post( $sub_id, true );
340 $redirect_url = admin_url( 'admin.php?page=wp-subscription&subscrpt_status=trash' );
341 break;
342 }
343
344 wp_safe_redirect( $redirect_url );
345 exit;
346 }
347 }
348
349 $args = [
350 'post_type' => 'subscrpt_order',
351 'post_status' => 'any',
352 'posts_per_page' => $per_page,
353 'paged' => $paged,
354 'orderby' => 'date',
355 'order' => 'DESC',
356 ];
357
358 if ( $status ) {
359 $args['post_status'] = $status;
360 }
361 // Search only by subscription ID
362 if ( $search !== '' ) {
363 if ( is_numeric( $search ) ) {
364 $args['p'] = intval( $search );
365 } else {
366 // If not numeric, return no results
367 $args['post__in'] = array( 0 );
368 }
369 }
370 // Dynamic date filter (YYYY-MM)
371 if ( $date_filter && preg_match( '/^\d{4}-\d{2}$/', $date_filter ) ) {
372 $year = substr( $date_filter, 0, 4 );
373 $month = substr( $date_filter, 5, 2 );
374 $args['date_query'][] = [
375 'year' => intval( $year ),
376 'month' => intval( $month ),
377 ];
378 }
379
380 $query = new \WP_Query( $args );
381 $subscriptions = $query->posts;
382 $total = $query->found_posts;
383 $max_num_pages = $query->max_num_pages;
384
385 // Get all possible statuses for filter dropdown
386 $all_statuses = get_post_stati( [ 'show_in_admin_all_list' => true ], 'objects' );
387
388 include __DIR__ . '/views/subscription-list.php';
389 ?>
390 <div style="text-align:center;margin:38px 0 0 0;font-size:14px;color:#888;">
391 Made with <span style="color:#e25555;font-size:1.1em;"></span> by the WP Subscription Team
392 <div style="margin-top:6px;">
393 <a href="https://wpsubscription.co/contact" target="_blank" style="color:#2563eb;text-decoration:none;">Support</a>
394 &nbsp;/&nbsp;
395 <a href="https://docs.converslabs.com/en" target="_blank" style="color:#2563eb;text-decoration:none;">Docs</a>
396 </div>
397 </div>
398 <?php
399 }
400
401 /**
402 * Render Stats page
403 */
404 public function render_stats_page() {
405 $this->render_admin_header();
406
407 if ( ! class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) {
408 ?>
409 <div class="wp-subscription-admin-content" style="max-width:1240px;margin:32px auto 0 auto">
410 <div class="wp-subscription-hero-upgrade" style="margin-bottom:18px;">
411 <div class="wp-subscription-hero-content">
412 <span class="wp-subscription-hero-icon"></span>
413 <span class="wp-subscription-hero-title">
414 Unlock advanced features, priority support,<br>
415 and more subscription control and reporting.
416 </span>
417 </div>
418 <a href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro"
419 target="_blank"
420 class="wp-subscription-hero-btn">
421 UPGRADE TO PRO
422 </a>
423 </div>
424 </div>
425 <?php
426 } else {
427 // Allow pro plugin to override the entire stats page content.
428 do_action( 'subscrpt_render_stats_page' );
429 }
430
431 $this->render_admin_footer();
432
433 return;
434 }
435
436 /**
437 * Render Support page
438 */
439 public function render_support_page() {
440 $this->render_admin_header();
441 ?>
442 <div class="wp-subscription-admin-content" style="max-width:1240px;margin:32px auto 0 auto">
443 <?php if ( ! class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) : ?>
444 <!-- HERO VARIANT 1: Emoji -->
445 <div class="wp-subscription-hero-upgrade" style="margin-bottom:18px;">
446 <div class="wp-subscription-hero-content">
447 <span class="wp-subscription-hero-icon"></span>
448 <span class="wp-subscription-hero-title">
449 Unlock advanced features, priority support,<br>
450 and more subscription control and reporting.
451 </span>
452 </div>
453 <a href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro"
454 target="_blank"
455 class="wp-subscription-hero-btn">
456 UPGRADE TO PRO
457 </a>
458 </div>
459 <?php endif; ?>
460
461 <!-- Product Overview & Video -->
462 <div class="wp-subscription-admin-box" style="margin-bottom:24px;display:flex;gap:32px;align-items:flex-start;flex-wrap:wrap;">
463 <div style="flex:1;">
464 <h3>Product Overview</h3>
465 <p style="font-size:14px;line-height:1.7;margin:0 0 10px 0;">
466 WPSubscription helps you to sell products and services on a recurring basis using your existing WooCommerce store. Whether you're offering digital licenses, physical product boxes, or ongoing service plans, this plugin provides the tools to build and manage subscription models.
467 </p>
468 <ul style="font-size:14px;line-height:1.6;margin:0 0 0 18px;padding:0;list-style:disc;">
469 <li>Create simple or variable subscription products</li>
470 <li>Set billing intervals (daily, weekly, monthly, yearly)</li>
471 <li>Offer free trials and sign-up fees</li>
472 <li>Allow customers to cancel or renew subscriptions manually</li>
473 <li>View and manage subscriptions from the admin dashboard</li>
474 <li>Customize subscription behavior and role assignment</li>
475 <li>Integrate with payment gateways that support recurring billing (Stripe, PayPal)</li>
476 </ul>
477 </div>
478 <div style="flex:1;">
479 <div style="background:#f4f7fa;border-radius:8px;padding:10px 10px 6px 10px;box-shadow:0 2px 8px #e0e7ef;">
480 <div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:6px;text-align: center;">
481 <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/o8usgcZp1nY?si=iPb5z7bvcy0rIyOQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
482 ß </div>
483 <!-- <div style="text-align:center;font-size:13px;color:#888;margin-top:4px;">Watch: Quick Product Tour</div> -->
484 </div>
485 </div>
486 </div>
487
488 <!-- PRO Features List -->
489 <div class="wp-subscription-admin-box wp-subscription-pro-features" style="margin-bottom:32px;">
490 <div style="font-size:1.3em;font-weight:600;margin-bottom:12px;display:flex;align-items:center;gap:10px;">
491 <svg width="28" height="28" fill="none" viewBox="0 0 28 28"><circle cx="14" cy="14" r="14" fill="#2196f3"/><path d="M9 14.5l3 3 7-7" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
492 <span>WP Subscription PRO Features</span>
493 </div>
494 <ul class="wp-subscription-pro-feature-list" style="list-style:none;padding:0;margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px;">
495 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
496 <span style="font-size:1.5em;color:#2196f3;">🔀</span>
497 <span><b>Variable Product</b><br><span style="color:#555;font-size:0.98em;">Offer flexible subscription options for variable products.</span></span>
498 </li>
499 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
500 <span style="font-size:1.5em;color:#2196f3;">🚚</span>
501 <span><b>Delivery Schedule</b><br><span style="color:#555;font-size:0.98em;">Set custom delivery intervals for each subscription.</span></span>
502 </li>
503 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
504 <span style="font-size:1.5em;color:#2196f3;">📜</span>
505 <span><b>Subscription History</b><br><span style="color:#555;font-size:0.98em;">Track all changes and events for every subscription.</span></span>
506 </li>
507 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
508 <span style="font-size:1.5em;color:#2196f3;">⏳</span>
509 <span><b>More Subscription Durations</b><br><span style="color:#555;font-size:0.98em;">Offer more flexible and custom subscription periods.</span></span>
510 </li>
511 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
512 <span style="font-size:1.5em;color:#2196f3;">💶</span>
513 <span><b>Sign Up Fee</b><br><span style="color:#555;font-size:0.98em;">Charge a one-time sign up fee for new subscribers.</span></span>
514 </li>
515 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
516 <span style="font-size:1.5em;color:#2196f3;">⏩</span>
517 <span><b>Early Renewal</b><br><span style="color:#555;font-size:0.98em;">Allow customers to renew their subscription before expiry.</span></span>
518 </li>
519 <li style="background:#f4f7fa;border-radius:8px;padding:18px 20px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 8px #e0e7ef;">
520 <span style="font-size:1.5em;color:#2196f3;">💳</span>
521 <span><b>Renewal Price</b><br><span style="color:#555;font-size:0.98em;">Set a different price for subscription renewals.</span></span>
522 </li>
523 </ul>
524 </div>
525
526 <!-- Support Resources: 2 rows, 2 columns, each in its own box -->
527 <div class="wp-subscription-support-resources" style="display:grid;grid-template-columns:repeat(2,1fr);gap:24px;margin-bottom:24px;">
528 <div class="wp-subscription-admin-box">
529 <h3>Documentation</h3>
530 <p style="font-size:14px;margin:0 0 8px 0;">Read our <a href="https://docs.converslabs.com/en\" target=\"_blank\" style=\"color:#2271b1;\">comprehensive docs</a> for setup, migration, and advanced usage.</p>
531 <a href="https://docs.converslabs.com/en" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;">View Docs</a>
532 </div>
533 <div class="wp-subscription-admin-box">
534 <h3>Facing An Issue?</h3>
535 <p style="font-size:14px;margin:0 0 8px 0;">If you have a problem, <a href="https://wpsubscription.co/contact\" target=\"_blank\" style=\"color:#d93025;\">open a support ticket</a> or check our FAQ.</p>
536 <a href="https://wpsubscription.co/contact" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;">Get Support</a>
537 </div>
538 <div class="wp-subscription-admin-box">
539 <h3>Request a Feature</h3>
540 <p style="font-size:14px;margin:0 0 8px 0;">Have an idea? <a href="https://wpsubscription.co/contact\" target=\"_blank\" style=\"color:#2271b1;\">Request a feature</a> or vote on others.</p>
541 <a href="https://wpsubscription.co/contact" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;">Request Feature</a>
542 </div>
543 <div class="wp-subscription-admin-box">
544 <h3>Show Your Love</h3>
545 <p style="font-size:14px;margin:0 0 8px 0;">Enjoying WP Subscription? <a href="https://wordpress.org/support/plugin/subscription/reviews/\" target=\"_blank\" style=\"color:#f59e42;\">Leave us a review</a> or share your experience!</p>
546 <a href="https://wordpress.org/support/plugin/subscription/reviews/" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;">Leave a Review</a>
547 </div>
548 </div>
549 </div>
550 <div style="text-align:center;margin:38px 0 0 0;font-size:14px;color:#888;">
551 Made with <span style="color:#e25555;font-size:1.1em;">♥</span> by the WP Subscription Team
552 <div style="margin-top:6px;">
553 <a href="https://wpsubscription.co/contact" target="_blank" style="color:#2563eb;text-decoration:none;">Support</a>
554 &nbsp;/&nbsp;
555 <a href="https://docs.converslabs.com/en" target="_blank" style="color:#2563eb;text-decoration:none;">Docs</a>
556 </div>
557 </div>
558 <?php
559 }
560
561 /**
562 * Render the legacy subscriptions list page (WP_List_Table based)
563 */
564 public function render_legacy_subscriptions_page() {
565 // No longer needed, as the menu now links directly to the post type list.
566 }
567
568 /**
569 * Handle bulk action AJAX
570 */
571 public function handle_bulk_action_ajax() {
572 // Verify nonce
573 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
574 if ( ! wp_verify_nonce( $nonce, 'subscrpt_bulk_action_nonce' ) ) {
575 wp_send_json_error( array( 'message' => __( 'Security check failed.', 'subscription' ) ) );
576 }
577
578 // Check permissions
579 if ( ! current_user_can( 'manage_woocommerce' ) ) {
580 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'subscription' ) ) );
581 }
582
583 // Get action and subscription IDs
584 $bulk_action = isset( $_POST['bulk_action'] ) ? sanitize_text_field( wp_unslash( $_POST['bulk_action'] ) ) : '';
585 $subscription_ids = isset( $_POST['subscription_ids'] ) ? array_map( 'intval', $_POST['subscription_ids'] ) : array();
586
587 if ( empty( $subscription_ids ) ) {
588 wp_send_json_error( array( 'message' => __( 'No subscriptions selected.', 'subscription' ) ) );
589 }
590
591 $processed_count = 0;
592 $errors = array();
593
594 foreach ( $subscription_ids as $subscription_id ) {
595 $post = get_post( $subscription_id );
596
597 if ( ! $post || $post->post_type !== 'subscrpt_order' ) {
598 // translators: Subscription ID.
599 $errors[] = sprintf( __( 'Subscription #%d not found.', 'subscription' ), $subscription_id );
600 continue;
601 }
602
603 try {
604 switch ( $bulk_action ) {
605 case 'trash':
606 if ( wp_trash_post( $subscription_id ) ) {
607 ++$processed_count;
608 } else {
609 $errors[] = sprintf(
610 // translators: Subscription ID.
611 __( 'Failed to move subscription #%d to trash.', 'subscription' ),
612 $subscription_id
613 );
614 }
615 break;
616
617 case 'restore':
618 if ( wp_untrash_post( $subscription_id ) ) {
619 ++$processed_count;
620 } else {
621 $errors[] = sprintf(
622 // translators: Subscription ID.
623 __( 'Failed to restore subscription #%d.', 'subscription' ),
624 $subscription_id
625 );
626 }
627 break;
628
629 case 'delete':
630 if ( wp_delete_post( $subscription_id, true ) ) {
631 ++$processed_count;
632 } else {
633 $errors[] = sprintf(
634 // translators: Subscription ID.
635 __( 'Failed to delete subscription #%d.', 'subscription' ),
636 $subscription_id
637 );
638 }
639 break;
640
641 default:
642 $errors[] = sprintf(
643 // translators: Bulk action.
644 __( 'Unknown action: %s', 'subscription' ),
645 $bulk_action
646 );
647 break;
648 }
649 } catch ( Exception $e ) {
650 $errors[] = sprintf(
651 // translators: Subscription ID, Error message.
652 __( 'Error processing subscription #%1$d: %2$s', 'subscription' ),
653 $subscription_id,
654 $e->getMessage()
655 );
656 }
657 }
658
659 // Prepare response message
660 $message = '';
661 if ( $processed_count > 0 ) {
662 switch ( $bulk_action ) {
663 case 'trash':
664 $message = sprintf(
665 // translators: Number of subscriptions.
666 _n( '%d subscription moved to trash.', '%d subscriptions moved to trash.', $processed_count, 'subscription' ),
667 $processed_count
668 );
669 break;
670 case 'restore':
671 $message = sprintf(
672 // translators: Number of subscriptions.
673 _n( '%d subscription restored.', '%d subscriptions restored.', $processed_count, 'subscription' ),
674 $processed_count
675 );
676 break;
677 case 'delete':
678 $message = sprintf(
679 // translators: Number of subscriptions.
680 _n( '%d subscription permanently deleted.', '%d subscriptions permanently deleted.', $processed_count, 'subscription' ),
681 $processed_count
682 );
683 break;
684 }
685 }
686
687 if ( ! empty( $errors ) ) {
688 $message .= ' ' . __( 'Some errors occurred:', 'subscription' ) . ' ' . implode( ', ', $errors );
689 }
690
691 if ( $processed_count > 0 ) {
692 wp_send_json_success( array( 'message' => $message ) );
693 } else {
694 wp_send_json_error( array( 'message' => $message ? $message : __( 'No subscriptions were processed.', 'subscription' ) ) );
695 }
696 }
697 }
698