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