PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.0.5
Tutor LMS – eLearning and online course solution v2.0.5
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 / Course.php
tutor / classes Last commit date
Addons.php 4 years ago Admin.php 4 years ago Ajax.php 4 years ago Announcements.php 4 years ago Assets.php 4 years ago Backend_Page_Trait.php 4 years ago Course.php 4 years ago Course_Filter.php 4 years ago Course_List.php 4 years ago Course_Settings_Tabs.php 4 years ago Course_Widget.php 4 years ago Custom_Validation.php 4 years ago Dashboard.php 4 years ago FormHandler.php 4 years ago Frontend.php 4 years ago Gutenberg.php 4 years ago Input.php 4 years ago Instructor.php 4 years ago Instructors_List.php 4 years ago Lesson.php 4 years ago Options_V2.php 4 years ago Post_types.php 4 years ago Private_Course_Access.php 4 years ago Q_and_A.php 4 years ago Question_Answers_List.php 4 years ago Quiz.php 4 years ago Quiz_Attempts_List.php 4 years ago RestAPI.php 4 years ago Reviews.php 4 years ago Rewrite_Rules.php 4 years ago Shortcode.php 4 years ago Student.php 4 years ago Students_List.php 4 years ago Taxonomies.php 4 years ago Template.php 4 years ago Theme_Compatibility.php 5 years ago Tools.php 4 years ago Tools_V2.php 4 years ago Tutor.php 4 years ago TutorEDD.php 4 years ago Tutor_Base.php 5 years ago Tutor_List_Table.php 4 years ago Tutor_Setup.php 4 years ago Upgrader.php 4 years ago User.php 4 years ago Utils.php 4 years ago Video_Stream.php 4 years ago Withdraw.php 4 years ago Withdraw_Requests_List.php 4 years ago WooCommerce.php 4 years ago
Course.php
1308 lines
1 <?php
2 namespace TUTOR;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 class Course extends Tutor_Base {
9
10 private $additional_meta=array(
11 '_tutor_enable_qa',
12 '_tutor_is_public_course'
13 );
14
15 public function __construct() {
16 parent::__construct();
17
18 add_action('add_meta_boxes', array($this, 'register_meta_box') );
19 add_action('save_post_'.$this->course_post_type, array($this, 'save_course_meta'), 10, 2);
20 add_action('wp_ajax_tutor_save_topic', array($this, 'tutor_save_topic'));
21
22 //Add Column
23 add_filter( "manage_{$this->course_post_type}_posts_columns", array($this, 'add_column'), 10,1 );
24 add_action( "manage_{$this->course_post_type}_posts_custom_column" , array($this, 'custom_lesson_column'), 10, 2 );
25
26 add_action('wp_ajax_tutor_delete_topic', array($this, 'tutor_delete_topic'));
27 add_action('admin_action_tutor_delete_announcement', array($this, 'tutor_delete_announcement'));
28
29 // Frontend Action
30 add_action( 'template_redirect', array( $this, 'enroll_now' ) );
31 add_action( 'init', array( $this, 'mark_course_complete' ) );
32
33 /**
34 * Frontend Dashboard
35 */
36 add_action( 'wp_ajax_tutor_delete_dashboard_course', array( $this, 'tutor_delete_dashboard_course' ) );
37
38 /**
39 * Gutenberg author support
40 */
41 add_filter( 'wp_insert_post_data', array( $this, 'tutor_add_gutenberg_author' ), '99', 2 );
42
43 /**
44 * Frontend metabox supports for course builder
45 *
46 * @since v.1.3.4
47 */
48 add_action( 'tutor/dashboard_course_builder_form_field_after', array( $this, 'register_meta_box_in_frontend' ) );
49
50 /**
51 * Do Stuff for the course save from frontend
52 */
53 add_action( 'save_tutor_course', array( $this, 'attach_product_with_course' ), 10, 2 );
54
55 /**
56 * Add course level to course settings
57 *
58 * @since v.1.4.1
59 */
60 add_filter( 'tutor_course_settings_tabs', array($this, 'add_course_level_to_settings'));
61
62 /**
63 * Enable Disable Course Details Page Feature
64 *
65 * @since v.1.4.8
66 */
67 $this->course_elements_enable_disable();
68
69 /**
70 * @since v.1.4.8
71 * Check if course starting, set meta if starting
72 */
73 add_action( 'tutor_lesson_load_before', array( $this, 'tutor_lesson_load_before' ) );
74
75 /**
76 * @since v.1.4.9
77 * Filter product in shop page
78 */
79 $this->filter_product_in_shop_page();
80
81 /**
82 * Remove the course price if enrolled
83 * @since 1.5.8
84 */
85 add_filter('tutor_course_price', array($this, 'remove_price_if_enrolled'));
86
87 /**
88 * Remove course complete button if course completion is strict mode
89 * @since v.1.6.1
90 */
91 add_filter('tutor_course/single/complete_form', array($this, 'tutor_lms_hide_course_complete_btn'));
92 add_filter('get_gradebook_generate_form_html', array($this, 'get_generate_greadbook'));
93
94 /**
95 * Add social share content in header
96 * @since v.1.6.3
97 */
98 add_action('wp_head', array($this, 'social_share_content'));
99
100 /**
101 * Delete course data after deleted course
102 * @since v.1.6.6
103 */
104 add_action('deleted_post', array($this, 'delete_tutor_course_data'));
105
106 /**
107 * Delete course data after deleted course
108 *
109 * @since v.1.8.2
110 */
111 add_action( 'before_delete_post', array( $this, 'delete_associated_enrollment' ) );
112
113 /**
114 * Show only own uploads in media library if user is instructor
115 *
116 * @since v1.8.9
117 */
118 add_filter( 'posts_where', array( $this, 'restrict_media' ) );
119
120 /**
121 * Restrict new enrol/purchase button if course member limit reached
122 *
123 * @since v1.9.0
124 */
125 add_filter( 'tutor_course_restrict_new_entry', array( $this, 'restrict_new_student_entry' ) );
126
127 /**
128 * Reset course progress on retake
129 *
130 * @since v1.9.5
131 */
132 add_action( 'wp_ajax_tutor_reset_course_progress', array( $this, 'tutor_reset_course_progress' ) );
133
134 /**
135 * Popup for review
136 *
137 * @since v1.9.7
138 */
139 add_action( 'wp_footer', array( $this, 'popup_review_form' ) );
140
141 /**
142 * Do enroll after login if guest take enroll attempt
143 *
144 * @since 1.9.8
145 */
146 add_action( 'tutor_do_enroll_after_login_if_attempt', array( $this, 'enroll_after_login_if_attempt' ), 10, 2 );
147
148 add_action( 'wp_ajax_tutor_update_course_content_order', array($this, 'tutor_update_course_content_order') );
149 }
150
151 public function tutor_update_course_content_order() {
152 tutor_utils()->checking_nonce();
153
154 if(isset($_POST['content_parent'])) {
155 $topic_id = (int)tutor_utils()->array_get('parent_topic_id', $_POST['content_parent']);
156 $content_id = (int)tutor_utils()->array_get('content_id', $_POST['content_parent']);
157
158 if(!tutor_utils()->can_user_manage('topic', $topic_id)) {
159 wp_send_json_success(array('message' => __('Access Denied!', 'tutor')));
160 exit;
161 }
162
163 // Update the parent topic id of the content
164 global $wpdb;
165 $wpdb->update($wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ));
166 }
167
168 // Save course content order
169 $this->save_course_content_order();
170
171 wp_send_json_success();
172 }
173
174 public function restrict_new_student_entry($content) {
175
176 if(!tutor_utils()->is_course_fully_booked()) {
177 // No restriction if not fully booked
178 return $content;
179 }
180
181 return '<div class="list-item-booking booking-full tutor-d-flex tutor-align-center"><div class="booking-progress tutor-d-flex"><span class="tutor-mr-8 tutor-color-warning tutor-icon-circle-info"></span></div><div class="tutor-fs-7 tutor-fw-medium">Fully Booked</div></div>';
182 }
183
184 function restrict_media( $where ) {
185
186 if ( isset( $_POST['action'] ) && $_POST['action'] == 'query-attachments' && tutor_utils()->is_instructor() ) {
187 if ( ! tutor_utils()->has_user_role( array( 'administrator', 'editor' ) ) ) {
188 $where .= ' AND post_author=' . get_current_user_id();
189 }
190 }
191
192 return $where;
193 }
194
195 /**
196 * Registering metabox
197 */
198 public function register_meta_box(){
199 $coursePostType = tutor()->course_post_type;
200
201 tutor_meta_box_wrapper( 'tutor-course-topics', __( 'Course Builder', 'tutor' ), array($this, 'course_meta_box'), $coursePostType, 'advanced', 'default', 'tutor-admin-post-meta' );
202
203 tutor_meta_box_wrapper( 'tutor-course-additional-data', __( 'Additional Data', 'tutor' ), array($this, 'course_additional_data_meta_box'), $coursePostType, 'advanced', 'default', 'tutor-admin-post-meta' );
204
205 tutor_meta_box_wrapper( 'tutor-course-videos', __( 'Video', 'tutor' ), array($this, 'video_metabox'), $coursePostType, 'advanced', 'default', 'tutor-admin-post-meta' );
206 }
207
208 public function course_meta_box( $echo = true ) {
209 ob_start();
210 include tutor()->path . 'views/metabox/course-topics.php';
211 $content = ob_get_clean();
212
213 if ( $echo ) {
214 // echo tutor_kses_html( $content ); Doesn't support SVG. It is restored in version 2 and we've got rid of SVG and used icon font instead.
215 echo $content;
216 } else {
217 return $content;
218 }
219 }
220
221 public function course_additional_data_meta_box( $echo = true ) {
222
223 ob_start();
224 include tutor()->path . 'views/metabox/course-additional-data.php';
225 $content = ob_get_clean();
226
227 if ( $echo ) {
228 echo tutor_kses_html( $content );
229 } else {
230 return $content;
231 }
232 }
233
234 public function video_metabox( $echo = true ) {
235 ob_start();
236 include tutor()->path . 'views/metabox/video-metabox.php';
237 $content = ob_get_clean();
238
239 if ( $echo ) {
240 echo tutor_kses_html( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
241 } else {
242 return $content;
243 }
244 }
245
246 /**
247 * Register metabox in course builder tutor
248 *
249 * @since v.1.3.4
250 */
251 public function register_meta_box_in_frontend(){
252 global $post;
253
254 do_action('tutor_course_builder_metabox_before', get_the_ID());
255
256 course_builder_section_wrap($this->video_metabox($echo = false), __( 'Video', 'tutor' ) );
257 do_action('tutor/frontend_course_edit/after/video', $post);
258
259 course_builder_section_wrap($this->course_meta_box($echo = false), __( 'Course Builder', 'tutor' ) );
260 do_action('tutor/frontend_course_edit/after/course_builder', $post);
261
262 course_builder_section_wrap($this->course_additional_data_meta_box($echo = false), __( 'Additional Data', 'tutor' ) );
263 do_action('tutor/frontend_course_edit/after/additional_data', $post);
264
265 do_action('tutor_course_builder_metabox_after', get_the_ID());
266 }
267
268 private function save_course_content_order(){
269 global $wpdb;
270
271 if ( ! empty( $_POST['tutor_topics_lessons_sorting'] ) ) {
272 $new_order = sanitize_text_field( stripslashes( $_POST['tutor_topics_lessons_sorting'] ) );
273 $order = json_decode( $new_order, true );
274
275 if ( is_array( $order ) && count( $order ) ) {
276 $i = 0;
277 foreach ( $order as $topic ) {
278 $i++;
279 $wpdb->update(
280 $wpdb->posts,
281 array( 'menu_order' => $i ),
282 array( 'ID' => $topic['topic_id'] )
283 );
284
285 /**
286 * Removing All lesson with topic
287 */
288
289 $wpdb->update(
290 $wpdb->posts,
291 array( 'post_parent' => 0 ),
292 array( 'post_parent' => $topic['topic_id'] )
293 );
294
295 /**
296 * Lesson Attaching with topic ID
297 * sorting lesson
298 */
299 if ( isset( $topic['lesson_ids'] ) ) {
300 $lesson_ids = $topic['lesson_ids'];
301 } else {
302 $lesson_ids = array();
303 }
304 if ( count( $lesson_ids ) ) {
305 foreach ( $lesson_ids as $lesson_key => $lesson_id ) {
306 $wpdb->update(
307 $wpdb->posts,
308 array(
309 'post_parent' => $topic['topic_id'],
310 'menu_order' => $lesson_key,
311 ),
312 array( 'ID' => $lesson_id )
313 );
314 }
315 }
316 }
317 }
318 }
319 }
320
321 /**
322 * @param $post_ID
323 *
324 * Insert Topic and attached it with Course
325 */
326 public function save_course_meta( $post_ID, $post ) {
327 global $wpdb;
328
329 do_action( 'tutor_save_course', $post_ID, $post );
330
331 /**
332 * Save course price type
333 */
334 $price_type = tutor_utils()->array_get('tutor_course_price_type', $_POST);
335 if ($price_type){
336 update_post_meta($post_ID, '_tutor_course_price_type', $price_type);
337 }
338
339 //Course Duration
340 if ( ! empty($_POST['course_duration'])){
341 $video = tutor_utils()->sanitize_array($_POST['course_duration']);
342 update_post_meta($post_ID, '_course_duration', $video);
343 }
344
345 if ( ! empty($_POST['_tutor_course_level'])){
346 $course_level = sanitize_text_field($_POST['_tutor_course_level']);
347 update_post_meta($post_ID, '_tutor_course_level', $course_level);
348 }
349
350 $additional_data_edit = tutor_utils()->avalue_dot('_tutor_course_additional_data_edit', $_POST);
351 if ($additional_data_edit) {
352 if (!empty($_POST['course_benefits'])) {
353 $course_benefits = wp_kses_post($_POST['course_benefits']);
354 update_post_meta($post_ID, '_tutor_course_benefits', $course_benefits);
355 } else {
356 delete_post_meta( $post_ID, '_tutor_course_benefits' );
357 }
358
359 if ( ! empty( $_POST['course_requirements'] ) ) {
360 $requirements = wp_kses_post( $_POST['course_requirements'] );
361 update_post_meta( $post_ID, '_tutor_course_requirements', $requirements );
362 } else {
363 delete_post_meta( $post_ID, '_tutor_course_requirements' );
364 }
365
366 if ( ! empty( $_POST['course_target_audience'] ) ) {
367 $target_audience = wp_kses_post( $_POST['course_target_audience'] );
368 update_post_meta( $post_ID, '_tutor_course_target_audience', $target_audience );
369 } else {
370 delete_post_meta( $post_ID, '_tutor_course_target_audience' );
371 }
372
373 if ( ! empty( $_POST['course_material_includes'] ) ) {
374 $material_includes = wp_kses_post( $_POST['course_material_includes'] );
375 update_post_meta( $post_ID, '_tutor_course_material_includes', $material_includes );
376 } else {
377 delete_post_meta( $post_ID, '_tutor_course_material_includes' );
378 }
379 }
380
381 /**
382 * Sorting Topics and lesson
383 */
384 $this->save_course_content_order();
385
386 // Additional data like course intro video
387 if ( $additional_data_edit ) {
388 if ( ! empty( $_POST['video']['source'] ) ) { // Video
389 $video = tutor_utils()->array_get( 'video', $_POST );
390 update_post_meta( $post_ID, '_video', $video );
391 } else {
392 delete_post_meta( $post_ID, '_video' );
393 }
394 }
395
396 /**
397 * Adding author to instructor automatically
398 */
399
400 $author_id = $post->post_author;
401 $attached = (int) $wpdb->get_var(
402 $wpdb->prepare(
403 "SELECT COUNT(umeta_id) FROM {$wpdb->usermeta}
404 WHERE user_id = %d AND meta_key = '_tutor_instructor_course_id' AND meta_value = %d ",
405 $author_id,
406 $post_ID
407 )
408 );
409
410 if ( ! $attached ) {
411 add_user_meta( $author_id, '_tutor_instructor_course_id', $post_ID );
412 }
413
414 /**
415 * Disable question and answer for this course
416 *
417 * @since 1.7.0
418 */
419 if ( $additional_data_edit ) {
420 foreach ( $this->additional_meta as $key ) {
421 update_post_meta( $post_ID, $key, ( isset( $_POST[ $key ] ) ? 'yes' : 'no' ) );
422 }
423 }
424
425 do_action( 'tutor_save_course_after', $post_ID, $post );
426 }
427
428 /**
429 * Tutor add course topic
430 */
431 public function tutor_save_topic(){
432 tutor_utils()->checking_nonce();
433
434 // Check required fields
435 if (empty($_POST['topic_title']) ) {
436 wp_send_json_error(array('message' => __('Topic title is required!', 'tutor')));
437 }
438
439 // Gather parameters
440 $course_id = (int) tutor_utils()->avalue_dot('topic_course_id', $_POST);
441 $topic_id = (int) tutor_utils()->avalue_dot('topic_id', $_POST);
442 $topic_title = sanitize_text_field( $_POST['topic_title'] );
443 $topic_summery = wp_kses_post( $_POST['topic_summery'] );
444 $next_topic_order_id = tutor_utils()->get_next_topic_order_id($course_id);
445
446 // Validate if user can manage the topic
447 if(!tutor_utils()->can_user_manage('course', $course_id) || ($topic_id && !tutor_utils()->can_user_manage('topic', $topic_id))) {
448 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
449 }
450
451 // Create payload to create/update the topic
452 $post_arr = array(
453 'post_type' => 'topics',
454 'post_title' => $topic_title,
455 'post_content' => $topic_summery,
456 'post_status' => 'publish',
457 'post_author' => get_current_user_id(),
458 'post_parent' => $course_id,
459 'menu_order' => $next_topic_order_id,
460 );
461 $topic_id ? $post_arr['ID']=$topic_id : 0;
462 $current_topic_id = wp_insert_post( $post_arr );
463
464 ob_start();
465 include tutor()->path.'views/metabox/course-contents.php';
466
467 wp_send_json_success(array(
468 'topic_title' => $topic_title,
469 'course_contents' => ob_get_clean()
470 ));
471 }
472
473 /**
474 * @param $columns
475 *
476 * @return mixed
477 *
478 * Add Lesson column
479 */
480 public function add_column( $columns ) {
481 $date_col = $columns['date'];
482 unset( $columns['date'] );
483 $columns['lessons'] = __( 'Lessons', 'tutor' );
484 $columns['students'] = __( 'Students', 'tutor' );
485 $columns['price'] = __( 'Price', 'tutor' );
486 $columns['date'] = $date_col;
487
488 return $columns;
489 }
490
491 /**
492 * @param $column
493 * @param $post_id
494 */
495 public function custom_lesson_column( $column, $post_id ) {
496 if ( $column === 'lessons' ) {
497 echo tutor_utils()->get_lesson_count_by_course( $post_id );
498 }
499
500 if ( $column === 'students' ) {
501 echo tutor_utils()->count_enrolled_users_by_course( $post_id );
502 }
503
504 if ( $column === 'price' ) {
505 $price = tutor_utils()->get_course_price( $post_id );
506 if ( $price ) {
507 $monetize_by = tutils()->get_option( 'monetize_by' );
508 if ( function_exists( 'wc_price' ) && $monetize_by === 'wc' ) {
509 echo '<span class="tutor-label-success">' . wc_price( $price ) . '</span>';
510 } else {
511 echo '<span class="tutor-label-success">' . $price . '</span>';
512 }
513 } else {
514 echo apply_filters( 'tutor-loop-default-price', __( 'free', 'tutor' ) );
515 }
516 }
517 }
518
519
520 public function tutor_delete_topic() {
521
522 tutor_utils()->checking_nonce();
523
524 global $wpdb;
525 $topic_id = sanitize_text_field(!empty($_POST['topic_id']) ? $_POST['topic_id'] : '');
526
527 if(!$topic_id || !is_numeric($topic_id) || !tutor_utils()->can_user_manage('topic', $topic_id)) {
528 wp_send_json_error(array('message' => 'Access Forbidden'));
529 }
530
531 // Assign course ID to orphan content IDs since the topic will be deleted.
532 $course_id = tutor_utils()->get_course_id_by('topic', $topic_id);
533 $content_ids = tutor_utils()->get_course_content_ids_by(null, 'topic', $topic_id);
534 foreach($content_ids as $content_id) {
535 update_post_meta( $content_id, '_tutor_course_id_for_lesson', $course_id );
536 // Actually all kind of contents.
537 // This keyword '_tutor_course_id_for_lesson' used just to support backward compatibillity
538 }
539
540 // Set contents under the topic orphan
541 $wpdb->update($wpdb->posts, array('post_parent' => 0), array('post_parent' => $topic_id));
542
543 // Then delete the topic from database
544 $wpdb->delete($wpdb->postmeta, array('post_id' => $topic_id));
545 wp_delete_post($topic_id);
546
547 wp_send_json_success();
548 }
549
550 public function tutor_delete_announcement() {
551 tutor_utils()->checking_nonce( 'get' );
552
553 $announcement_id = (int) $_GET['topic_id'];
554
555 wp_delete_post( $announcement_id );
556 wp_safe_redirect( wp_get_referer() );
557 }
558
559 public function enroll_now() {
560
561 // Checking if action comes from Enroll form
562 if ( tutor_utils()->array_get( 'tutor_course_action', tutor_sanitize_data($_POST) ) !== '_tutor_course_enroll_now' || ! isset( $_POST['tutor_course_id'] ) ) {
563 return;
564 }
565
566 //Checking Nonce
567 tutor_utils()->checking_nonce();
568
569 $user_id = get_current_user_id();
570 if ( ! $user_id ) {
571 exit( __( 'Please Sign In first', 'tutor' ) );
572 }
573
574 $course_id = (int) $_POST['tutor_course_id'];
575 $user_id = get_current_user_id();
576
577 /**
578 * TODO: need to check purchase information
579 */
580
581 $is_purchasable = tutor_utils()->is_course_purchasable( $course_id );
582
583 /**
584 * If is is not purchasable, it's free, and enroll right now
585 *
586 * if purchasable, then process purchase.
587 *
588 * @since: v.1.0.0
589 */
590 if ( $is_purchasable ) {
591 // process purchase
592
593 } else {
594 // Free enroll
595 tutor_utils()->do_enroll( $course_id );
596 }
597
598 $referer_url = wp_get_referer();
599 wp_redirect( $referer_url );
600 }
601
602 /**
603 *
604 * Mark complete completed
605 *
606 * @since v.1.0.0
607 */
608 public function mark_course_complete() {
609 if ( ! isset( $_POST['tutor_action'] ) || $_POST['tutor_action'] !== 'tutor_complete_course' ) {
610 return;
611 }
612 // Checking nonce
613 tutor_utils()->checking_nonce();
614
615 $user_id = get_current_user_id();
616
617 // TODO: need to show view if not signed_in
618 if ( ! $user_id ) {
619 die( __( 'Please Sign-In', 'tutor' ) );
620 }
621
622 $course_id = (int) $_POST['course_id'];
623
624 do_action( 'tutor_course_complete_before', $course_id );
625 /**
626 * Marking course completed at Comment
627 */
628
629 global $wpdb;
630
631 $date = date( 'Y-m-d H:i:s', tutor_time() );
632
633 // Making sure that, hash is unique
634 do {
635 $hash = substr( md5( wp_generate_password( 32 ) . $date . $course_id . $user_id ), 0, 16 );
636 $hasHash = (int) $wpdb->get_var(
637 $wpdb->prepare(
638 "SELECT COUNT(comment_ID) from {$wpdb->comments}
639 WHERE comment_agent = 'TutorLMSPlugin' AND comment_type = 'course_completed' AND comment_content = %s ",
640 $hash
641 )
642 );
643
644 } while ( $hasHash > 0 );
645
646 $data = array(
647 'comment_post_ID' => $course_id,
648 'comment_author' => $user_id,
649 'comment_date' => $date,
650 'comment_date_gmt' => get_gmt_from_date( $date ),
651 'comment_content' => $hash, // Identification Hash
652 'comment_approved' => 'approved',
653 'comment_agent' => 'TutorLMSPlugin',
654 'comment_type' => 'course_completed',
655 'user_id' => $user_id,
656 );
657
658 $wpdb->insert( $wpdb->comments, $data );
659
660 do_action( 'tutor_course_complete_after', $course_id, $user_id );
661
662 $permalink = get_the_permalink( $course_id );
663
664 // Set temporary identifier to show review pop up
665 if(get_tutor_option( 'enable_course_review' )) {
666 $rating = tutor_utils()->get_course_rating_by_user($course_id, $user_id);
667 if(!$rating || (empty($rating->rating) && empty($rating->review))) {
668 update_option( 'tutor_course_complete_popup_'.$user_id, array(
669 'course_id' => $course_id,
670 'course_url' => $permalink,
671 'expires' => time()+10
672 ));
673 }
674 }
675
676 wp_redirect( $permalink );
677 exit;
678 }
679
680 public function popup_review_form() {
681 if ( is_user_logged_in() ) {
682 $key = 'tutor_course_complete_popup_' . get_current_user_id();
683 $popup = get_option( $key );
684
685 if ( is_array( $popup ) ) {
686
687 if ( $popup['expires'] > time() ) {
688 $course_id = $popup['course_id'];
689 include tutor()->path . 'views/modal/review.php';
690 }
691
692 delete_option( $key );
693 }
694 }
695 }
696
697 public function tutor_delete_dashboard_course(){
698 tutor_utils()->checking_nonce();
699
700 $course_id = intval(sanitize_text_field($_POST['course_id']));
701 if(!tutor_utils()->can_user_manage('course', $course_id)) {
702 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
703 }
704
705 wp_delete_post($course_id, true);
706 wp_send_json_success();
707 }
708
709
710 public function tutor_add_gutenberg_author( $data, $postarr ) {
711 global $wpdb;
712
713 $courses_post_type = tutor()->course_post_type;
714 $post_type = tutor_utils()->array_get('post_type', $postarr);
715
716 if ( $courses_post_type === $post_type ) {
717 $post_ID = (int) tutor_utils()->avalue_dot( 'ID', $postarr );
718 $post_author = (int) $wpdb->get_var( $wpdb->prepare( "SELECT post_author FROM {$wpdb->posts} WHERE ID = %d ", $post_ID ) );
719
720 if ( $post_author > 0 ) {
721 $data['post_author'] = $post_author;
722 } else {
723 $data['post_author'] = get_current_user_id();
724 }
725 }
726
727 return $data;
728 }
729
730
731 /**
732 * @param $post_ID
733 * @param $postData
734 *
735 * Attach product during save course from the frontend course dashboard.
736 *
737 * @return string
738 *
739 * @since v.1.3.4
740 */
741 public function attach_product_with_course( $post_ID, $postData ) {
742 $attached_product_id = tutor_utils()->get_course_product_id( $post_ID );
743 $course_price = sanitize_text_field( tutor_utils()->array_get( 'course_price', $_POST ) );
744
745 if ( ! $course_price){
746 // Return if price not set or 0
747 return;
748 }
749
750 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
751 $course = get_post( $post_ID );
752
753 if ( $monetize_by === 'wc' ) {
754
755 $is_update = false;
756 if ( $attached_product_id ) {
757 $wc_product = get_post_meta( $attached_product_id, '_product_version', true );
758 if ( $wc_product ) {
759 $is_update = true;
760 }
761 }
762
763 if ( $is_update ) {
764 $productObj = wc_get_product( $attached_product_id );
765 $productObj->set_price( $course_price ); // set product price
766 $productObj->set_regular_price( $course_price ); // set product regular price
767 $productObj->set_sold_individually( true );
768 $product_id = $productObj->save();
769 if ( $productObj->is_type( 'subscription' ) ) {
770 update_post_meta( $attached_product_id, '_subscription_price', $course_price );
771 }
772 } else {
773 $productObj = new \WC_Product();
774 $productObj->set_name( $course->post_title );
775 $productObj->set_status( 'publish' );
776 $productObj->set_price( $course_price ); // set product price
777 $productObj->set_regular_price( $course_price ); // set product regular price
778 $productObj->set_sold_individually( true );
779
780 $product_id = $productObj->save();
781 if ( $product_id ) {
782 update_post_meta( $post_ID, '_tutor_course_product_id', $product_id );
783 // Mark product for woocommerce
784 update_post_meta( $product_id, '_virtual', 'yes' );
785 update_post_meta( $product_id, '_tutor_product', 'yes' );
786
787 $coursePostThumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
788 if ( $coursePostThumbnail ) {
789 set_post_thumbnail( $product_id, $coursePostThumbnail );
790 }
791 }
792 }
793 } elseif ( $monetize_by === 'edd' ) {
794
795 $is_update = false;
796
797 if ( $attached_product_id ) {
798 $edd_price = get_post_meta( $attached_product_id, 'edd_price', true );
799 if ( $edd_price ) {
800 $is_update = true;
801 }
802 }
803
804 if ( $is_update ) {
805 // Update the product
806 update_post_meta( $attached_product_id, 'edd_price', $course_price );
807 } else {
808 // Create new product
809
810 $post_arr = array(
811 'post_type' => 'download',
812 'post_title' => $course->post_title,
813 'post_status' => 'publish',
814 'post_author' => get_current_user_id(),
815 );
816 $download_id = wp_insert_post( $post_arr );
817 if ( $download_id ) {
818 // edd_price
819 update_post_meta( $download_id, 'edd_price', $course_price );
820
821 update_post_meta( $post_ID, '_tutor_course_product_id', $download_id );
822 // Mark product for EDD
823 update_post_meta( $download_id, '_tutor_product', 'yes' );
824
825 $coursePostThumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
826 if ( $coursePostThumbnail ) {
827 set_post_thumbnail( $download_id, $coursePostThumbnail );
828 }
829 }
830 }
831 }
832 }
833
834 /**
835 * Add Course level to course settings
836 *
837 * @since v.1.4.1
838 */
839 public function add_course_level_to_settings($args){
840 $course_id = get_the_ID();
841 $levels = tutor_utils()->course_levels();
842 $course_level = get_post_meta($course_id, '_tutor_course_level', true);
843
844 $args['general']['fields']['_tutor_course_level'] = array(
845 'type' => 'select',
846 'label' => __('Difficulty Level', 'tutor'),
847 'label_title'=> __('Enable', 'tutor'),
848 'options' => $levels,
849 'value' => $course_level ? $course_level : 'intermediate',
850 'desc' => __('Course difficulty level', 'tutor'),
851 );
852
853 return $args;
854 }
855
856 /**
857 * Check if course starting
858 *
859 * @since v.1.4.8
860 */
861 public function tutor_lesson_load_before(){
862 $course_id = tutor_utils()->get_course_id_by_content(get_the_ID());
863 $completed_lessons = tutor_utils()->get_completed_lesson_count_by_course($course_id);
864 if (is_user_logged_in()){
865 $is_course_started = get_post_meta($course_id, '_tutor_course_started', true);
866 if ( ! $completed_lessons && ! $is_course_started){
867 update_post_meta($course_id, '_tutor_course_started', tutor_time());
868 do_action('tutor/course/started', $course_id);
869 }
870 }
871 }
872
873 /**
874 * Add Course level to course settings
875 *
876 * @since v.1.4.8
877 */
878 public function course_elements_enable_disable(){
879 add_filter('tutor_course/single/completing-progress-bar', array($this, 'enable_disable_course_progress_bar') );
880 add_filter('tutor_course/single/material_includes', array($this, 'enable_disable_material_includes') );
881 add_filter('tutor_course/single/content', array($this, 'enable_disable_course_content') );
882 add_filter('tutor_course/single/benefits_html', array($this, 'enable_disable_course_benefits') );
883 add_filter('tutor_course/single/requirements_html', array($this, 'enable_disable_course_requirements') );
884 add_filter('tutor_course/single/audience_html', array($this, 'enable_disable_course_target_audience') );
885 add_filter('tutor_course/single/nav_items', array($this, 'enable_disable_course_nav_items'), 999, 2 );
886 }
887
888 /**
889 * Enable disable course progress bar
890 *
891 * @since v.1.4.8
892 */
893 public function enable_disable_course_progress_bar($html){
894 $disable_option = !(bool)tutor_utils()->get_option('enable_course_progress_bar', true, true);
895 if($disable_option){
896 return '';
897 }
898 return $html;
899 }
900
901 /**
902 * Enable disable material includes
903 *
904 * @since v.1.4.8
905 */
906 public function enable_disable_material_includes($html){
907 $disable_option = !(bool)get_tutor_option('enable_course_material', true, true);
908 if($disable_option){
909 return '';
910 }
911 return $html;
912 }
913
914 /**
915 * Enable disable course content
916 *
917 * @since v.1.4.8
918 */
919 public function enable_disable_course_content($html){
920 $disable_option = !(bool)tutor_utils()->get_option('enable_course_description', true, true);
921 if($disable_option){
922 return '';
923 }
924 return $html;
925 }
926
927 /**
928 * Enable disable course benefits
929 *
930 * @since v.1.4.8
931 */
932 public function enable_disable_course_benefits($html){
933 $disable_option = !(bool) tutor_utils()->get_option('enable_course_benefits', true, true);
934 if($disable_option){
935 return '';
936 }
937 return $html;
938 }
939
940 /**
941 * Enable disable course requirements
942 *
943 * @since v.1.4.8
944 */
945 public function enable_disable_course_requirements($html){
946 $disable_option = !(bool) tutor_utils()->get_option('enable_course_requirements', true, true);
947 if($disable_option){
948 return '';
949 }
950 return $html;
951 }
952
953 /**
954 * Enable disable course target audience
955 *
956 * @since v.1.4.8
957 */
958 public function enable_disable_course_target_audience($html){
959 $disable_option = !(bool) tutor_utils()->get_option('enable_course_target_audience', true, true);
960 if($disable_option){
961 return '';
962 }
963 return $html;
964 }
965
966 /**
967 * Enable disable course nav items
968 *
969 * @since v.1.4.8
970 */
971 public function enable_disable_course_nav_items($items, $course_id){
972 global $wp_query, $post;
973 $enable_q_and_a_on_course = (bool) get_tutor_option('enable_q_and_a_on_course');
974 $disable_course_announcements = !(bool) tutor_utils()->get_option('enable_course_announcements', true, true);
975 $disable_qa_for_this_course = ($wp_query->is_single && !empty($post)) ? get_post_meta($post->ID, '_tutor_enable_qa', true)!='yes' : false;
976
977 // Whether Q&A enabled
978 if(!$enable_q_and_a_on_course || $disable_qa_for_this_course) {
979 if(tutor_utils()->array_get('questions', $items)) {
980 unset($items['questions']);
981 }
982 }
983
984 // Whether announcment enabled
985 if($disable_course_announcements){
986 if(tutor_utils()->array_get('announcements', $items)) {
987 unset($items['announcements']);
988 }
989 }
990
991 // Hide review section if disabled
992 if(!get_tutor_option('enable_course_review')) {
993 unset($items['reviews']);
994 }
995
996 // Whether enrolment require
997 $is_enrolled = tutor_utils()->is_enrolled();
998
999 return array_filter($items, function($item) use($is_enrolled) {
1000 if(isset($item['require_enrolment']) && $item['require_enrolment']) {
1001 return $is_enrolled;
1002 }
1003 return true;
1004 });
1005 }
1006
1007 /**
1008 * Filter product in shop page
1009 *
1010 * @since v.1.4.9
1011 */
1012 public function filter_product_in_shop_page() {
1013 $hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' );
1014 if ( ! $hide_course_from_shop_page ) {
1015 return;
1016 }
1017 add_action( 'woocommerce_product_query', array( $this, 'filter_woocommerce_product_query' ) );
1018 add_filter( 'edd_downloads_query', array( $this, 'filter_edd_downloads_query' ), 10, 2 );
1019 add_action( 'pre_get_posts', array( $this, 'filter_archive_meta_query' ), 1 );
1020 }
1021
1022 /**
1023 * Tutor product meta query
1024 *
1025 * @since v.1.4.9
1026 */
1027 public function tutor_product_meta_query() {
1028 $meta_query = array(
1029 'key' => '_tutor_product',
1030 'compare' => 'NOT EXISTS',
1031 );
1032 return $meta_query;
1033 }
1034
1035 /**
1036 * Filter product in woocommerce shop page
1037 *
1038 * @since v.1.4.9
1039 */
1040 public function filter_woocommerce_product_query( $wp_query ) {
1041 $wp_query->set( 'meta_query', array( $this->tutor_product_meta_query() ) );
1042 return $wp_query;
1043 }
1044
1045 /**
1046 * Filter product in edd downloads shortcode page
1047 *
1048 * @since v.1.4.9
1049 */
1050 public function filter_edd_downloads_query( $query ) {
1051 $query['meta_query'][] = $this->tutor_product_meta_query();
1052 return $query;
1053 }
1054
1055 /**
1056 * Filter product in edd downloads archive page
1057 *
1058 * @since v.1.4.9
1059 */
1060 public function filter_archive_meta_query( $wp_query ) {
1061 if ( ! is_admin() && $wp_query->is_archive && $wp_query->get( 'post_type' ) === 'download' ) {
1062 $wp_query->set( 'meta_query', array( $this->tutor_product_meta_query() ) );
1063 }
1064 return $wp_query;
1065 }
1066
1067 /**
1068 * @param $html
1069 * @return string
1070 *
1071 * Removed course price if already enrolled at single course
1072 *
1073 * @since v.1.5.8
1074 */
1075 public function remove_price_if_enrolled($html){
1076 $should_removed = apply_filters('should_remove_price_if_enrolled', true);
1077
1078 if ($should_removed){
1079 $course_id = get_the_ID();
1080 $enrolled = tutor_utils()->is_enrolled($course_id);
1081 if ($enrolled){
1082 $html = '';
1083 }
1084 }
1085 return $html;
1086 }
1087
1088 /**
1089 * @param $html
1090 * @return string
1091 *
1092 * Check if all lessons and quizzes done before mark course complete.
1093 */
1094 function tutor_lms_hide_course_complete_btn($html){
1095
1096 $completion_mode = tutor_utils()->get_option('course_completion_process');
1097 if ($completion_mode !== 'strict'){
1098 return $html;
1099 }
1100
1101 $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course();
1102 $lesson_count = tutor_utils()->get_lesson_count_by_course();
1103
1104 if ($completed_lesson < $lesson_count){
1105 return '<div class="tutor-alert tutor-warning tutor-mt-28">
1106 <div class="tutor-alert-text">
1107 <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span>
1108 <span>'.__('Complete all lessons to mark this course as complete', 'tutor').'</span>
1109 </div>
1110 </div>';
1111 }
1112
1113 $quizzes = array();
1114
1115 $course_contents = tutor_utils()->get_course_contents_by_id();
1116 if (tutor_utils()->count($course_contents)){
1117 foreach ($course_contents as $content){
1118 if ($content->post_type === 'tutor_quiz'){
1119 $quizzes[] = $content;
1120 }
1121 }
1122 }
1123
1124 $is_pass = true;
1125 $required_quiz_pass = 0;
1126
1127 if (tutor_utils()->count($quizzes)){
1128 foreach ($quizzes as $quiz){
1129
1130 $attempt = tutor_utils()->get_quiz_attempt($quiz->ID);
1131 if ($attempt) {
1132 $passing_grade = tutor_utils()->get_quiz_option($quiz->ID, 'passing_grade', 0);
1133 $earned_percentage = $attempt->earned_marks > 0 ? (number_format(($attempt->earned_marks * 100) / $attempt->total_marks)) : 0;
1134
1135 if ($earned_percentage < $passing_grade) {
1136 $required_quiz_pass++;
1137 $is_pass = false;
1138 }
1139 }else{
1140 $required_quiz_pass++;
1141 $is_pass = false;
1142 }
1143 }
1144 }
1145
1146 if ( ! $is_pass){
1147 return '<div class="tutor-alert tutor-warning tutor-mt-28">
1148 <div class="tutor-alert-text">
1149 <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span>
1150 <span>'.sprintf(__('You have to pass %s quizzes to complete this course.', 'tutor'), $required_quiz_pass).'</span>
1151 </div>
1152 </div>';
1153 }
1154
1155 return $html;
1156 }
1157
1158 public function get_generate_greadbook($html){
1159 if ( ! tutor_utils()->is_completed_course()){
1160 return '';
1161 }
1162 return $html;
1163 }
1164
1165 /**
1166 * Add social share content in header
1167 *
1168 * @since v.1.6.3
1169 */
1170 public function social_share_content() {
1171 global $wp_query, $post;
1172 if ( $wp_query->is_single && ! empty( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] === $this->course_post_type ) { ?>
1173 <!--Facebook-->
1174 <meta property="og:type" content="website"/>
1175 <meta property="og:image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>" />
1176 <meta property="og:description" content="<?php echo esc_html( $post->post_content ); ?>" />
1177 <!--Twitter-->
1178 <meta name="twitter:image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>">
1179 <meta name="twitter:description" content="<?php echo esc_html( $post->post_content ); ?>">
1180 <!--Google+-->
1181 <meta itemprop="image" content="<?php echo esc_url( get_tutor_course_thumbnail_src() ); ?>">
1182 <meta itemprop="description" content="<?php echo esc_html( $post->post_content ); ?>">
1183 <?php
1184 }
1185 }
1186
1187 /**
1188 * Get posts by type and parent
1189 *
1190 * @since v.1.6.6
1191 */
1192 public function tutor_get_post_ids( $post_type, $post_parent ) {
1193 $args = array(
1194 'fields' => 'ids',
1195 'post_type' => $post_type,
1196 'post_parent' => $post_parent,
1197 'post_status' => 'any',
1198 'posts_per_page' => -1,
1199 );
1200 return get_posts( $args );
1201 }
1202
1203 /**
1204 * Delete course data when permanently deleting a course.
1205 *
1206 * @since v.1.6.6
1207 */
1208 function delete_tutor_course_data( $post_id ) {
1209 $course_post_type = tutor()->course_post_type;
1210 $lesson_post_type = tutor()->lesson_post_type;
1211
1212 if ( get_post_type( $post_id ) == $course_post_type ) {
1213 global $wpdb;
1214 $topic_ids = $this->tutor_get_post_ids( 'topics', $post_id );
1215 if ( ! empty( $topic_ids ) ) {
1216 foreach ( $topic_ids as $topic_id ) {
1217 $content_post_type = apply_filters( 'tutor_course_contents_post_types', array( $lesson_post_type, 'tutor_quiz' ) );
1218 $topic_content_ids = $this->tutor_get_post_ids( $content_post_type, $topic_id );
1219
1220 foreach ( $topic_content_ids as $content_id ) {
1221 if ( get_post_type( $content_id ) == 'tutor_quiz' ) {
1222 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempts', array( 'quiz_id' => $content_id ) );
1223 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempt_answers', array( 'quiz_id' => $content_id ) );
1224
1225 $questions_ids = $wpdb->get_col( $wpdb->prepare( "SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = %d ", $content_id ) );
1226 if ( is_array( $questions_ids ) && count( $questions_ids ) ) {
1227 $in_question_ids = "'" . implode( "','", $questions_ids ) . "'";
1228 $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) " );
1229 }
1230 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'quiz_id' => $content_id ) );
1231 }
1232 wp_delete_post( $content_id, true );
1233 }
1234 wp_delete_post( $topic_id, true );
1235 }
1236 }
1237 $child_post_ids = $this->tutor_get_post_ids( array( 'tutor_announcements', 'tutor_enrolled' ), $post_id );
1238 if ( ! empty( $child_post_ids ) ) {
1239 foreach ( $child_post_ids as $child_post_id ) {
1240 wp_delete_post( $child_post_id, true );
1241 }
1242 }
1243 }
1244 }
1245
1246 /**
1247 * Delete associated enrolment
1248 *
1249 * @since v.1.8.2
1250 */
1251 public function delete_associated_enrollment( $post_id ) {
1252 global $wpdb;
1253
1254 $enroll_id = $wpdb->get_var(
1255 $wpdb->prepare(
1256 "SELECT
1257 post_id
1258 FROM
1259 {$wpdb->postmeta}
1260 WHERE
1261 meta_key='_tutor_enrolled_by_order_id'
1262 AND meta_value = %d
1263 ",
1264 $post_id
1265 )
1266 );
1267
1268 if ( is_numeric( $enroll_id ) && $enroll_id > 0 ) {
1269
1270 $course_id = get_post_field( 'post_parent', $enroll_id );
1271 $user_id = get_post_field( 'post_author', $enroll_id );
1272
1273 tutor_utils()->cancel_course_enrol($course_id, $user_id);
1274 }
1275 }
1276
1277 public function tutor_reset_course_progress() {
1278 tutor_utils()->checking_nonce();
1279 $course_id = tutor_utils()->array_get('course_id', $_POST);
1280
1281 if ( ! $course_id || ! is_numeric( $course_id ) || ! tutor_utils()->is_enrolled( $course_id ) ) {
1282 wp_send_json_error( array( 'message' => __( 'Invalid Course ID or Access Denied.', 'tutor' ) ) );
1283 return;
1284 }
1285
1286 tutor_utils()->delete_course_progress( $course_id );
1287 wp_send_json_success( array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) );
1288 }
1289
1290 /**
1291 * Do enroll if guest attempt to enroll and course is free
1292 *
1293 * @param $course_id
1294 *
1295 * @since 1.9.8
1296 */
1297 public function enroll_after_login_if_attempt( int $course_id, int $user_id ) {
1298 $course_id = sanitize_text_field( $course_id );
1299 if ( $course_id ) {
1300 $is_purchasable = tutor_utils()->is_course_purchasable( $course_id );
1301 if ( ! $is_purchasable ) {
1302 tutor_utils()->do_enroll( $course_id, $order_id = 0, $user_id );
1303 do_action( 'guest_attempt_after_enrollment', $course_id );
1304 }
1305 }
1306 }
1307 }
1308