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