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