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