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