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