PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.3.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.3.2
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / order / class-lp-order.php

class-lp-order.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.7.3.2, at inc/order/class-lp-order.php

1,406 lines 36.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LP_Order
4 *
5 * @author ThimPress
6 * @package LearnPress/Classes
7 * @version 4.0.0
8 */
9
10 /**
11 * Prevent loading this file directly
12 */
13 defined( 'ABSPATH' ) || exit();
14
15 if ( ! class_exists( 'LP_Order' ) ) {
16
17 /**
18 * Class LP_Order
19 */
20 class LP_Order extends LP_Abstract_Post_Data {
21
22 /**
23 * @var string
24 */
25 protected $_post_type = LP_ORDER_CPT;
26
27 /**
28 * @var array
29 */
30 protected $_data = array(
31 'user_id' => '',
32 'order_date' => '',
33 'date_modified' => '',
34 'customer_message' => '',
35 'customer_note' => '',
36 'status' => '',
37 'order_key' => '',
38 'total' => 0,
39 'subtotal' => 0,
40 'created_via' => '',
41 'checkout_email' => '',
42 );
43
44 /**
45 * @var array
46 */
47 protected $_meta_keys = array(
48 '_user_id' => 'user',
49 '_order_currency' => 'currency',
50 '_order_subtotal' => 'subtotal',
51 '_order_total' => 'total',
52 '_payment_method' => 'payment_method',
53 '_payment_method_title' => 'payment_method_title',
54 '_order_version' => 'order_version',
55 '_edit_last' => '',
56 '_edit_lock' => '',
57 '_prices_include_tax' => '',
58 '_order_key' => '',
59 '_user_ip' => '',
60 '_checkout_email' => '',
61 );
62
63 /**
64 * Store order status in transactions.
65 *
66 * @var array
67 */
68 protected $_status = array();
69
70 /**
71 * LP_Order constructor.
72 *
73 * @param bool $order_id
74 *
75 * @throws Exception
76 */
77 public function __construct( $order_id = false ) {
78 $this->_curd = new LP_Order_CURD();
79 if ( is_numeric( $order_id ) && $order_id > 0 ) {
80 $this->set_id( $order_id );
81 } elseif ( $order_id instanceof self ) {
82 $this->set_id( absint( $order_id->get_id() ) );
83 } elseif ( ! empty( $order_id->ID ) ) {
84 $this->set_id( absint( $order_id->ID ) );
85 }
86
87 if ( $this->get_id() > 0 ) {
88 $this->load();
89 }
90 }
91
92 /**
93 * Load the order data.
94 * Check if the id is not zero but it's post type does not exists.
95 *
96 * @throws Exception
97 */
98 public function load() {
99 $this->_curd->load( $this );
100 }
101
102 /**
103 * Set order date.
104 *
105 * @param int|string $date
106 */
107 public function set_order_date( $date ): LP_Order {
108 $this->set_data_date( 'order_date', $date );
109
110 return $this;
111 }
112
113 /**
114 * Get date of this order.
115 *
116 * @param string $context
117 *
118 * @return string|LP_Datetime
119 */
120 public function get_order_date( $context = '' ) {
121 $date = $this->get_data( 'order_date' );
122
123 if ( 'edit' !== $context ) {
124
125 if ( $date instanceof LP_Datetime ) {
126 $strtime = strtotime( $date->toSql() );
127
128 switch ( $context ) {
129 case 'd':
130 $date = date_i18n( 'Y-m-d', $strtime );
131 break;
132 case 'h':
133 $date = date_i18n( 'H', $strtime );
134 break;
135 case 'm':
136 $date = date_i18n( 'i', $strtime );
137 break;
138 case 'timestamp':
139 $date = $strtime;
140 break;
141 default:
142 $post = get_post( $this->get_id() );
143 $m_time = $post->post_date;
144 $time = get_post_time( 'G', true, $post );
145 $time_diff = time() - $time;
146
147 if ( $time_diff > 0 && $time_diff < DAY_IN_SECONDS ) {
148 $date = sprintf( __( '%s ago', 'learnpress' ), human_time_diff( $time ) );
149 } else {
150 $date = mysql2date( get_option( 'date_format' ), $m_time );
151 }
152 }
153 }
154 } elseif ( ! $date instanceof LP_Datetime ) {
155 $date = new LP_Datetime( $date );
156 }
157
158 return $date;
159 }
160
161 /**
162 * Set order key
163 *
164 * @param string $order_key
165 */
166 public function set_order_key( $order_key ) {
167 $this->_set_data( 'order_key', $order_key );
168 }
169
170 /**
171 * Get order key.
172 *
173 * @return array|mixed
174 */
175 public function get_order_key() {
176 return $this->get_data( 'order_key', '' );
177 }
178
179 /**
180 * Get confirm received text
181 *
182 * @return string
183 * @since 3.0.0
184 */
185 public function get_confirm_order_received_text() {
186 return apply_filters( 'learn-press/confirm-order-received-text', __( 'Thank you. Your order has been received.', 'learnpress' ), $this->get_id() );
187 }
188
189 /**
190 * Get thank you message after the order is placed.
191 *
192 * @return mixed
193 * @since 3.0.0
194 */
195 public function get_thankyou_message() {
196 return apply_filters( 'learn-press/confirm-order-received-text', __( 'Thank you. Your order has been received.', 'learnpress' ), $this->get_id() );
197 }
198
199 /**
200 * Magic function for getting object property dynamic.
201 *
202 * @param string $prop
203 *
204 * @return int|mixed|null
205 * @deprecated
206 */
207 public function __get( $prop ) {
208 if ( $prop == 'post' ) {
209 // print_r( debug_backtrace() );
210 // die( '$post is deprecated' );
211 } elseif ( $prop == 'id' ) {
212 return $this->get_id();
213 }
214 $value = null;
215 if ( ! property_exists( $this, $prop ) ) {
216 $value = get_post_meta( $this->get_id(), '_' . $prop, true );
217 }
218
219 return $value;
220 }
221
222 /**
223 * Checks to see if current order has status as passed.
224 *
225 * @param string|array $status String or an array of statuses
226 *
227 * @return mixed
228 */
229 public function has_status( $status ) {
230 settype( $status, 'array' );
231 $has = in_array( $this->get_status(), $status );
232
233 return apply_filters( 'learn-press/has-order-status', $has, $status, $this->get_id() );
234 }
235
236 /**
237 * Check if order has a status as wp default.
238 *
239 * @return bool
240 */
241 public function has_invalid_status() {
242 return ! $this->has_status( learn_press_get_order_statuses( false, true ) );
243 }
244
245 /**
246 * Updates order to new status if needed
247 *
248 * @param mixed $new_status
249 * @param bool $manual Is this a manual order status change?.
250 *
251 * @return bool
252 * @throws Exception
253 */
254 public function update_status( $new_status = 'pending', $manual = false ) {
255 $old_status = $this->get_status();
256
257 do_action( 'learn-press/before-update-status-lp-order', $new_status, $old_status, $this, $manual );
258
259 $this->set_status( $new_status );
260 $result = $this->save();
261
262 do_action( 'learn-press/after-update-status-lp-order', $new_status, $old_status, $this, $manual );
263
264 return $result;
265 }
266
267 /**
268 * Set payment method for this order.
269 * If payment method is an instance of LP_Gateway_Abstract then
270 * update it to database.
271 *
272 * @param LP_Gateway_Abstract|string $payment_method
273 */
274 public function set_payment_method( $payment_method ) {
275 if ( $payment_method instanceof LP_Gateway_Abstract ) {
276 update_post_meta( $this->get_id(), '_payment_method', $payment_method->get_id() );
277 update_post_meta( $this->get_id(), '_payment_method_title', $payment_method->get_title() );
278 }
279
280 $this->payment_method = $payment_method;
281 }
282
283 /**
284 * Format order number id
285 *
286 * @return string
287 */
288 public function get_order_number(): string {
289 return learn_press_transaction_order_number( $this->get_id() );
290 }
291
292 /**
293 * Get status of the order
294 *
295 * @return mixed
296 */
297 public function get_order_status() {
298 return $this->get_status();
299 }
300
301 public function get_user_ip_address() {
302 return $this->get_data( 'user_ip_address' );
303 }
304
305 /**
306 * Get current status of order
307 *
308 * @return mixed
309 */
310 public function get_status() {
311 $status = $this->get_data( 'status', '' );
312 $status = apply_filters( 'learn_press_order_status', $status, $this );
313
314 apply_filters( 'learn-press/order/status', $status, $this->get_id() );
315
316 return $status;
317 }
318
319 /**
320 * Set order status.
321 *
322 * @param string $new_status
323 * @param string $note - Optional. Note for changing status.
324 */
325 public function set_status( string $new_status = '', string $note = '' ) {
326 $new_status = 'lp-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
327 $valid_statuses = learn_press_get_order_statuses( false, true );
328
329 if ( ! in_array( $new_status, $valid_statuses ) && 'trash' !== $new_status ) {
330 $new_status = 'pending';
331 }
332
333 $this->_set_data( 'status', $new_status );
334 }
335
336 public function get_order_status_html() {
337 $statuses = learn_press_get_order_statuses();
338 $order_status = $this->get_status();
339
340 if ( ! empty( $statuses[ $order_status ] ) ) {
341 $status = $statuses[ $order_status ];
342 } elseif ( ! empty( $statuses[ 'lp-' . $order_status ] ) ) {
343 $status = $statuses[ 'lp-' . $order_status ];
344 } elseif ( $order_status == 'trash' ) {
345 $status = __( 'Removed', 'learnpress' );
346 } else {
347 $status = ucfirst( $order_status );
348 }
349
350 $class = 'order-status order-status-' . sanitize_title( $status );
351 $html = sprintf( '<span class="%s">%s</span>', apply_filters( 'learn_press_order_status_class', $class, $status, $this ), $status );
352
353 return apply_filters( 'learn_press_order_status_html', $html, $this );
354 }
355
356 /**
357 * Mark order as complete
358 *
359 * @param string - transaction ID provided payment gateway
360 *
361 * @return bool
362 * @throws Exception
363 */
364 public function payment_complete( $transaction_id = '' ): bool {
365 do_action( 'learn-press/payment-pre-complete', $this->get_id() );
366
367 //TODO: tungnx - check to change code below - use LearnPress::instance()->session->set()
368 LearnPress::instance()->session->order_awaiting_payment = null;
369
370 $valid_order_statuses = apply_filters(
371 'learn-press/valid-order-statuses-for-payment-complete',
372 array(
373 'pending',
374 'processing',
375 ),
376 $this
377 );
378
379 if ( $this->get_id() && $this->has_status( $valid_order_statuses ) ) {
380
381 $this->update_status( 'completed' );
382
383 if ( ! empty( $transaction_id ) ) {
384 add_post_meta( $this->get_id(), '_transaction_id', $transaction_id, true );
385 }
386
387 do_action( 'learn-press/payment-complete', $this->get_id() );
388 } else {
389 do_action( 'learn-press/payment-complete-order-status-' . $this->get_status(), $this->get_id() );
390 }
391
392 return true;
393 }
394
395 /**
396 * Get checkout order successful url.
397 *
398 * @return string
399 */
400 public function get_checkout_order_received_url() {
401 $received_url = learn_press_get_endpoint_url( 'lp-order-received', $this->get_id(), learn_press_get_page_link( 'checkout' ) );
402
403 $received_url = add_query_arg( 'key', $this->get_order_key(), $received_url );
404
405 $received_url = apply_filters( 'learn_press_get_checkout_order_received_url', $received_url, $this );
406
407 /**
408 * @since 3.0.0
409 */
410 return apply_filters( 'learn-press/checkout-order-received-url', $received_url, $this );
411 }
412
413 /*********************************/
414
415 /**
416 * Get customer name of the order
417 */
418 public function get_customer_name() {
419 $customer_name = '';
420
421 if ( 'auto-draft' !== get_post_status( $this->get_id() ) ) {
422 $user_ids = $this->get_users();
423 if ( ! empty( $user_ids ) ) {
424 $customer_name = array();
425 foreach ( $user_ids as $uid ) {
426 $customer = learn_press_get_user( $uid );
427 if ( $customer && $customer->is_exists() ) {
428 if ( $customer->get_data( 'display_name' ) ) {
429 $customer_name[] = $customer->get_data( 'display_name' );
430 } elseif ( $customer->get_data( 'user_nicename' ) ) {
431 $customer_name[] = $customer->get_data( 'user_nicename' );
432 } elseif ( $customer->get_data( 'user_login' ) ) {
433 $customer_name[] = $customer->get_data( 'user_login' );
434 }
435 } else {
436 $customer_name[] = $this->get_guest_customer_name();
437 }
438 }
439
440 $customer_name = join( ', ', $customer_name );
441 }
442 }
443
444 if ( ! $customer_name ) {
445 $customer_name = $this->get_guest_customer_name();
446 }
447
448 return $customer_name;
449 }
450
451 public function get_guest_customer_name() {
452 $checkout_email = $this->get_checkout_email();
453 if ( $checkout_email ) {
454 $customer_name = sprintf( __( '%s (Guest)', 'learnpress' ), $checkout_email );
455 } else {
456 $customer_name = sprintf( __( '(Guest)', 'learnpress' ), $checkout_email );
457 }
458
459 return apply_filters( 'learn-press/order/guest-customer-name', $customer_name );
460 }
461
462 /*public function customer_exists() {
463 return false !== get_userdata( $this->get_data( 'user_id' ) );
464 }*/
465
466 public $order_items_loaded = false;
467
468 /**
469 * Get items of the order
470 *
471 * @return mixed
472 */
473 public function get_items() {
474 if ( $this->order_items_loaded ) {
475 return $this->order_items_loaded;
476 }
477
478 $items = $this->_curd->read_items( $this );
479
480 $this->order_items_loaded = $items;
481
482 return apply_filters( 'learn-press/order-items', $items );
483 }
484
485 /**
486 * Get items of the order by filter
487 *
488 * @param array $filter
489 *
490 * @return mixed
491 * @author tungnx
492 */
493 public function get_items_filter( $filter = array() ) {
494 $items = $this->_curd->read_items_filter( $this, $filter );
495
496 return apply_filters( 'learn-press/order-items', $items );
497 }
498
499 public function is_child() {
500 return $this->get_parent_id();
501 }
502
503 public function get_parent() {
504 return $this->get_parent_id() ? learn_press_get_order( $this->get_parent_id() ) : false;
505 }
506
507 /**
508 * Get list of course ids from order.
509 *
510 * @return array|bool
511 * @editor tungnx
512 */
513 public function get_item_ids() {
514 $items = $this->get_items();
515
516 if ( $items ) {
517 $course_ids = array();
518 foreach ( $items as $item ) {
519 if ( isset( $item['course_id'] ) ) {
520 $course_ids[] = $item['course_id'];
521 }
522 }
523
524 return $course_ids;
525 /**
526 * Comment by tungnx. Because it will error if item didn't have key 'course_id'.
527 * Ex: case buy certificate, will not have that key
528 */
529 //return @wp_list_pluck( $items, 'course_id' );
530 }
531
532 return false;
533 }
534
535 /**
536 * Check is order of Guest (user not login)
537 *
538 * @return bool
539 */
540 public function is_guest(): bool {
541 return ! get_user_by( 'ID', $this->get_user_id() );
542 }
543
544 public function get_item_meta( &$item ) {
545 $metas = get_metadata( 'learnpress_order_item', $item['id'] );
546 if ( $metas ) {
547 foreach ( $metas as $k => $v ) {
548 $item[ preg_replace( '!^_!', '', $k ) ] = LP_Helper::maybe_unserialize( $v[0] );
549 }
550 }
551 }
552
553 /**
554 * Remove all items from an order
555 */
556 public function remove_order_items() {
557 global $wpdb;
558 $wpdb->query(
559 $wpdb->prepare(
560 "
561 DELETE FROM itemmeta
562 USING $wpdb->learnpress_order_itemmeta itemmeta
563 INNER JOIN $wpdb->learnpress_order_items items
564 WHERE itemmeta.learnpress_order_item_id = items.order_item_id
565 AND items.order_id = %d",
566 $this->get_id()
567 )
568 );
569 $wpdb->query(
570 $wpdb->prepare(
571 "
572 DELETE FROM {$wpdb->learnpress_order_items}
573 WHERE order_id = %d",
574 $this->get_id()
575 )
576 );
577 $wpdb->query( $wpdb->prepare( "ALTER TABLE {$wpdb->learnpress_order_itemmeta} AUTO_INCREMENT = %d", 1 ) );
578 $wpdb->query( $wpdb->prepare( "ALTER TABLE {$wpdb->learnpress_order_items} AUTO_INCREMENT = %d", 1 ) );
579 }
580
581 /**
582 * Add a new item to order.
583 *
584 * @param array|int $item
585 *
586 * @return int
587 * @throws Exception
588 * @editor tungnx
589 * @modify 4.1.5
590 */
591 public function add_item( $item ): int {
592 global $wpdb;
593 $lp_user_items_db = LP_User_Items_DB::getInstance();
594
595 try {
596 if ( is_numeric( $item ) ) {
597 $item = array(
598 'item_id' => absint( $item ),
599 'order_item_name' => get_the_title( $item ),
600 );
601 }
602
603 $item_type = get_post_type( $item['item_id'] );
604 if ( ! in_array( $item_type, learn_press_get_item_types_can_purchase() ) ) {
605 return false;
606 }
607
608 $item = wp_parse_args(
609 $item,
610 array(
611 'item_id' => 0,
612 'item_type' => '',
613 'order_item_name' => '',
614 'quantity' => 1,
615 'subtotal' => 0,
616 'total' => 0,
617 'meta' => array(),
618 )
619 );
620
621 switch ( $item_type ) {
622 case LP_COURSE_CPT:
623 $course = learn_press_get_course( $item['item_id'] );
624 $item['subtotal'] = apply_filters( 'learnpress/order/item/subtotal', $course->get_price() * $item['quantity'], $course, $item );
625 $item['total'] = apply_filters( 'learnpress/order/item/total', $course->get_price() * $item['quantity'], $course, $item );
626 $item['order_item_name'] = apply_filters( 'learnpress/order/item/title', $course->get_title(), $course, $item );
627
628 if ( $this->check_can_delete_item_old( $course ) ) {
629 // Delete lp_user_items old
630 $user_ids = $this->get_users();
631 foreach ( $user_ids as $user_id ) {
632 $lp_user_items_db->delete_user_items_old( $user_id, $course->get_id() );
633 }
634 // End
635 }
636
637 //learn_press_add_order_item_meta( $order_item_id, '_course_id', $item['item_id'] );
638 $item['meta']['_course_id'] = $item['item_id'];
639 break;
640 default:
641 $item = apply_filters( 'learnpress/order/add-item/item_type_' . $item_type, $item );
642 break;
643 }
644
645 // Insert new order item
646 $wpdb->insert(
647 $wpdb->learnpress_order_items,
648 array(
649 'order_item_name' => $item['order_item_name'],
650 'order_id' => $this->get_id(),
651 'item_id' => $item['item_id'],
652 'item_type' => $item_type,
653 ),
654 array(
655 '%s',
656 '%d',
657 '%d',
658 '%s',
659 )
660 );
661 $order_item_id = absint( $wpdb->insert_id );
662 // End insert new order item
663
664 // Add learnpress_order_itemmeta
665 $item['meta']['_quantity'] = $item['quantity'];
666 $item['meta']['_subtotal'] = $item['subtotal'] ?? 0;
667 $item['meta']['_total'] = $item['total'] ?? 0;
668
669 if ( is_array( $item['meta'] ) ) {
670 foreach ( $item['meta'] as $k => $v ) {
671 learn_press_add_order_item_meta( $order_item_id, $k, $v );
672 }
673 }
674
675 do_action( 'learn-press/added-order-item-data', $order_item_id, $item, $this->get_id() );
676 } catch ( Throwable $e ) {
677 error_log( __FUNCTION__ . ': ' . $e->getMessage() );
678 }
679
680 return $order_item_id;
681 }
682
683 /**
684 * Set total
685 *
686 * @param int|float $total
687 */
688 public function set_total( $total = 0 ) {
689 $this->_set_data( 'total', $total );
690 }
691
692 /**
693 * Get total
694 *
695 * @return int|float
696 */
697 public function get_total() {
698 return $this->get_data( 'total' );
699 }
700
701 /**
702 * @param float|int $subtotal
703 */
704 public function set_subtotal( $subtotal = 0 ) {
705 $this->_set_data( 'subtotal', $subtotal );
706 }
707
708 /**
709 * Get subtotal
710 *
711 * @return float
712 */
713 public function get_subtotal() {
714 return $this->get_data( 'subtotal' );
715 }
716
717 public function cln() {
718 return $this->_curd->cln( $this );
719 }
720
721 public function cln_items( $to ) {
722 return $this->_curd->cln_items( $this->get_id(), $to );
723 }
724
725 /**
726 * Remove an item from database and it's data.
727 *
728 * @param int $item_id
729 *
730 * @return bool
731 */
732 public function remove_item( $item_id ) {
733 global $wpdb;
734
735 $item_id = absint( $item_id );
736
737 if ( ! $item_id ) {
738 return false;
739 }
740
741 do_action( 'learn_press_before_delete_order_item', $item_id );
742
743 /**
744 * @since 3.0.0
745 */
746 do_action( 'learn-press/before-delete-order-item', $item_id, $this->get_id() );
747
748 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}learnpress_order_items WHERE order_item_id = %d", $item_id ) );
749 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}learnpress_order_itemmeta WHERE learnpress_order_item_id = %d", $item_id ) );
750
751 /**
752 * @since 3.0.0
753 */
754 do_action( 'learn-press/deleted-order-item', $item_id, $this->get_id() );
755
756 do_action( 'learn_press_delete_order_item', $item_id );
757
758 return true;
759 }
760
761 /**
762 * Add multiple items.
763 *
764 * @param array $items
765 *
766 * @return array
767 */
768 public function add_items( $items ) {
769 settype( $items, 'array' );
770 $item_ids = array();
771 foreach ( $items as $item ) {
772 $item_id = $this->add_item( $item );
773 if ( $item_id ) {
774 $item_ids[] = $item_id;
775 }
776 }
777
778 return $item_ids;
779 }
780
781 /**
782 * @param string $field
783 *
784 * @return array|bool|int|LP_User|mixed
785 */
786 public function get_user( $field = '' ) {
787
788 $users = $this->get_users();
789 $uid = reset( $users );
790
791 $user = learn_press_get_user( $uid );
792 if ( false === $user ) {
793 return false;
794 }
795
796 if ( strtolower( $field ) == 'id' ) {
797 return $user->get_id();
798 }
799
800 if ( $field && $user ) {
801 return $user->get_data( $field );
802 }
803
804 return $user;
805 }
806
807 /**
808 * Get user id in array.
809 * user_id = 0 -> User type Guest
810 * @return int[]
811 * @editor tungnx
812 * @modify 4.1.4
813 * @version 1.0.1
814 */
815 public function get_users(): array {
816 $users = $this->get_user_id();
817 if ( $users !== -1 ) {
818 settype( $users, 'array' );
819 $users = array_unique( $users );
820 } else {
821 $users = array();
822 }
823
824 return $users;
825 }
826
827 public function dropdown_users() {
828 $order_users = $this->get_users();
829 $users = get_users( array() );
830 echo '<select name="order-customer[]" id="order-customer" multiple="multiple">';
831 foreach ( (array) $users as $user ) {
832 // $user->get_id() = (int) $user->get_id();
833 if ( in_array( $user->get_id(), $order_users ) ) {
834 $found_selected = true;
835 } else {
836 $found_selected = false;
837 }
838 echo sprintf( '<option value="%d" %s>%s</option>', esc_attr( $user->get_id() ), selected( $found_selected, true, false ), esc_html( $user->user_login ) );
839 }
840 echo '</select>';
841 }
842
843
844 public function get_checkout_payment_url() {
845
846 }
847
848 public function get_formatted_order_subtotal() {
849 $currency_symbol = learn_press_get_currency_symbol( $this->get_currency() );
850
851 return learn_press_format_price( $this->get_subtotal(), $currency_symbol );
852 }
853
854 public function get_formatted_order_total() {
855 $currency_symbol = learn_press_get_currency_symbol( $this->get_currency() );
856
857 return learn_press_format_price( $this->get_total(), $currency_symbol );
858 }
859
860 public function get_currency() {
861 return $this->get_data( 'currency' ) ? $this->get_data( 'currency' ) : learn_press_get_currency();
862 }
863
864 public function set_currency( $value ) {
865 $this->_set_data( 'currency', $value );
866 }
867
868 public function get_payment_method_title() {
869 if ( $this->order_total == 0 ) {
870 $title = '';
871 } else {
872 $title = $this->payment_method_title;
873 }
874
875 return $title;
876 }
877
878 public function get_view_order_url() {
879 global $wp_query;
880
881 $view_order_url = learn_press_get_endpoint_url( 'view-order', $this->get_id(), learn_press_get_page_link( 'profile' ) );
882 $user = learn_press_get_current_user();
883 $view_order_endpoint = LP_Settings::instance()->get( 'profile_endpoints.order-details' );
884
885 if ( ! $view_order_endpoint ) {
886 $view_order_endpoint = 'order-details';
887 }
888
889 $view_order_endpoint = urlencode( $view_order_endpoint );
890 if ( get_option( 'permalink_structure' ) ) {
891 $view_order_url = learn_press_get_page_link( 'profile' ) . $user->get_data( 'user_login' ) . '/' . $view_order_endpoint . '/' . $this->get_id() . '/';
892 } else {
893 $args = array(
894 'user' => $user->get_data( 'user_login' ),
895 );
896 $args['view'] = $view_order_endpoint;
897
898 if ( $view_order_endpoint ) {
899 $args['id'] = $this->get_id();
900 }
901 $view_order_url = add_query_arg(
902 $args,
903 learn_press_get_page_link( 'profile' )
904 );
905 }
906
907 return apply_filters( 'learn_press_view_order_url', $view_order_url, $this );
908 }
909
910 /**
911 * Get cancel url if it's status is pending.
912 *
913 * @param bool $force
914 *
915 * @return mixed
916 */
917 public function get_cancel_order_url( $force = false ) {
918
919 $url = false;
920 if ( $this->has_status( 'pending' ) ) {
921 $user = learn_press_get_current_user();
922 $url = learn_press_user_profile_link( $user->get_id(), LP_Settings::instance()->get( 'profile_endpoints.profile-orders' ) );
923 if ( ! $force ) {
924 $url = esc_url_raw( add_query_arg( 'cancel-order', $this->get_id(), $url ) );
925 } else {
926 $url = esc_url_raw( add_query_arg( 'cancelled-order', $this->get_id(), $url ) );
927 }
928
929 $url = wp_nonce_url( $url, 'cancel-order', 'lp-nonce' );
930 }
931
932 return apply_filters( 'learn-press/order-cancel-url', $url, $this->get_id() );
933 }
934
935 /**
936 * Get profile order's actions.
937 *
938 * @return array|mixed
939 */
940 public function get_profile_order_actions() {
941 $actions = array(
942 'view' => array(
943 'url' => $this->get_view_order_url(),
944 'text' => __( 'View', 'learnpress' ),
945 ),
946 );
947
948 $cancel_url = $this->get_cancel_order_url();
949 if ( $cancel_url ) {
950 $actions['cancel'] = array(
951 'url' => $this->get_cancel_order_url(),
952 'text' => __( 'Cancel', 'learnpress' ),
953 );
954 }
955 $actions = apply_filters( 'learn-press/profile-order-actions', $actions, $this->get_id() );
956
957 return $actions;
958 }
959
960 public function add_note( $note = null ) {
961 if ( is_user_logged_in() ) {
962 $user = get_user_by( 'id', get_current_user_id() );
963 $comment_author = $user->display_name;
964 $comment_author_email = $user->user_email;
965 $comment_post_ID = $this->get_id();
966 $comment_author_url = '';
967 $comment_content = $note;
968 $comment_agent = 'LearnPress';
969 $comment_type = 'lp_order_note';
970 $comment_parent = 0;
971 $comment_approved = 1;
972
973 $commentdata = apply_filters(
974 'learn_press_new_order_note_data',
975 compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_agent', 'comment_type', 'comment_parent', 'comment_approved' ),
976 $this->get_id()
977 );
978
979 $comment_id = wp_insert_comment( $commentdata );
980
981 return $comment_id;
982 }
983
984 return false;
985 }
986
987 /**
988 * Get user_name
989 *
990 * @param int $user_id
991 * @return string
992 */
993 public function get_user_name( int $user_id = 0 ): string {
994 $user = learn_press_get_user( $user_id );
995
996 if ( $user && ! $user instanceof LP_User_Guest ) {
997 $user_name = $user->get_display_name();
998 } else {
999 $user_name = $this->get_checkout_email();
1000 }
1001
1002 return apply_filters( 'learn-press/order/user-name', sprintf( _x( '%1$s', 'full name', 'learnpress' ), $user_name ) );
1003 }
1004
1005 /**
1006 * Check to see if this order is for multi users
1007 *
1008 * @return bool
1009 * @since 2.1.5
1010 */
1011 public function is_multi_users(): bool {
1012 return is_array( $this->get_user_id() );
1013 }
1014
1015 /**
1016 * Print the list of all users has assigned to this order
1017 * in case this order is for multi users
1018 *
1019 * @since 2.1.5
1020 * @deprecated 4.1.6.9
1021 */
1022 /*public function print_users() {
1023 $user_ids = get_post_meta( $this->get_id(), '_user_id' );
1024 if ( $user_ids ) {
1025 global $wpdb;
1026 if ( is_array( $user_ids[0] ) ) {
1027 $user_ids = reset( $user_ids );
1028 }
1029 $format_ids = array_fill( 0, sizeof( $user_ids ), '%d' );
1030 $sql = $wpdb->prepare( "SELECT user_login, user_email FROM {$wpdb->users} WHERE ID IN(" . join( ',', $format_ids ) . ')', $user_ids );
1031 $users = $wpdb->get_results( $sql );
1032 $size = sizeof( $users );
1033
1034 foreach ( $users as $i => $user ) {
1035 printf( '<strong>%s</strong> ( %s )', $user->user_login, $user->user_email );
1036 if ( $i < $size - 1 ) {
1037 echo ', ';
1038 }
1039 }
1040 } else {
1041 _e( 'No user assigned', 'learnpress' );
1042 }
1043 }*/
1044
1045 /**
1046 * Get email of user has bought this order.
1047 * In case this order is for multi users return an array with multi email addresses.
1048 *
1049 * @return array
1050 * @since 2.1.5
1051 */
1052 public function get_user_data(): array {
1053 $data = array();
1054
1055 $user_ids = $this->get_users();
1056 if ( ! empty( $user_ids ) ) {
1057 foreach ( $user_ids as $user_id ) {
1058 $user = learn_press_get_user( $user_id );
1059 if ( $user->is_exists() ) {
1060 $data[ $user_id ] = $user->get_data(
1061 array(
1062 'id',
1063 'email',
1064 'user_login',
1065 'description',
1066 'first_name',
1067 'last_name',
1068 'nickname',
1069 'display_name',
1070 )
1071 );
1072 }
1073 }
1074 }
1075
1076 return $data;
1077 }
1078
1079 /**
1080 * Get email's user by user_id
1081 *
1082 * @param int $user_id
1083 * @return string
1084 * @editor tungnx
1085 * @modify 4.1.3
1086 */
1087 public function get_user_email( int $user_id = 0 ): string {
1088 $user = learn_press_get_user( $user_id );
1089 if ( $user && ! $user instanceof LP_User_Guest ) {
1090 $email = $user->get_email();
1091 } else {
1092 $email = $this->get_checkout_email();
1093 }
1094
1095 return $email;
1096 }
1097
1098 public function get_child_orders( $force = false ) {
1099 if ( $force ) {
1100 wp_cache_delete( 'order-' . $this->get_id(), 'learn-press/child-orders' );
1101 }
1102
1103 return apply_filters( 'learn-press/child-orders', $this->_curd->get_child_orders( $this->get_id() ), $this->get_id() );
1104 }
1105
1106 /**
1107 * Order title
1108 *
1109 * @param string $context
1110 *
1111 * @return array|mixed
1112 */
1113 public function get_title( $context = '' ) {
1114 return $this->get_data( 'order_title', __( 'Order on', 'learnpress' ) . ' ' . current_time( 'l jS F Y h:i:s A' ) );
1115 }
1116
1117 public function get_parent_id() {
1118 return $this->get_data( 'parent_id', 0 );
1119 }
1120
1121 public function set_parent_id( $parent_id ) {
1122 $this->_set_data( 'parent_id', $parent_id );
1123 }
1124
1125 public function set_user_id( $user_id ) {
1126 $this->_set_data( 'user_id', $user_id );
1127 }
1128
1129 /**
1130 * Set 1 for is user guest
1131 *
1132 * @param int $user_type_guest
1133 */
1134 public function set_user_type_guest( int $user_type_guest ) {
1135 $this->_set_data( 'user_type_guest', $user_type_guest );
1136 }
1137
1138 /**
1139 * Get user's ids of order.
1140 *
1141 * @return array|int
1142 */
1143 public function get_user_id() {
1144 return $this->get_data( 'user_id', -1 );
1145 }
1146
1147 /**
1148 * Get date modified of order.
1149 *
1150 * @return LP_Datetime
1151 */
1152 public function get_date_modified() {
1153 return $this->get_data( 'date_modified' );
1154 }
1155
1156 /**
1157 * Set date modified of order.
1158 *
1159 * @param mixed $date
1160 */
1161 public function set_date_modified( $date ) {
1162 $this->_set_data( 'date_modified', $date );
1163 }
1164
1165 /**
1166 * Set method for creating the order, such as: checkout
1167 *
1168 * @param string $created_via
1169 */
1170 public function set_created_via( $created_via ) {
1171 $this->_set_data( 'created_via', $created_via );
1172 }
1173
1174 /**
1175 * Get method which order is created, such as: checkout
1176 *
1177 * @return string
1178 */
1179 public function get_created_via(): string {
1180 return $this->get_data( 'created_via', '' );
1181 }
1182
1183 /**
1184 * Update order status if changed and trigger actions.
1185 *
1186 * @return bool
1187 * @throws Exception
1188 *
1189 * @editor tungnx
1190 * @modify 4.1.3 - comment - not use
1191 */
1192 // public function _save_status(): bool {
1193 //
1194 // // Nothing changed
1195 // if ( ! $this->_status ) {
1196 // return false;
1197 // }
1198 //
1199 // $order_id = $this->get_id();
1200 // $old_status = $this->_status['from'] ?? '';
1201 // $new_status = $this->_status['to'] ?? '';
1202 //
1203 // // Only update if new status is difference with old status.
1204 // if ( $new_status !== $old_status ) {
1205 //
1206 // if ( ! $this->get_user_id() ) {
1207 // // $new_status = 'pending';
1208 // }
1209 //
1210 // if ( doing_action( 'save_post' ) ) {
1211 // // Update post's status using wpdb to preventing loop
1212 // global $wpdb;
1213 // $updated = $wpdb->update( $wpdb->posts, array( 'post_status' => 'lp-' . $new_status ), array( 'ID' => $order_id ), array( '%s' ) );
1214 // } else {
1215 // $updated = wp_update_post(
1216 // array(
1217 // 'post_status' => 'lp-' . $new_status,
1218 // 'ID' => $order_id,
1219 // )
1220 // );
1221 // }
1222 //
1223 // // Clear cache
1224 // wp_cache_delete( $order_id, 'posts' );
1225 //
1226 // /**
1227 // * Trigger actions after status was changed
1228 // *
1229 // * @deprecated
1230 // */
1231 // //do_action( 'learn_press_order_status_' . $new_status, $order_id );
1232 // //do_action( 'learn_press_order_status_' . $old_status . '_to_' . $new_status, $order_id );
1233 // //do_action( 'learn_press_order_status_changed', $order_id, $old_status, $new_status );
1234 // // backward compatible
1235 // do_action( 'learn_press_update_order_status', $new_status, $order_id );
1236 //
1237 // /**
1238 // * @since 3.0.0
1239 // */
1240 // do_action( 'learn-press/order/status-' . $new_status, $order_id, $old_status );
1241 // do_action( 'learn-press/order/status-' . $old_status . '-to-' . $new_status, $order_id );
1242 // do_action( 'learn-press/order/status-changed', $order_id, $old_status, $new_status );
1243 //
1244 // return true;
1245 // }
1246 //
1247 // return false;
1248 // }
1249
1250 /**
1251 * Save order data.
1252 *
1253 * @return mixed
1254 *
1255 * @throws Exception
1256 */
1257 public function save() {
1258 $old_status = '';
1259
1260 if ( $this->get_id() ) {
1261 $old_status = str_replace( 'lp-', '', get_post_status( $this->get_id() ) );
1262 $return = $this->_curd->update( $this );
1263 } else {
1264 $return = $this->_curd->create( $this );
1265 }
1266
1267 $new_status = str_replace( 'lp-', '', get_post_status( $this->get_id() ) );
1268
1269 $order_id = $this->get_id();
1270
1271 if ( $new_status !== $old_status ) {
1272 do_action( 'learn-press/order/status-' . $new_status, $order_id, $old_status );
1273 do_action( 'learn-press/order/status-' . $old_status . '-to-' . $new_status, $order_id );
1274 do_action( 'learn-press/order/status-changed', $order_id, $old_status, $new_status );
1275 //do_action( 'learn_press_update_order_status', $new_status, $order_id );
1276 }
1277
1278 //$this->_save_status();
1279
1280 return $return;
1281 }
1282
1283 /**
1284 * Get an instance of LP_Order by post ID or WP_Post object
1285 *
1286 * @param $order
1287 * @param $force
1288 *
1289 * @return LP_Order
1290 */
1291 public static function instance( $order, $force = true ) {
1292 // learn_press_deprecated_function( 'new LP_Order', '3.0', 'learn_press_get_order' );
1293
1294 return learn_press_get_order( $order );
1295 }
1296
1297 /** Getter/Setter **/
1298
1299 public function set_customer_message( $message ) {
1300 $this->_set_data( 'customer_message', $message );
1301 }
1302
1303 public function set_customer_note( $note ) {
1304 $this->_set_data( 'customer_note', $note );
1305 }
1306
1307 public function get_customer_note() {
1308 return $this->get_data( 'customer_note' ) . '';
1309 }
1310
1311 public function set_user_ip_address( $value ) {
1312 $this->_set_data( 'user_ip_address', $value );
1313 }
1314
1315 public function set_user_agent( $value ) {
1316 $this->_set_data( 'user_agent', $value );
1317 }
1318
1319 public function get_user_agent() {
1320 return $this->get_data( 'user_agent' );
1321 }
1322
1323 public function set_checkout_email( $email ) {
1324 $this->_set_data( 'checkout_email', $email );
1325 }
1326
1327 /**
1328 * Get email checkout for case Guest
1329 *
1330 * @return string
1331 */
1332 public function get_checkout_email(): string {
1333 return $this->get_data( 'checkout_email', '' );
1334 }
1335
1336 /**
1337 * Short function to check order is completed.
1338 *
1339 * @return bool
1340 */
1341 public function is_completed(): bool {
1342 return $this->get_order_status() === 'completed';
1343 }
1344
1345 /**
1346 * Check Order is created manual.
1347 *
1348 * @return bool
1349 * @since 4.1.3
1350 * @author tungnx
1351 * @version 1.0.0
1352 */
1353 public function is_manual(): bool {
1354 return $this->get_created_via() === 'manual';
1355 }
1356
1357 /**
1358 * Check can delete lp_user_items old
1359 *
1360 * @param LP_Course $course
1361 *
1362 * @return bool
1363 * @throws Exception
1364 * @author tungnx
1365 * @since 4.1.4
1366 * @version 1.0.0
1367 */
1368 public function check_can_delete_item_old( LP_Course $course ): bool {
1369 $can_delete = false;
1370
1371 $lp_user_items_db = LP_User_Items_DB::getInstance();
1372
1373 /**
1374 * For case user buy on frontend (not LP Order manual)
1375 * And course enable repurchase and repurchase_type is keep
1376 */
1377 $allow_repurchase_type = '';
1378
1379 $filter = new LP_User_Items_Filter();
1380 $filter->user_id = get_current_user_id();
1381 $filter->item_id = $course->get_id();
1382 $user_course = $lp_user_items_db->get_last_user_course( $filter );
1383
1384 if ( $user_course && isset( $user_course->user_item_id ) ) {
1385 $latest_user_item_id = $user_course->user_item_id;
1386
1387 /** Get allow_repurchase_type for reset or update. Add in: rest-api/v1/frontend/class-lp-courses-controller.php: purchase_course */
1388 $allow_repurchase_type = learn_press_get_user_item_meta( $latest_user_item_id, '_lp_allow_repurchase_type' );
1389 }
1390
1391 /**
1392 * Course is free
1393 * Or not allow repurchase
1394 * Or repurchase not type 'Keep'
1395 * And else
1396 * Will deleted lp_user_items old
1397 */
1398 if ( $course->is_free() || empty( $allow_repurchase_type ) || ! $course->allow_repurchase() || $allow_repurchase_type != 'keep' ) {
1399 $can_delete = true;
1400 }
1401
1402 return apply_filters( 'learnpress/order/can_delete_old_item', $can_delete, $course );
1403 }
1404 }
1405 }
1406