This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | if ( ! defined( 'ABSPATH' ) ) exit; // security check, don't load file outside WP |
||
3 | |||
4 | /** |
||
5 | * Sensei WooCommerce class |
||
6 | * |
||
7 | * All functions needed to integrate Sensei and WooCommerce |
||
8 | * |
||
9 | * @package Access-Management |
||
10 | * @author Automattic |
||
11 | * @since 1.9.0 |
||
12 | */ |
||
13 | |||
14 | Class Sensei_WC{ |
||
15 | |||
16 | /** |
||
17 | * Load the files needed for the woocommerce integration. |
||
18 | * |
||
19 | * @since 1.9.0 |
||
20 | */ |
||
21 | public static function load_woocommerce_integration_hooks(){ |
||
22 | |||
23 | $woocommerce_hooks_file_path = Sensei()->plugin_path() . 'includes/hooks/woocommerce.php'; |
||
24 | require_once( $woocommerce_hooks_file_path ); |
||
25 | |||
26 | } |
||
27 | /** |
||
28 | * check if WooCommerce plugin is loaded and allowed by Sensei |
||
29 | * |
||
30 | * @since 1.9.0 |
||
31 | * @return bool |
||
32 | */ |
||
33 | public static function is_woocommerce_active(){ |
||
34 | |||
35 | $is_woocommerce_enabled_in_settings = isset( Sensei()->settings->settings['woocommerce_enabled'] ) && Sensei()->settings->settings['woocommerce_enabled']; |
||
36 | return self::is_woocommerce_present() && $is_woocommerce_enabled_in_settings; |
||
37 | |||
38 | } // end is_woocommerce_active |
||
39 | |||
40 | /** |
||
41 | * Checks if the WooCommerce plugin is installed and activation. |
||
42 | * |
||
43 | * If you need to check if WooCommerce is activated use Sensei_Utils::is_woocommerce_active(). |
||
44 | * This function does nott check to see if the Sensei setting for WooCommerce is enabled. |
||
45 | * |
||
46 | * @since 1.9.0 |
||
47 | * |
||
48 | * @return bool |
||
49 | */ |
||
50 | public static function is_woocommerce_present(){ |
||
51 | |||
52 | $active_plugins = (array) get_option( 'active_plugins', array() ); |
||
53 | |||
54 | if ( is_multisite() ){ |
||
55 | |||
56 | $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) ); |
||
57 | |||
58 | } |
||
59 | |||
60 | $is_woocommerce_plugin_present_and_activated = in_array( 'woocommerce/woocommerce.php', $active_plugins ) || array_key_exists( 'woocommerce/woocommerce.php', $active_plugins ); |
||
61 | |||
62 | return class_exists( 'Woocommerce' ) || $is_woocommerce_plugin_present_and_activated; |
||
63 | |||
64 | }// end is_woocommerce_present |
||
65 | |||
66 | /** |
||
67 | * Find the order active number (completed or processing ) for a given user on a course. It will return the latest order. |
||
68 | * |
||
69 | * If multiple exist we will return the latest order. |
||
70 | * |
||
71 | * @param $user_id |
||
72 | * @param $course_id |
||
73 | * @return array $user_course_orders |
||
74 | */ |
||
75 | public static function get_learner_course_active_order_id( $user_id, $course_id ){ |
||
76 | |||
77 | $course_product_id = get_post_meta( $course_id, '_course_woocommerce_product', true ); |
||
78 | |||
79 | $orders_query = new WP_Query( array( |
||
80 | 'post_type' => 'shop_order', |
||
81 | 'posts_per_page' => -1, |
||
82 | 'post_status' => array( 'wc-processing', 'wc-completed' ), |
||
83 | 'meta_key'=> '_customer_user', |
||
84 | 'meta_value'=> $user_id, |
||
85 | ) ); |
||
86 | |||
87 | if( $orders_query->post_count == 0 ){ |
||
88 | |||
89 | return false; |
||
90 | |||
91 | } |
||
92 | |||
93 | foreach( $orders_query->get_posts() as $order ){ |
||
94 | |||
95 | $order = new WC_Order( $order->ID ); |
||
96 | $items = $order->get_items(); |
||
97 | |||
98 | $user_orders = array(); |
||
99 | |||
100 | foreach( $items as $item ){ |
||
101 | |||
102 | // if the product id on the order and the one given to this function |
||
103 | // this order has been placed by the given user on the given course. |
||
104 | $product = wc_get_product( $item['product_id'] ); |
||
105 | |||
106 | if ( is_object( $product ) && $product->is_type( 'variable' )) { |
||
107 | |||
108 | $item_product_id = $item['variation_id']; |
||
109 | |||
110 | } else { |
||
111 | |||
112 | $item_product_id = $item['product_id']; |
||
113 | |||
114 | } |
||
115 | |||
116 | if( $course_product_id == $item_product_id ){ |
||
117 | |||
118 | return $order->id; |
||
119 | |||
120 | } |
||
121 | |||
122 | |||
123 | }//end for each order item |
||
124 | |||
125 | } // end for each order |
||
126 | |||
127 | // if we reach this place we found no order |
||
128 | return false; |
||
129 | |||
130 | } // end get_learner_course_active_order_ids |
||
131 | |||
132 | /** |
||
133 | * Output WooCommerce specific course filters |
||
134 | * Removing the paged argument |
||
135 | * |
||
136 | * @since 1.9.0 |
||
137 | * @param $filter_links |
||
138 | * @return mixed |
||
139 | */ |
||
140 | public static function add_course_archive_wc_filter_links( $filter_links ){ |
||
141 | |||
142 | $free_courses = self::get_free_courses(); |
||
143 | $paid_courses = self::get_paid_courses(); |
||
144 | |||
145 | if ( empty( $free_courses ) || empty( $paid_courses ) ){ |
||
146 | // do not show any WooCommerce filters if all courses are |
||
147 | // free or if all courses are paid |
||
148 | return $filter_links; |
||
149 | |||
150 | } |
||
151 | |||
152 | $filter_links[] = array( |
||
153 | 'id'=>'paid' , |
||
154 | 'url'=> add_query_arg( array( 'course_filter'=>'paid'), Sensei_Course::get_courses_page_url() ), |
||
155 | 'title'=>__( 'Paid', 'woothemes-sensei' ) |
||
156 | ); |
||
157 | |||
158 | $filter_links[] = array( |
||
159 | 'id'=>'free', |
||
160 | 'url'=> add_query_arg( array( 'course_filter'=>'free'), Sensei_Course::get_courses_page_url() ), |
||
161 | 'title'=>__( 'Free', 'woothemes-sensei' ) |
||
162 | ); |
||
163 | |||
164 | return $filter_links; |
||
165 | |||
166 | }// end add_course_archive_wc_filter_links |
||
167 | |||
168 | /** |
||
169 | * Apply the free filter the the course query |
||
170 | * getting all course with no products or products with zero price |
||
171 | * |
||
172 | * hooked into pre_get_posts |
||
173 | * |
||
174 | * @since 1.9.0 |
||
175 | * @param WP_Query $query |
||
176 | * @return WP_Query $query |
||
177 | */ |
||
178 | public static function course_archive_wc_filter_free( $query ){ |
||
179 | |||
180 | if( isset( $_GET['course_filter'] ) && 'free' == $_GET['course_filter'] |
||
181 | && 'course' == $query->get( 'post_type') && $query->is_main_query() ){ |
||
182 | |||
183 | // setup the course meta query |
||
184 | $meta_query = self::get_free_courses_meta_query_args(); |
||
185 | |||
186 | // manipulate the query to return free courses |
||
187 | $query->set('meta_query', $meta_query ); |
||
188 | |||
189 | // don't show any paid courses |
||
190 | $courses = self::get_paid_courses(); |
||
191 | $ids = array(); |
||
192 | foreach( $courses as $course ){ |
||
193 | $ids[] = $course->ID; |
||
194 | } |
||
195 | $query->set( 'post__not_in', $ids ); |
||
196 | |||
197 | }// end if course_filter |
||
198 | |||
199 | return $query; |
||
200 | |||
201 | }// course_archive_wc_filter_free |
||
202 | |||
203 | /** |
||
204 | * Apply the paid filter to the course query on the courses page |
||
205 | * will include all course with a product attached with a price |
||
206 | * more than 0 |
||
207 | * |
||
208 | * hooked into pre_get_posts |
||
209 | * |
||
210 | * @since 1.9.0 |
||
211 | * @param WP_Query $query |
||
212 | * @return WP_Query $query |
||
213 | */ |
||
214 | public static function course_archive_wc_filter_paid( $query ){ |
||
215 | |||
216 | if( isset( $_GET['course_filter'] ) && 'paid' == $_GET['course_filter'] |
||
217 | && 'course' == $query->get( 'post_type') && $query->is_main_query() ){ |
||
218 | |||
219 | // setup the course meta query |
||
220 | $meta_query = self::get_paid_courses_meta_query_args(); |
||
221 | |||
222 | // manipulate the query to return free courses |
||
223 | $query->set('meta_query', $meta_query ); |
||
224 | |||
225 | } |
||
226 | |||
227 | return $query; |
||
228 | |||
229 | } |
||
230 | |||
231 | /** |
||
232 | * Load the WooCommerce single product actions above |
||
233 | * single courses if woocommerce is active allowing purchase |
||
234 | * information and actions to be hooked from WooCommerce. |
||
235 | */ |
||
236 | public static function do_single_course_wc_single_product_action(){ |
||
237 | |||
238 | /** |
||
239 | * this hooks is documented within the WooCommerce plugin. |
||
240 | */ |
||
241 | if ( Sensei_WC::is_woocommerce_active() ) { |
||
242 | |||
243 | do_action( 'woocommerce_before_single_product' ); |
||
244 | |||
245 | } // End If Statement |
||
246 | |||
247 | }// end do_single_course_wc_single_product_action |
||
248 | |||
249 | /** |
||
250 | * Hooking into the single lesson page to alter the |
||
251 | * user access permissions based on if they have purchased the |
||
252 | * course the lesson belongs to. |
||
253 | * |
||
254 | * This function will only return false or the passed in user_access value. |
||
255 | * It doesn't return true in order to avoid altering other options. |
||
256 | * |
||
257 | * @since 1.9.0 |
||
258 | * |
||
259 | * @param $can_user_view_lesson |
||
260 | * @param $lesson_id |
||
261 | * @param $user_id |
||
262 | * @return bool |
||
263 | */ |
||
264 | public static function alter_can_user_view_lesson ( $can_user_view_lesson, $lesson_id, $user_id ){ |
||
265 | |||
266 | // do not override access to admins |
||
267 | $course_id = Sensei()->lesson->get_course_id( $lesson_id ); |
||
268 | if ( sensei_all_access() || Sensei_Utils::is_preview_lesson( $lesson_id ) |
||
269 | || Sensei_Utils::user_started_course( $course_id, $user_id ) ){ |
||
270 | |||
271 | return true; |
||
272 | |||
273 | } |
||
274 | |||
275 | // check if the course has a valid product attached to it |
||
276 | // which the user should have purchased if they want to access |
||
277 | // the current lesson |
||
278 | $course_id = get_post_meta( $lesson_id , '_lesson_course', true); |
||
279 | $wc_post_id = get_post_meta( $course_id, '_course_woocommerce_product', true ); |
||
280 | $product = Sensei()->sensei_get_woocommerce_product_object($wc_post_id); |
||
0 ignored issues
–
show
|
|||
281 | if( isset ($product) && is_object($product) ){ |
||
282 | |||
283 | // valid product found |
||
284 | $order_id = self::get_learner_course_active_order_id( $user_id, $course_id ); |
||
285 | |||
286 | // product has a successful order so this user may access the content |
||
287 | // this function may only return false or the default |
||
288 | // returning true may override other negatives which we don't want |
||
289 | if( ! $order_id ){ |
||
290 | |||
291 | return false; |
||
292 | |||
293 | } |
||
294 | |||
295 | } |
||
296 | |||
297 | // return the passed in value |
||
298 | return $can_user_view_lesson; |
||
299 | |||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Add course link to order thank you and details pages. |
||
304 | * |
||
305 | * @since 1.4.5 |
||
306 | * @access public |
||
307 | * |
||
308 | * @return void |
||
309 | */ |
||
310 | public static function course_link_from_order( ) { |
||
311 | |||
312 | if( ! is_order_received_page() ){ |
||
313 | return; |
||
314 | } |
||
315 | |||
316 | $order_id = get_query_var( 'order-received' ); |
||
317 | $order = new WC_Order( $order_id ); |
||
318 | |||
319 | // exit early if not wc-completed or wc-processing |
||
320 | if( 'wc-completed' != $order->post_status |
||
321 | && 'wc-processing' != $order->post_status ) { |
||
322 | return; |
||
323 | } |
||
324 | |||
325 | $course_links = array(); // store the for links for courses purchased |
||
326 | foreach ( $order->get_items() as $item ) { |
||
327 | |||
328 | if ( isset( $item['variation_id'] ) && ( 0 < $item['variation_id'] ) ) { |
||
329 | |||
330 | // If item has variation_id then its a variation of the product |
||
331 | $item_id = $item['variation_id']; |
||
332 | |||
333 | } else { |
||
334 | |||
335 | //If not its real product set its id to item_id |
||
336 | $item_id = $item['product_id']; |
||
337 | |||
338 | } // End If Statement |
||
339 | |||
340 | $user_id = get_post_meta( $order->id, '_customer_user', true ); |
||
341 | |||
342 | if( $user_id ) { |
||
343 | |||
344 | // Get all courses for product |
||
345 | $args = Sensei_Course::get_default_query_args(); |
||
346 | $args['meta_query'] = array( array( |
||
347 | 'key' => '_course_woocommerce_product', |
||
348 | 'value' => $item_id |
||
349 | ) ); |
||
350 | $args['orderby'] = 'menu_order date'; |
||
351 | $args['order'] = 'ASC'; |
||
352 | |||
353 | // loop through courses |
||
354 | $courses = get_posts( $args ); |
||
355 | if( $courses && count( $courses ) > 0 ) { |
||
356 | |||
357 | foreach( $courses as $course ) { |
||
358 | |||
359 | $title = $course->post_title; |
||
360 | $permalink = get_permalink( $course->ID ); |
||
361 | $course_links[] .= '<a href="' . esc_url( $permalink ) . '" >' . $title . '</a> '; |
||
362 | |||
363 | } // end for each |
||
364 | |||
365 | // close the message div |
||
366 | |||
367 | }// end if $courses check |
||
368 | } |
||
369 | }// end loop through orders |
||
370 | |||
371 | // add the courses to the WooCommerce notice |
||
372 | if( ! empty( $course_links) ){ |
||
373 | |||
374 | $courses_html = _nx( |
||
375 | 'You have purchased the following course:', |
||
376 | 'You have purchased the following courses:', |
||
377 | count( $course_links ), |
||
378 | 'Purchase thank you note on Checkout page. The course link(s) will be show', 'woothemes-sensei' |
||
379 | ); |
||
380 | |||
381 | foreach( $course_links as $link ){ |
||
382 | |||
383 | $courses_html .= '<li>' . $link . '</li>'; |
||
384 | |||
385 | } |
||
386 | |||
387 | $courses_html .= ' </ul>'; |
||
388 | |||
389 | wc_add_notice( $courses_html, 'success' ); |
||
390 | } |
||
391 | |||
392 | } // end course_link_order_form |
||
393 | |||
394 | /** |
||
395 | * Show the message that a user should complete |
||
396 | * their purchase if the course is in the cart |
||
397 | * |
||
398 | * This should be used within the course loop or single course page |
||
399 | * |
||
400 | * @since 1.9.0 |
||
401 | */ |
||
402 | public static function course_in_cart_message(){ |
||
403 | |||
404 | global $post; |
||
405 | |||
406 | if( self::is_course_in_cart( $post->ID ) ){ ?> |
||
407 | |||
408 | <div class="sensei-message info"> |
||
409 | <?php |
||
410 | |||
411 | $cart_link = '<a class="cart-complete" href="' . WC()->cart->get_checkout_url() |
||
412 | . '" title="' . __('complete purchase', 'woothemes-sensei') . '">' |
||
413 | . __('complete the purchase', 'woothemes-sensei') . '</a>'; |
||
414 | |||
415 | echo sprintf( __('You have already added this Course to your cart. Please %1$s to access the course.', 'woothemes-sensei'), $cart_link ); |
||
416 | |||
417 | ?> |
||
418 | </div> |
||
419 | <?php } |
||
420 | |||
421 | } // End sensei_woocommerce_in_cart_message() |
||
422 | |||
423 | /** |
||
424 | * Checks the cart to see if a course is in the cart. |
||
425 | * |
||
426 | * @param $course_id |
||
427 | * @return bool |
||
428 | */ |
||
429 | public static function is_course_in_cart( $course_id ){ |
||
430 | |||
431 | $wc_post_id = absint( get_post_meta( $course_id, '_course_woocommerce_product', true ) ); |
||
432 | $user_course_status_id = Sensei_Utils::user_started_course( $course_id , get_current_user_id() ); |
||
433 | |||
434 | if ( 0 < intval( $wc_post_id ) && ! $user_course_status_id ) { |
||
435 | |||
436 | if ( self::is_product_in_cart( $wc_post_id ) ) { |
||
437 | |||
438 | return true; |
||
439 | |||
440 | } |
||
441 | |||
442 | } |
||
443 | |||
444 | return false; |
||
445 | |||
446 | }// is_course_in_cart |
||
447 | |||
448 | /** |
||
449 | * Check the cart to see if the product is in the cart |
||
450 | * |
||
451 | * @param $product_id |
||
452 | * @return bool |
||
453 | */ |
||
454 | public static function is_product_in_cart( $product_id ){ |
||
455 | |||
456 | if ( 0 < $product_id ) { |
||
457 | |||
458 | $product = wc_get_product( $product_id ); |
||
459 | |||
460 | $parent_id = ''; |
||
461 | if( isset( $product->variation_id ) && 0 < intval( $product->variation_id ) ) { |
||
462 | $wc_product_id = $product->parent->id; |
||
463 | } |
||
464 | foreach( WC()->cart->get_cart() as $cart_item_key => $values ) { |
||
465 | |||
466 | $cart_product = $values['data']; |
||
467 | if( $product_id == $cart_product->id ) { |
||
468 | |||
469 | return true; |
||
470 | |||
471 | } |
||
472 | |||
473 | } |
||
474 | } // End If Statement |
||
475 | |||
476 | return false; |
||
477 | |||
478 | } // end is_product_in_car |
||
479 | |||
480 | /** |
||
481 | * Get all free WooCommerce products |
||
482 | * |
||
483 | * @since 1.9.0 |
||
484 | * |
||
485 | * @return array $free_products{ |
||
486 | * @type int $wp_post_id |
||
487 | * } |
||
488 | */ |
||
489 | public static function get_free_product_ids(){ |
||
490 | |||
491 | return get_posts( array( |
||
492 | 'post_type' => 'product', |
||
493 | 'posts_per_page' => '1000', |
||
494 | 'fields' => 'ids', |
||
495 | 'meta_query'=> array( |
||
496 | 'relation' => 'OR', |
||
497 | array( |
||
498 | 'key'=> '_regular_price', |
||
499 | 'value' => 0, |
||
500 | ), |
||
501 | array( |
||
502 | 'key'=> '_sale_price', |
||
503 | 'value' => 0, |
||
504 | ), |
||
505 | ), |
||
506 | )); |
||
507 | |||
508 | }// end get free product query |
||
509 | |||
510 | /** |
||
511 | * The metat query for courses that are free |
||
512 | * |
||
513 | * @since 1.9.0 |
||
514 | * @return array $wp_meta_query_param |
||
515 | */ |
||
516 | public static function get_free_courses_meta_query_args(){ |
||
517 | |||
518 | return array( |
||
519 | 'relation' => 'OR', |
||
520 | array( |
||
521 | 'key' => '_course_woocommerce_product', |
||
522 | 'value' => '-', |
||
523 | 'compare' => '=', |
||
524 | ), |
||
525 | array( |
||
526 | 'key' => '_course_woocommerce_product', |
||
527 | 'value' => self::get_free_product_ids(), |
||
528 | 'compare' => 'IN', |
||
529 | ), |
||
530 | ); |
||
531 | |||
532 | }// get_free_courses_meta_query |
||
533 | |||
534 | /** |
||
535 | * The metat query for courses that are free |
||
536 | * |
||
537 | * @since 1.9.0 |
||
538 | * @return array $wp_query_meta_query_args_param |
||
539 | */ |
||
540 | public static function get_paid_courses_meta_query_args(){ |
||
541 | |||
542 | $paid_product_ids = self::get_paid_product_ids(); |
||
543 | |||
544 | return array( |
||
545 | array( |
||
546 | 'key' => '_course_woocommerce_product', |
||
547 | // when empty we give a false post_id to ensure the caller doesn't get any courses for their |
||
548 | // query |
||
549 | 'value' => empty( $paid_product_ids )? '-1000' : $paid_product_ids, |
||
550 | 'compare' => 'IN', |
||
551 | ), |
||
552 | ); |
||
553 | |||
554 | }// get_free_courses_meta_query |
||
555 | |||
556 | /** |
||
557 | * The WordPress Query args |
||
558 | * for paid products on sale |
||
559 | * |
||
560 | * @since 1.9.0 |
||
561 | * @return array $product_query_args |
||
562 | */ |
||
563 | View Code Duplication | public static function get_paid_products_on_sale_query_args(){ |
|
564 | |||
565 | $args = array( |
||
566 | 'post_type' => 'product', |
||
567 | 'posts_per_page' => 1000, |
||
568 | 'orderby' => 'date', |
||
569 | 'order' => 'DESC', |
||
570 | 'suppress_filters' => 0 |
||
571 | ); |
||
572 | |||
573 | $args[ 'fields' ] = 'ids'; |
||
574 | |||
575 | $args[ 'meta_query' ] = array( |
||
576 | 'relation' => 'AND', |
||
577 | array( |
||
578 | 'key'=> '_regular_price', |
||
579 | 'compare' => '>', |
||
580 | 'value' => 0, |
||
581 | ), |
||
582 | array( |
||
583 | 'key'=> '_sale_price', |
||
584 | 'compare' => '>', |
||
585 | 'value' => 0, |
||
586 | ), |
||
587 | ); |
||
588 | |||
589 | return $args; |
||
590 | |||
591 | } // get_paid_products_on_sale_query_args |
||
592 | |||
593 | |||
594 | /** |
||
595 | * Return the WordPress query args for |
||
596 | * products not on sale but that is not a free |
||
597 | * |
||
598 | * @since 1.9.0 |
||
599 | * |
||
600 | * @return array |
||
601 | */ |
||
602 | View Code Duplication | public static function get_paid_products_not_on_sale_query_args(){ |
|
603 | |||
604 | $args = array( |
||
605 | 'post_type' => 'product', |
||
606 | 'posts_per_page' => 1000, |
||
607 | 'orderby' => 'date', |
||
608 | 'order' => 'DESC', |
||
609 | 'suppress_filters' => 0 |
||
610 | ); |
||
611 | |||
612 | $args[ 'fields' ] = 'ids'; |
||
613 | $args[ 'meta_query' ] = array( |
||
614 | 'relation' => 'AND', |
||
615 | array( |
||
616 | 'key'=> '_regular_price', |
||
617 | 'compare' => '>', |
||
618 | 'value' => 0, |
||
619 | ), |
||
620 | array( |
||
621 | 'key'=> '_sale_price', |
||
622 | 'compare' => '=', |
||
623 | 'value' => '', |
||
624 | ), |
||
625 | ); |
||
626 | |||
627 | return $args; |
||
628 | |||
629 | |||
630 | } // get_paid_courses_meta_query |
||
631 | |||
632 | /** |
||
633 | * Get all WooCommerce non-free product id's |
||
634 | * |
||
635 | * @since 1.9.0 |
||
636 | * |
||
637 | * @return array $woocommerce_paid_product_ids |
||
638 | */ |
||
639 | public static function get_paid_product_ids(){ |
||
640 | |||
641 | // get all the paid WooCommerce products that has regular |
||
642 | // and sale price greater than 0 |
||
643 | // will be used later to check for course with the id as meta |
||
644 | $paid_product_ids_with_sale = get_posts( self::get_paid_products_on_sale_query_args() ); |
||
645 | |||
646 | // get all the paid WooCommerce products that has regular price |
||
647 | // greater than 0 without a sale price |
||
648 | // will be used later to check for course with the id as meta |
||
649 | $paid_product_ids_without_sale = get_posts( self::get_paid_products_not_on_sale_query_args() ); |
||
650 | |||
651 | // combine products ID's with regular and sale price grater than zero and those without |
||
652 | // sale but regular price greater than zero |
||
653 | $woocommerce_paid_product_ids = array_merge( $paid_product_ids_with_sale, $paid_product_ids_without_sale ); |
||
654 | |||
655 | // if |
||
656 | if( empty($woocommerce_paid_product_ids) ){ |
||
657 | return array( ); |
||
658 | } |
||
659 | return $woocommerce_paid_product_ids; |
||
660 | |||
661 | } |
||
662 | |||
663 | /** |
||
664 | * Get all free courses. |
||
665 | * |
||
666 | * This course that have a WC product attached |
||
667 | * that has a price or sale price of zero and |
||
668 | * other courses with no WooCommerce products |
||
669 | * attached. |
||
670 | * |
||
671 | * @since 1.9.0 |
||
672 | * |
||
673 | * @param array $args |
||
674 | * @return array |
||
675 | */ |
||
676 | public static function get_free_courses( $args = array() ){ |
||
677 | |||
678 | $free_course_query_args = Sensei_Course::get_default_query_args(); |
||
679 | $free_course_query_args[ 'meta_query' ] = self::get_free_courses_meta_query_args(); |
||
680 | |||
681 | if( !empty( $args ) ){ |
||
682 | wp_parse_args( $args, $free_course_query_args ); |
||
683 | } |
||
684 | |||
685 | // don't show any paid courses |
||
686 | $courses = self::get_paid_courses(); |
||
687 | $ids = array(); |
||
688 | foreach( $courses as $course ){ |
||
689 | $ids[] = $course->ID; |
||
690 | } |
||
691 | $free_course_query_args[ 'post__not_in' ] = $ids; |
||
692 | |||
693 | return get_posts( $free_course_query_args ); |
||
694 | |||
695 | } |
||
696 | |||
697 | /** |
||
698 | * Return all products that are not free |
||
699 | * |
||
700 | * @since 1.9.0 |
||
701 | * @param array $args override default arg values |
||
702 | * |
||
703 | * @return array |
||
704 | */ |
||
705 | public static function get_paid_courses( $args = array() ){ |
||
706 | |||
707 | $paid_course_query_args = Sensei_Course::get_default_query_args(); |
||
708 | |||
709 | $paid_course_query_args[ 'meta_query' ] = self::get_paid_courses_meta_query_args(); |
||
710 | |||
711 | if( !empty( $args ) ){ |
||
712 | wp_parse_args( $args, $paid_course_query_args ); |
||
713 | } |
||
714 | |||
715 | return get_posts( $paid_course_query_args ); |
||
716 | } |
||
717 | |||
718 | /** |
||
719 | * Show the WooCommerce add to cart button for the current course |
||
720 | * |
||
721 | * The function will only show the button if |
||
722 | * 1- the user can buy the course |
||
723 | * 2- if they have completed their pre-requisite |
||
724 | * 3- if the course has a valid product attached |
||
725 | * |
||
726 | * @since 1.9.0 |
||
727 | * @param int $course_id |
||
728 | * @return string $html markup for the button or nothing if user not allowed to buy |
||
729 | */ |
||
730 | public static function the_add_to_cart_button_html( $course_id ){ |
||
731 | |||
732 | if ( ! Sensei_Course::is_prerequisite_complete( $course_id ) || self::is_course_in_cart( $course_id ) ) { |
||
733 | return ''; |
||
734 | } |
||
735 | |||
736 | $wc_post_id = self::get_course_product_id( $course_id ); |
||
737 | |||
738 | // Check if customer purchased the product |
||
739 | if ( self::has_customer_bought_product( get_current_user_id(), $wc_post_id ) |
||
740 | || empty( $wc_post_id ) ) { |
||
741 | |||
742 | return ''; |
||
743 | |||
744 | } |
||
745 | |||
746 | // based on simple.php in WC templates/single-product/add-to-cart/ |
||
747 | // Get the product |
||
748 | $product = self::get_product_object( $wc_post_id ); |
||
749 | |||
750 | // do not show the button for invalid products, non purchasable products, out |
||
751 | // of stock product or if course is already in cart |
||
752 | if ( ! isset ( $product ) |
||
753 | || ! is_object( $product ) |
||
754 | || ! $product->is_purchasable() |
||
755 | || ! $product->is_in_stock() |
||
756 | || self::is_course_in_cart( $wc_post_id ) ) { |
||
757 | |||
758 | return ''; |
||
759 | |||
760 | } |
||
761 | |||
762 | // |
||
763 | // button output: |
||
764 | // |
||
765 | ?> |
||
766 | |||
767 | <form action="<?php echo esc_url( $product->add_to_cart_url() ); ?>" |
||
768 | class="cart" |
||
769 | method="post" |
||
770 | enctype="multipart/form-data"> |
||
771 | |||
772 | <input type="hidden" name="product_id" value="<?php echo esc_attr( $product->id ); ?>" /> |
||
773 | |||
774 | <input type="hidden" name="quantity" value="1" /> |
||
775 | |||
776 | <?php if ( isset( $product->variation_id ) && 0 < intval( $product->variation_id ) ) { ?> |
||
777 | |||
778 | <input type="hidden" name="variation_id" value="<?php echo $product->variation_id; ?>" /> |
||
779 | <?php if( isset( $product->variation_data ) && is_array( $product->variation_data ) && count( $product->variation_data ) > 0 ) { ?> |
||
780 | |||
781 | <?php foreach( $product->variation_data as $att => $val ) { ?> |
||
782 | |||
783 | <input type="hidden" name="<?php echo esc_attr( $att ); ?>" id="<?php echo esc_attr( str_replace( 'attribute_', '', $att ) ); ?>" value="<?php echo esc_attr( $val ); ?>" /> |
||
784 | |||
785 | <?php } ?> |
||
786 | |||
787 | <?php } ?> |
||
788 | |||
789 | <?php } ?> |
||
790 | |||
791 | <button type="submit" class="single_add_to_cart_button button alt"> |
||
792 | <?php $button_text = $product->get_price_html() . ' - ' . __( 'Purchase this Course', 'woothemes-sensei' ); ?> |
||
793 | <?php |
||
794 | /** |
||
795 | * Filter Add to Cart button text |
||
796 | * |
||
797 | * @since 1.9.1 |
||
798 | * |
||
799 | * @param string $button_text |
||
800 | */ |
||
801 | echo apply_filters( 'sensei_wc_single_add_to_cart_button_text', $button_text ); |
||
802 | ?> |
||
803 | </button> |
||
804 | |||
805 | </form> |
||
806 | |||
807 | <?php |
||
808 | } // end the_add_to_cart_button_html |
||
809 | |||
810 | /** |
||
811 | * Alter the no permissions message on the single course page |
||
812 | * Changes the message to a WooCommerce specific message. |
||
813 | * |
||
814 | * @since 1.9.0 |
||
815 | * |
||
816 | * @param $message |
||
817 | * @param $post_id |
||
818 | * |
||
819 | * @return string $message |
||
820 | */ |
||
821 | public static function alter_no_permissions_message( $message, $post_id ){ |
||
822 | |||
823 | if( empty( $post_id ) || 'course'!=get_post_type( $post_id ) ){ |
||
824 | return $message; |
||
825 | } |
||
826 | |||
827 | $product_id = self::get_course_product_id( $post_id ); |
||
828 | |||
829 | if( ! $product_id |
||
830 | || ! self::has_customer_bought_product( get_current_user_id(),$product_id ) ){ |
||
831 | |||
832 | return $message; |
||
833 | |||
834 | } |
||
835 | |||
836 | ob_start(); |
||
837 | self::the_course_no_permissions_message( $post_id ); |
||
838 | $woocommerce_course_no_permissions_message = ob_get_clean(); |
||
839 | |||
840 | return $woocommerce_course_no_permissions_message ; |
||
841 | |||
842 | } |
||
843 | /** |
||
844 | * Show the no permissions message when a user is logged in |
||
845 | * and have not yet purchased the current course |
||
846 | * |
||
847 | * @since 1.9.0 |
||
848 | */ |
||
849 | public static function the_course_no_permissions_message( $course_id ){ |
||
850 | |||
851 | // login link |
||
852 | $my_courses_page_id = intval( Sensei()->settings->settings[ 'my_course_page' ] ); |
||
853 | $login_link = '<a href="' . esc_url( get_permalink( $my_courses_page_id ) ) . '">' . __( 'log in', 'woothemes-sensei' ) . '</a>'; |
||
854 | $wc_product_id = self::get_course_product_id( $course_id ); |
||
855 | |||
856 | if ( self::is_product_in_cart( $wc_product_id ) ) { |
||
857 | |||
858 | $cart_link = '<a href="' . wc_get_checkout_url() . '" title="' . __( 'Checkout','woocommerce' ) . '">' . __( 'checkout', 'woocommerce' ) . '</a>'; |
||
859 | |||
860 | $message = sprintf( __( 'This course is already in your cart, please proceed to %1$s, to gain access.', 'woothemes-sensei' ), $cart_link ); |
||
861 | ?> |
||
862 | <span class="add-to-cart-login"> |
||
863 | <?php echo $message; ?> |
||
864 | </span> |
||
865 | |||
866 | <?php |
||
867 | |||
868 | } elseif ( is_user_logged_in() ) { |
||
869 | |||
870 | ?> |
||
871 | <style> |
||
872 | .sensei-message.alert { |
||
873 | display: none; |
||
874 | } |
||
875 | </style> |
||
876 | |||
877 | <?php |
||
878 | |||
879 | } else { |
||
880 | $message = sprintf( __( 'Or %1$s to access your purchased courses', 'woothemes-sensei' ), $login_link ); |
||
881 | ?> |
||
882 | <span class="add-to-cart-login"> |
||
883 | <?php echo $message; ?> |
||
884 | </span> |
||
885 | |||
886 | <?php |
||
887 | } |
||
888 | } |
||
889 | |||
890 | /** |
||
891 | * Checks if a user has bought a product item. |
||
892 | * |
||
893 | * @since 1.9.0 |
||
894 | * |
||
895 | * @param int $user_id |
||
896 | * @param int $product_id |
||
897 | * |
||
898 | * @return bool |
||
899 | */ |
||
900 | public static function has_customer_bought_product ( $user_id, $product_id ){ |
||
901 | |||
902 | $orders = self::get_user_product_orders( $user_id, $product_id ); |
||
903 | |||
904 | foreach ( $orders as $order_id ) { |
||
905 | |||
906 | $order = new WC_Order( $order_id->ID ); |
||
907 | |||
908 | // wc-active is the subscriptions complete status |
||
909 | if ( ! in_array( $order->post_status, array( 'wc-processing', 'wc-completed' ) ) |
||
910 | || ! ( 0 < sizeof( $order->get_items() ) ) ){ |
||
911 | |||
912 | continue; |
||
913 | |||
914 | } |
||
915 | |||
916 | foreach( $order->get_items() as $item ) { |
||
917 | |||
918 | // Check if user has bought product |
||
919 | if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) { |
||
920 | |||
921 | // Check if user has an active subscription for product |
||
922 | if( class_exists( 'WC_Subscriptions_Manager' ) ) { |
||
923 | $sub_key = wcs_get_subscription( $order ); |
||
924 | if( $sub_key ) { |
||
925 | $sub = wcs_get_subscription( $sub_key ); |
||
926 | if( $sub && isset( $sub['status'] ) ) { |
||
927 | if( 'active' == $sub['status'] ) { |
||
928 | return true; |
||
929 | } else { |
||
930 | return false; |
||
931 | } |
||
932 | } |
||
933 | } |
||
934 | } |
||
935 | |||
936 | // Customer has bought product |
||
937 | return true; |
||
938 | } // End If Statement |
||
939 | |||
940 | } // End For each item |
||
941 | |||
942 | } // End For each order |
||
943 | |||
944 | // default is no order |
||
945 | return false; |
||
946 | |||
947 | } // end has customer bought product |
||
948 | |||
949 | /** |
||
950 | * Return the product id for the given course |
||
951 | * |
||
952 | * @since 1.9.0 |
||
953 | * |
||
954 | * @param int $course_id |
||
955 | * |
||
956 | * @return string $woocommerce_product_id or false if none exist |
||
957 | * |
||
958 | */ |
||
959 | public static function get_course_product_id( $course_id ){ |
||
960 | |||
961 | $product_id = get_post_meta( $course_id, '_course_woocommerce_product', true ); |
||
962 | |||
963 | if( empty( $product_id ) || 'product' != get_post_type( $product_id ) ){ |
||
964 | return false; |
||
965 | } |
||
966 | |||
967 | return $product_id; |
||
968 | |||
969 | } |
||
970 | |||
971 | /** |
||
972 | * Alter the body classes adding WooCommerce to the body |
||
973 | * |
||
974 | * Speciall cases where this is needed is template no-permissions.php |
||
975 | * |
||
976 | * @param array $classes |
||
977 | * @return array |
||
978 | */ |
||
979 | public static function add_woocommerce_body_class( $classes ){ |
||
980 | |||
981 | if( ! in_array( 'woocommerce', $classes ) && defined( 'SENSEI_NO_PERMISSION' ) && SENSEI_NO_PERMISSION ){ |
||
982 | |||
983 | $classes[] ='woocommerce'; |
||
984 | |||
985 | } |
||
986 | |||
987 | return $classes; |
||
988 | |||
989 | } |
||
990 | |||
991 | /** |
||
992 | * Responds to when a subscription product is purchased |
||
993 | * |
||
994 | * @since 1.2.0 |
||
995 | * @since 1.9.0 move to class Sensei_WC |
||
996 | * |
||
997 | * @param WC_Order $order |
||
998 | * |
||
999 | * @return void |
||
1000 | */ |
||
1001 | public static function activate_subscription( $order ) { |
||
1002 | |||
1003 | $order_user = get_user_by('id', $order->user_id); |
||
1004 | $user['ID'] = $order_user->ID; |
||
1005 | $user['user_login'] = $order_user->user_login; |
||
1006 | $user['user_email'] = $order_user->user_email; |
||
1007 | $user['user_url'] = $order_user->user_url; |
||
1008 | |||
1009 | // Run through each product ordered |
||
1010 | if ( ! sizeof($order->get_items() )>0 ) { |
||
1011 | |||
1012 | return; |
||
1013 | |||
1014 | } |
||
1015 | |||
1016 | foreach($order->get_items() as $item) { |
||
1017 | |||
1018 | $product_type = ''; |
||
1019 | |||
1020 | if (isset($item['variation_id']) && $item['variation_id'] > 0) { |
||
1021 | |||
1022 | $item_id = $item['variation_id']; |
||
1023 | $product_type = 'subscription_variation'; |
||
1024 | |||
1025 | } else { |
||
1026 | |||
1027 | $item_id = $item['product_id']; |
||
1028 | |||
1029 | } // End If Statement |
||
1030 | |||
1031 | $_product = self::get_product_object( $item_id, $product_type ); |
||
1032 | |||
1033 | // Get courses that use the WC product |
||
1034 | $courses = array(); |
||
1035 | |||
1036 | if ( ! in_array( $product_type, self::get_subscription_types() ) ) { |
||
1037 | |||
1038 | $courses = Sensei()->course->get_product_courses( $item_id ); |
||
1039 | |||
1040 | } // End If Statement |
||
1041 | |||
1042 | // Loop and add the user to the course. |
||
1043 | foreach ( $courses as $course_item ){ |
||
1044 | |||
1045 | Sensei_Utils::user_start_course( intval( $user['ID'] ), $course_item->ID ); |
||
1046 | |||
1047 | } // End For Loop |
||
1048 | |||
1049 | } // End For Loop |
||
1050 | |||
1051 | } // End activate_subscription() |
||
1052 | |||
1053 | /** |
||
1054 | * Adds detail to to the WooCommerce order |
||
1055 | * |
||
1056 | * @since 1.4.5 |
||
1057 | * @since 1.9.0 function moved to class Sensei_WC and renamed from sensei_woocommerce_email_course_details to email_course_details |
||
1058 | * |
||
1059 | * @param WC_Order $order |
||
1060 | * |
||
1061 | * @return void |
||
1062 | */ |
||
1063 | public static function email_course_details( $order ){ |
||
1064 | |||
1065 | global $woocommerce; |
||
1066 | |||
1067 | // exit early if not wc-completed or wc-processing |
||
1068 | if( 'wc-completed' != $order->post_status |
||
1069 | && 'wc-processing' != $order->post_status ) { |
||
1070 | return; |
||
1071 | } |
||
1072 | |||
1073 | $order_items = $order->get_items(); |
||
1074 | $order_id = $order->id; |
||
1075 | |||
1076 | //If object have items go through them all to find course |
||
1077 | if ( 0 < sizeof( $order_items ) ) { |
||
1078 | |||
1079 | $course_details_html = '<h2>' . __( 'Course details', 'woothemes-sensei' ) . '</h2>'; |
||
1080 | $order_contains_courses = false; |
||
1081 | |||
1082 | |||
1083 | foreach ( $order_items as $item ) { |
||
1084 | |||
1085 | $product_type = ''; |
||
1086 | if ( isset( $item['variation_id'] ) && ( 0 < $item['variation_id'] ) ) { |
||
1087 | // If item has variation_id then its from variation |
||
1088 | $item_id = $item['variation_id']; |
||
1089 | $product_type = 'variation'; |
||
1090 | } else { |
||
1091 | // If not its real product set its id to item_id |
||
1092 | $item_id = $item['product_id']; |
||
1093 | } // End If Statement |
||
1094 | |||
1095 | $user_id = get_post_meta( $order_id, '_customer_user', true ); |
||
1096 | |||
1097 | if( $user_id ) { |
||
1098 | |||
1099 | // Get all courses for product |
||
1100 | $args = array( |
||
1101 | 'posts_per_page' => -1, |
||
1102 | 'post_type' => 'course', |
||
1103 | 'meta_query' => array( |
||
1104 | array( |
||
1105 | 'key' => '_course_woocommerce_product', |
||
1106 | 'value' => $item_id |
||
1107 | ) |
||
1108 | ), |
||
1109 | 'orderby' => 'menu_order date', |
||
1110 | 'order' => 'ASC', |
||
1111 | ); |
||
1112 | $courses = get_posts( $args ); |
||
1113 | |||
1114 | if( $courses && count( $courses ) > 0 ) { |
||
1115 | |||
1116 | foreach( $courses as $course ) { |
||
1117 | |||
1118 | $title = $course->post_title; |
||
1119 | $permalink = get_permalink( $course->ID ); |
||
1120 | $order_contains_courses = true; |
||
1121 | $course_details_html .= '<p><strong>' . sprintf( __( 'View course: %1$s', 'woothemes-sensei' ), '</strong><a href="' . esc_url( $permalink ) . '">' . $title . '</a>' ) . '</p>'; |
||
1122 | } |
||
1123 | |||
1124 | |||
1125 | } // end if has courses |
||
1126 | |||
1127 | } // end if $userPid |
||
1128 | |||
1129 | } // end for each order item |
||
1130 | |||
1131 | // Output Course details |
||
1132 | if( $order_contains_courses ){ |
||
1133 | |||
1134 | echo $course_details_html; |
||
1135 | |||
1136 | } |
||
1137 | |||
1138 | |||
1139 | } // end if order items not empty |
||
1140 | |||
1141 | }// end email_course_details |
||
1142 | |||
1143 | /** |
||
1144 | * sensei_woocommerce_complete_order description |
||
1145 | * @since 1.0.3 |
||
1146 | * @access public |
||
1147 | * @param int $order_id WC order ID |
||
1148 | * @return void |
||
1149 | */ |
||
1150 | public static function complete_order ( $order_id = 0 ) { |
||
1151 | |||
1152 | $order_user = array(); |
||
1153 | |||
1154 | // Check for WooCommerce |
||
1155 | if ( Sensei_WC::is_woocommerce_active() && ( 0 < $order_id ) ) { |
||
1156 | // Get order object |
||
1157 | $order = new WC_Order( $order_id ); |
||
1158 | |||
1159 | if ( ! in_array( $order->get_status(), array( 'complete', 'processing' ) ) ) { |
||
1160 | |||
1161 | return; |
||
1162 | |||
1163 | } |
||
1164 | |||
1165 | $user = get_user_by( 'id', $order->get_user_id() ); |
||
1166 | $order_user['ID'] = $user->ID; |
||
1167 | $order_user['user_login'] = $user->user_login; |
||
1168 | $order_user['user_email'] = $user->user_email; |
||
1169 | $order_user['user_url'] = $user->user_url; |
||
1170 | // Run through each product ordered |
||
1171 | if ( 0 < sizeof( $order->get_items() ) ) { |
||
1172 | |||
1173 | foreach( $order->get_items() as $item ) { |
||
1174 | |||
1175 | $product_type = ''; |
||
1176 | if ( isset( $item['variation_id'] ) && ( 0 < $item['variation_id'] ) ) { |
||
1177 | |||
1178 | $item_id = $item['variation_id']; |
||
1179 | $product_type = 'variation'; |
||
1180 | |||
1181 | } else { |
||
1182 | |||
1183 | $item_id = $item['product_id']; |
||
1184 | |||
1185 | } // End If Statement |
||
1186 | |||
1187 | $_product = Sensei_WC::get_product_object( $item_id, $product_type ); |
||
1188 | |||
1189 | // Get courses that use the WC product |
||
1190 | $courses = Sensei()->course->get_product_courses( $_product->id ); |
||
1191 | |||
1192 | // Loop and update those courses |
||
1193 | foreach ( $courses as $course_item ) { |
||
1194 | |||
1195 | $update_course = self::course_update( $course_item->ID, $order_user ); |
||
1196 | |||
1197 | } // End For Loop |
||
1198 | |||
1199 | } // End For Loop |
||
1200 | |||
1201 | } // End If Statement |
||
1202 | // Add meta to indicate that payment has been completed successfully |
||
1203 | update_post_meta( $order_id, 'sensei_payment_complete', '1' ); |
||
1204 | |||
1205 | } // End If Statement |
||
1206 | |||
1207 | } // End sensei_woocommerce_complete_order() |
||
1208 | |||
1209 | /** |
||
1210 | * Responds to when an order is cancelled. |
||
1211 | * |
||
1212 | * @since 1.2.0 |
||
1213 | * @since 1.9.0 Move function to the Sensei_WC class |
||
1214 | * @param integer| WC_Order $order_id order ID |
||
1215 | * @return void |
||
1216 | */ |
||
1217 | public static function cancel_order ( $order_id ) { |
||
1218 | |||
1219 | // Get order object |
||
1220 | if( is_object( $order_id ) ){ |
||
1221 | |||
1222 | $order = $order_id; |
||
1223 | |||
1224 | }else{ |
||
1225 | |||
1226 | $order = new WC_Order( $order_id ); |
||
1227 | } |
||
1228 | |||
1229 | if ( ! in_array( $order->get_status(), array( 'cancelled', 'refunded' ) ) ) { |
||
1230 | |||
1231 | return; |
||
1232 | |||
1233 | } |
||
1234 | |||
1235 | // Run through each product ordered |
||
1236 | if ( 0 < sizeof( $order->get_items() ) ) { |
||
1237 | |||
1238 | // Get order user |
||
1239 | $user_id = $order->__get( 'user_id' ); |
||
1240 | |||
1241 | foreach( $order->get_items() as $item ) { |
||
1242 | |||
1243 | $product_type = ''; |
||
1244 | if ( isset( $item['variation_id'] ) && ( 0 < $item['variation_id'] ) ) { |
||
1245 | |||
1246 | $item_id = $item['variation_id']; |
||
1247 | $product_type = 'variation'; |
||
1248 | |||
1249 | } else { |
||
1250 | |||
1251 | $item_id = $item['product_id']; |
||
1252 | |||
1253 | } // End If Statement |
||
1254 | |||
1255 | $_product = Sensei_WC::get_product_object( $item_id, $product_type ); |
||
1256 | |||
1257 | // Get courses that use the WC product |
||
1258 | $courses = array(); |
||
1259 | $courses = Sensei()->course->get_product_courses( $item_id ); |
||
1260 | |||
1261 | // Loop and update those courses |
||
1262 | foreach ($courses as $course_item){ |
||
1263 | |||
1264 | if( self::has_customer_bought_product( $user_id, $course_item->ID ) ){ |
||
1265 | continue; |
||
1266 | } |
||
1267 | // Check and Remove course from courses user meta |
||
1268 | $dataset_changes = Sensei_Utils::sensei_remove_user_from_course( $course_item->ID, $user_id ); |
||
1269 | |||
1270 | } // End For Loop |
||
1271 | |||
1272 | } // End For Loop |
||
1273 | |||
1274 | } // End If Statement |
||
1275 | |||
1276 | } // End sensei_woocommerce_cancel_order() |
||
1277 | |||
1278 | /** |
||
1279 | * Returns the WooCommerce Product Object |
||
1280 | * |
||
1281 | * The code caters for pre and post WooCommerce 2.2 installations. |
||
1282 | * |
||
1283 | * @since 1.1.1 |
||
1284 | * @access public |
||
1285 | * @param integer $wc_product_id Product ID or Variation ID |
||
1286 | * @param string $product_type '' or 'variation' |
||
1287 | * @return WC_Product $wc_product_object |
||
1288 | */ |
||
1289 | public static function get_product_object ( $wc_product_id = 0, $product_type = '' ) { |
||
1290 | |||
1291 | $wc_product_object = false; |
||
1292 | if ( 0 < intval( $wc_product_id ) ) { |
||
1293 | |||
1294 | // Get the product |
||
1295 | if ( function_exists( 'wc_get_product' ) ) { |
||
1296 | |||
1297 | $wc_product_object = wc_get_product( $wc_product_id ); // Post WC 2.3 |
||
1298 | |||
1299 | } elseif ( function_exists( 'get_product' ) ) { |
||
1300 | |||
1301 | $wc_product_object = get_product( $wc_product_id ); // Post WC 2.0 |
||
1302 | |||
1303 | } else { |
||
1304 | |||
1305 | // Pre WC 2.0 |
||
1306 | if ( 'variation' == $product_type || 'subscription_variation' == $product_type ) { |
||
1307 | |||
1308 | $wc_product_object = new WC_Product_Variation( $wc_product_id ); |
||
1309 | |||
1310 | } else { |
||
1311 | |||
1312 | $wc_product_object = new WC_Product( $wc_product_id ); |
||
1313 | |||
1314 | } // End If Statement |
||
1315 | |||
1316 | } // End If Statement |
||
1317 | |||
1318 | } // End If Statement |
||
1319 | |||
1320 | return $wc_product_object; |
||
1321 | |||
1322 | } // End sensei_get_woocommerce_product_object() |
||
1323 | |||
1324 | /** |
||
1325 | * If customer has purchased the course, update Sensei to indicate that they are taking the course. |
||
1326 | * |
||
1327 | * @since 1.0.0 |
||
1328 | * @since 1.9.0 move to class Sensei_WC |
||
1329 | * |
||
1330 | * @param int $course_id (default: 0) |
||
1331 | * @param array/Object $order_user (default: array()) Specific user's data. |
||
1332 | * |
||
1333 | * @return bool|int |
||
1334 | */ |
||
1335 | public static function course_update ( $course_id = 0, $order_user = array() ) { |
||
1336 | |||
1337 | global $current_user; |
||
1338 | |||
1339 | if ( ! isset( $current_user ) || !$current_user->ID > 0 ) return false; |
||
1340 | |||
1341 | $data_update = false; |
||
1342 | |||
1343 | // Get the product ID |
||
1344 | $wc_post_id = get_post_meta( intval( $course_id ), '_course_woocommerce_product', true ); |
||
1345 | |||
1346 | // Check if in the admin |
||
1347 | if ( is_admin() ) { |
||
1348 | |||
1349 | $user_login = $order_user['user_login']; |
||
1350 | $user_email = $order_user['user_email']; |
||
1351 | $user_url = $order_user['user_url']; |
||
1352 | $user_id = $order_user['ID']; |
||
1353 | |||
1354 | } else { |
||
1355 | |||
1356 | $user_login = $current_user->user_login; |
||
1357 | $user_email = $current_user->user_email; |
||
1358 | $user_url = $current_user->user_url; |
||
1359 | $user_id = $current_user->ID; |
||
1360 | |||
1361 | } // End If Statement |
||
1362 | |||
1363 | // This doesn't appear to be purely WooCommerce related. Should it be in a separate function? |
||
1364 | $course_prerequisite_id = (int) get_post_meta( $course_id, '_course_prerequisite', true ); |
||
1365 | if( 0 < absint( $course_prerequisite_id ) ) { |
||
1366 | |||
1367 | $prereq_course_complete = Sensei_Utils::user_completed_course( $course_prerequisite_id, intval( $user_id ) ); |
||
1368 | if ( ! $prereq_course_complete ) { |
||
1369 | |||
1370 | // Remove all course user meta |
||
1371 | return Sensei_Utils::sensei_remove_user_from_course( $course_id, $user_id ); |
||
1372 | |||
1373 | } |
||
1374 | } |
||
1375 | |||
1376 | $is_user_taking_course = Sensei_Utils::user_started_course( intval( $course_id ), intval( $user_id ) ); |
||
1377 | |||
1378 | if ( ! $is_user_taking_course |
||
1379 | && Sensei_WC::is_woocommerce_active() |
||
1380 | && 0 < $wc_post_id |
||
1381 | && Sensei_WC::has_customer_bought_product( $user_id, $wc_post_id ) ) { |
||
1382 | |||
1383 | $activity_logged = Sensei_Utils::user_start_course( intval( $user_id ), intval( $course_id ) ); |
||
1384 | |||
1385 | if ( true == $activity_logged ) { |
||
1386 | |||
1387 | $is_user_taking_course = true; |
||
1388 | |||
1389 | } // End If Statement |
||
1390 | |||
1391 | }// end if is user taking course |
||
1392 | |||
1393 | return $is_user_taking_course; |
||
1394 | |||
1395 | } // End course_update() |
||
1396 | |||
1397 | /** |
||
1398 | * Disable guest checkout if a course product is in the cart |
||
1399 | * |
||
1400 | * @since 1.1.0 |
||
1401 | * @since 1.9.0 move to class Sensei_WC |
||
1402 | * |
||
1403 | * @param boolean $guest_checkout Current guest checkout setting |
||
1404 | * |
||
1405 | * @return boolean Modified guest checkout setting |
||
1406 | */ |
||
1407 | public static function disable_guest_checkout( $guest_checkout ) { |
||
1408 | |||
1409 | if( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { |
||
1410 | |||
1411 | if( isset( WC()->cart->cart_contents ) && count( WC()->cart->cart_contents ) > 0 ) { |
||
1412 | |||
1413 | foreach( WC()->cart->cart_contents as $cart_key => $product ) { |
||
1414 | if( isset( $product['product_id'] ) ) { |
||
1415 | |||
1416 | $args = array( |
||
1417 | 'posts_per_page' => -1, |
||
1418 | 'post_type' => 'course', |
||
1419 | 'meta_query' => array( |
||
1420 | array( |
||
1421 | 'key' => '_course_woocommerce_product', |
||
1422 | 'value' => $product['product_id'] |
||
1423 | ) |
||
1424 | ) |
||
1425 | ); |
||
1426 | |||
1427 | $posts = get_posts( $args ); |
||
1428 | |||
1429 | if( $posts && count( $posts ) > 0 ) { |
||
1430 | |||
1431 | foreach( $posts as $course ) { |
||
1432 | $guest_checkout = ''; |
||
1433 | break; |
||
1434 | |||
1435 | } |
||
1436 | } |
||
1437 | |||
1438 | } |
||
1439 | |||
1440 | } |
||
1441 | |||
1442 | } |
||
1443 | } |
||
1444 | |||
1445 | return $guest_checkout; |
||
1446 | |||
1447 | }// end disable_guest_checkout |
||
1448 | |||
1449 | /** |
||
1450 | * Change order status with virtual products to completed |
||
1451 | * |
||
1452 | * @since 1.1.0 |
||
1453 | * @since 1.9.0 move to class Sensei_WC |
||
1454 | * |
||
1455 | * @param string $order_status |
||
1456 | * @param int $order_id |
||
1457 | * |
||
1458 | * @return string |
||
1459 | **/ |
||
1460 | public static function virtual_order_payment_complete( $order_status, $order_id ) { |
||
1461 | |||
1462 | $order = new WC_Order( $order_id ); |
||
1463 | |||
1464 | if ( ! isset ( $order ) ) return ''; |
||
1465 | |||
1466 | if ( $order_status == 'wc-processing' && ( $order->post_status == 'wc-on-hold' || $order->post_status == 'wc-pending' || $order->post_status == 'wc-failed' ) ) { |
||
1467 | |||
1468 | $virtual_order = true; |
||
1469 | |||
1470 | if ( count( $order->get_items() ) > 0 ) { |
||
1471 | |||
1472 | foreach( $order->get_items() as $item ) { |
||
1473 | |||
1474 | if ( $item['product_id'] > 0 ) { |
||
1475 | $_product = $order->get_product_from_item( $item ); |
||
1476 | if ( ! $_product->is_virtual() ) { |
||
1477 | |||
1478 | $virtual_order = false; |
||
1479 | break; |
||
1480 | |||
1481 | } // End If Statement |
||
1482 | |||
1483 | } // End If Statement |
||
1484 | |||
1485 | } // End For Loop |
||
1486 | |||
1487 | } // End If Statement |
||
1488 | |||
1489 | // virtual order, mark as completed |
||
1490 | if ( $virtual_order ) { |
||
1491 | |||
1492 | return 'completed'; |
||
1493 | |||
1494 | } // End If Statement |
||
1495 | |||
1496 | } // End If Statement |
||
1497 | |||
1498 | return $order_status; |
||
1499 | |||
1500 | }// end virtual_order_payment_complete |
||
1501 | |||
1502 | |||
1503 | /** |
||
1504 | * Determine if the user has and active subscription to give them access |
||
1505 | * to the requested resource. |
||
1506 | * |
||
1507 | * @since 1.9.0 |
||
1508 | * |
||
1509 | * @param boolean$user_access_permission |
||
1510 | * @param integer $user_id |
||
1511 | * @return boolean $user_access_permission |
||
1512 | */ |
||
1513 | public static function get_subscription_permission( $user_access_permission , $user_id ){ |
||
1514 | |||
1515 | global $post; |
||
1516 | |||
1517 | // ignore the current case if the following conditions are met |
||
1518 | if ( ! class_exists( 'WC_Subscriptions' ) || empty( $user_id ) |
||
1519 | || ! in_array( $post->post_type, array( 'course','lesson','quiz' ) ) |
||
1520 | || ! wcs_user_has_subscription( $user_id) ){ |
||
1521 | |||
1522 | return $user_access_permission; |
||
1523 | |||
1524 | } |
||
1525 | |||
1526 | // at this user has a subscription |
||
1527 | // is the subscription on the the current course? |
||
1528 | |||
1529 | $course_id = 0; |
||
1530 | if ( 'course' == $post->post_type ){ |
||
1531 | |||
1532 | $course_id = $post->ID; |
||
1533 | |||
1534 | } elseif ( 'lesson' == $post->post_type ) { |
||
1535 | |||
1536 | $course_id = Sensei()->lesson->get_course_id( $post->ID ); |
||
1537 | |||
1538 | } else { |
||
1539 | |||
1540 | $lesson_id = Sensei()->quiz->get_lesson_id( $post->ID ); |
||
1541 | $course_id = Sensei()->lesson->get_course_id( $lesson_id ); |
||
1542 | |||
1543 | } |
||
1544 | |||
1545 | // if the course has no subscription WooCommerce product attached to return the permissions as is |
||
1546 | $product_id = Sensei_WC::get_course_product_id( $course_id ); |
||
1547 | $product = wc_get_product( $product_id ); |
||
1548 | if( ! in_array( $product->get_type(), self::get_subscription_types() ) ){ |
||
1549 | |||
1550 | return $user_access_permission; |
||
1551 | |||
1552 | } |
||
1553 | |||
1554 | // give access if user has active subscription on the product otherwise restrict it. |
||
1555 | // also check if the user was added to the course directly after the subscription started. |
||
1556 | if( wcs_user_has_subscription( $user_id, $product_id, 'active' ) |
||
1557 | || wcs_user_has_subscription( $user_id, $product_id, 'pending-cancel' ) |
||
1558 | || self::was_user_added_without_subscription( $user_id, $product_id, $course_id ) ){ |
||
1559 | |||
1560 | $user_access_permission = true; |
||
1561 | |||
1562 | }else{ |
||
1563 | |||
1564 | $user_access_permission = false; |
||
1565 | // do not show the WC permissions message |
||
1566 | remove_filter( 'sensei_the_no_permissions_message', array( 'Sensei_WC', 'alter_no_permissions_message' ), 20, 2 ); |
||
1567 | Sensei()->permissions_message['title'] = __( 'No active subscription', 'woothemes-sensei' ); |
||
1568 | Sensei()->permissions_message['message'] = __( 'Sorry, you do not have an access to this content without an active subscription.', 'woothemes-sensei' ); |
||
1569 | } |
||
1570 | |||
1571 | return $user_access_permission; |
||
1572 | |||
1573 | } // end get_subscription_permission |
||
1574 | |||
1575 | /** |
||
1576 | * @since 1.9.0 |
||
1577 | * |
||
1578 | * @param $has_user_started_course |
||
1579 | * @param $course_id |
||
1580 | * @param $user_id |
||
1581 | * |
||
1582 | * @return bool $has_user_started_course |
||
1583 | */ |
||
1584 | public static function get_subscription_user_started_course( $has_user_started_course, $course_id, $user_id ){ |
||
1585 | |||
1586 | if ( ! is_user_logged_in( ) ) { |
||
1587 | |||
1588 | return $has_user_started_course; |
||
1589 | |||
1590 | } |
||
1591 | |||
1592 | // cached user course access for this process instance |
||
1593 | global $sensei_wc_subscription_access_store; |
||
1594 | if ( ! is_array( $sensei_wc_subscription_access_store ) ) { |
||
1595 | $sensei_wc_subscription_access_store = array(); |
||
1596 | } |
||
1597 | |||
1598 | // user temp cached data so we don't output the mesage again |
||
1599 | $user_data_index_key = $course_id .'_' . $user_id; |
||
1600 | if ( isset( $sensei_wc_subscription_access_store[ $user_data_index_key ] ) ) { |
||
1601 | |||
1602 | return $sensei_wc_subscription_access_store[ $user_data_index_key ]; |
||
1603 | |||
1604 | } else { |
||
1605 | |||
1606 | if( empty( $course_id ) || empty( $user_id ) ){ |
||
1607 | return $has_user_started_course; |
||
1608 | } |
||
1609 | |||
1610 | // if the course has no subscription WooCommerce product attached to return the permissions as is |
||
1611 | $product_id = Sensei_WC::get_course_product_id( $course_id ); |
||
1612 | $product = wc_get_product( $product_id ); |
||
1613 | if( ! in_array( $product->get_type(), self::get_subscription_types() ) ){ |
||
1614 | |||
1615 | return $has_user_started_course; |
||
1616 | |||
1617 | } |
||
1618 | |||
1619 | // give access if user has active subscription on the product otherwise restrict it. |
||
1620 | // also check if the user was added to the course directly after the subscription started. |
||
1621 | if( wcs_user_has_subscription( $user_id, $product_id, 'active' ) |
||
1622 | || wcs_user_has_subscription( $user_id, $product_id, 'pending-cancel' ) |
||
1623 | || self::was_user_added_without_subscription( $user_id, $product_id, $course_id ) ){ |
||
1624 | |||
1625 | $has_user_started_course = true; |
||
1626 | |||
1627 | } else { |
||
1628 | |||
1629 | $has_user_started_course = false; |
||
1630 | |||
1631 | } |
||
1632 | $sensei_wc_subscription_access_store[ $user_data_index_key ] = $has_user_started_course; |
||
1633 | return $has_user_started_course; |
||
1634 | } |
||
1635 | |||
1636 | } |
||
1637 | |||
1638 | /** |
||
1639 | * Get all the valid subscription types. |
||
1640 | * |
||
1641 | * @since 1.9.0 |
||
1642 | * @return array |
||
1643 | */ |
||
1644 | public static function get_subscription_types(){ |
||
1645 | |||
1646 | return array( 'subscription','subscription_variation','variable-subscription' ); |
||
1647 | |||
1648 | } |
||
1649 | |||
1650 | /** |
||
1651 | * Compare the user's subscriptions end date with the date |
||
1652 | * the user was added to the course. If the user was added after |
||
1653 | * the subscription ended they were manually added and this will return |
||
1654 | * true. |
||
1655 | * |
||
1656 | * Important to note that all subscriptions for the user is compared. |
||
1657 | * |
||
1658 | * @since 1.9.0 |
||
1659 | * |
||
1660 | * @param $user_id |
||
1661 | * @param $product_id |
||
1662 | * @param $course_id |
||
1663 | * |
||
1664 | * @return bool |
||
1665 | */ |
||
1666 | public static function was_user_added_without_subscription($user_id, $product_id, $course_id ){ |
||
1667 | |||
1668 | $course_start_date = ''; |
||
1669 | $subscription_start_date = ''; |
||
1670 | $is_a_subscription =''; |
||
1671 | $was_user_added_without_subscription = false; |
||
1672 | |||
1673 | // if user is not on the course they were not added |
||
1674 | remove_filter( 'sensei_user_started_course', array( 'Sensei_WC', 'get_subscription_user_started_course' ), 10, 3 ); |
||
1675 | if( ! Sensei_Utils::user_started_course( $course_id, $user_id ) ){ |
||
1676 | |||
1677 | return false; |
||
1678 | |||
1679 | } |
||
1680 | |||
1681 | // if user doesn't have a subscription and is taking the course |
||
1682 | // they were added manually |
||
1683 | if ( ! wcs_user_has_subscription($user_id, $product_id) |
||
1684 | && Sensei_Utils::user_started_course( $course_id, get_current_user_id() ) ){ |
||
1685 | |||
1686 | return true; |
||
1687 | |||
1688 | } |
||
1689 | |||
1690 | add_filter( 'sensei_user_started_course', array( 'Sensei_WC', 'get_subscription_user_started_course' ), 10, 3 ); |
||
1691 | |||
1692 | $course_status = Sensei_Utils::user_course_status( $course_id, $user_id ); |
||
1693 | |||
1694 | // comparing dates setup data |
||
1695 | $course_start_date = date_create( $course_status->comment_date ); |
||
1696 | $subscriptions = wcs_get_users_subscriptions( $user_id ); |
||
1697 | |||
1698 | // comparing every subscription |
||
1699 | foreach( $subscriptions as $subscription ){ |
||
1700 | |||
1701 | // for the following statuses we know the user was not added |
||
1702 | // manually |
||
1703 | $status = $subscription->get_status(); |
||
1704 | if ( in_array( $status, array( 'pending-canceled', 'active', 'on-hold', 'pending' ) ) ) { |
||
1705 | |||
1706 | continue; |
||
1707 | |||
1708 | } |
||
1709 | |||
1710 | $current_subscription_start_date = date_create( $subscription->modified_date ); |
||
1711 | |||
1712 | // is the last updated subscription date newer than course start date |
||
1713 | if ( $current_subscription_start_date > $course_start_date ) { |
||
1714 | |||
1715 | return false; |
||
1716 | |||
1717 | } |
||
1718 | |||
1719 | } |
||
1720 | |||
1721 | return $was_user_added_without_subscription; |
||
1722 | } |
||
1723 | |||
1724 | /** |
||
1725 | * Get all the orders for a specific user and product combination |
||
1726 | * |
||
1727 | * @param int $user_id |
||
1728 | * @param $product_id |
||
1729 | * |
||
1730 | * @return array $orders |
||
1731 | */ |
||
1732 | public static function get_user_product_orders( $user_id = 0, $product_id ) { |
||
1733 | |||
1734 | $args = array( |
||
1735 | 'numberposts' => -1, |
||
1736 | 'post_type' => 'shop_order', |
||
1737 | 'meta_key' => '_customer_user', |
||
1738 | 'meta_value' => intval( $user_id ), |
||
1739 | ); |
||
1740 | |||
1741 | if( class_exists( 'WC_Subscriptions_Manager' ) ) { |
||
1742 | $args['post_type'] = array( 'shop_order', 'shop_subscription' ); |
||
1743 | } |
||
1744 | |||
1745 | return get_posts( $args ); |
||
1746 | |||
1747 | } |
||
1748 | |||
1749 | /** |
||
1750 | * Determine if a course can be purchased. Purchasable |
||
1751 | * courses have valid products attached. These can also be products |
||
1752 | * with price of Zero. |
||
1753 | * |
||
1754 | * |
||
1755 | * @since 1.9.0 |
||
1756 | * |
||
1757 | * @param int $course_id |
||
1758 | * |
||
1759 | * @return bool |
||
1760 | */ |
||
1761 | public static function is_course_purchasable( $course_id = 0 ){ |
||
1762 | |||
1763 | if( ! self::is_woocommerce_active() ){ |
||
1764 | return false; |
||
1765 | } |
||
1766 | $course_product = wc_get_product( self::get_course_product_id( $course_id ) ); |
||
1767 | |||
1768 | return $course_product->is_purchasable(); |
||
1769 | |||
1770 | } |
||
1771 | |||
1772 | }// end Sensei_WC |
||
1773 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.