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