PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.1.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.1.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
subscription / includes / Admin / Subscriptions.php

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

514 lines 20.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( 'load-post.php', array( $this, 'redirect_legacy_edit_screen' ) );
30 add_filter( 'woocommerce_order_item_get_formatted_meta_data', array( $this, 'remove_order_meta' ), 10, 1 );
31 add_filter( 'bulk_actions-edit-subscrpt_order', array( $this, 'remove_bulk_actions' ) );
32 add_action( 'restrict_manage_posts', array( $this, 'add_subscription_filter_select' ) );
33 add_action( 'admin_menu', array( $this, 'add_overview_submenu' ), 40 );
34 }
35
36 /**
37 * Redirect the legacy CPT edit screen to the standalone details page.
38 *
39 * The subscription details UI now lives at
40 * admin.php?page=wp-subscription-details. Anyone landing on the old
41 * post.php edit screen for a subscrpt_order is sent there.
42 *
43 * @return void
44 */
45 public function redirect_legacy_edit_screen() {
46 $post_id = isset( $_GET['post'] ) ? absint( wp_unslash( $_GET['post'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
47 if ( $post_id && 'subscrpt_order' === get_post_type( $post_id ) ) {
48 wp_safe_redirect( admin_url( 'admin.php?page=wp-subscription-details&id=' . $post_id ) );
49 exit;
50 }
51 }
52
53 /**
54 * Remove 'Edit` and 'Trash' from bulk actions.
55 *
56 * @param array $actions Action list.
57 *
58 * @return array
59 */
60 public function remove_bulk_actions( $actions ) {
61 unset( $actions['edit'] );
62 unset( $actions['trash'] );
63
64 return $actions;
65 }
66
67 /**
68 * Hide order meta key from custom fields.
69 *
70 * @param array $formatted_meta Data with key-value.
71 *
72 * @return array
73 */
74 public function remove_order_meta( $formatted_meta ): array {
75 $temp_metas = array();
76 foreach ( $formatted_meta as $key => $meta ) {
77 if ( isset( $meta->key ) && '_renew_subscrpt' !== $meta->key ) {
78 $temp_metas[ $key ] = $meta;
79 }
80 }
81
82 return $temp_metas;
83 }
84
85 /**
86 * Enqueue admin assets.
87 *
88 * @return void
89 */
90 public function enqueue_scripts() {
91 wp_enqueue_style( 'subscrpt_admin_css' );
92 wp_enqueue_style( 'subscrpt_status_css' );
93 }
94
95 /**
96 * Remove default post actions.
97 *
98 * @param array $actions Actions.
99 *
100 * @return array
101 */
102 public function post_row_actions( $actions ) {
103 global $current_screen;
104 if ( 'subscrpt_order' !== $current_screen->post_type ) {
105 return $actions;
106 }
107 unset( $actions['inline hide-if-no-js'] );
108 unset( $actions['view'] );
109 unset( $actions['trash'] );
110 unset( $actions['edit'] );
111
112 return $actions;
113 }
114
115 /**
116 * Register custom columns.
117 *
118 * @param array $columns Columns.
119 *
120 * @return array
121 */
122 public function add_custom_columns( $columns ) {
123 $columns['subscrpt_start_date'] = __( 'Start Date', 'subscription' );
124 $columns['subscrpt_customer'] = __( 'Customer', 'subscription' );
125 $columns['subscrpt_next_date'] = __( 'Next Date', 'subscription' );
126 $columns['subscrpt_status'] = __( 'Status', 'subscription' );
127 unset( $columns['date'] );
128 unset( $columns['cb'] );
129
130 return $columns;
131 }
132
133 /**
134 * Display column data.
135 *
136 * @param string $column Column.
137 * @param int $post_id Post Id.
138 *
139 * @return void
140 */
141 public function add_custom_columns_data( $column, $post_id ) {
142 // HPOS: Safe. Only retrieves WooCommerce order via CRUD, and subscription meta via post meta.
143 $order_id = get_post_meta( $post_id, '_subscrpt_order_id', true ); // HPOS: Only subscription meta, not order meta.
144 $order = wc_get_order( $order_id ); // HPOS: Safe, uses WooCommerce CRUD.
145 if ( $order ) {
146 if ( 'subscrpt_start_date' === $column ) {
147 $start_date = get_post_meta( $post_id, '_subscrpt_start_date', true );
148 echo ! empty( $start_date ) ? esc_html( wp_date( 'F d, Y', $start_date ) ) : '-';
149 } elseif ( 'subscrpt_customer' === $column ) {
150 ?>
151 <?php echo wp_kses_post( $order->get_formatted_billing_full_name() ); ?>
152 <br />
153 <a href="mailto:<?php echo wp_kses_post( $order->get_billing_email() ); ?>"><?php echo wp_kses_post( $order->get_billing_email() ); ?></a>
154 <br />
155 <?php if ( ! empty( $order->get_billing_phone() ) ) : ?>
156 Phone : <a
157 href="tel:<?php echo esc_js( $order->get_billing_phone() ); ?>"><?php echo esc_js( $order->get_billing_phone() ); ?></a>
158 <?php endif; ?>
159 <?php
160 } elseif ( 'subscrpt_next_date' === $column ) {
161 $next_date = get_post_meta( $post_id, '_subscrpt_next_date', true );
162 echo ! empty( $next_date ) ? esc_html( wp_date( 'F d, Y', $next_date ) ) : '-';
163 } elseif ( 'subscrpt_status' === $column ) {
164 $status_obj = get_post_status_object( get_post_status( $post_id ) );
165 ?>
166 <span
167 class="subscrpt-legacy-status subscrpt-legacy-status--<?php echo esc_html( $status_obj->name ); ?>"><?php echo esc_html( $status_obj->label ); ?></span>
168 <?php
169 }
170 } else {
171 esc_html_e( 'Order not found !!', 'subscription' );
172 }
173 }
174
175 /**
176 * Build the subscription info rows.
177 *
178 * Assembles the label/value rows describing a subscription (product, cost,
179 * dates, status, payment method, addresses, trial, etc.) and applies the
180 * `subscrpt_admin_info_rows` filter so extensions (e.g. the Pro plugin) can
181 * add or reorder rows. Returns null when the related order is missing.
182 *
183 * @param int $subscription_id Subscription (subscrpt_order) post ID.
184 *
185 * @return array<string,array{label:string,value:string}>|null
186 */
187 public static function get_info_rows( $subscription_id ) {
188 $order_id = get_post_meta( $subscription_id, '_subscrpt_order_id', true );
189 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
190 $trial = get_post_meta( $subscription_id, '_subscrpt_trial', true );
191 $start_date = get_post_meta( $subscription_id, '_subscrpt_start_date', true );
192 $next_date = get_post_meta( $subscription_id, '_subscrpt_next_date', true );
193 $trial_start_date = get_post_meta( $subscription_id, '_subscrpt_trial_started', true );
194 $trial_end_date = get_post_meta( $subscription_id, '_subscrpt_trial_ended', true );
195 $trial_mode = get_post_meta( $subscription_id, '_subscrpt_trial_mode', true );
196 $order = wc_get_order( $order_id );
197 if ( ! $order ) {
198 return null;
199 }
200 $order_item = $order->get_item( $order_item_id );
201
202 $product_name = $order_item->get_name();
203 $product_link = get_the_permalink( $order_item->get_product_id() );
204
205 // Get payment information
206 $product_id = $order_item->get_product_id();
207 $max_payments = subscrpt_get_max_payments( $subscription_id ) ? subscrpt_get_max_payments( $subscription_id ) : 0;
208 $payments_made = subscrpt_count_payments_made( $subscription_id );
209
210 $rows = array(
211 'product' => array(
212 'label' => __( 'Product', 'subscription' ),
213 'value' => '<a href="' . esc_html( $product_link ) . '" target="_blank">' . esc_html( $product_name ) . '</a>',
214 ),
215 'cost' => array(
216 'label' => __( 'Cost', 'subscription' ),
217 'value' => Helper::get_subscription_recurring_price_html( $subscription_id, $order_item ),
218 ),
219 'quantity' => array(
220 'label' => __( 'Qty', 'subscription' ),
221 'value' => "x{$order_item->get_quantity()}",
222 ),
223 );
224
225 // Add payment information if max_payments is set and not unlimited
226 if ( ! empty( $max_payments ) && $max_payments > 0 ) {
227 $rows['total_payments'] = array(
228 'label' => __( 'Total Payments', 'subscription' ),
229 'value' => esc_html( $payments_made ) . ' / ' . esc_html( $max_payments ),
230 );
231 }
232
233 $rows += array(
234 'start_date' => array(
235 'label' => __( 'Started date', 'subscription' ),
236 'value' => ! empty( $start_date ) ? wp_date( 'F d, Y', $trial && $trial_start_date ? $trial_start_date : $start_date ) : '-',
237 ),
238 'next_date' => array(
239 'label' => __( 'Payment due date', 'subscription' ),
240 'value' => ! empty( $next_date ) ? wp_date( 'F d, Y', $trial && $trial_end_date && 'on' === $trial_mode ? $trial_end_date : ( $next_date ?? '-' ) ) : '-',
241 ),
242 'status' => array(
243 'label' => __( 'Status', 'subscription' ),
244 'value' => '<span class="subscrpt-legacy-status subscrpt-legacy-status--' . get_post_status( $subscription_id ) . '">' . get_post_status_object( get_post_status( $subscription_id ) )->label . '</span>',
245 ),
246 'payment_method' => array(
247 'label' => __( 'Payment Method', 'subscription' ),
248 'value' => empty( $order->get_payment_method_title() ) ? '-' : $order->get_payment_method_title(),
249 ),
250 'billing_address' => array(
251 'label' => __( 'Billing', 'subscription' ),
252 'value' => $order->get_formatted_billing_address() ? $order->get_formatted_billing_address() : __( 'No billing address set.', 'subscription' ),
253 ),
254 'shipping_address' => array(
255 'label' => __( 'Shipping', 'subscription' ),
256 'value' => $order->get_formatted_shipping_address() ? $order->get_formatted_shipping_address() : __( 'No shipping address set.', 'subscription' ),
257 ),
258 );
259 if ( $trial ) {
260 $rows = array_slice( $rows, 0, 3, true ) + array(
261 'trial' => array(
262 'label' => __( 'Trial', 'subscription' ),
263 'value' => $trial,
264 ),
265 'trial_period' => array(
266 'label' => __( 'Trial Period', 'subscription' ),
267 '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' ) ),
268 ),
269 ) + array_slice( $rows, 3, count( $rows ) - 1, true );
270 }
271
272 if ( class_exists( 'WC_Stripe' ) && 'stripe' === $order->get_payment_method() ) {
273 $is_auto_renew = get_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
274 $new_rows = array();
275 foreach ( $rows as $key => $value ) {
276 $new_rows[ $key ] = $value;
277 if ( 'payment_method' === $key ) {
278 $new_rows['stripe_auto_renewal'] = array(
279 'label' => __( 'Stripe Auto Renewal', 'subscription' ),
280 'value' => '0' !== $is_auto_renew ? 'On' : 'Off',
281 );
282 }
283 }
284
285 $rows = $new_rows;
286 }
287
288 $rows = apply_filters( 'subscrpt_admin_info_rows', $rows, $subscription_id, $order );
289
290 return $rows;
291 }
292
293 /**
294 * Apply a status change to a subscription.
295 *
296 * Updates the subscription post status, fires the admin status-change email
297 * notification, runs the subscription status action, and completes the
298 * related order when the subscription becomes active. Called from the
299 * standalone subscription details page.
300 *
301 * @param int $post_id Subscription (subscrpt_order) post ID.
302 * @param string $action Target status slug.
303 *
304 * @return void
305 */
306 public static function process_status_change( $post_id, $action ) {
307 $old_status = get_post_status( $post_id );
308
309 // Do not wp_update_post() the status here: Action::status() and the order
310 // cascade below both skip their hooks (incl. next-date generation) when
311 // the post_status already equals the target, so this must stay the only
312 // place a real transition happens.
313 if ( $old_status !== $action ) {
314 $old_status_object = get_post_status_object( $old_status );
315 $new_status_object = get_post_status_object( $action );
316 WC()->mailer();
317 do_action( 'subscrpt_status_changed_admin_email_notification', $post_id, $old_status_object->label, $new_status_object->label );
318 }
319
320 $order_id = get_post_meta( $post_id, '_subscrpt_order_id', true );
321 if ( 'active' === $action ) {
322 $order = wc_get_order( $order_id );
323 if ( $order ) {
324 $order->update_status( 'completed' );
325 }
326 Action::status( $action, $post_id );
327 } else {
328 Action::status( $action, $post_id );
329 }
330 }
331
332 public function add_subscription_filter_select() {
333 // Implementation of add_subscription_filter_select method
334 }
335
336 public function add_overview_submenu() {
337 // Remove and re-add submenu to ensure Overview is first
338 remove_submenu_page( 'edit.php?post_type=subscrpt_order', 'edit.php?post_type=subscrpt_order' );
339 add_submenu_page(
340 'edit.php?post_type=subscrpt_order',
341 __( 'Overview', 'subscription' ),
342 __( 'Overview', 'subscription' ),
343 'manage_options',
344 'subscription_overview',
345 array( $this, 'render_overview_page' ),
346 0
347 );
348 add_submenu_page(
349 'edit.php?post_type=subscrpt_order',
350 __( 'All Subscriptions', 'subscription' ),
351 __( 'All Subscriptions', 'subscription' ),
352 'manage_options',
353 'edit.php?post_type=subscrpt_order',
354 '',
355 1
356 );
357 if ( ! class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) {
358 add_submenu_page(
359 'edit.php?post_type=subscrpt_order',
360 __( 'Go Pro', 'subscription' ),
361 __( 'Go Pro', 'subscription' ),
362 'manage_options',
363 'wp_subscription_go_pro',
364 array( $this, 'render_go_pro_page' ),
365 99
366 );
367 }
368 }
369
370 public function render_overview_page() {
371 ?>
372 <div class="wrap wpsubscription-overview" style="max-width:1100px;margin:40px auto 0 auto;">
373 <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;">
374 <div class="wpsubscription-overview-top" style="display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:start;margin-bottom:40px;">
375 <div class="wpsubscription-overview-info" style="display:flex;flex-direction:column;gap:18px;">
376 <h1 style="margin-bottom:0.2em;"><?php esc_html_e( 'WPSubscription Overview', 'subscription' ); ?></h1>
377 <p class="product-desc" style="font-size:1.15em;line-height:1.6;max-width:500px;">
378 <?php esc_html_e( 'WPSubscription 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' ); ?>
379 </p>
380 <div class="wpsubscription-links" style="display:flex;gap:12px;flex-wrap:wrap;">
381 <a href="https://docs.wpsubscription.co/en?utm_source=plugin&utm_medium=admin&utm_campaign=docs" target="_blank" class="button button-secondary"><?php esc_html_e( 'Documentation', 'subscription' ); ?></a>
382 <a href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro" target="_blank" class="button button-secondary"><?php esc_html_e( 'Website', 'subscription' ); ?></a>
383 </div>
384 </div>
385 <div class="promo-video" style="text-align:center;">
386 <iframe width="420" height="236" src="https://www.youtube.com/embed/2e6o5p0M7L4" title="WPSubscription Promo" frameborder="0" allowfullscreen style="max-width:100%;border-radius:8px;"></iframe>
387 </div>
388 </div>
389
390 <div class="wpsubscription-what-section" style="margin-bottom:40px;">
391 <h2><?php esc_html_e( 'What does Subscriptions for WooCommerce do?', 'subscription' ); ?></h2>
392 <p style="font-size:1.08em;max-width:900px;line-height:1.7;">
393 <?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, WPSubscription gives you the tools to grow your recurring revenue.', 'subscription' ); ?>
394 </p>
395 </div>
396
397 <h2 style="margin-top:2em;"><?php esc_html_e( 'Highlights', 'subscription' ); ?></h2>
398 <div class="wpsubscription-features-grid">
399 <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>
400 <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>
401 <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>
402 <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>
403 </div>
404 </div>
405 </div>
406 <style>
407 .wpsubscription-overview .promo-video { text-align:center; }
408 .wpsubscription-features-grid {
409 display: grid;
410 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
411 gap: 24px;
412 margin-top: 32px;
413 }
414 .feature-box {
415 background: #fff;
416 border-radius: 10px;
417 box-shadow: 0 2px 8px rgba(0,0,0,0.06);
418 padding: 24px 18px 18px 18px;
419 text-align: center;
420 transition: transform 0.2s, box-shadow 0.2s;
421 will-change: transform;
422 }
423 .feature-box:hover {
424 transform: translateY(-6px) scale(1.03);
425 box-shadow: 0 6px 24px rgba(0,0,0,0.10);
426 }
427 .feature-box .dashicons {
428 font-size: 2.2em;
429 color: #7f54b3;
430 margin-bottom: 10px;
431 display: block;
432 }
433 .feature-box h3 {
434 margin: 12px 0 8px 0;
435 font-size: 1.15em;
436 }
437 .feature-box p {
438 color: #555;
439 font-size: 1em;
440 margin: 0;
441 }
442 </style>
443 <?php
444 }
445
446 public function render_go_pro_page() {
447 if ( class_exists( 'Sdevs_Wc_Subscription_Pro' ) ) {
448 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>';
449 return;
450 }
451 ?>
452 <div class="wrap wpsubscription-go-pro" style="max-width:900px;margin:40px auto 0 auto;">
453 <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;">
454 <h1 style="margin-bottom:0.5em;"><?php esc_html_e( 'Upgrade to WPSubscription Pro', 'subscription' ); ?></h1>
455 <p style="font-size:1.12em;max-width:600px;line-height:1.6;">
456 <?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' ); ?>
457 </p>
458 <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;">
459 <thead>
460 <tr style="background:#f8f9fa;">
461 <th style="padding:18px 12px 18px 24px;font-size:1.08em;text-align:left;border:none;"></th>
462 <th style="padding:18px 12px;font-size:1.08em;text-align:center;border:none;">Free</th>
463 <th style="padding:18px 12px;font-size:1.08em;text-align:center;border:none;color:#7f54b3;">Pro</th>
464 </tr>
465 </thead>
466 <tbody>
467 <tr>
468 <td style="padding:16px 12px 16px 24px;">Simple subscription products</td>
469 <td style="text-align:center;">✔️</td>
470 <td style="text-align:center;">✔️</td>
471 </tr>
472 <tr style="background:#f6f7f7;">
473 <td style="padding:16px 12px 16px 24px;">Automated recurring billing</td>
474 <td style="text-align:center;">✔️</td>
475 <td style="text-align:center;">✔️</td>
476 </tr>
477 <tr>
478 <td style="padding:16px 12px 16px 24px;">Multiple payment gateways</td>
479 <td style="text-align:center;">✔️</td>
480 <td style="text-align:center;">✔️</td>
481 </tr>
482 <tr style="background:#f6f7f7;">
483 <td style="padding:16px 12px 16px 24px;">Customer self-service portal</td>
484 <td style="text-align:center;">✔️</td>
485 <td style="text-align:center;">✔️</td>
486 </tr>
487 <tr>
488 <td style="padding:16px 12px 16px 24px;">Priority support</td>
489 <td style="text-align:center;">—</td>
490 <td style="text-align:center;">✔️</td>
491 </tr>
492 <tr style="background:#f6f7f7;">
493 <td style="padding:16px 12px 16px 24px;">Advanced reporting & analytics</td>
494 <td style="text-align:center;">—</td>
495 <td style="text-align:center;">✔️</td>
496 </tr>
497 <tr>
498 <td style="padding:16px 12px 16px 24px;">Variable product support</td>
499 <td style="text-align:center;">—</td>
500 <td style="text-align:center;font-weight:600;color:#43a047;">✔️</td>
501 </tr>
502 </tbody>
503 </table>
504 <div style="text-align:center;margin-top:24px;">
505 <a href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro" 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);">
506 <?php esc_html_e( 'Upgrade to Pro', 'subscription' ); ?>
507 </a>
508 </div>
509 </div>
510 </div>
511 <?php
512 }
513 }
514