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