PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.2.4
Tutor LMS – eLearning and online course solution v2.2.4
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 2 years ago Admin.php 2 years ago Ajax.php 3 years ago Announcements.php 3 years ago Assets.php 3 years ago Backend_Page_Trait.php 3 years ago Course.php 3 years ago Course_Embed.php 3 years ago Course_Filter.php 3 years ago Course_List.php 2 years ago Course_Settings_Tabs.php 3 years ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 3 years ago FormHandler.php 2 years ago Frontend.php 3 years ago Gutenberg.php 3 years ago Input.php 3 years ago Instructor.php 2 years ago Instructors_List.php 3 years ago Lesson.php 2 years ago Options_V2.php 2 years ago Post_types.php 3 years ago Private_Course_Access.php 3 years ago Q_and_A.php 3 years ago Question_Answers_List.php 3 years ago Quiz.php 2 years ago Quiz_Attempts_List.php 2 years ago RestAPI.php 3 years ago Reviews.php 3 years ago Rewrite_Rules.php 3 years ago Shortcode.php 2 years ago Student.php 2 years ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 2 years ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 3 years ago Tutor.php 3 years ago TutorEDD.php 2 years ago Tutor_Base.php 3 years ago Tutor_Setup.php 3 years ago Upgrader.php 3 years ago User.php 3 years ago Utils.php 2 years ago Video_Stream.php 3 years ago Withdraw.php 2 years ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 2 years ago
WooCommerce.php
828 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 option settings.
33 add_filter( 'tutor_monetization_options', array( $this, 'tutor_monetization_options' ) );
34
35 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
36 if ( 'wc' !== $monetize_by ) {
37 return;
38 }
39
40 add_filter( 'tutor/options/attr', array( $this, 'add_options' ) );
41
42 /**
43 * Is Course Purchasable
44 */
45 add_filter( 'is_course_purchasable', array( $this, 'is_course_purchasable' ), 10, 2 );
46 add_filter( 'get_tutor_course_price', array( $this, 'get_tutor_course_price' ), 10, 2 );
47 add_filter( 'tutor_course_sell_by', array( $this, 'tutor_course_sell_by' ) );
48
49 add_filter( 'product_type_options', array( $this, 'add_tutor_type_in_wc_product' ) );
50
51 add_action( 'add_meta_boxes', array( $this, 'register_meta_box' ) );
52 add_action( 'save_post_' . $this->course_post_type, array( $this, 'save_course_meta' ), 10, 2 );
53 add_action( 'save_post_product', array( $this, 'save_wc_product_meta' ) );
54
55 add_action( 'tutor_course/single/before/enroll', 'wc_print_notices' );
56
57 /**
58 * After place new order
59 */
60 add_action( 'woocommerce_new_order', array( $this, 'course_placing_order_from_admin' ), 10, 3 );
61 add_action( 'woocommerce_new_order_item', array( $this, 'course_placing_order_from_customer' ), 10, 3 );
62
63 /**
64 * Order Status Hook
65 *
66 * Remove course from active courses if an order is cancelled or refunded
67 */
68 add_action( 'woocommerce_order_status_changed', array( $this, 'enrolled_courses_status_change' ), 10, 3 );
69
70 /**
71 * Add Earning Data
72 */
73 add_action( 'woocommerce_new_order_item', array( $this, 'add_earning_data' ), 10, 3 );
74 add_action( 'woocommerce_order_status_changed', array( $this, 'add_earning_data_status_change' ), 10, 3 );
75
76 /**
77 * WC Print Notices After Enroll
78 *
79 * @since 1.3.5
80 */
81 if ( tutor_utils()->has_wc() ) {
82 add_action( 'tutor_course/single/before/inner-wrap', 'wc_print_notices', 10 );
83 add_action( 'tutor_course/single/enrolled/before/inner-wrap', 'wc_print_notices', 10 );
84 }
85
86 /**
87 * Manage WooCommerce plugin dependency
88 *
89 * @since 1.7.8
90 */
91 $woocommerce_path = dirname( dirname( __DIR__ ) ) . DIRECTORY_SEPARATOR . 'woocommerce' . DIRECTORY_SEPARATOR . 'woocommerce.php';
92 register_deactivation_hook( $woocommerce_path, array( $this, 'disable_tutor_monetization' ) );
93 /**
94 * Redirect student on enrolled courses after course
95 * Enrollment complete
96 *
97 * @since 1.9.0
98 */
99 add_action( 'woocommerce_thankyou', array( $this, 'redirect_to_enrolled_courses' ) );
100
101 /**
102 * Change woo commerce cart product link if it is tutor product
103 */
104 add_filter( 'woocommerce_cart_item_permalink', array( $this, 'tutor_update_product_url' ), 10, 2 );
105 add_filter( 'woocommerce_order_item_permalink', array( $this, 'filter_order_item_permalink_callback' ), 10, 3 );
106
107 /**
108 * On WC product delete clear course linked product
109 *
110 * @since 2.0.7
111 */
112 add_action( 'delete_post', array( $this, 'clear_course_linked_product' ) );
113 }
114
115 /**
116 * On WC product delete, clear course linked product
117 *
118 * @since 2.0.7
119 *
120 * @param int $post_id post id.
121 *
122 * @return void
123 */
124 public function clear_course_linked_product( $post_id ) {
125 if ( get_post_type( $post_id ) === 'product' ) {
126 global $wpdb;
127 $wpdb->query(
128 $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key=%s AND meta_value=%d", '_tutor_course_product_id', $post_id )
129 );
130 }
131 }
132
133 /**
134 * Order item callback handler
135 *
136 * @since 1.0.0
137 *
138 * @param string $product_permalink permalink.
139 * @param mixed $item order item.
140 * @param mixed $order orders.
141 *
142 * @return string permalink of course
143 */
144 public function filter_order_item_permalink_callback( $product_permalink, $item, $order ) {
145
146 // For product variations.
147 if ( $item->get_variation_id() > 0 ) {
148 $product = $item->get_product();
149
150 $is_visible = $product && $product->is_visible();
151
152 // Get the instance of the parent variable product Object.
153 $parent_product = wc_get_product( $item->get_product_id() );
154
155 // Return the parent product permalink (if product is visible).
156 return $is_visible ? $parent_product->get_permalink() : '';
157 }
158
159 $course_id = $this->get_post_id_by_meta_key_and_value( '_tutor_course_product_id', $item->get_product_id() );
160
161 return get_permalink( $course_id );
162 }
163
164 /**
165 * Get post id my meta key & value
166 *
167 * @since 1.0.0
168 *
169 * @param mixed $key meta key.
170 * @param mixed $value meta value.
171 *
172 * @return mixed post id on success, false on failure
173 */
174 public function get_post_id_by_meta_key_and_value( $key, $value ) {
175 global $wpdb;
176 $meta = $wpdb->get_results( 'SELECT * FROM `' . $wpdb->postmeta . "` WHERE meta_key='" . esc_sql( $key ) . "' AND meta_value='" . esc_sql( $value ) . "'" );
177 if ( is_array( $meta ) && ! empty( $meta ) && isset( $meta[0] ) ) {
178 $meta = $meta[0];
179 }
180 if ( is_object( $meta ) ) {
181 return $meta->post_id;
182 } else {
183 return false;
184 }
185 }
186
187 /**
188 * Check if course is purchase able
189 *
190 * @since 1.0.0
191 *
192 * @param bool $bool default value.
193 * @param int $course_id course id.
194 *
195 * @return boolean
196 */
197 public function is_course_purchasable( $bool, $course_id ) {
198 if ( ! tutor_utils()->has_wc() ) {
199 return false;
200 }
201
202 $course_id = tutor_utils()->get_post_id( $course_id );
203 $has_product_id = get_post_meta( $course_id, '_tutor_course_product_id', true );
204 if ( $has_product_id ) {
205 return true;
206 }
207 return false;
208 }
209
210 /**
211 * Get course price
212 *
213 * @since 1.0.0
214 *
215 * @param mixed $price course price.
216 * @param int $course_id course id.
217 *
218 * @return string
219 */
220 public function get_tutor_course_price( $price, $course_id ) {
221 $price = null;
222
223 if ( tutor_utils()->is_course_purchasable( $course_id ) ) {
224 if ( tutor_utils()->has_wc() ) {
225 $product_id = tutor_utils()->get_course_product_id( $course_id );
226 $product = wc_get_product( $product_id );
227
228 if ( $product ) {
229 ob_start();
230 ?>
231 <div class="price">
232 <?php echo $product->get_price_html(); //phpcs:ignore ?>
233 </div>
234 <?php
235 return ob_get_clean();
236 }
237 }
238 }
239
240 return $price;
241 }
242
243 /**
244 * Sell by filter handler
245 *
246 * @since 1.0.0
247 *
248 * @return string
249 */
250 public function tutor_course_sell_by() {
251 return 'woocommerce';
252 }
253
254 /**
255 * Add tutor type in WC product
256 *
257 * @since 1.0.0
258 *
259 * @param array $types types.
260 *
261 * @return array
262 */
263 public function add_tutor_type_in_wc_product( $types ) {
264 $types['tutor_product'] = array(
265 'id' => '_tutor_product',
266 'wrapper_class' => 'show_if_simple',
267 'label' => __( 'For Tutor', 'tutor' ),
268 'description' => __( 'This checkmark ensure that you will sell a specif course via this product.', 'tutor' ),
269 'default' => 'no',
270 );
271
272 return $types;
273 }
274
275 /**
276 * Save course meta for attaching WC product
277 *
278 * @since 1.0.0
279 *
280 * @param int $post_ID this is course ID.
281 * @param mixed $post course details.
282 *
283 * @return void
284 */
285 public function save_course_meta( $post_ID, $post ) {
286 do_action( 'save_tutor_course', $post_ID, $post );
287 }
288
289 /**
290 * Register meta box
291 *
292 * @since 1.0.0
293 *
294 * @return void
295 */
296 public function register_meta_box() {
297 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' );
298 }
299
300 /**
301 * Meta box view
302 *
303 * @since 1.0.0
304 *
305 * @return void
306 */
307 public function course_add_product_metabox() {
308 include tutor()->path . 'views/metabox/course-add-product-metabox.php';
309 }
310
311 /**
312 * Save WC product meta
313 *
314 * @since 1.0.0
315 *
316 * @param int $post_ID post id.
317 *
318 * @return void
319 */
320 public function save_wc_product_meta( $post_ID ) {
321 $is_tutor_product = Input::post( '_tutor_product', '' );
322 if ( 'on' === $is_tutor_product ) {
323 update_post_meta( $post_ID, '_tutor_product', 'yes' );
324 } else {
325 delete_post_meta( $post_ID, '_tutor_product' );
326 }
327 }
328
329 /**
330 * Take enrolled course action based on order status change
331 *
332 * Order auto complete
333 *
334 * @param int $order_id wc order id.
335 * @param string $status_from from status.
336 * @param string $status_to to status.
337 *
338 * @return void
339 */
340 public function enrolled_courses_status_change( $order_id, $status_from, $status_to ) {
341 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
342 return;
343 }
344
345 $enrolled_ids_with_course = $this->get_course_enrolled_ids_by_order_id( $order_id );
346
347 if ( $enrolled_ids_with_course ) {
348 $enrolled_ids = wp_list_pluck( $enrolled_ids_with_course, 'enrolled_id' );
349
350 if ( is_array( $enrolled_ids ) && count( $enrolled_ids ) ) {
351 foreach ( $enrolled_ids as $enrolled_id ) {
352 /**
353 * If order status is processing and payment is not cash on
354 * delivery then mark enrollment as completed.
355 *
356 * Note: Order status processing simply mean customer have done
357 * payment.
358 *
359 * @since v2.0.5
360 */
361 if ( self::should_order_auto_complete( $order_id ) ) {
362 // Mark enrollment as completed.
363 tutor_utils()->course_enrol_status_change( $enrolled_id, 'completed' );
364 // Mark WC order as completed.
365 self::mark_order_complete( $order_id );
366 } else {
367 tutor_utils()->course_enrol_status_change( $enrolled_id, $status_to );
368 }
369
370 // Invoke enrolled hook.
371 if ( 'completed' === $status_to ) {
372 $user_id = get_post_field( 'post_author', $enrolled_id );
373 $course_id = get_post_field( 'post_parent', $enrolled_id );
374 do_action( 'tutor_after_enrolled', $course_id, $user_id, $enrolled_id );
375 }
376 }
377 }
378 }
379 }
380
381 /**
382 * Get enrollment ids by order id
383 *
384 * @since 1.0.0
385 *
386 * @param int $order_id WC order id.
387 *
388 * @return array|bool
389 */
390 public function get_course_enrolled_ids_by_order_id( $order_id ) {
391 global $wpdb;
392 // Getting all of courses ids within this order.
393
394 $courses_ids = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key LIKE '_tutor_order_for_course_id_%' ", $order_id ) );
395
396 if ( is_array( $courses_ids ) && count( $courses_ids ) ) {
397 $course_enrolled_by_order = array();
398 foreach ( $courses_ids as $courses_id ) {
399 $course_id = str_replace( '_tutor_order_for_course_id_', '', $courses_id->meta_key );
400
401 $course_enrolled_by_order[] = array(
402 'course_id' => $course_id,
403 'enrolled_id' => $courses_id->meta_value,
404 'order_id' => $courses_id->post_id,
405 );
406 }
407 return $course_enrolled_by_order;
408 }
409 return false;
410 }
411
412 /**
413 * Add option for WooCommerce settings
414 *
415 * @since 1.0.0
416 *
417 * @param array $attr option attrs.
418 *
419 * @return mixed
420 */
421 public function add_options( $attr ) {
422 $attr['monetization']['blocks']['block_options']['fields'][] = array(
423 'key' => 'enable_guest_course_cart',
424 'type' => 'toggle_switch',
425 'label' => __( 'Enable Guest Mode', 'tutor' ),
426 'label_title' => '',
427 'default' => 'off',
428 'desc' => __( 'Allow customers to place orders without an account.', 'tutor' ),
429 );
430
431 return $attr;
432 }
433
434 /**
435 * Returning monetization options
436 *
437 * @since v.1.3.5
438 *
439 * @param array $arr attrs.
440 *
441 * @return mixed
442 */
443 public function tutor_monetization_options( $arr ) {
444 $has_wc = tutor_utils()->has_wc();
445 if ( $has_wc ) {
446 $arr['wc'] = __( 'WooCommerce', 'tutor' );
447 }
448 return $arr;
449 }
450
451 /**
452 * Adding Earning Data processing WooCommerce
453 *
454 * @param int $item_id item id.
455 * @param mixed $item item.
456 * @param int $order_id order id.
457 *
458 * @since 1.1.2
459 */
460 public function add_earning_data( $item_id, $item, $order_id ) {
461
462 if ( tutor_utils()->get_option( 'monetize_by' ) != 'wc' ) {
463 return;
464 }
465
466 global $wpdb;
467 $item = new \WC_Order_Item_Product( $item );
468
469 $product_id = $item->get_product_id();
470 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
471
472 if ( $if_has_course ) {
473 $course_id = $if_has_course->post_id;
474 $user_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_author FROM {$wpdb->posts} WHERE ID = %d ", $course_id ) );
475 $order_status = $wpdb->get_var( $wpdb->prepare( "SELECT post_status from {$wpdb->posts} where ID = %d ", $order_id ) );
476
477 /**
478 * Return here if already added this product from this order
479 *
480 * @since v1.9.7
481 */
482 $exist_count = (int) $wpdb->get_var(
483 $wpdb->prepare(
484 "SELECT COUNT(earning_id)
485 FROM {$wpdb->prefix}tutor_earnings
486 WHERE course_id=%d
487 AND order_id=%d
488 AND user_id=%d",
489 $course_id,
490 $order_id,
491 $user_id
492 )
493 );
494
495 if ( $exist_count > 0 ) {
496 return;
497 }
498
499 $total_price = $item->get_total();
500
501 $fees_deduct_data = array();
502 $tutor_earning_fees = tutor_utils()->get_option( 'fee_amount_type' );
503 $enable_fees_deducting = tutor_utils()->get_option( 'enable_fees_deducting' );
504
505 $course_price_grand_total = $total_price;
506
507 // Deduct predefined amount (percent or fixed).
508 if ( $enable_fees_deducting ) {
509 $fees_name = tutor_utils()->get_option( 'fees_name', '' );
510 $fees_amount = (int) tutor_utils()->avalue_dot( 'fees_amount', $tutor_earning_fees );
511 $fees_type = tutor_utils()->avalue_dot( 'fees_type', $tutor_earning_fees );
512
513 if ( $fees_amount > 0 ) {
514 if ( $fees_type === 'percent' ) {
515 $fees_amount = ( $total_price * $fees_amount ) / 100;
516 }
517
518 $course_price_grand_total = $total_price - $fees_amount;
519 }
520
521 $fees_deduct_data = array(
522 'deduct_fees_amount' => $fees_amount,
523 'deduct_fees_name' => $fees_name,
524 'deduct_fees_type' => $fees_type,
525 );
526 }
527
528 // Distribute amount between admin and instructor.
529 $sharing_enabled = tutor_utils()->get_option( 'enable_revenue_sharing' );
530 $instructor_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_instructor_commission' ) : 0;
531 $admin_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_admin_commission' ) : 100;
532 $commission_type = 'percent';
533 $instructor_amount = $instructor_rate > 0 ? ( ( $course_price_grand_total * $instructor_rate ) / 100 ) : 0;
534 $admin_amount = $admin_rate > 0 ? ( ( $course_price_grand_total * $admin_rate ) / 100 ) : 0;
535
536 // (Use Pro Filter - Start)
537 // The response must be same array structure.
538 // Do not change used variable names here, or change in both of here and pro plugin
539 $pro_arg = array(
540 'user_id' => $user_id,
541 'instructor_rate' => $instructor_rate,
542 'admin_rate' => $admin_rate,
543 'instructor_amount' => $instructor_amount,
544 'admin_amount' => $admin_amount,
545 'course_price_grand_total' => $course_price_grand_total,
546 'commission_type' => $commission_type,
547 );
548 $pro_calculation = apply_filters( 'tutor_pro_earning_calculator', $pro_arg );
549 extract( $pro_calculation );
550 // (Use Pro Filter - End).
551
552 // Prepare insertable earning data.
553 $earning_data = array(
554 'user_id' => $user_id,
555 'course_id' => $course_id,
556 'order_id' => $order_id,
557 'order_status' => $order_status,
558 'course_price_total' => $total_price,
559 'course_price_grand_total' => $course_price_grand_total,
560
561 'instructor_amount' => $instructor_amount,
562 'instructor_rate' => $instructor_rate,
563 'admin_amount' => $admin_amount,
564 'admin_rate' => $admin_rate,
565
566 'commission_type' => $commission_type,
567 'process_by' => 'woocommerce',
568 'created_at' => gmdate( 'Y-m-d H:i:s', tutor_time() ),
569 );
570 $earning_data = apply_filters( 'tutor_new_earning_data', array_merge( $earning_data, $fees_deduct_data ) );
571
572 $wpdb->insert( $wpdb->prefix . 'tutor_earnings', $earning_data );
573 }
574 }
575
576 /**
577 * Change Earning data status
578 *
579 * @since 1.0.0
580 *
581 * @param int $order_id wc order id.
582 * @param string $status_from previous status.
583 * @param string $status_to current status.
584 *
585 * @return void
586 */
587 public function add_earning_data_status_change( $order_id, $status_from, $status_to ) {
588 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
589 tutor_log( 'not tutor order' );
590 return;
591 }
592
593 /**
594 * If it is auto complete order then make earning status complete
595 * to reflect earning for admin & instructor
596 *
597 * @since 2.0.9
598 */
599 if ( self::should_order_auto_complete( $order_id ) ) {
600 $status_to = 'completed';
601 }
602
603 tutor_utils()->change_earning_status( $order_id, $status_to );
604 }
605
606 /**
607 * Course placing order from admin
608 *
609 * @since v.1.6.7
610 *
611 * @param int $order_id wc order id.
612 *
613 * @return void
614 */
615 public function course_placing_order_from_admin( $order_id ) {
616 if ( ! is_admin() ) {
617 return;
618 }
619
620 $order = wc_get_order( $order_id );
621 foreach ( $order->get_items() as $item ) {
622 $product_id = $item->get_product_id();
623 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
624 if ( $if_has_course ) {
625 $course_id = $if_has_course->post_id;
626 $customer_id = $order->get_customer_id();
627 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
628 }
629 }
630 }
631
632 /**
633 * Course placing order from customer
634 *
635 * @since 1.6.7
636 *
637 * @param int $item_id item id.
638 * @param mixed $item order item.
639 * @param int $order_id wc order id.
640 *
641 * @return void
642 */
643 public function course_placing_order_from_customer( $item_id, $item, $order_id ) {
644 if ( is_admin() ) {
645 return;
646 }
647
648 $item = new \WC_Order_Item_Product( $item );
649 $product_id = $item->get_product_id();
650 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
651
652 if ( $if_has_course ) {
653 $order = wc_get_order( $order_id );
654
655 /**
656 * Get customer ID from from order
657 *
658 * @since 2.1.7
659 */
660 $customer_id = $order->get_customer_id();
661 $course_id = $if_has_course->post_id;
662 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
663 }
664 }
665
666 /**
667 * Disable course monetization on woocommerce deactivation
668 *
669 * @since 1.7.8
670 *
671 * @return void
672 */
673 public function disable_tutor_monetization() {
674 tutor_utils()->update_option( 'monetize_by', 'free' );
675 update_option( 'tutor_show_woocommerce_notice', true );
676 }
677
678 /**
679 * Redirect student on enrolled courses after course
680 * enrollment complete if course is purchasable
681 *
682 * @since 1.0.0
683 *
684 * @param int $order_id wc order id.
685 *
686 * @return void
687 */
688 public function redirect_to_enrolled_courses( $order_id ) {
689 if ( ! tutor_utils()->get_option( 'wc_automatic_order_complete_redirect_to_courses' ) ) {
690 // Since 1.9.1.
691 return;
692 }
693
694 // get woo order details.
695 $order = wc_get_order( $order_id );
696 $tutor_product = false;
697 $url = tutor_utils()->tutor_dashboard_url() . 'enrolled-courses/';
698
699 foreach ( $order->get_items() as $item ) {
700 $product_id = $item->get_product_id();
701 // check if product associated with tutor course.
702 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
703 if ( $if_has_course ) {
704 $tutor_product = true;
705 }
706 }
707
708 // if tutor product & order status completed.
709 if ( $order->has_status( 'completed' ) && $tutor_product ) {
710 wp_safe_redirect( $url );
711 exit;
712 }
713 }
714
715 /**
716 * Change product url on cart page if product is tutor course
717 *
718 * @since 1.9.8
719 *
720 * @param string $permalink permalink.
721 * @param mixed $cart_item cart item.
722 *
723 * @return mixed
724 */
725 public function tutor_update_product_url( $permalink, $cart_item ) {
726
727 $woo_product_id = $cart_item['product_id'];
728 $product_meta = get_post_meta( $woo_product_id );
729
730 if ( isset( $product_meta['_tutor_product'] ) && $product_meta['_tutor_product'][0] ) {
731
732 global $wpdb;
733 $table = $wpdb->base_prefix . 'postmeta';
734 $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 ) );
735
736 if ( $post_id ) {
737 $data = get_post_permalink( $post_id );
738 return $data;
739 }
740 }
741 }
742
743 /**
744 * Mark woocommerce order as complete only from the
745 * client side.
746 *
747 * @since 2.0.5
748 *
749 * @param int $order_id wc order id.
750 *
751 * @return bool
752 */
753 public static function mark_order_complete( int $order_id ): bool {
754 if ( is_admin() ) {
755 return false;
756 }
757
758 $order = \wc_get_order( $order_id );
759 $order->set_status( 'completed' );
760 $update = $order->save();
761
762 return (bool) $update;
763 }
764
765 /**
766 * Check if order should auto complete
767 *
768 * @since 2.0.9
769 *
770 * Bank transfer & check payments will consider as manual
771 * payments. Hence, if user pay with bank & check will not
772 * be auto complete
773 *
774 * @since 2.1.4
775 *
776 * @param int $order_id wc order id.
777 *
778 * @return boolean return true if it should auto complete, otherwise false
779 */
780 public static function should_order_auto_complete( int $order_id ): bool {
781 $auto_complete = false;
782
783 $order = wc_get_order( $order_id );
784 $order_data = is_object( $order ) && method_exists( $order, 'get_data' ) ? $order->get_data() : array();
785
786 $payment_method = isset( $order_data['payment_method'] ) ? $order_data['payment_method'] : '';
787 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
788
789 $should_auto_complete = tutor_utils()->get_option( 'tutor_woocommerce_order_auto_complete' );
790 $is_enabled_auto_complete = 'wc' === $monetize_by && $should_auto_complete ? true : false;
791
792 $manual_payments = array( 'cod', 'cheque', 'bacs' );
793 $order_status = method_exists( $order, 'get_status' ) ? $order->get_status() : '';
794
795 if ( 'completed' !== $order_status ) {
796 $is_tutor_order = get_post_meta( $order->get_id(), '_is_tutor_order_for_course', true );
797
798 /**
799 * Is tutor order condition added with other conditions,
800 * to prevent order other than Tutor get completed
801 *
802 * @since 2.1.6
803 */
804 if ( ! is_admin() && $is_enabled_auto_complete && 'processing' === $order_status && ! in_array( $payment_method, $manual_payments ) && $is_tutor_order ) {
805 $auto_complete = true;
806 }
807 } else {
808 $auto_complete = true;
809 }
810
811 return $auto_complete;
812 }
813 }
814
815
816 add_action(
817 'admin_notices',
818 function() {
819
820 $show = get_option( 'tutor_show_woocommerce_notice' ) && tutor_utils()->get_option( 'monetize_by', 'free' ) == 'free';
821
822 if ( $show ) {
823 $message = __( 'Since WooCommerce is disabled, your monetized courses have been set to free. Please make sure to enable Tutor LMS monetization if you decide to re-enable WooCommerce.', 'tutor' );
824 echo '<div class="notice notice-error"><p>' . esc_html( $message ) . '</p></div>';
825 }
826 }
827 );
828