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