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