PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 4.0.3
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v4.0.3
4.17.3 4.17.2 4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / Admin / SettingsPages / Membership / OrdersPage / SettingsPage.php
wp-user-avatar / src / Admin / SettingsPages / Membership / OrdersPage Last commit date
OrderWPListTable.php 4 years ago SettingsPage.php 4 years ago index.php 4 years ago
SettingsPage.php
584 lines
1 <?php
2
3 namespace ProfilePress\Core\Admin\SettingsPages\Membership\OrdersPage;
4
5 use ProfilePress\Core\Admin\SettingsPages\AbstractSettingsPage;
6 use ProfilePress\Core\Admin\SettingsPages\Membership\ContextualStateChangeHelper;
7 use ProfilePress\Core\Base;
8 use ProfilePress\Core\Membership\CheckoutFields;
9 use ProfilePress\Core\Membership\Models\Coupon\CouponFactory;
10 use ProfilePress\Core\Membership\Models\Coupon\CouponUnit;
11 use ProfilePress\Core\Membership\Models\Customer\CustomerFactory;
12 use ProfilePress\Core\Membership\Models\Order\OrderEntity;
13 use ProfilePress\Core\Membership\Models\Order\OrderFactory;
14 use ProfilePress\Core\Membership\Models\Order\OrderStatus;
15 use ProfilePress\Core\Membership\Models\Plan\PlanFactory;
16 use ProfilePress\Core\Membership\Models\Subscription\SubscriptionEntity;
17 use ProfilePress\Core\Membership\PaymentMethods\PaymentMethods;
18 use ProfilePress\Core\Membership\Repositories\PlanRepository;
19 use ProfilePress\Core\Membership\Services\Calculator;
20 use ProfilePress\Core\Membership\Services\CouponService;
21 use ProfilePress\Core\Membership\Services\OrderService;
22 use ProfilePress\Core\Membership\Services\SubscriptionService;
23 use ProfilePress\Core\Membership\Services\TaxService;
24 use ProfilePress\Custom_Settings_Page_Api;
25 use ProfilePressVendor\Carbon\CarbonImmutable;
26
27 class SettingsPage extends AbstractSettingsPage
28 {
29 public $error_bucket;
30
31 /** @var OrderWPListTable */
32 private $orderListTable;
33
34 function __construct()
35 {
36 add_action('ppress_register_menu_page', [$this, 'register_cpf_settings_page']);
37 add_action('ppress_admin_settings_page_orders', [$this, 'settings_page_function']);
38
39 if (ppressGET_var('page') == PPRESS_MEMBERSHIP_ORDERS_SETTINGS_SLUG) {
40 add_filter('set-screen-option', [__CLASS__, 'set_screen'], 10, 3);
41 add_filter('set_screen_option_rules_per_page', [__CLASS__, 'set_screen'], 10, 3);
42
43 add_action('admin_init', function () {
44 $this->save_order();
45 $this->add_order();
46 });
47 }
48
49 add_action('wp_ajax_ppress_mb_search_customers', [$this, 'search_customers']);
50 add_action('wp_ajax_ppress_delete_order_note', [$this, 'delete_order_note']);
51 add_action('wp_ajax_ppress_mb_order_modal_search', [$this, 'search_plan_coupon']);
52 add_action('wp_ajax_ppress_modal_replace_order_item', [$this, 'replace_order_item_modal']);
53 }
54
55 public function default_header_menu()
56 {
57 return 'orders';
58 }
59
60 public function register_cpf_settings_page()
61 {
62 $hook = add_submenu_page(
63 PPRESS_DASHBOARD_SETTINGS_SLUG,
64 $this->admin_page_title() . ' - ProfilePress',
65 esc_html__('Orders', 'wp-user-avatar'),
66 'manage_options',
67 PPRESS_MEMBERSHIP_ORDERS_SETTINGS_SLUG,
68 array($this, 'admin_page_callback'));
69
70 add_action("load-$hook", array($this, 'add_options'));
71
72 do_action('ppress_membership_orders_settings_page_register', $hook);
73 }
74
75 public function search_customers()
76 {
77 if ( ! current_user_can('manage_options')) return;
78
79 check_ajax_referer('ppress-admin-nonce', 'nonce');
80
81 global $wpdb;
82
83 $wp_user_table = $wpdb->users;
84 $wp_user_meta = $wpdb->usermeta;
85 $customer_Table = Base::customers_db_table();
86
87 $search = '%' . $wpdb->esc_like(sanitize_text_field($_GET['search'])) . '%';
88
89
90 $results['results'] = [];
91
92 $users = $wpdb->get_results(
93 $wpdb->prepare(
94 "SELECT customer.id AS customer_id FROM $wp_user_table AS user
95 LEFT JOIN $wp_user_meta AS usermeta on usermeta.user_id = user.id
96 LEFT JOIN $customer_Table AS customer on customer.user_id = user.id
97 WHERE user_email LIKE %s OR (usermeta.meta_key = 'first_name' AND usermeta.meta_value LIKE %s) OR (usermeta.meta_key = 'last_name' AND usermeta.meta_value LIKE %s)",
98 [$search, $search, $search]
99 ),
100 ARRAY_A
101 );
102
103 if (is_array($users) && ! empty($users)) {
104 foreach ($users as $user) {
105 if ( ! empty($user['customer_id'])) {
106 $customer_id = (int)$user['customer_id'];
107 $results['results'][$customer_id] = array(
108 'id' => $customer_id,
109 'text' => CustomerFactory::fromId($customer_id)->get_name(),
110 );
111 }
112 }
113 }
114
115 $results['results'] = array_values($results['results']);
116
117 wp_send_json($results, 200);
118 }
119
120 public function search_plan_coupon()
121 {
122 if ( ! current_user_can('manage_options')) return;
123
124 check_ajax_referer('ppress-admin-nonce', 'nonce');
125
126 global $wpdb;
127
128 $search = sanitize_text_field(ppressGET_var('search'));
129
130 $results['results'] = [];
131
132 if ( ! empty($search)) {
133
134 if (ppressGET_var('type') == 'subscription-plans') {
135 $table = Base::subscription_plans_db_table();
136 $result = $wpdb->get_results(
137 $wpdb->prepare(
138 "SELECT id, name, price FROM $table WHERE name LIKE %s",
139 '%' . $wpdb->esc_like($search) . '%'
140 )
141 );
142
143 if ( ! empty($result)) {
144 foreach ($result as $plan) {
145 $results['prices'][$plan->id] = ppress_sanitize_amount($plan->price);
146 $results['results'][] = array(
147 'id' => $plan->id,
148 'text' => $plan->name
149 );
150 }
151 }
152 }
153
154 if (ppressGET_var('type') == 'order_coupon') {
155 $table = Base::coupons_db_table();
156 $result = $wpdb->get_results(
157 $wpdb->prepare(
158 "SELECT id, code FROM $table WHERE code LIKE %s",
159 '%' . $wpdb->esc_like($search) . '%'
160 )
161 );
162
163 if ( ! empty($result)) {
164 foreach ($result as $coupon) {
165 $results['results'][] = array(
166 'id' => $coupon->id,
167 'text' => $coupon->code,
168 );
169 }
170 }
171 }
172 }
173
174 wp_send_json($results, 200);
175 }
176
177 public function delete_order_note()
178 {
179 if ( ! current_user_can('manage_options')) return;
180
181 check_ajax_referer('ppress-admin-nonce', 'security');
182
183 OrderService::init()->delete_order_note_by_id(intval($_POST['note_id']));
184
185 wp_send_json_success();
186 }
187
188 public function replace_order_item_modal()
189 {
190 if ( ! current_user_can('manage_options')) return;
191
192 check_ajax_referer('ppress-admin-nonce', 'security');
193
194 $order_id = (int)$_POST['order_id'];
195 $plan_id = (int)$_POST['plan'];
196 $plan_price = sanitize_text_field($_POST['plan_price']);
197 $coupon_id = (int)$_POST['coupon_code'];
198 $tax_amount = sanitize_text_field($_POST['tax']);
199
200 $plan_price = ! empty($plan_price) ? $plan_price : PlanFactory::fromId($plan_id)->get_price();
201
202 $order = OrderFactory::fromId($order_id);
203 $order->plan_id = ppress_sanitize_amount($plan_id);
204 $order->coupon_code = '';
205 $order->discount = '0.00';
206 $order->tax = '0.00';
207 $order->subtotal = $plan_price;
208
209 if ( ! empty($coupon_id)) {
210
211 $couponObj = CouponFactory::fromId($coupon_id);
212
213 $order->coupon_code = $couponObj->code;
214 $order->discount = $couponObj->get_amount();
215
216 if ($couponObj->unit == CouponUnit::PERCENTAGE) {
217
218 $order->discount = CouponService::init()->get_coupon_percentage_fee(
219 $couponObj->get_amount(),
220 Calculator::init($order->subtotal)->val()
221 );
222 }
223 }
224
225 if (TaxService::init()->is_tax_enabled() && ! empty($tax_amount)) {
226 $order->tax = ppress_sanitize_amount($tax_amount);
227 }
228
229 $order_total = Calculator::init($order->subtotal)->plus($order->tax)->minus($order->discount);
230
231 if (TaxService::init()->is_tax_enabled() && ! empty($tax_amount) && TaxService::init()->is_price_inclusive_tax()) {
232
233 $subtotal = Calculator::init($plan_price)->minus($order->discount)->minus($tax_amount);
234
235 $order->subtotal = $subtotal->val();
236
237 if ($subtotal->isNegativeOrZero()) $order->subtotal = '0.00';
238
239 $order_total = Calculator::init($order->subtotal)->plus($order->tax);
240 }
241
242 $order->total = $order_total->val();
243
244 if ($order_total->isNegativeOrZero()) $order->total = '0.00';
245
246 $order->save();
247
248 wp_send_json_success();
249 }
250
251 /**
252 * @return void|string
253 * @throws \Exception
254 */
255 public function save_order()
256 {
257 if ( ! isset($_POST['ppress_save_order'])) return;
258
259 ppress_verify_nonce();
260
261 if ( ! current_user_can('manage_options')) return;
262
263 $order = OrderFactory::fromId(absint(ppressGET_var('id')));
264 $old_status = $order->status;
265
266 $order->date_created = ppress_local_datetime_to_utc(sanitize_text_field($_POST['order_date']));
267 if (in_array($_POST['order_status'], array_keys(OrderStatus::get_all()))) {
268 $order->status = sanitize_text_field($_POST['order_status']);
269 }
270
271 $order->customer_id = absint($_POST['customer_user']);
272
273 foreach ($_POST as $key => $value) {
274
275 if (in_array($key, array_keys(CheckoutFields::standard_billing_fields()))) {
276
277 $key = str_replace('ppress_', '', $key);
278
279 $order->$key = ppress_clean($value, 'sanitize_text_field');
280 }
281 }
282
283 $order_id = $order->save();
284
285 if ($order_id && OrderStatus::PENDING == $old_status && OrderStatus::COMPLETED == $order->status) {
286 $order->complete_order();
287 }
288
289 wp_safe_redirect(add_query_arg(['ppress_order_action' => 'edit', 'id' => $order_id, 'saved' => 'true'], PPRESS_MEMBERSHIP_ORDERS_SETTINGS_PAGE));
290 exit;
291 }
292
293 /**
294 * @return void|string
295 * @throws \Exception
296 */
297 public function add_order()
298 {
299 if ( ! isset($_POST['save_ppress_orders'])) return;
300
301 if ( ! current_user_can('manage_options')) {
302 wp_die(__('You do not have permission to perform this action.', 'wp-user-avatar'), __('Error', 'wp-user-avatar'), array('response' => 403));
303 }
304
305 check_admin_referer('wp-csa-nonce', 'wp_csa_nonce');
306
307 global $wpdb;
308
309 $order_data = ppressPOST_var('ppress_orders', []);
310
311 $date_created = current_time('mysql');
312
313 if ( ! empty($order_data['order_date'])) {
314 $date_created = CarbonImmutable::parse(sanitize_text_field($order_data['order_date']), wp_timezone())
315 ->setTimeFromTimeString(CarbonImmutable::now(wp_timezone())->toTimeString())
316 ->utc()->toDateTimeString();
317 }
318
319 $plan_obj = ppress_get_plan((int)$order_data['plan']);
320
321 $customer_id = (int)$order_data['customer'];
322
323 if (CustomerFactory::fromId($customer_id)->has_active_subscription($plan_obj->id)) {
324 wp_die(
325 sprintf(__('Customer already has an active subscription for %s.', 'wp-user-avatar'), $plan_obj->name),
326 __('Error', 'wp-user-avatar'),
327 ['response' => 403]
328 );
329 }
330
331 $order = new OrderEntity();
332 $order->plan_id = $plan_obj->id;
333 $order->customer_id = $customer_id;
334 $order->total = ppress_sanitize_amount($order_data['amount']);
335 $order->status = sanitize_text_field($order_data['order_status']);
336 $order->payment_method = sanitize_text_field($order_data['payment_method']);
337 $order->transaction_id = sanitize_text_field($order_data['transaction_id']);
338 $order->date_created = $date_created;
339 $order_id = $order->save();
340
341 if ( ! $order_id) {
342
343 wp_die(! empty($wpdb->last_error) ? $wpdb->last_error : esc_html__('Unable to add new order. Please try again', 'wp-user-avatar'),
344 __('Error', 'wp-user-avatar'),
345 array('response' => 400)
346 );
347 }
348
349 $subscription = new SubscriptionEntity();
350 $subscription->parent_order_id = $order_id;
351 $subscription->customer_id = $customer_id;
352 $subscription->plan_id = (int)$order_data['plan'];
353 $subscription->billing_frequency = $plan_obj->billing_frequency;
354 $subscription->initial_amount = ppress_sanitize_amount($order_data['amount']);
355 $subscription->recurring_amount = $plan_obj->price;
356 $subscription->expiration_date = SubscriptionService::init()->get_plan_expiration_datetime($plan_obj->id);
357
358 if ($order->is_completed()) {
359 $subscription_id = $subscription->activate_subscription();
360 } else {
361 $subscription_id = $subscription->save();
362 }
363
364 if ( ! $subscription_id) {
365
366 wp_die(! empty($wpdb->last_error) ? $wpdb->last_error : esc_html__('Unable to add new subscription. Please try again', 'wp-user-avatar'),
367 __('Error', 'wp-user-avatar'),
368 array('response' => 400)
369 );
370 }
371
372 $order->id = $order_id;
373 $order->subscription_id = $subscription_id;
374 if ($order->is_completed()) {
375 $order->date_completed = current_time('mysql');
376 }
377 $order->save();
378
379 if ($order_data['send_receipt'] == 'true' && $order->is_completed()) {
380 // important we call complete_order with synced/updated order object.
381 OrderFactory::fromId($order_id)->complete_order();
382 }
383
384 wp_safe_redirect(esc_url_raw(OrderWPListTable::view_edit_order_url($order_id)));
385 exit;
386 }
387
388 public function admin_page_title()
389 {
390 $title = esc_html__('Orders', 'wp-user-avatar');
391
392 if (ppressGET_var('ppress_order_action') == 'new') {
393 $title = esc_html__('Add New Order', 'wp-user-avatar');
394 }
395
396 if (ppressGET_var('ppress_order_action') == 'edit') {
397 $title = esc_html__('Edit Order', 'wp-user-avatar');
398 }
399
400 return $title;
401 }
402
403 public static function set_screen($status, $option, $value)
404 {
405 return $value;
406 }
407
408 public function add_options()
409 {
410 $args = [
411 'label' => esc_html__('Orders', 'wp-user-avatar'),
412 'default' => 10,
413 'option' => 'orders_per_page'
414 ];
415
416 add_screen_option('per_page', $args);
417
418 $this->orderListTable = new OrderWPListTable();
419 }
420
421 public function admin_notices()
422 {
423 if ( ! isset($_GET['saved']) && ! isset($this->error_bucket)) return;
424
425 $status = 'updated';
426 $message = '';
427
428 if ( ! empty($this->error_bucket)) {
429 $message = $this->error_bucket;
430 $status = 'error';
431 }
432
433 if (isset($_GET['saved'])) {
434 $message = esc_html__('Changes saved.', 'wp-user-avatar');
435 }
436
437 printf('<div id="message" class="%s notice is-dismissible"><p>%s</strong></p></div>', $status, $message);
438 }
439
440 public function settings_page_function()
441 {
442 add_action('wp_cspa_main_content_area', [$this, 'admin_settings_page_callback'], 10, 2);
443 add_action('wp_cspa_before_closing_header', [$this, 'add_new_button']);
444 add_action('wp_cspa_form_tag', function ($option_name) {
445 if ($option_name == 'ppress_orders') {
446 printf(' action="%s"', ppress_get_current_url_query_string());
447 }
448 });
449
450 $instance = Custom_Settings_Page_Api::instance();
451 if ( ! isset($_GET['ppress_order_action'])) {
452 $instance->form_method('get');
453 $instance->remove_nonce_field();
454 }
455
456 if (ppressGET_var('ppress_order_action') == 'new') {
457
458 $instance->main_content([
459 [
460 'plan' => [
461 'label' => __('Membership Plan', 'wp-user-avatar'),
462 'type' => 'select',
463 'options' => (function () {
464 return array_reduce(PlanRepository::init()->retrieveAll(), function ($carry, $item) {
465 $carry[$item->id] = $item->name;
466
467 return $carry;
468 }, ['' => '&mdash;&mdash;&mdash;&mdash;']);
469 })()
470 ],
471 'customer' => [
472 'label' => __('Customer', 'wp-user-avatar'),
473 'type' => 'select',
474 'options' => [],
475 'attributes' => ['class' => 'ppress-select2-field customer_user']
476 ],
477 'amount' => [
478 'label' => __('Amount', 'wp-user-avatar'),
479 'type' => 'text',
480 'placeholder' => ppress_display_amount('0.00'),
481 'description' => esc_html__('Enter the total order amount, or leave blank to use the price of the selected plan.', 'wp-user-avatar')
482 ],
483 'order_status' => [
484 'label' => __('Order Status', 'wp-user-avatar'),
485 'type' => 'select',
486 'options' => OrderStatus::get_all()
487 ],
488 'payment_method' => [
489 'label' => __('Payment Method', 'wp-user-avatar'),
490 'type' => 'select',
491 'options' => (function () {
492 return array_reduce(PaymentMethods::get_instance()->get_all(), function ($carry, $item) {
493 $carry[$item->id] = $item->method_title;
494
495 return $carry;
496 }, ['' => '&mdash;&mdash;&mdash;&mdash;']);
497 })()
498 ],
499 'transaction_id' => [
500 'label' => __('Transaction ID', 'wp-user-avatar'),
501 'type' => 'text',
502 'description' => esc_html__('Enter the transaction ID, if any.', 'wp-user-avatar')
503 ],
504 'order_date' => [
505 'label' => __('Date', 'wp-user-avatar'),
506 'type' => 'text',
507 'class' => 'ppress_datepicker',
508 'description' => esc_html__("Enter the purchase date, or leave blank for today's date.", 'wp-user-avatar')
509 ],
510 'send_receipt' => [
511 'label' => __('Send Receipt', 'wp-user-avatar'),
512 'checkbox_label' => __('Check to send the order receipt to the customer.', 'wp-user-avatar'),
513 'type' => 'checkbox',
514 'default_value' => 'true'
515 ]
516 ]
517 ]);
518
519 $instance->remove_white_design();
520
521 $instance->sidebar(self::sidebar_args());
522 }
523
524 $instance->add_view_classes('ppview');
525 $instance->option_name('ppress_orders');
526 $instance->page_header($this->admin_page_title());
527 $instance->build(ppressGET_var('ppress_order_action') != 'new');
528 }
529
530 public function add_new_button()
531 {
532 if ( ! isset($_GET['ppress_order_action'])) {
533 $url = esc_url_raw(add_query_arg('ppress_order_action', 'new', PPRESS_MEMBERSHIP_ORDERS_SETTINGS_PAGE));
534 echo "<a class=\"add-new-h2\" href=\"$url\">" . esc_html__('Add New Order', 'wp-user-avatar') . '</a>';
535 }
536
537 if (ppressGET_var('ppress_order_action') == 'edit') {
538 $url = esc_url_raw(PPRESS_MEMBERSHIP_ORDERS_SETTINGS_PAGE);
539 echo "<a class=\"add-new-h2\" href=\"$url\">" . esc_html__('Back to Orders', 'wp-user-avatar') . '</a>';
540 }
541 }
542
543 public function admin_settings_page_callback($content)
544 {
545 if (ppressGET_var('ppress_order_action') == 'new') {
546 return $content;
547 }
548
549 if (ppressGET_var('ppress_order_action') == 'edit') {
550 $this->admin_notices();
551 ContextualStateChangeHelper::init();
552 require_once dirname(dirname(__FILE__)) . '/views/orders/add-edit-order.php';
553
554 return;
555 }
556
557 $this->orderListTable->prepare_items();
558
559 echo '<input type="hidden" name="page" value="' . PPRESS_MEMBERSHIP_ORDERS_SETTINGS_SLUG . '" />';
560 echo '<input type="hidden" name="view" value="orders" />';
561 if (isset($_GET['status'])) {
562 echo '<input type="hidden" name="status" value="' . sanitize_text_field($_GET['status']) . '" />';
563 }
564 if (isset($_GET['by_ci'])) {
565 echo '<input type="hidden" name="by_ci" value="' . sanitize_text_field($_GET['by_ci']) . '" />';
566 }
567 $this->orderListTable->views();
568 $this->orderListTable->filter_bar();
569 $this->orderListTable->display();
570
571 do_action('ppress_order_wp_list_table_bottom');
572 }
573
574 public static function get_instance()
575 {
576 static $instance = null;
577
578 if (is_null($instance)) {
579 $instance = new self();
580 }
581
582 return $instance;
583 }
584 }