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