PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.0.0
Tutor LMS – eLearning and online course solution v3.0.0
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 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.5.9 1.6.0 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.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 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.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 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.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / WooCommerce.php
tutor / classes Last commit date
Addons.php 1 year ago Admin.php 1 year ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 1 year ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 1 year ago Course_Settings_Tabs.php 1 year ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 1 year ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Input.php 1 year ago Instructor.php 1 year ago Instructors_List.php 1 year ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 1 year ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 1 year ago QuizBuilder.php 1 year ago Quiz_Attempts_List.php 1 year ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Singleton.php 1 year ago Student.php 1 year ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 1 year ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 1 year ago Tutor.php 1 year ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 1 year ago Upgrader.php 1 year ago User.php 1 year ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 1 year ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 1 year ago
WooCommerce.php
787 lines
1 <?php
2 /**
3 * Manage WooCommerce integration
4 *
5 * @package Tutor\WooCommerce
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Handle woocommerce hooks
19 *
20 * @since 1.0.0
21 */
22 class WooCommerce extends Tutor_Base {
23
24 /**
25 * Register hooks
26 *
27 * @since 1.0.0
28 */
29 public function __construct() {
30 parent::__construct();
31
32 add_action( 'admin_notices', array( $this, 'notice_on_disabled_wc' ) );
33
34 // Add option settings.
35 add_filter( 'tutor_monetization_options', array( $this, 'tutor_monetization_options' ) );
36
37 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
38 if ( 'wc' !== $monetize_by ) {
39 return;
40 }
41
42 add_filter( 'tutor/options/attr', array( $this, 'add_options' ) );
43
44 /**
45 * Is Course Purchasable
46 */
47 add_filter( 'is_course_purchasable', array( $this, 'is_course_purchasable' ), 10, 2 );
48 add_filter( 'get_tutor_course_price', array( $this, 'get_tutor_course_price' ), 10, 2 );
49 add_filter( 'tutor_course_sell_by', array( $this, 'tutor_course_sell_by' ) );
50
51 add_filter( 'product_type_options', array( $this, 'add_tutor_type_in_wc_product' ) );
52
53 add_action( 'add_meta_boxes', array( $this, 'register_meta_box' ) );
54 add_action( 'save_post_' . $this->course_post_type, array( $this, 'save_course_meta' ), 10, 2 );
55 add_action( 'save_post_product', array( $this, 'save_wc_product_meta' ) );
56
57 add_action( 'tutor_course/single/before/enroll', 'wc_print_notices' );
58
59 /**
60 * After place new order
61 */
62 add_action( 'woocommerce_new_order', array( $this, 'course_placing_order_from_admin' ), 10, 3 );
63 add_action( 'woocommerce_new_order_item', array( $this, 'course_placing_order_from_customer' ), 10, 3 );
64
65 /**
66 * Order Status Hook
67 *
68 * Remove course from active courses if an order is cancelled or refunded
69 */
70 add_action( 'woocommerce_order_status_changed', array( $this, 'enrolled_courses_status_change' ), 10, 3 );
71
72 /**
73 * Add Earning Data
74 */
75 add_action( 'woocommerce_new_order_item', array( $this, 'add_earning_data' ), 10, 3 );
76 add_action( 'woocommerce_order_status_changed', array( $this, 'add_earning_data_status_change' ), 10, 3 );
77
78 /**
79 * WC Print Notices After Enroll
80 *
81 * @since 1.3.5
82 */
83 if ( tutor_utils()->has_wc() ) {
84 add_action( 'tutor_course/single/before/inner-wrap', 'wc_print_notices', 10 );
85 add_action( 'tutor_course/single/enrolled/before/inner-wrap', 'wc_print_notices', 10 );
86 }
87
88 /**
89 * Manage WooCommerce plugin dependency
90 *
91 * @since 1.7.8
92 */
93 $woocommerce_path = dirname( dirname( __DIR__ ) ) . DIRECTORY_SEPARATOR . 'woocommerce' . DIRECTORY_SEPARATOR . 'woocommerce.php';
94 register_deactivation_hook( $woocommerce_path, array( $this, 'disable_tutor_monetization' ) );
95 /**
96 * Redirect student on enrolled courses after course
97 * Enrollment complete
98 *
99 * @since 1.9.0
100 */
101 add_action( 'woocommerce_thankyou', array( $this, 'redirect_to_enrolled_courses' ) );
102
103 /**
104 * Change woo commerce cart product link if it is tutor product
105 */
106 add_filter( 'woocommerce_cart_item_permalink', array( $this, 'tutor_update_product_url' ), 10, 2 );
107 add_filter( 'woocommerce_order_item_permalink', array( $this, 'filter_order_item_permalink_callback' ), 10, 3 );
108
109 /**
110 * On WC product delete clear course linked product
111 *
112 * @since 2.0.7
113 */
114 add_action( 'delete_post', array( $this, 'clear_course_linked_product' ) );
115
116 add_action( 'before_woocommerce_init', array( $this, 'declare_tutor_compatibility_with_hpos' ) );
117 }
118
119 /**
120 * Show admin notice if user disable the WC plugin.
121 *
122 * @since 1.0.0
123 *
124 * @return void
125 */
126 public function notice_on_disabled_wc() {
127 $show = get_option( 'tutor_show_woocommerce_notice' ) && 'free' === tutor_utils()->get_option( 'monetize_by', 'free' );
128
129 if ( $show ) {
130 $message = __( 'Since monetization is currently disabled, your courses are set to free. Enable Tutor LMS monetization to start selling your courses.', 'tutor' );
131 echo '<div class="notice notice-error"><p>' . esc_html( $message ) . '</p></div>';
132 }
133 }
134
135 /**
136 * Check HPOS feature enabled.
137 * WC declared HPOS (Hight Performance Order Storage) feature stable on october 2023 from WC v8.2
138 *
139 * @see https://woo.com/document/high-performance-order-storage
140 *
141 * @since 2.6.0
142 *
143 * @return bool
144 */
145 public static function hpos_enabled() {
146 $hpos = false;
147
148 if ( tutor_utils()->has_wc() && version_compare( WC()->version, '8.2.0', '>=' ) ) {
149 $hpos = 'yes' === get_option( 'woocommerce_custom_orders_table_enabled' );
150 }
151
152 return $hpos;
153 }
154
155 /**
156 * Declare tutor compatibility with WC HPOS feature
157 *
158 * @since 2.6.0
159 *
160 * @return void
161 */
162 public function declare_tutor_compatibility_with_hpos() {
163 if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
164 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', TUTOR_FILE, true );
165 }
166 }
167
168 /**
169 * On WC product delete, clear course linked product
170 *
171 * @since 2.0.7
172 *
173 * @param int $post_id post id.
174 *
175 * @return void
176 */
177 public function clear_course_linked_product( $post_id ) {
178 if ( get_post_type( $post_id ) === 'product' ) {
179 global $wpdb;
180 $wpdb->query(
181 $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key=%s AND meta_value=%d", '_tutor_course_product_id', $post_id )
182 );
183 }
184 }
185
186 /**
187 * Order item callback handler
188 *
189 * @since 1.0.0
190 *
191 * @param string $product_permalink permalink.
192 * @param mixed $item order item.
193 * @param mixed $order orders.
194 *
195 * @return string permalink of course
196 */
197 public function filter_order_item_permalink_callback( $product_permalink, $item, $order ) {
198
199 // For product variations.
200 if ( $item->get_variation_id() > 0 ) {
201 $product = $item->get_product();
202
203 $is_visible = $product && $product->is_visible();
204
205 // Get the instance of the parent variable product Object.
206 $parent_product = wc_get_product( $item->get_product_id() );
207
208 // Return the parent product permalink (if product is visible).
209 return $is_visible ? $parent_product->get_permalink() : '';
210 }
211
212 $course_id = $this->get_post_id_by_meta_key_and_value( '_tutor_course_product_id', $item->get_product_id() );
213
214 return get_permalink( $course_id );
215 }
216
217 /**
218 * Get post id my meta key & value
219 *
220 * @since 1.0.0
221 *
222 * @param mixed $key meta key.
223 * @param mixed $value meta value.
224 *
225 * @return mixed post id on success, false on failure
226 */
227 public function get_post_id_by_meta_key_and_value( $key, $value ) {
228 global $wpdb;
229 $meta = $wpdb->get_results( 'SELECT * FROM `' . $wpdb->postmeta . "` WHERE meta_key='" . esc_sql( $key ) . "' AND meta_value='" . esc_sql( $value ) . "'" );
230 if ( is_array( $meta ) && ! empty( $meta ) && isset( $meta[0] ) ) {
231 $meta = $meta[0];
232 }
233 if ( is_object( $meta ) ) {
234 return $meta->post_id;
235 } else {
236 return false;
237 }
238 }
239
240 /**
241 * Check if course is purchase able
242 *
243 * @since 1.0.0
244 *
245 * @param bool $bool default value.
246 * @param int $course_id course id.
247 *
248 * @return boolean
249 */
250 public function is_course_purchasable( $bool, $course_id ) {
251 if ( ! tutor_utils()->has_wc() ) {
252 return false;
253 }
254
255 $course_id = tutor_utils()->get_post_id( $course_id );
256 $price_type = tutor_utils()->price_type( $course_id );
257 $has_product_id = tutor_utils()->get_course_product_id( $course_id );
258 if ( Course::PRICE_TYPE_PAID === $price_type && $has_product_id ) {
259 return true;
260 }
261
262 return false;
263 }
264
265 /**
266 * Get course price
267 *
268 * @since 1.0.0
269 *
270 * @param mixed $price course price.
271 * @param int $course_id course id.
272 *
273 * @return string
274 */
275 public function get_tutor_course_price( $price, $course_id ) {
276 $price = null;
277
278 if ( tutor_utils()->is_course_purchasable( $course_id ) ) {
279 if ( tutor_utils()->has_wc() ) {
280 $product_id = tutor_utils()->get_course_product_id( $course_id );
281 $product = wc_get_product( $product_id );
282
283 if ( $product ) {
284 ob_start();
285 ?>
286 <div class="price">
287 <?php echo $product->get_price_html(); //phpcs:ignore ?>
288 </div>
289 <?php
290 return ob_get_clean();
291 }
292 }
293 }
294
295 return $price;
296 }
297
298 /**
299 * Sell by filter handler
300 *
301 * @since 1.0.0
302 *
303 * @return string
304 */
305 public function tutor_course_sell_by() {
306 return 'woocommerce';
307 }
308
309 /**
310 * Add tutor type in WC product
311 *
312 * @since 1.0.0
313 *
314 * @param array $types types.
315 *
316 * @return array
317 */
318 public function add_tutor_type_in_wc_product( $types ) {
319 $types['tutor_product'] = array(
320 'id' => '_tutor_product',
321 'wrapper_class' => 'show_if_simple',
322 'label' => __( 'For Tutor', 'tutor' ),
323 'description' => __( 'This checkmark ensure that you will sell a specific course via this product.', 'tutor' ),
324 'default' => 'no',
325 );
326
327 return $types;
328 }
329
330 /**
331 * Save course meta for attaching WC product
332 *
333 * @since 1.0.0
334 *
335 * @param int $post_ID this is course ID.
336 * @param mixed $post course details.
337 *
338 * @return void
339 */
340 public function save_course_meta( $post_ID, $post ) {
341 do_action( 'save_tutor_course', $post_ID, $post );
342 }
343
344 /**
345 * Register meta box
346 *
347 * @since 1.0.0
348 *
349 * @return void
350 */
351 public function register_meta_box() {
352 tutor_meta_box_wrapper( 'tutor-attach-product', __( 'Add Product', 'tutor' ), array( $this, 'course_add_product_metabox' ), $this->course_post_type, 'advanced', 'high', 'tutor-admin-post-meta' );
353 }
354
355 /**
356 * Meta box view
357 *
358 * @since 1.0.0
359 *
360 * @return void
361 */
362 public function course_add_product_metabox() {
363 include tutor()->path . 'views/metabox/course-add-product-metabox.php';
364 }
365
366 /**
367 * Save WC product meta
368 *
369 * @since 1.0.0
370 *
371 * @param int $post_ID post id.
372 *
373 * @return void
374 */
375 public function save_wc_product_meta( $post_ID ) {
376 $is_tutor_product = Input::post( '_tutor_product', '' );
377 if ( 'on' === $is_tutor_product ) {
378 update_post_meta( $post_ID, '_tutor_product', 'yes' );
379 } else {
380 delete_post_meta( $post_ID, '_tutor_product' );
381 }
382 }
383
384 /**
385 * Take enrolled course action based on order status change
386 *
387 * Order auto complete
388 *
389 * @param int $order_id wc order id.
390 * @param string $status_from from status.
391 * @param string $status_to to status.
392 *
393 * @return void
394 */
395 public function enrolled_courses_status_change( $order_id, $status_from, $status_to ) {
396 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
397 return;
398 }
399
400 $enrolled_ids_with_course = tutor_utils()->get_course_enrolled_ids_by_order_id( $order_id );
401
402 if ( $enrolled_ids_with_course ) {
403 $enrolled_ids = wp_list_pluck( $enrolled_ids_with_course, 'enrolled_id' );
404
405 if ( is_array( $enrolled_ids ) && count( $enrolled_ids ) ) {
406 foreach ( $enrolled_ids as $enrolled_id ) {
407 /**
408 * If order status is processing and payment is not cash on
409 * delivery then mark enrollment as completed.
410 *
411 * Note: Order status processing simply mean customer have done
412 * payment.
413 *
414 * @since v2.0.5
415 */
416 if ( self::should_order_auto_complete( $order_id ) ) {
417 // Mark enrollment as completed.
418 tutor_utils()->course_enrol_status_change( $enrolled_id, 'completed' );
419 // Mark WC order as completed.
420 self::mark_order_complete( $order_id );
421 } else {
422 tutor_utils()->course_enrol_status_change( $enrolled_id, $status_to );
423 }
424
425 // Invoke enrolled hook.
426 if ( 'completed' === $status_to ) {
427 $user_id = get_post_field( 'post_author', $enrolled_id );
428 $course_id = get_post_field( 'post_parent', $enrolled_id );
429 do_action( 'tutor_after_enrolled', $course_id, $user_id, $enrolled_id );
430 }
431 }
432 }
433 }
434 }
435
436 /**
437 * Add option for WooCommerce settings
438 *
439 * @since 1.0.0
440 *
441 * @param array $attr option attrs.
442 *
443 * @return mixed
444 */
445 public function add_options( $attr ) {
446 $attr['monetization']['blocks']['block_woocommerce']['fields'][] = array(
447 'key' => 'enable_guest_course_cart',
448 'type' => 'toggle_switch',
449 'label' => __( 'Enable Guest Mode', 'tutor' ),
450 'label_title' => '',
451 'default' => 'off',
452 'desc' => __( 'Allow customers to place orders without an account.', 'tutor' ),
453 );
454
455 return $attr;
456 }
457
458 /**
459 * Returning monetization options
460 *
461 * @since v.1.3.5
462 *
463 * @param array $arr attrs.
464 *
465 * @return mixed
466 */
467 public function tutor_monetization_options( $arr ) {
468 $has_wc = tutor_utils()->has_wc();
469 if ( $has_wc ) {
470 $arr['wc'] = __( 'WooCommerce', 'tutor' );
471 }
472 return $arr;
473 }
474
475 /**
476 * Adding Earning Data processing WooCommerce
477 *
478 * @param int $item_id item id.
479 * @param mixed $item item.
480 * @param int $order_id order id.
481 *
482 * @since 1.1.2
483 */
484 public function add_earning_data( $item_id, $item, $order_id ) {
485
486 if ( 'wc' !== tutor_utils()->get_option( 'monetize_by' ) ) {
487 return;
488 }
489
490 global $wpdb;
491 $item = new \WC_Order_Item_Product( $item );
492
493 $product_id = $item->get_product_id();
494 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
495
496 if ( $if_has_course ) {
497 $order = wc_get_order( $order_id );
498 $course_id = $if_has_course->post_id;
499 $user_id = get_post_field( 'post_author', $course_id );
500 $order_status = "wc-{$order->get_status()}";
501
502 /**
503 * Return here if already added this product from this order
504 *
505 * @since v1.9.7
506 */
507 $exist_count = (int) $wpdb->get_var(
508 $wpdb->prepare(
509 "SELECT COUNT(earning_id)
510 FROM {$wpdb->prefix}tutor_earnings
511 WHERE course_id=%d
512 AND order_id=%d
513 AND user_id=%d",
514 $course_id,
515 $order_id,
516 $user_id
517 )
518 );
519
520 if ( $exist_count > 0 ) {
521 return;
522 }
523
524 $total_price = $item->get_total();
525
526 list( $admin_amount, $instructor_amount ) = array_values( tutor_split_amounts( $total_price ) );
527
528 $earnings = Earnings::get_instance();
529 $earning_data = apply_filters( 'tutor_new_earning_data', $earnings->prepare_earning_data( $total_price, $course_id, $order_id, $order_status, $admin_amount, $instructor_amount ) );
530
531 $wpdb->insert( $wpdb->prefix . 'tutor_earnings', $earning_data );
532 }
533 }
534
535 /**
536 * Change Earning data status
537 *
538 * @since 1.0.0
539 *
540 * @param int $order_id wc order id.
541 * @param string $status_from previous status.
542 * @param string $status_to current status.
543 *
544 * @return void
545 */
546 public function add_earning_data_status_change( $order_id, $status_from, $status_to ) {
547 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
548 tutor_log( 'not tutor order' );
549 return;
550 }
551
552 /**
553 * If it is auto complete order then make earning status complete
554 * to reflect earning for admin & instructor
555 *
556 * @since 2.0.9
557 */
558 if ( self::should_order_auto_complete( $order_id ) ) {
559 $status_to = 'completed';
560 }
561
562 tutor_utils()->change_earning_status( $order_id, $status_to );
563 }
564
565 /**
566 * Course placing order from admin
567 *
568 * @since v.1.6.7
569 *
570 * @param int $order_id wc order id.
571 *
572 * @return void
573 */
574 public function course_placing_order_from_admin( $order_id ) {
575 if ( ! is_admin() ) {
576 return;
577 }
578
579 $order = wc_get_order( $order_id );
580 /**
581 * Loop though each WC order item.
582 *
583 * @var WC_Order_Item $item WC order item object.
584 */
585 foreach ( $order->get_items() as $item ) {
586 $product_id = $item->get_product_id();
587 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
588 if ( $if_has_course ) {
589 $course_id = $if_has_course->post_id;
590 $customer_id = $order->get_customer_id();
591 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
592 }
593 }
594 }
595
596 /**
597 * Course placing order from customer
598 *
599 * @since 1.6.7
600 *
601 * @param int $item_id item id.
602 * @param mixed $item order item.
603 * @param int $order_id wc order id.
604 *
605 * @return void
606 */
607 public function course_placing_order_from_customer( $item_id, $item, $order_id ) {
608 if ( is_admin() ) {
609 return;
610 }
611
612 $item = new \WC_Order_Item_Product( $item );
613 $product_id = $item->get_product_id();
614 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
615
616 if ( $if_has_course ) {
617 $order = wc_get_order( $order_id );
618
619 /**
620 * Get customer ID from from order
621 *
622 * @since 2.1.7
623 */
624 $customer_id = $order->get_customer_id();
625 $course_id = $if_has_course->post_id;
626 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
627 }
628 }
629
630 /**
631 * Disable course monetization on woocommerce deactivation
632 *
633 * @since 1.7.8
634 *
635 * @return void
636 */
637 public function disable_tutor_monetization() {
638 tutor_utils()->update_option( 'monetize_by', 'free' );
639 update_option( 'tutor_show_woocommerce_notice', true );
640 }
641
642 /**
643 * Redirect student on enrolled courses after course
644 * enrollment complete if course is purchasable
645 *
646 * @since 1.0.0
647 *
648 * @param int $order_id wc order id.
649 *
650 * @return void
651 */
652 public function redirect_to_enrolled_courses( $order_id ) {
653 if ( ! tutor_utils()->get_option( 'wc_automatic_order_complete_redirect_to_courses' ) ) {
654 // Since 1.9.1.
655 return;
656 }
657
658 // get woo order details.
659 $order = wc_get_order( $order_id );
660 $tutor_product = false;
661 $url = tutor_utils()->tutor_dashboard_url() . 'enrolled-courses/';
662
663 /**
664 * Loop though each WC order item.
665 *
666 * @var WC_Order_Item $item WC order item object.
667 */
668 foreach ( $order->get_items() as $item ) {
669 $product_id = $item->get_product_id();
670 // check if product associated with tutor course.
671 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
672 if ( $if_has_course ) {
673 $tutor_product = true;
674 }
675 }
676
677 // filter redirection url after woocommerce product purchase.
678 $redirect_url = apply_filters( 'tutor_woocommerce_redirect_url', $url );
679
680 // if tutor product & order status completed.
681 if ( $order->has_status( 'completed' ) && $tutor_product ) {
682 wp_safe_redirect( $redirect_url );
683 exit;
684 }
685 }
686
687 /**
688 * Change product url on cart page if product is tutor course
689 *
690 * @since 1.9.8
691 *
692 * @param string $permalink permalink.
693 * @param mixed $cart_item cart item.
694 *
695 * @return mixed
696 */
697 public function tutor_update_product_url( $permalink, $cart_item ) {
698
699 $woo_product_id = $cart_item['product_id'];
700 $product_meta = get_post_meta( $woo_product_id );
701
702 if ( isset( $product_meta['_tutor_product'] ) && $product_meta['_tutor_product'][0] ) {
703
704 global $wpdb;
705 $table = $wpdb->base_prefix . 'postmeta';
706 $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM {$table} WHERE meta_key = '_tutor_course_product_id' AND meta_value = %d ", $woo_product_id ) ); //phpcs:ignore
707
708 if ( $post_id ) {
709 $data = get_post_permalink( $post_id );
710 return $data;
711 }
712 }
713 }
714
715 /**
716 * Mark woocommerce order as complete only from the
717 * client side.
718 *
719 * @since 2.0.5
720 *
721 * @param int $order_id wc order id.
722 *
723 * @return bool
724 */
725 public static function mark_order_complete( int $order_id ): bool {
726 if ( is_admin() ) {
727 return false;
728 }
729
730 $order = \wc_get_order( $order_id );
731 $order->set_status( 'completed' );
732 $update = $order->save();
733
734 return (bool) $update;
735 }
736
737 /**
738 * Check if order should auto complete
739 *
740 * @since 2.0.9
741 *
742 * Bank transfer & check payments will consider as manual
743 * payments. Hence, if user pay with bank & check will not
744 * be auto complete
745 *
746 * @since 2.1.4
747 *
748 * @param int $order_id wc order id.
749 *
750 * @return boolean return true if it should auto complete, otherwise false
751 */
752 public static function should_order_auto_complete( int $order_id ): bool {
753 $auto_complete = false;
754
755 $order = wc_get_order( $order_id );
756 $order_data = is_object( $order ) && method_exists( $order, 'get_data' ) ? $order->get_data() : array();
757
758 $payment_method = isset( $order_data['payment_method'] ) ? $order_data['payment_method'] : '';
759 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
760
761 $should_auto_complete = tutor_utils()->get_option( 'tutor_woocommerce_order_auto_complete' );
762 $is_enabled_auto_complete = 'wc' === $monetize_by && $should_auto_complete ? true : false;
763
764 $manual_payments = array( 'cod', 'cheque', 'bacs' );
765 $order_status = method_exists( $order, 'get_status' ) ? $order->get_status() : '';
766
767 if ( 'completed' !== $order_status ) {
768 $is_tutor_order = tutor_utils()->is_tutor_order( $order->get_id() );
769
770 /**
771 * Is tutor order condition added with other conditions,
772 * to prevent order other than Tutor get completed
773 *
774 * @since 2.1.6
775 */
776 if ( ! is_admin() && $is_enabled_auto_complete && 'processing' === $order_status && ! in_array( $payment_method, $manual_payments ) && $is_tutor_order ) {
777 $auto_complete = true;
778 }
779 } else {
780 $auto_complete = true;
781 }
782
783 return $auto_complete;
784 }
785 }
786
787