From 179b8ea9ecaba747d78ec734a6d2d7a64435d188 Mon Sep 17 00:00:00 2001 From: shewa12 Date: Wed, 31 Jan 2024 15:26:19 +0600 Subject: [PATCH 01/53] Update: param added on the get_tutor_avatar method If echo is true then we it will esc and print otherwise return --- classes/Utils.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 1d0e530d32..464e9df195 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -3777,10 +3777,11 @@ public function str_split( $string ) { * * @param integer|object $user user id or object. * @param string $size size of avatar like sm, md, lg. + * @param bool $echo whether to echo or return. * * @return string */ - public function get_tutor_avatar( $user = null, $size = '' ) { + public function get_tutor_avatar( $user = null, $size = '', $echo = false ) { if ( ! $user ) { return ''; @@ -3815,7 +3816,11 @@ public function get_tutor_avatar( $user = null, $size = '' ) { $output .= ''; $output .= ''; - return apply_filters( 'tutor_text_avatar', $output ); + if ( $echo ) { + echo wp_kses( $output, $this->allowed_avatar_tags() ); + } else { + return apply_filters( 'tutor_text_avatar', $output ); + } } /** From e0ea089d20aa0c17f7bd6d358e17210a7e6f6881 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Thu, 29 Feb 2024 12:46:29 +0600 Subject: [PATCH 02/53] fix: frontend assignment deprication warning message --- templates/single/lesson/content.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single/lesson/content.php b/templates/single/lesson/content.php index 00e060c398..2cb74d9d36 100644 --- a/templates/single/lesson/content.php +++ b/templates/single/lesson/content.php @@ -88,7 +88,7 @@ Date: Fri, 1 Mar 2024 12:31:29 +0600 Subject: [PATCH 03/53] add/remove wishlist function updated, user is_student function updated --- classes/Ajax.php | 55 +++++++++++++++++++++++++++++++++++------------- classes/User.php | 14 +++++++----- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 6417ec7065..856ddf287b 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -121,7 +121,6 @@ public function sync_video_playback() { * @return void */ public function sync_video_playback_noprev() { - } /** @@ -276,7 +275,7 @@ public function delete_tutor_review() { * Add course in wishlist * * @since 1.0.0 - * @return void + * @return void|string */ public function tutor_course_add_to_wishlist() { tutor_utils()->checking_nonce(); @@ -290,10 +289,40 @@ public function tutor_course_add_to_wishlist() { ); } - global $wpdb; $user_id = get_current_user_id(); $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); + $result = $this->add_or_delete_wishlist( $user_id, $course_id ); + + if ( tutor_is_rest() ) { + return $result; + } elseif ( 'added' === $result ) { + wp_send_json_success( + array( + 'status' => 'added', + 'message' => __( 'Course added to wish list', 'tutor' ), + ) + ); + } else { + wp_send_json_success( + array( + 'status' => 'removed', + 'message' => __( 'Course removed from wish list', 'tutor' ), + ) + ); + } + } + + /** + * Add or Delete wishlist by user_id and course_id + * + * @param int $user_id the user id. + * @param int $course_id the course_id to add to the wishlist. + * @return string + */ + public function add_or_delete_wishlist( $user_id, $course_id ) { + global $wpdb; + $if_added_to_list = $wpdb->get_row( $wpdb->prepare( "SELECT * from {$wpdb->usermeta} @@ -305,6 +334,8 @@ public function tutor_course_add_to_wishlist() { ) ); + $result = ''; + if ( $if_added_to_list ) { $wpdb->delete( $wpdb->usermeta, @@ -314,21 +345,15 @@ public function tutor_course_add_to_wishlist() { 'meta_value' => $course_id, ) ); - wp_send_json_success( - array( - 'status' => 'removed', - 'message' => __( 'Course removed from wish list', 'tutor' ), - ) - ); + + $result = 'removed'; } else { add_user_meta( $user_id, '_tutor_course_wishlist', $course_id ); - wp_send_json_success( - array( - 'status' => 'added', - 'message' => __( 'Course added to wish list', 'tutor' ), - ) - ); + + $result = 'added'; } + + return $result; } /** diff --git a/classes/User.php b/classes/User.php index b5b0069420..48dd48efdd 100644 --- a/classes/User.php +++ b/classes/User.php @@ -96,13 +96,15 @@ public static function is( string $role ) { * Check user has any role. * * @since 2.2.0 + * @since 2.6.2 $user_id param added. * * @param array $roles roles. + * @param int $user_id user id. * * @return boolean */ - public static function has_any_role( array $roles ) { - $user = wp_get_current_user(); + public static function has_any_role( array $roles, $user_id = 0 ) { + $user = get_userdata( tutor_utils()->get_user_id( $user_id ) ); if ( empty( $user->roles ) || empty( $roles ) ) { return false; } @@ -121,11 +123,14 @@ public static function has_any_role( array $roles ) { * Check user is student. * * @since 2.2.0 + * @since 2.6.2 $user_id param added. + * + * @param int $user_id user id. * * @return boolean */ - public static function is_student() { - return current_user_can( self::STUDENT ); + public static function is_student( $user_id = 0 ) { + return self::has_any_role( array( self::STUDENT ), $user_id ); } /** @@ -391,5 +396,4 @@ public function show_registration_disabled() { public function update_user_last_login( $user_login, $user ) { update_user_meta( $user->ID, self::LAST_LOGIN_META, time() ); } - } From c63fd083c5ed9c32ea11f826659b5f7699a97fe7 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Fri, 1 Mar 2024 14:42:05 +0600 Subject: [PATCH 04/53] wpcs fix --- classes/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/User.php b/classes/User.php index 48dd48efdd..cdaa419545 100644 --- a/classes/User.php +++ b/classes/User.php @@ -99,7 +99,7 @@ public static function is( string $role ) { * @since 2.6.2 $user_id param added. * * @param array $roles roles. - * @param int $user_id user id. + * @param int $user_id user id. * * @return boolean */ From 696199a49592c2539d01d060d13119616f9a3371 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Fri, 1 Mar 2024 17:42:30 +0600 Subject: [PATCH 05/53] review add update function updated for api --- classes/Ajax.php | 49 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 6417ec7065..c681f3780c 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -121,7 +121,6 @@ public function sync_video_playback() { * @return void */ public function sync_video_playback_noprev() { - } /** @@ -133,21 +132,35 @@ public function sync_video_playback_noprev() { public function tutor_place_rating() { tutor_utils()->checking_nonce(); - global $wpdb; - - $moderation = tutor_utils()->get_option( 'enable_course_review_moderation', false, true, true ); - $rating = Input::post( 'tutor_rating_gen_input', 0, Input::TYPE_INT ); - $course_id = Input::post( 'course_id' ); - $review = Input::post( 'review', '', Input::TYPE_TEXTAREA ); + $user_id = get_current_user_id(); + $course_id = Input::post( 'course_id' ); + $rating = Input::post( 'tutor_rating_gen_input', 0, Input::TYPE_INT ); + $review = Input::post( 'review', '', Input::TYPE_TEXTAREA ); $rating <= 0 ? $rating = 1 : 0; $rating > 5 ? $rating = 5 : 0; - $user_id = get_current_user_id(); - $user = get_userdata( $user_id ); + $this->add_or_update_review( $user_id, $course_id, $rating, $review ); + } + + /** + * Add/Update rating + * + * @param int $user_id the user id. + * @param int $course_id the course id. + * @param int $rating rating star number. + * @param string $review review description. + * + * @return void|string + */ + public function add_or_update_review( $user_id, $course_id, $rating, $review ) { + global $wpdb; + + $moderation = tutor_utils()->get_option( 'enable_course_review_moderation', false, true, true ); + $user = get_userdata( $user_id ); $date = date( 'Y-m-d H:i:s', tutor_time() ); //phpcs:ignore - if ( ! tutor_utils()->has_enrolled_content_access( 'course', $course_id ) ) { + if ( ! tutor_is_rest() && ! tutor_utils()->has_enrolled_content_access( 'course', $course_id ) ) { wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) ); exit; } @@ -241,12 +254,16 @@ public function tutor_place_rating() { } } - wp_send_json_success( - array( - 'message' => __( 'Rating placed successsully!', 'tutor' ), - 'review_id' => $review_id, - ) - ); + if ( ! tutor_is_rest() ) { + wp_send_json_success( + array( + 'message' => __( 'Rating placed successsully!', 'tutor' ), + 'review_id' => $review_id, + ) + ); + } else { + return $previous_rating_id ? 'updated' : 'created'; + } } /** From 629198006c8f00270c3e61d7448449a733ef08eb Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Fri, 1 Mar 2024 18:06:32 +0600 Subject: [PATCH 06/53] delete review function updated for api --- classes/Ajax.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index c681f3780c..5874e6f2b3 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -150,7 +150,7 @@ public function tutor_place_rating() { * @param int $course_id the course id. * @param int $rating rating star number. * @param string $review review description. - * + * * @return void|string */ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { @@ -270,14 +270,18 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { * Delete a review * * @since 1.0.0 - * @return void + * @since 2.6.2 added params user_id. + * @param int $user_id the user id. + * @return void|bool */ - public function delete_tutor_review() { - tutor_utils()->checking_nonce(); + public function delete_tutor_review( $user_id = 0 ) { + if ( ! tutor_is_rest() ) { + tutor_utils()->checking_nonce(); + } $review_id = Input::post( 'review_id' ); - if ( ! tutor_utils()->can_user_manage( 'review', $review_id, get_current_user_id() ) ) { + if ( ! tutor_utils()->can_user_manage( 'review', $review_id, tutils()->get_user_id( $user_id ) ) ) { wp_send_json_error( array( 'message' => __( 'Permissioned Denied!', 'tutor' ) ) ); exit; } @@ -286,6 +290,10 @@ public function delete_tutor_review() { $wpdb->delete( $wpdb->commentmeta, array( 'comment_id' => $review_id ) ); $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $review_id ) ); + if ( tutor_is_rest() ) { + return true; + } + wp_send_json_success(); } From 025f095cb88c8c1bb2769d7702b9ee6f4f2adc86 Mon Sep 17 00:00:00 2001 From: shewa12 Date: Mon, 4 Mar 2024 12:16:48 +0600 Subject: [PATCH 07/53] Security Fix: SQL injection vulnerability fixed Type cast the query param as INT to avoid unwanted strings --- templates/dashboard/question-answer.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/dashboard/question-answer.php b/templates/dashboard/question-answer.php index fab9339442..e788a4b7fa 100644 --- a/templates/dashboard/question-answer.php +++ b/templates/dashboard/question-answer.php @@ -13,10 +13,10 @@ use TUTOR\Instructor; use TUTOR\Q_and_A; -if ( Input::has( 'question_id' ) ) { - $question_id = Input::get( 'question_id' ); - $question = tutor_utils()->get_qa_question( $question_id ); - $user_id = get_current_user_id(); +$question_id = Input::get( 'question_id', null, Input::TYPE_INT ); +if ( $question_id ) { + $question = tutor_utils()->get_qa_question( $question_id ); + $user_id = get_current_user_id(); if ( $question && ! Q_and_A::has_qna_access( $user_id, $question->comment_post_ID ) ) { tutor_utils()->tutor_empty_state( tutor_utils()->error_message() ); From 68262043aead5a15a66b37a700e4ba2e0f6bb93b Mon Sep 17 00:00:00 2001 From: shewa12 Date: Mon, 4 Mar 2024 12:40:35 +0600 Subject: [PATCH 08/53] Remove: Legacy code removed that was making vulnerability --- classes/Course.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/classes/Course.php b/classes/Course.php index 61b325ec23..bbae5fa161 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -54,7 +54,6 @@ public function __construct() { add_action( "manage_{$this->course_post_type}_posts_custom_column", array( $this, 'custom_lesson_column' ), 10, 2 ); add_action( 'wp_ajax_tutor_delete_topic', array( $this, 'tutor_delete_topic' ) ); - add_action( 'admin_action_tutor_delete_announcement', array( $this, 'tutor_delete_announcement' ) ); /** * Frontend Action @@ -773,21 +772,6 @@ public function tutor_delete_topic() { wp_send_json_success(); } - /** - * Delete announcement - * - * @since 1.0.0 - * @return void - */ - public function tutor_delete_announcement() { - tutor_utils()->checking_nonce( 'get' ); - - $announcement_id = Input::get( 'topic_id', 0, Input::TYPE_INT ); - - wp_delete_post( $announcement_id ); - wp_safe_redirect( wp_get_referer() ); - } - /** * Handle enroll now action * From 239253d32957f54558ac0c38bc59822157dd1d4d Mon Sep 17 00:00:00 2001 From: shewa12 Date: Mon, 4 Mar 2024 15:20:30 +0600 Subject: [PATCH 09/53] Fix: Tutor data is not erasing upon plugin uninstallation admin_action_{} hook removed to avoid unwanted data loss, uninstall hook registered --- classes/Admin.php | 87 ------------------------------------ classes/Tutor.php | 93 +++++++++++++++++++++++++++++++++++++++ tutor.php | 5 +++ views/pages/uninstall.php | 23 ---------- 4 files changed, 98 insertions(+), 110 deletions(-) delete mode 100644 views/pages/uninstall.php diff --git a/classes/Admin.php b/classes/Admin.php index 803f0bf976..95cde5742f 100644 --- a/classes/Admin.php +++ b/classes/Admin.php @@ -37,7 +37,6 @@ public function __construct() { add_action( 'admin_init', array( $this, 'filter_posts_for_instructors' ) ); add_action( 'load-post.php', array( $this, 'check_if_current_users_post' ) ); - add_action( 'admin_action_uninstall_tutor_and_erase', array( $this, 'erase_tutor_data' ) ); add_filter( 'plugin_action_links_' . plugin_basename( TUTOR_FILE ), array( $this, 'plugin_action_links' ) ); // Plugin Row Meta. @@ -456,92 +455,6 @@ public static function template_overridden_files() { return $override_files; } - /** - * Erase tutor data - * - * @since 1.0.0 - * @return void - */ - public function erase_tutor_data() { - global $wpdb; - - $is_erase_data = tutor_utils()->get_option( 'delete_on_uninstall' ); - // Deleting Data. - - $plugin_file = tutor()->basename; - if ( $is_erase_data && current_user_can( 'deactivate_plugin', $plugin_file ) ) { - /** - * Deleting Post Type, Meta Data, taxonomy - */ - $course_post_type = tutor()->course_post_type; - $lesson_post_type = tutor()->lesson_post_type; - - $post_types = array( - $course_post_type, - $lesson_post_type, - 'tutor_quiz', - 'tutor_enrolled', - 'topics', - 'tutor_enrolled', - 'tutor_announcements', - ); - - $post_type_strings = "'" . implode( "','", $post_types ) . "'"; - $tutor_posts = $wpdb->get_col( "SELECT ID from {$wpdb->posts} WHERE post_type in({$post_type_strings}) ;" ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - - if ( is_array( $tutor_posts ) && count( $tutor_posts ) ) { - foreach ( $tutor_posts as $post_id ) { - // Delete categories. - $terms = wp_get_object_terms( $post_id, 'course-category' ); - foreach ( $terms as $term ) { - wp_remove_object_terms( $post_id, array( $term->term_id ), 'course-category' ); - } - - // Delete tags if available. - $terms = wp_get_object_terms( $post_id, 'course-tag' ); - foreach ( $terms as $term ) { - wp_remove_object_terms( $post_id, array( $term->term_id ), 'course-tag' ); - } - - // Delete All Meta. - $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) ); - $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) ); - } - } - - /** - * Deleting Comments (reviews, questions, quiz_answers, etc) - */ - $tutor_comments = $wpdb->get_col( "SELECT comment_ID from {$wpdb->comments} WHERE comment_agent = 'comment_agent' ;" ); - $comments_ids_strings = "'" . implode( "','", $tutor_comments ) . "'"; - if ( is_array( $tutor_comments ) && count( $tutor_comments ) ) { - $wpdb->query( "DELETE from {$wpdb->commentmeta} WHERE comment_ID in({$comments_ids_strings}) " ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - } - $wpdb->delete( $wpdb->comments, array( 'comment_agent' => 'comment_agent' ) ); - - /** - * Delete Options - */ - - delete_option( 'tutor_option' ); - $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_is_tutor_student' ) ); - $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_tutor_instructor_approved' ) ); - $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_tutor_instructor_status' ) ); - $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_is_tutor_instructor' ) ); - $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '%_tutor_completed_lesson_id_%' " ); - - // Deleting Table. - $prefix = $wpdb->prefix; - //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $wpdb->query( "DROP TABLE IF EXISTS {$prefix}tutor_quiz_attempts, {$prefix}tutor_quiz_attempt_answers, {$prefix}tutor_quiz_questions, {$prefix}tutor_quiz_question_answers, {$prefix}tutor_earnings, {$prefix}tutor_withdraws " ); - - deactivate_plugins( $plugin_file ); - } - - wp_redirect( 'plugins.php' ); - die(); - } - /** * Plugin activation link * diff --git a/classes/Tutor.php b/classes/Tutor.php index ec742ba9d2..9308ae9d63 100644 --- a/classes/Tutor.php +++ b/classes/Tutor.php @@ -1109,4 +1109,97 @@ public function wp_doing_ajax( $bool ) { } return $bool; } + + /** + * Handle plugin un-installation + * + * @since 2.6.2 + * + * @return void + */ + public static function tutor_uninstall() { + self::erase_tutor_data(); + } + + /** + * Erase tutor data + * + * @since 2.6.2 + * + * @return void + */ + public function erase_tutor_data() { + global $wpdb; + + $is_erase_data = tutor_utils()->get_option( 'delete_on_uninstall' ); + // Deleting Data. + + if ( $is_erase_data ) { + /** + * Deleting Post Type, Meta Data, taxonomy + */ + $course_post_type = tutor()->course_post_type; + $lesson_post_type = tutor()->lesson_post_type; + + $post_types = array( + $course_post_type, + $lesson_post_type, + 'tutor_quiz', + 'tutor_enrolled', + 'topics', + 'tutor_enrolled', + 'tutor_announcements', + ); + + $post_type_strings = "'" . implode( "','", $post_types ) . "'"; + $tutor_posts = $wpdb->get_col( "SELECT ID from {$wpdb->posts} WHERE post_type in({$post_type_strings}) ;" ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + + if ( is_array( $tutor_posts ) && count( $tutor_posts ) ) { + foreach ( $tutor_posts as $post_id ) { + // Delete categories. + $terms = wp_get_object_terms( $post_id, 'course-category' ); + foreach ( $terms as $term ) { + wp_remove_object_terms( $post_id, array( $term->term_id ), 'course-category' ); + } + + // Delete tags if available. + $terms = wp_get_object_terms( $post_id, 'course-tag' ); + foreach ( $terms as $term ) { + wp_remove_object_terms( $post_id, array( $term->term_id ), 'course-tag' ); + } + + // Delete All Meta. + $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) ); + $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) ); + } + } + + /** + * Deleting Comments (reviews, questions, quiz_answers, etc) + */ + $tutor_comments = $wpdb->get_col( "SELECT comment_ID from {$wpdb->comments} WHERE comment_agent = 'comment_agent' ;" ); + $comments_ids_strings = "'" . implode( "','", $tutor_comments ) . "'"; + if ( is_array( $tutor_comments ) && count( $tutor_comments ) ) { + $wpdb->query( "DELETE from {$wpdb->commentmeta} WHERE comment_ID in({$comments_ids_strings}) " ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } + $wpdb->delete( $wpdb->comments, array( 'comment_agent' => 'comment_agent' ) ); + + /** + * Delete Options + */ + + delete_option( 'tutor_option' ); + $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_is_tutor_student' ) ); + $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_tutor_instructor_approved' ) ); + $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_tutor_instructor_status' ) ); + $wpdb->delete( $wpdb->usermeta, array( 'meta_key' => '_is_tutor_instructor' ) ); + $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '%_tutor_completed_lesson_id_%' " ); + + // Deleting Table. + $prefix = $wpdb->prefix; + //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query( "DROP TABLE IF EXISTS {$prefix}tutor_quiz_attempts, {$prefix}tutor_quiz_attempt_answers, {$prefix}tutor_quiz_questions, {$prefix}tutor_quiz_question_answers, {$prefix}tutor_earnings, {$prefix}tutor_withdraws " ); + + } + } } diff --git a/tutor.php b/tutor.php index d18722fd30..46de8d0c45 100644 --- a/tutor.php +++ b/tutor.php @@ -143,9 +143,14 @@ function tutils() { * Do some task during activation * * @since 1.5.2 + * + * @since 2.6.2 + * + * Uninstall hook registered */ register_activation_hook( TUTOR_FILE, array( '\TUTOR\Tutor', 'tutor_activate' ) ); register_deactivation_hook( TUTOR_FILE, array( '\TUTOR\Tutor', 'tutor_deactivation' ) ); +register_uninstall_hook( TUTOR_FILE, array( '\TUTOR\Tutor', 'tutor_uninstall' ) ); if ( ! function_exists( 'tutor_lms' ) ) { /** diff --git a/views/pages/uninstall.php b/views/pages/uninstall.php deleted file mode 100644 index b76ed4a52e..0000000000 --- a/views/pages/uninstall.php +++ /dev/null @@ -1,23 +0,0 @@ - - * @link https://themeum.com - * @since 1.0.0 - */ - -?> - -
-

-

- -
- basename; ?> - - -
-
From 22df186cd9e2e6fc9b2b5fef4bb531008dea4ef3 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Tue, 5 Mar 2024 10:49:23 +0600 Subject: [PATCH 10/53] necessary function added for qna api --- classes/Q_and_A.php | 124 ++++++++++++++++++++++++++++---------------- classes/Utils.php | 33 ++++++++++++ 2 files changed, 112 insertions(+), 45 deletions(-) diff --git a/classes/Q_and_A.php b/classes/Q_and_A.php index 037ee41625..7909e199f8 100644 --- a/classes/Q_and_A.php +++ b/classes/Q_and_A.php @@ -25,30 +25,34 @@ class Q_and_A { /** * Register hooks + * + * @param boolean $allow_hooks true/false to execute the hooks */ - public function __construct() { - add_action( 'wp_ajax_tutor_qna_create_update', array( $this, 'tutor_qna_create_update' ) ); - - /** - * Delete question - * - * @since v.1.6.4 - */ - add_action( 'wp_ajax_tutor_delete_dashboard_question', array( $this, 'tutor_delete_dashboard_question' ) ); - - /** - * Take action against single qna - * - * @since v2.0.0 - */ - add_action( 'wp_ajax_tutor_qna_single_action', array( $this, 'tutor_qna_single_action' ) ); - add_action( 'wp_ajax_tutor_qna_bulk_action', array( $this, 'process_bulk_action' ) ); - /** - * Q & A load more - * - * @since v2.0.6 - */ - add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' ); + public function __construct( $allow_hooks = true ) { + if ( $allow_hooks ) { + add_action( 'wp_ajax_tutor_qna_create_update', array( $this, 'tutor_qna_create_update' ) ); + + /** + * Delete question + * + * @since v.1.6.4 + */ + add_action( 'wp_ajax_tutor_delete_dashboard_question', array( $this, 'tutor_delete_dashboard_question' ) ); + + /** + * Take action against single qna + * + * @since v2.0.0 + */ + add_action( 'wp_ajax_tutor_qna_single_action', array( $this, 'tutor_qna_single_action' ) ); + add_action( 'wp_ajax_tutor_qna_bulk_action', array( $this, 'process_bulk_action' ) ); + /** + * Q & A load more + * + * @since v2.0.6 + */ + add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' ); + } } /** @@ -67,7 +71,7 @@ public static function has_qna_access( $user_id, $course_id ) { $has_access = $is_public_course || User::is_admin() || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id ) - || tutor_utils()->is_enrolled( $course_id ); + || tutor_utils()->is_enrolled( $course_id, $user_id ); return $has_access; } @@ -88,7 +92,6 @@ public function tutor_qna_create_update() { wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) ); } - global $wpdb; $qna_text = Input::post( 'answer', '', tutor()->has_pro ? Input::TYPE_KSES_POST : Input::TYPE_TEXTAREA ); if ( ! $qna_text ) { @@ -105,6 +108,49 @@ public function tutor_qna_create_update() { $user = get_userdata( $user_id ); $date = gmdate( 'Y-m-d H:i:s', tutor_time() ); + $qna_object = new \stdClass(); + $qna_object->user_id = $user_id; + $qna_object->course_id = $course_id; + $qna_object->question_id = $question_id; + $qna_object->qna_text = $qna_text; + $qna_object->user = $user; + $qna_object->date = $date; + + $this->inset_qna( $qna_object ); + + // Provide the html now. + // phpcs:disable WordPress.Security.NonceVerification.Missing + ob_start(); + tutor_load_template_from_custom_path( + tutor()->path . '/views/qna/qna-single.php', + array( + 'question_id' => $question_id, + 'back_url' => isset( $_POST['back_url'] ) ? esc_url_raw( wp_unslash( $_POST['back_url'] ) ) : '', + 'context' => $context, + ) + ); + wp_send_json_success( + array( + 'html' => ob_get_clean(), + 'editor_id' => 'tutor_qna_reply_editor_' . $question_id, + ) + ); + } + + /** + * Function to insert Q&A + * + * @param object $qna_object + * @return void|bool + */ + public function inset_qna( $qna_object ) { + $course_id = $qna_object->course_id; + $question_id = $qna_object->question_id; + $qna_text = $qna_object->qna_text; + $user_id = $qna_object->user_id; + $user = $qna_object->user; + $date = $qna_object->date; + // Insert data prepare. $data = apply_filters( 'tutor_qna_insert_data', @@ -122,6 +168,8 @@ public function tutor_qna_create_update() { ) ); + global $wpdb; + // Insert new question/answer. $wpdb->insert( $wpdb->comments, $data ); ! $question_id ? $question_id = (int) $wpdb->insert_id : 0; @@ -140,23 +188,9 @@ public function tutor_qna_create_update() { do_action( 'tutor_after_answer_to_question', $answer_id ); } - // Provide the html now. - // phpcs:disable WordPress.Security.NonceVerification.Missing - ob_start(); - tutor_load_template_from_custom_path( - tutor()->path . '/views/qna/qna-single.php', - array( - 'question_id' => $question_id, - 'back_url' => isset( $_POST['back_url'] ) ? esc_url_raw( wp_unslash( $_POST['back_url'] ) ) : '', - 'context' => $context, - ) - ); - wp_send_json_success( - array( - 'html' => ob_get_clean(), - 'editor_id' => 'tutor_qna_reply_editor_' . $question_id, - ) - ); + if ( tutor_is_rest() ) { + return ! empty( $question_id ) ? true : false; + } } /** @@ -184,7 +218,7 @@ public function tutor_delete_dashboard_question() { * * @return void */ - private function delete_qna_permanently( $question_ids ) { + public function delete_qna_permanently( $question_ids ) { if ( is_array( $question_ids ) && count( $question_ids ) ) { global $wpdb; // Prepare in clause. @@ -245,7 +279,7 @@ public function process_bulk_action() { $qa_ids = explode( ',', $qa_ids ); $qa_ids = array_filter( $qa_ids, - function( $id ) use ( $user_id ) { + function ( $id ) use ( $user_id ) { return is_numeric( $id ) && tutor_utils()->can_user_manage( 'qa_question', $id, $user_id ); } ); @@ -335,7 +369,7 @@ public static function tabs_key_value( $asker_id = null ) { // Assign value, url etc to the tab array. $tabs = array_map( - function( $tab ) use ( $stats ) { + function ( $tab ) use ( $stats ) { return array( 'key' => $tab, 'title' => tutor_utils()->translate_dynamic_text( $tab ), diff --git a/classes/Utils.php b/classes/Utils.php index 1d0e530d32..22bfda7891 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -4703,6 +4703,39 @@ public function get_qa_answer_by_answer_id( $answer_id ) { return false; } + /** + * Funcion to check if a user can delete qa by id + * + * @param int $user_id + * @param int $question_id + * @return boolean + */ + public function can_delete_qa( $user_id, $question_id ) { + global $wpdb; + + $is_admin = $this->has_user_role( 'administrator', $user_id); + + if ($is_admin) { + return true; + } + + $result = $wpdb->get_row( + $wpdb->prepare( + "SELECT * + FROM {$wpdb->comments} qa + WHERE qa.comment_ID = %d + ", + $question_id + ) + ); + + if ( $result && (int) $result->user_id === $user_id) { + return true; + } + + return false; + } + /** * Get total number of un-answered question. * From dda4acfc85260fe3eac49a9c91991c8795dcab18 Mon Sep 17 00:00:00 2001 From: shewa12 Date: Tue, 5 Mar 2024 12:18:38 +0600 Subject: [PATCH 11/53] Meta Info: Plugin version and change log updated --- readme.txt | 9 ++++++++- tutor.php | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index 6627128357..4f7b956ca6 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: lms, course, elearning, education, learning management system Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 7.4 -Stable tag: 2.6.1 +Stable tag: 2.6.2 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -316,6 +316,13 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == += 2.6.2 - March 7, 2024 + +New: Introduced Assignment submission APIs to enable students to submit assignments (Pro) +New: Introduced Course Wishlist APIs to allow students to add courses to their wishlists (Pro) +New: Introduced Course Reviews APIs to enable students to review and rate courses (Pro) +Fix: Enhanced security measures to mitigate SQL injection and prevent unwanted data loss + = 2.6.1 - February 19, 2024 New: Added API functionality for submitting and retrieving list of quizzes (Pro) diff --git a/tutor.php b/tutor.php index 46de8d0c45..e8cfbca53c 100644 --- a/tutor.php +++ b/tutor.php @@ -4,7 +4,7 @@ * Plugin URI: https://www.themeum.com/product/tutor-lms/ * Description: Tutor is a complete solution for creating a Learning Management System in WordPress way. It can help you to create small to large scale online education site very conveniently. Power features like report, certificate, course preview, private file sharing make Tutor a robust plugin for any educational institutes. * Author: Themeum - * Version: 2.6.1 + * Version: 2.6.2 * Author URI: https://themeum.com * Requires PHP: 7.4 * Requires at least: 5.3 @@ -24,7 +24,7 @@ /** * Defined the tutor main file */ -define( 'TUTOR_VERSION', '2.6.1' ); +define( 'TUTOR_VERSION', '2.6.2' ); define( 'TUTOR_FILE', __FILE__ ); /** From 7af554ff7666a577475eac2155aa41cbcc184ea9 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Tue, 5 Mar 2024 14:43:58 +0600 Subject: [PATCH 12/53] is_added_to_wishlist utils added, functionality updated for wishlist --- classes/Ajax.php | 87 +++++++++++++++++++++++------------------------ classes/Utils.php | 27 +++++++++++++++ 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 856ddf287b..6655f3ec8d 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -28,42 +28,47 @@ class Ajax { * Constructor * * @since 1.0.0 + * @since 2.6.2 added allow_hooks param. + * + * @param bool $allow_hooks default value true. + * * @return void */ - public function __construct() { - - add_action( 'wp_ajax_sync_video_playback', array( $this, 'sync_video_playback' ) ); - add_action( 'wp_ajax_nopriv_sync_video_playback', array( $this, 'sync_video_playback_noprev' ) ); - add_action( 'wp_ajax_tutor_place_rating', array( $this, 'tutor_place_rating' ) ); - add_action( 'wp_ajax_delete_tutor_review', array( $this, 'delete_tutor_review' ) ); - - add_action( 'wp_ajax_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) ); - add_action( 'wp_ajax_nopriv_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) ); - - /** - * Get all addons - */ - add_action( 'wp_ajax_tutor_get_all_addons', array( $this, 'tutor_get_all_addons' ) ); - - /** - * Addon Enable Disable Control - */ - add_action( 'wp_ajax_addon_enable_disable', array( $this, 'addon_enable_disable' ) ); - - /** - * Ajax login - * - * @since v.1.6.3 - */ - add_action( 'tutor_action_tutor_user_login', array( $this, 'process_tutor_login' ) ); - - /** - * Announcement - * - * @since v.1.7.9 - */ - add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_annoucement' ) ); - add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_annoucement' ) ); + public function __construct( $allow_hooks = true ) { + if ( $allow_hooks ) { + add_action( 'wp_ajax_sync_video_playback', array( $this, 'sync_video_playback' ) ); + add_action( 'wp_ajax_nopriv_sync_video_playback', array( $this, 'sync_video_playback_noprev' ) ); + add_action( 'wp_ajax_tutor_place_rating', array( $this, 'tutor_place_rating' ) ); + add_action( 'wp_ajax_delete_tutor_review', array( $this, 'delete_tutor_review' ) ); + + add_action( 'wp_ajax_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) ); + add_action( 'wp_ajax_nopriv_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) ); + + /** + * Get all addons + */ + add_action( 'wp_ajax_tutor_get_all_addons', array( $this, 'tutor_get_all_addons' ) ); + + /** + * Addon Enable Disable Control + */ + add_action( 'wp_ajax_addon_enable_disable', array( $this, 'addon_enable_disable' ) ); + + /** + * Ajax login + * + * @since v.1.6.3 + */ + add_action( 'tutor_action_tutor_user_login', array( $this, 'process_tutor_login' ) ); + + /** + * Announcement + * + * @since v.1.7.9 + */ + add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_annoucement' ) ); + add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_annoucement' ) ); + } } @@ -316,23 +321,17 @@ public function tutor_course_add_to_wishlist() { /** * Add or Delete wishlist by user_id and course_id * + * @since 2.6.2 + * * @param int $user_id the user id. * @param int $course_id the course_id to add to the wishlist. + * * @return string */ public function add_or_delete_wishlist( $user_id, $course_id ) { global $wpdb; - $if_added_to_list = $wpdb->get_row( - $wpdb->prepare( - "SELECT * from {$wpdb->usermeta} - WHERE user_id = %d - AND meta_key = '_tutor_course_wishlist' - AND meta_value = %d;", - $user_id, - $course_id - ) - ); + $if_added_to_list = tutor_utils()->is_added_to_wishlist( $user_id, $course_id ); $result = ''; diff --git a/classes/Utils.php b/classes/Utils.php index 464e9df195..96c4db30de 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -5451,6 +5451,33 @@ public function get_wishlist( $user_id = 0, int $offset = 0, int $limit = PHP_IN return $pageposts; } + /** + * Function to check if already added to wishlist + * + * @since 2.6.2 + * + * @param int $user_id user id. + * @param int $course_id course id. + * + * @return boolean + */ + public function is_added_to_wishlist( $user_id, $course_id ) { + global $wpdb; + + $if_added_to_list = $wpdb->get_row( + $wpdb->prepare( + "SELECT * from {$wpdb->usermeta} + WHERE user_id = %d + AND meta_key = '_tutor_course_wishlist' + AND meta_value = %d;", + $user_id, + $course_id + ) + ); + + return $if_added_to_list ? true : false; + } + /** * Getting popular courses * From de92f65da04cdf471e6c40bb9ef55d473dcb08ee Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Tue, 5 Mar 2024 17:16:47 +0600 Subject: [PATCH 13/53] review crud api functionality updated --- classes/Ajax.php | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 5874e6f2b3..ea462d8617 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -150,10 +150,11 @@ public function tutor_place_rating() { * @param int $course_id the course id. * @param int $rating rating star number. * @param string $review review description. + * @param int $review_id review id needed for api update. * * @return void|string */ - public function add_or_update_review( $user_id, $course_id, $rating, $review ) { + public function add_or_update_review( $user_id, $course_id, $rating, $review, $review_id = 0 ) { global $wpdb; $moderation = tutor_utils()->get_option( 'enable_course_review_moderation', false, true, true ); @@ -167,21 +168,24 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { do_action( 'tutor_before_rating_placed' ); - $previous_rating_id = $wpdb->get_var( - $wpdb->prepare( - "SELECT comment_ID - from {$wpdb->comments} - WHERE comment_post_ID = %d AND - user_id = %d AND - comment_type = 'tutor_course_rating' - LIMIT 1;", - $course_id, - $user_id - ) - ); + if ( empty( $review_id ) ) { + $previous_rating_id = $wpdb->get_var( + $wpdb->prepare( + "SELECT comment_ID + from {$wpdb->comments} + WHERE comment_post_ID = %d AND + user_id = %d AND + comment_type = 'tutor_course_rating' + LIMIT 1;", + $course_id, + $user_id + ) + ); + + $review_id = $previous_rating_id; + } - $review_id = $previous_rating_id; - if ( $previous_rating_id ) { + if ( $review_id ) { $wpdb->update( $wpdb->comments, array( @@ -190,7 +194,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { 'comment_date' => $date, 'comment_date_gmt' => get_gmt_from_date( $date ), ), - array( 'comment_ID' => $previous_rating_id ) + array( 'comment_ID' => $review_id ) ); $rating_info = $wpdb->get_row( @@ -198,7 +202,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { "SELECT * FROM {$wpdb->commentmeta} WHERE comment_id = %d AND meta_key = 'tutor_rating'; ", - $previous_rating_id + $review_id ) ); @@ -207,7 +211,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { $wpdb->commentmeta, array( 'meta_value' => $rating ), array( - 'comment_id' => $previous_rating_id, + 'comment_id' => $review_id, 'meta_key' => 'tutor_rating', ) ); @@ -215,7 +219,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { $wpdb->insert( $wpdb->commentmeta, array( - 'comment_id' => $previous_rating_id, + 'comment_id' => $review_id, 'meta_key' => 'tutor_rating', 'meta_value' => $rating, ) @@ -262,7 +266,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review ) { ) ); } else { - return $previous_rating_id ? 'updated' : 'created'; + return $review_id ? 'updated' : 'created'; } } From a00768dc47c1b92f0580c71b2d6058b1c0f28447 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Tue, 5 Mar 2024 18:04:16 +0600 Subject: [PATCH 14/53] qna read unread functionality updated --- classes/Q_and_A.php | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/classes/Q_and_A.php b/classes/Q_and_A.php index 7909e199f8..de17ac75ab 100644 --- a/classes/Q_and_A.php +++ b/classes/Q_and_A.php @@ -327,12 +327,33 @@ public function tutor_qna_single_action() { } // Get who asked the question. - $context = Input::post( 'context', '' ); - $asker_prefix = 'frontend-dashboard-qna-table-student' === $context ? '_' . get_current_user_id() : ''; + $context = Input::post( 'context', '' ); + $user_id = get_current_user_id(); // Get the existing value from meta. $action = Input::post( 'qna_action', '' ); + $new_value = $this->trigger_qna_action( $question_id, $action, $context, $user_id ); + + // Transfer the new status. + wp_send_json_success( array( 'new_value' => $new_value ) ); + } + + /** + * Function to update Q&A action + * + * @since 2.6.2 + * + * @param int $question_id question id. + * @param string $action action name. + * @param string $context context name. + * @param int $user_id user id. + * + * @return int + */ + public function trigger_qna_action( $question_id, $action, $context, $user_id ) { + $asker_prefix = 'frontend-dashboard-qna-table-student' === $context ? '_' . $user_id : ''; + // If current user asker, then make it unread for self. // If it is instructor, then make unread for instructor side. $meta_key = 'tutor_qna_' . $action . $asker_prefix; @@ -344,8 +365,7 @@ public function tutor_qna_single_action() { // Update the reverted value. update_comment_meta( $question_id, $meta_key, $new_value ); - // Transfer the new status. - wp_send_json_success( array( 'new_value' => $new_value ) ); + return $new_value; } /** From b691445ec61811436903f4221add8bb89f044675 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Wed, 6 Mar 2024 11:58:08 +0600 Subject: [PATCH 15/53] phpcs fix --- classes/Q_and_A.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/classes/Q_and_A.php b/classes/Q_and_A.php index de17ac75ab..42116d0d2d 100644 --- a/classes/Q_and_A.php +++ b/classes/Q_and_A.php @@ -21,12 +21,12 @@ * * @since 1.0.0 */ -class Q_and_A { +class Q_And_A { /** * Register hooks * - * @param boolean $allow_hooks true/false to execute the hooks + * @param boolean $allow_hooks true/false to execute the hooks. */ public function __construct( $allow_hooks = true ) { if ( $allow_hooks ) { @@ -140,7 +140,7 @@ public function tutor_qna_create_update() { /** * Function to insert Q&A * - * @param object $qna_object + * @param object $qna_object the object to insert. * @return void|bool */ public function inset_qna( $qna_object ) { @@ -229,9 +229,10 @@ public function delete_qna_permanently( $question_ids ) { $wpdb->prepare( "DELETE FROM {$wpdb->comments} - WHERE {$wpdb->comments}.comment_ID IN($question_ids) + WHERE {$wpdb->comments}.comment_ID IN(%s) AND 1 = %d ", + $question_ids, 1 ) ); @@ -240,9 +241,10 @@ public function delete_qna_permanently( $question_ids ) { $wpdb->prepare( "DELETE FROM {$wpdb->comments} - WHERE {$wpdb->comments}.comment_parent IN($question_ids) + WHERE {$wpdb->comments}.comment_parent IN(%s) AND 1 = %d ", + $question_ids, 1 ) ); @@ -251,9 +253,10 @@ public function delete_qna_permanently( $question_ids ) { $wpdb->prepare( "DELETE FROM {$wpdb->commentmeta} - WHERE {$wpdb->commentmeta}.comment_id IN($question_ids) + WHERE {$wpdb->commentmeta}.comment_id IN(%s) AND 1 = %d ", + $question_ids, 1 ) ); From 72719a4b411bcdca16c18703d0461b88eca0e0c0 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Wed, 6 Mar 2024 12:17:48 +0600 Subject: [PATCH 16/53] is_added_to_wishlist funcition removed for duplication --- classes/Ajax.php | 2 +- classes/Utils.php | 90 +++++++++++++++-------------------------------- 2 files changed, 29 insertions(+), 63 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 6655f3ec8d..17c8c1c8b7 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -331,7 +331,7 @@ public function tutor_course_add_to_wishlist() { public function add_or_delete_wishlist( $user_id, $course_id ) { global $wpdb; - $if_added_to_list = tutor_utils()->is_added_to_wishlist( $user_id, $course_id ); + $if_added_to_list = tutor_utils()->is_wishlisted( $course_id, $user_id ); $result = ''; diff --git a/classes/Utils.php b/classes/Utils.php index 96c4db30de..a9b71ac52b 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -902,7 +902,7 @@ public function get_course_completed_percent( $course_id = 0, $user_id = 0, $get */ $is_completed = apply_filters( 'tutor_is_zoom_lesson_done', false, $content->ID, $user_id ); if ( $is_completed ) { - $completed_count++; + ++$completed_count; } } elseif ( 'tutor-google-meet' === $content->post_type ) { /** @@ -912,7 +912,7 @@ public function get_course_completed_percent( $course_id = 0, $user_id = 0, $get */ $is_completed = apply_filters( 'tutor_google_meet_lesson_done', false, $content->ID, $user_id ); if ( $is_completed ) { - $completed_count++; + ++$completed_count; } } } @@ -1127,7 +1127,6 @@ public function get_course_price( $course_id = 0 ) { } } return apply_filters( 'get_tutor_course_price', $price, $course_id ); - } /** @@ -3313,7 +3312,7 @@ public function get_instructors_by_course( $course_id = 0 ) { // Exclude instructor if already in main instructor. $instructors = array_filter( $instructors, - function( $instructor ) use ( $main_instructor ) { + function ( $instructor ) use ( $main_instructor ) { if ( $instructor->ID !== $main_instructor[0]->ID ) { return true; } @@ -3645,12 +3644,10 @@ public function star_rating_generator( $current_rating = 0.00, $echo = true ) { for ( $i = 1; $i <= 5; $i++ ) { if ( (int) $current_rating >= $i ) { $output .= ''; - } else { - if ( ( $current_rating - $i ) >= -0.5 ) { + } elseif ( ( $current_rating - $i ) >= -0.5 ) { $output .= ''; - } else { - $output .= ''; - } + } else { + $output .= ''; } } @@ -3724,12 +3721,10 @@ public function star_rating_generator_course( $current_rating = 0.00, $echo = tr for ( $i = 1; $i <= 5; $i++ ) { if ( (int) $current_rating >= $i ) { $output .= ''; - } else { - if ( ( $current_rating - $i ) >= -0.5 ) { + } elseif ( ( $current_rating - $i ) >= -0.5 ) { $output .= ''; - } else { - $output .= ''; - } + } else { + $output .= ''; } } @@ -5338,7 +5333,7 @@ public function student_register_url() { * @return bool|false|string */ public function instructor_register_url() { - $instructor_register_page = (int) $this->get_option( 'instructor_register_page' ); + $instructor_register_page = (int) $this->get_option( 'instructor_register_page' ); if ( $instructor_register_page ) { return apply_filters( 'tutor_instructor_register_url', get_the_permalink( $instructor_register_page ) ); @@ -5388,6 +5383,7 @@ public function dashboard_page_id() { public function is_wishlisted( $course_id = 0, $user_id = 0 ) { $course_id = $this->get_post_id( $course_id ); $user_id = $this->get_user_id( $user_id ); + if ( ! $user_id ) { return false; } @@ -5451,33 +5447,6 @@ public function get_wishlist( $user_id = 0, int $offset = 0, int $limit = PHP_IN return $pageposts; } - /** - * Function to check if already added to wishlist - * - * @since 2.6.2 - * - * @param int $user_id user id. - * @param int $course_id course id. - * - * @return boolean - */ - public function is_added_to_wishlist( $user_id, $course_id ) { - global $wpdb; - - $if_added_to_list = $wpdb->get_row( - $wpdb->prepare( - "SELECT * from {$wpdb->usermeta} - WHERE user_id = %d - AND meta_key = '_tutor_course_wishlist' - AND meta_value = %d;", - $user_id, - $course_id - ) - ); - - return $if_added_to_list ? true : false; - } - /** * Getting popular courses * @@ -6606,7 +6575,7 @@ public function referer() { * @since 2.5.0 * * @param boolean $url_decode URL decode for unicode support. - * + * * @return void|string */ public function referer_field( $url_decode = true ) { @@ -6614,8 +6583,8 @@ public function referer_field( $url_decode = true ) { if ( $url_decode ) { $url = urldecode( $url ); } - - echo ''; + + echo ''; } /** @@ -6781,7 +6750,7 @@ public function get_assignments_by_course( $course_id = 0 ) { * @return bool */ public function is_script_debug() { - return ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ); + return ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ); } /** @@ -6989,7 +6958,6 @@ public function get_current_url( $post_id = 0 ) { * * @return object */ - public function get_rating_by_id( $rating_id = 0 ) { global $wpdb; @@ -7288,7 +7256,7 @@ public function get_course_prev_next_contents_by_id( $content_id = 0 ) { $next_id = $ids[ $next_i ]; } } - $i++; + ++$i; } } @@ -8051,7 +8019,7 @@ public function get_earning_chart_yearly( $user_id, $year ) { * @return object */ function get_package_object() { - $params = func_get_args(); + $params = func_get_args(); $is_pro = $params[0]; $class = $params[1]; @@ -8983,26 +8951,26 @@ public function count_completed_contents_by_topic( int $topic_id ): array { case $lesson_post_type: $is_lesson_completed = $this->is_completed_lesson( $content->ID, $user_id ); if ( $is_lesson_completed ) { - $completed++; + ++$completed; } break; case $quiz_post_type: $has_attempt = $this->has_attempted_quiz( $user_id, $content->ID ); if ( $has_attempt ) { - $completed++; + ++$completed; } break; case $assignment_post_type: $is_assignment_completed = $this->is_assignment_submitted( $content->ID, $user_id ); if ( $is_assignment_completed ) { - $completed++; + ++$completed; } break; case $zoom_lesson_post_type: if ( \class_exists( '\TUTOR_ZOOM\Zoom' ) ) { $is_zoom_lesson_completed = \TUTOR_ZOOM\Zoom::is_zoom_lesson_done( '', $content->ID, $user_id ); if ( $is_zoom_lesson_completed ) { - $completed++; + ++$completed; } } break; @@ -9011,7 +8979,7 @@ public function count_completed_contents_by_topic( int $topic_id ): array { if ( \TutorPro\GoogleMeet\Validator\Validator::is_addon_enabled() ) { $is_completed = \TutorPro\GoogleMeet\Frontend\Frontend::is_lesson_completed( false, $content->ID, $user_id ); if ( $is_completed ) { - $completed++; + ++$completed; } } } @@ -9355,7 +9323,6 @@ public function can_user_retake_course() { * * @return string */ - public function clean_html_content( $content = '', $allowed = array() ) { $default = array( @@ -9591,7 +9558,7 @@ private function assign_child_count( array $course_meta, $post_type ) { ); foreach ( $results as $result ) { - $course_meta[ $result->course_id ][ $post_type ]++; + ++$course_meta[ $result->course_id ][ $post_type ]; } return $course_meta; @@ -9610,7 +9577,7 @@ public function get_course_meta_data( $course_id ) { // Prepare course IDs to get quiz count based on. $course_ids = is_array( $course_id ) ? $course_id : array( $course_id ); $course_ids = array_map( - function( $id ) { + function ( $id ) { return (int) $id; }, $course_ids @@ -9656,8 +9623,8 @@ function( $id ) { if ( $result->content_id ) { $course_meta[ $result->course_id ][ $result->content_type ][] = $result->content_id; } - } catch (\Throwable $th) { - tutor_log( 'Affected course ID : ' . $result->course_id . ' Error : '. $th->getMessage() ); + } catch ( \Throwable $th ) { + tutor_log( 'Affected course ID : ' . $result->course_id . ' Error : ' . $th->getMessage() ); } } @@ -9817,7 +9784,7 @@ public function get_current_page_slug() { * * @return array allowed tags */ - public function allowed_avatar_tags( array $tags = array() ):array { + public function allowed_avatar_tags( array $tags = array() ): array { $defaults = array( 'a' => array( 'href' => true, @@ -9853,7 +9820,7 @@ public function allowed_avatar_tags( array $tags = array() ):array { * * @return array allowed tags */ - public function allowed_icon_tags( array $tags = array() ):array { + public function allowed_icon_tags( array $tags = array() ): array { $defaults = array( 'span' => array( 'class' => true, @@ -9942,5 +9909,4 @@ public function get_remote_plugin_info( $plugin_slug = 'tutor' ) { return (object) json_decode( $response['body'], true ); } - } From e8ada8fe7df68926cdbb3c817abaa6aa5623ede3 Mon Sep 17 00:00:00 2001 From: shewa12 Date: Wed, 6 Mar 2024 12:46:57 +0600 Subject: [PATCH 17/53] Fix: Non static method called statically --- classes/Tutor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Tutor.php b/classes/Tutor.php index 9308ae9d63..05be6341bf 100644 --- a/classes/Tutor.php +++ b/classes/Tutor.php @@ -1128,7 +1128,7 @@ public static function tutor_uninstall() { * * @return void */ - public function erase_tutor_data() { + public static function erase_tutor_data() { global $wpdb; $is_erase_data = tutor_utils()->get_option( 'delete_on_uninstall' ); From 57c98c7ce7c581e1fcaf26876a4ad9fd1b2b20bc Mon Sep 17 00:00:00 2001 From: shewa12 Date: Wed, 6 Mar 2024 12:50:33 +0600 Subject: [PATCH 18/53] Whats New: Isset check added before using array index to avoid undefined error --- views/pages/whats-new.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/views/pages/whats-new.php b/views/pages/whats-new.php index 271f09a840..2d772ab138 100644 --- a/views/pages/whats-new.php +++ b/views/pages/whats-new.php @@ -90,16 +90,22 @@

Changelog (v)

From b5b3ea0be1547ac49668867180dd318f8cc0d734 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Wed, 6 Mar 2024 16:49:57 +0600 Subject: [PATCH 19/53] review get api fix --- restapi/REST_Rating.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/restapi/REST_Rating.php b/restapi/REST_Rating.php index 1aae3a438b..35d5ab086d 100644 --- a/restapi/REST_Rating.php +++ b/restapi/REST_Rating.php @@ -59,11 +59,17 @@ class REST_Rating { * @return mixed */ public function course_rating( WP_REST_Request $request ) { - $this->post_id = $request->get_param( 'id' ); + $this->post_id = (int) $request->get_param( 'id' ); + $offset = (int) sanitize_text_field( $request->get_param( 'offset' ) ); + $limit = (int) sanitize_text_field( $request->get_param( 'limit' ) ); - $ratings = tutor_utils()->get_course_rating( $this->post_id ); + $offset = !empty( $offset ) ? $offset : 0; + $limit = !empty( $limit ) ? $limit : 10; - if ( count( $ratings ) > 0 ) { + $ratings = tutor_utils()->get_course_rating( $this->post_id ); + $ratings->reviews = tutor_utils()->get_course_reviews( $this->post_id, $offset, $limit, false, array( 'approved' ) ); + + if ( ! empty( $ratings ) ) { $response = array( 'status_code' => 'success', 'message' => __( 'Course rating retrieved successfully', 'tutor' ), From e3cd91e7184b04c3082e9c63a6ae7fe7dcf9212b Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Wed, 6 Mar 2024 16:53:47 +0600 Subject: [PATCH 20/53] phpcs fix --- restapi/REST_Rating.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/restapi/REST_Rating.php b/restapi/REST_Rating.php index 35d5ab086d..158aee17b0 100644 --- a/restapi/REST_Rating.php +++ b/restapi/REST_Rating.php @@ -63,8 +63,8 @@ public function course_rating( WP_REST_Request $request ) { $offset = (int) sanitize_text_field( $request->get_param( 'offset' ) ); $limit = (int) sanitize_text_field( $request->get_param( 'limit' ) ); - $offset = !empty( $offset ) ? $offset : 0; - $limit = !empty( $limit ) ? $limit : 10; + $offset = ! empty( $offset ) ? $offset : 0; + $limit = ! empty( $limit ) ? $limit : 10; $ratings = tutor_utils()->get_course_rating( $this->post_id ); $ratings->reviews = tutor_utils()->get_course_reviews( $this->post_id, $offset, $limit, false, array( 'approved' ) ); From 06fcbc421c3cbed430e1bff2c3fc819f16c4e35f Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Thu, 7 Mar 2024 10:34:47 +0600 Subject: [PATCH 21/53] utils class reverted to original --- classes/Utils.php | 74 ++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index a9b71ac52b..b2eaab253d 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -902,7 +902,7 @@ public function get_course_completed_percent( $course_id = 0, $user_id = 0, $get */ $is_completed = apply_filters( 'tutor_is_zoom_lesson_done', false, $content->ID, $user_id ); if ( $is_completed ) { - ++$completed_count; + $completed_count++; } } elseif ( 'tutor-google-meet' === $content->post_type ) { /** @@ -912,7 +912,7 @@ public function get_course_completed_percent( $course_id = 0, $user_id = 0, $get */ $is_completed = apply_filters( 'tutor_google_meet_lesson_done', false, $content->ID, $user_id ); if ( $is_completed ) { - ++$completed_count; + $completed_count++; } } } @@ -1127,6 +1127,7 @@ public function get_course_price( $course_id = 0 ) { } } return apply_filters( 'get_tutor_course_price', $price, $course_id ); + } /** @@ -3312,7 +3313,7 @@ public function get_instructors_by_course( $course_id = 0 ) { // Exclude instructor if already in main instructor. $instructors = array_filter( $instructors, - function ( $instructor ) use ( $main_instructor ) { + function( $instructor ) use ( $main_instructor ) { if ( $instructor->ID !== $main_instructor[0]->ID ) { return true; } @@ -3644,10 +3645,12 @@ public function star_rating_generator( $current_rating = 0.00, $echo = true ) { for ( $i = 1; $i <= 5; $i++ ) { if ( (int) $current_rating >= $i ) { $output .= ''; - } elseif ( ( $current_rating - $i ) >= -0.5 ) { - $output .= ''; } else { - $output .= ''; + if ( ( $current_rating - $i ) >= -0.5 ) { + $output .= ''; + } else { + $output .= ''; + } } } @@ -3721,10 +3724,12 @@ public function star_rating_generator_course( $current_rating = 0.00, $echo = tr for ( $i = 1; $i <= 5; $i++ ) { if ( (int) $current_rating >= $i ) { $output .= ''; - } elseif ( ( $current_rating - $i ) >= -0.5 ) { - $output .= ''; } else { - $output .= ''; + if ( ( $current_rating - $i ) >= -0.5 ) { + $output .= ''; + } else { + $output .= ''; + } } } @@ -3772,11 +3777,10 @@ public function str_split( $string ) { * * @param integer|object $user user id or object. * @param string $size size of avatar like sm, md, lg. - * @param bool $echo whether to echo or return. * * @return string */ - public function get_tutor_avatar( $user = null, $size = '', $echo = false ) { + public function get_tutor_avatar( $user = null, $size = '' ) { if ( ! $user ) { return ''; @@ -3811,11 +3815,7 @@ public function get_tutor_avatar( $user = null, $size = '', $echo = false ) { $output .= ''; $output .= ''; - if ( $echo ) { - echo wp_kses( $output, $this->allowed_avatar_tags() ); - } else { - return apply_filters( 'tutor_text_avatar', $output ); - } + return apply_filters( 'tutor_text_avatar', $output ); } /** @@ -5333,7 +5333,7 @@ public function student_register_url() { * @return bool|false|string */ public function instructor_register_url() { - $instructor_register_page = (int) $this->get_option( 'instructor_register_page' ); + $instructor_register_page = (int) $this->get_option( 'instructor_register_page' ); if ( $instructor_register_page ) { return apply_filters( 'tutor_instructor_register_url', get_the_permalink( $instructor_register_page ) ); @@ -5383,7 +5383,6 @@ public function dashboard_page_id() { public function is_wishlisted( $course_id = 0, $user_id = 0 ) { $course_id = $this->get_post_id( $course_id ); $user_id = $this->get_user_id( $user_id ); - if ( ! $user_id ) { return false; } @@ -6575,7 +6574,7 @@ public function referer() { * @since 2.5.0 * * @param boolean $url_decode URL decode for unicode support. - * + * * @return void|string */ public function referer_field( $url_decode = true ) { @@ -6583,8 +6582,8 @@ public function referer_field( $url_decode = true ) { if ( $url_decode ) { $url = urldecode( $url ); } - - echo ''; + + echo ''; } /** @@ -6750,7 +6749,7 @@ public function get_assignments_by_course( $course_id = 0 ) { * @return bool */ public function is_script_debug() { - return ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ); + return ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ); } /** @@ -6958,6 +6957,7 @@ public function get_current_url( $post_id = 0 ) { * * @return object */ + public function get_rating_by_id( $rating_id = 0 ) { global $wpdb; @@ -7256,7 +7256,7 @@ public function get_course_prev_next_contents_by_id( $content_id = 0 ) { $next_id = $ids[ $next_i ]; } } - ++$i; + $i++; } } @@ -8019,7 +8019,7 @@ public function get_earning_chart_yearly( $user_id, $year ) { * @return object */ function get_package_object() { - $params = func_get_args(); + $params = func_get_args(); $is_pro = $params[0]; $class = $params[1]; @@ -8951,26 +8951,26 @@ public function count_completed_contents_by_topic( int $topic_id ): array { case $lesson_post_type: $is_lesson_completed = $this->is_completed_lesson( $content->ID, $user_id ); if ( $is_lesson_completed ) { - ++$completed; + $completed++; } break; case $quiz_post_type: $has_attempt = $this->has_attempted_quiz( $user_id, $content->ID ); if ( $has_attempt ) { - ++$completed; + $completed++; } break; case $assignment_post_type: $is_assignment_completed = $this->is_assignment_submitted( $content->ID, $user_id ); if ( $is_assignment_completed ) { - ++$completed; + $completed++; } break; case $zoom_lesson_post_type: if ( \class_exists( '\TUTOR_ZOOM\Zoom' ) ) { $is_zoom_lesson_completed = \TUTOR_ZOOM\Zoom::is_zoom_lesson_done( '', $content->ID, $user_id ); if ( $is_zoom_lesson_completed ) { - ++$completed; + $completed++; } } break; @@ -8979,7 +8979,7 @@ public function count_completed_contents_by_topic( int $topic_id ): array { if ( \TutorPro\GoogleMeet\Validator\Validator::is_addon_enabled() ) { $is_completed = \TutorPro\GoogleMeet\Frontend\Frontend::is_lesson_completed( false, $content->ID, $user_id ); if ( $is_completed ) { - ++$completed; + $completed++; } } } @@ -9323,6 +9323,7 @@ public function can_user_retake_course() { * * @return string */ + public function clean_html_content( $content = '', $allowed = array() ) { $default = array( @@ -9558,7 +9559,7 @@ private function assign_child_count( array $course_meta, $post_type ) { ); foreach ( $results as $result ) { - ++$course_meta[ $result->course_id ][ $post_type ]; + $course_meta[ $result->course_id ][ $post_type ]++; } return $course_meta; @@ -9577,7 +9578,7 @@ public function get_course_meta_data( $course_id ) { // Prepare course IDs to get quiz count based on. $course_ids = is_array( $course_id ) ? $course_id : array( $course_id ); $course_ids = array_map( - function ( $id ) { + function( $id ) { return (int) $id; }, $course_ids @@ -9623,8 +9624,8 @@ function ( $id ) { if ( $result->content_id ) { $course_meta[ $result->course_id ][ $result->content_type ][] = $result->content_id; } - } catch ( \Throwable $th ) { - tutor_log( 'Affected course ID : ' . $result->course_id . ' Error : ' . $th->getMessage() ); + } catch (\Throwable $th) { + tutor_log( 'Affected course ID : ' . $result->course_id . ' Error : '. $th->getMessage() ); } } @@ -9784,7 +9785,7 @@ public function get_current_page_slug() { * * @return array allowed tags */ - public function allowed_avatar_tags( array $tags = array() ): array { + public function allowed_avatar_tags( array $tags = array() ):array { $defaults = array( 'a' => array( 'href' => true, @@ -9820,7 +9821,7 @@ public function allowed_avatar_tags( array $tags = array() ): array { * * @return array allowed tags */ - public function allowed_icon_tags( array $tags = array() ): array { + public function allowed_icon_tags( array $tags = array() ):array { $defaults = array( 'span' => array( 'class' => true, @@ -9909,4 +9910,5 @@ public function get_remote_plugin_info( $plugin_slug = 'tutor' ) { return (object) json_decode( $response['body'], true ); } -} + +} \ No newline at end of file From ee1bfd19bbe4e604a244467676f4c58ed0f435c8 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Thu, 7 Mar 2024 10:38:06 +0600 Subject: [PATCH 22/53] utils fix --- classes/Utils.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index b2eaab253d..809c85a621 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -3777,10 +3777,11 @@ public function str_split( $string ) { * * @param integer|object $user user id or object. * @param string $size size of avatar like sm, md, lg. + * @param bool $echo whether to echo or return. * * @return string */ - public function get_tutor_avatar( $user = null, $size = '' ) { + public function get_tutor_avatar( $user = null, $size = '', $echo = false ) { if ( ! $user ) { return ''; @@ -3815,7 +3816,11 @@ public function get_tutor_avatar( $user = null, $size = '' ) { $output .= ''; $output .= ''; - return apply_filters( 'tutor_text_avatar', $output ); + if ( $echo ) { + echo wp_kses( $output, $this->allowed_avatar_tags() ); + } else { + return apply_filters( 'tutor_text_avatar', $output ); + } } /** From 5d3040167d17e723f0761dcbe4c041b23f01a4b4 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Fri, 8 Mar 2024 10:33:54 +0600 Subject: [PATCH 23/53] set only required current user data --- classes/Assets.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/classes/Assets.php b/classes/Assets.php index 2dafa9c787..f04d78f85b 100644 --- a/classes/Assets.php +++ b/classes/Assets.php @@ -100,6 +100,24 @@ private function get_default_localized_data() { $current_page = tutor_utils()->get_current_page_slug(); + /** + * Only required current user data. + * + * @since 2.6.2 + */ + $current_user = array(); + $userdata = get_userdata( get_current_user_id() ); + + if ( $userdata ) { + $current_user = array( + 'roles' => $userdata->roles, + 'data' => array( + 'id' => $userdata->ID, + 'display_name' => $userdata->display_name, + ), + ); + } + return array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'home_url' => get_home_url(), @@ -117,7 +135,7 @@ private function get_default_localized_data() { 'is_admin' => is_admin(), 'is_admin_bar_showing' => is_admin_bar_showing(), 'addons_data' => tutor_utils()->prepare_free_addons_data(), - 'current_user' => wp_get_current_user(), + 'current_user' => $current_user, 'content_change_event' => 'tutor_content_changed_event', 'is_tutor_course_edit' => isset( $_GET['action'] ) && 'edit' === $_GET['action'] && tutor()->course_post_type === get_post_type( get_the_ID() ) ? true : false, 'assignment_max_file_allowed' => 'tutor_assignments' === $post_type ? (int) tutor_utils()->get_assignment_option( $post_id, 'upload_files_limit' ) : 0, From 941e091fca23c62980ad0f5867d6bb512f75f276 Mon Sep 17 00:00:00 2001 From: prakash-a7x Date: Fri, 8 Mar 2024 16:10:56 +0600 Subject: [PATCH 24/53] review api functionality issue fix --- classes/Ajax.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/classes/Ajax.php b/classes/Ajax.php index 96c1d3fb15..abcda5a6a4 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -173,7 +173,9 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review, $r do_action( 'tutor_before_rating_placed' ); - if ( empty( $review_id ) ) { + $is_edit = 0 === $review_id ? false : true; + + if ( ! tutor_is_rest() ) { $previous_rating_id = $wpdb->get_var( $wpdb->prepare( "SELECT comment_ID @@ -187,10 +189,13 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review, $r ) ); - $review_id = $previous_rating_id; + if ( ! empty( $previous_rating_id ) ) { + $review_id = $previous_rating_id; + $is_edit = true; + } } - if ( $review_id ) { + if ( $is_edit ) { $wpdb->update( $wpdb->comments, array( @@ -271,7 +276,7 @@ public function add_or_update_review( $user_id, $course_id, $rating, $review, $r ) ); } else { - return $review_id ? 'updated' : 'created'; + return $is_edit ? 'updated' : 'created'; } } From 12ae6a8753fc8637ad8312273d0bf9313cfd0885 Mon Sep 17 00:00:00 2001 From: shewa12 Date: Fri, 8 Mar 2024 16:19:13 +0600 Subject: [PATCH 25/53] Update: Change log date updated --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 4f7b956ca6..de4e9b7f6c 100644 --- a/readme.txt +++ b/readme.txt @@ -316,7 +316,7 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == -= 2.6.2 - March 7, 2024 += 2.6.2 - March 8, 2024 New: Introduced Assignment submission APIs to enable students to submit assignments (Pro) New: Introduced Course Wishlist APIs to allow students to add courses to their wishlists (Pro) From 72f727225444b3c10087306127383535b20ec77b Mon Sep 17 00:00:00 2001 From: shewa12 Date: Mon, 11 Mar 2024 10:55:16 +0600 Subject: [PATCH 26/53] Update: Change log date updated --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index de4e9b7f6c..a96586dc8d 100644 --- a/readme.txt +++ b/readme.txt @@ -316,7 +316,7 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == -= 2.6.2 - March 8, 2024 += 2.6.2 - March 11, 2024 New: Introduced Assignment submission APIs to enable students to submit assignments (Pro) New: Introduced Course Wishlist APIs to allow students to add courses to their wishlists (Pro) From 98000f0fd589a53d468e887b228fa7a7672bd83b Mon Sep 17 00:00:00 2001 From: shewa12 Date: Mon, 11 Mar 2024 15:47:14 +0600 Subject: [PATCH 27/53] Changelog: Change log updated --- readme.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/readme.txt b/readme.txt index a96586dc8d..b30cce8cb2 100644 --- a/readme.txt +++ b/readme.txt @@ -318,10 +318,11 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu = 2.6.2 - March 11, 2024 -New: Introduced Assignment submission APIs to enable students to submit assignments (Pro) -New: Introduced Course Wishlist APIs to allow students to add courses to their wishlists (Pro) -New: Introduced Course Reviews APIs to enable students to review and rate courses (Pro) -Fix: Enhanced security measures to mitigate SQL injection and prevent unwanted data loss +New: APIs for enabling students to submit assignments (Pro) +New: APIs allowing students to add courses to their wishlists (Pro) +New: APIs enabling students to review and rate courses (Pro) +Update: Some enhancements to improve the overall experience +Fix: Strengthened security to prevent data loss = 2.6.1 - February 19, 2024 From fe754fdd256737bfe384a9f62cee7bfba8d6a01c Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Tue, 19 Mar 2024 13:00:46 +0600 Subject: [PATCH 28/53] feat: added course_content api endpoint --- classes/RestAPI.php | 21 +++++++++++++++ restapi/REST_Course.php | 57 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/classes/RestAPI.php b/classes/RestAPI.php index 31b2fbae6d..6ffb8bb13d 100644 --- a/classes/RestAPI.php +++ b/classes/RestAPI.php @@ -395,5 +395,26 @@ public function init_routes() { 'permission_callback' => array( RestAuth::class, 'process_api_request' ), ) ); + + // Get course content by id. + register_rest_route( + $this->namespace, + '/course/contents/(?P\d+)', + array( + 'methods' => 'GET', + 'callback' => array( + $this->course_obj, + 'course_contents', + ), + 'args' => array( + 'id' => array( + 'validate_callback' => function ( $param ) { + return is_numeric( $param ); + }, + ), + ), + 'permission_callback' => array( RestAuth::class, 'process_api_request' ), + ) + ); } } diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 3e415281d9..85c84758a2 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -95,7 +95,7 @@ public function course( WP_REST_Request $request ) { if ( count( $query->posts ) > 0 ) { // unset filter property. array_map( - function( $post ) { + function ( $post ) { unset( $post->filter ); }, $query->posts @@ -219,7 +219,6 @@ public function course_additional_info( int $post_id ) { ); return apply_filters( 'tutor_course_additional_info', $detail ); - } /** @@ -275,7 +274,7 @@ public function course_by_terms( WP_REST_Request $request ) { if ( count( $query->posts ) > 0 ) { // unset filter property. array_map( - function( $post ) { + function ( $post ) { unset( $post->filter ); }, $query->posts @@ -365,7 +364,7 @@ public function course_sort_by_price( WP_REST_Request $request ) { if ( count( $query->posts ) > 0 ) { // unset filter property. array_map( - function( $post ) { + function ( $post ) { unset( $post->filter ); }, $query->posts @@ -400,4 +399,54 @@ function( $post ) { ); return self::send( $response ); } + + /** + * Retreive the course contents for a given course id + * + * @since 2.6.3 + * + * @param WP_REST_Request $request request params. + * + * @return WP_REST_Response + */ + public function course_contents( WP_REST_Request $request ) { + $course_id = $request->get_param( 'id' ); + $topics = tutor_utils()->get_topics( $course_id ); + + if ( $topics->have_posts() ) { + $data = array(); + foreach ( $topics->get_posts() as $post ) { + $current_topic = array( + 'topic' => array( + 'id' => $post->ID, + 'summary' => $post->post_content, + 'contents' => array(), + ), + ); + + $topic_contents = tutor_utils()->get_course_contents_by_topic( $post->ID, -1 ); + + if ( $topic_contents->have_posts() ) { + foreach ( $topic_contents->get_posts() as $post ) { + array_push( $current_topic['topic']['contents'], $post ); + } + } + + array_push( $data, $current_topic ); + } + + $response = array( + 'status' => 'success', + 'message' => __( 'Course contents retreived sucessfully', 'tutor' ), + 'data' => $data, + ); + return self::send( $response ); + } + $response = array( + 'status' => 'not_found', + 'message' => __( 'Topic for this course with the given course id not found', 'tutor' ), + 'data' => array(), + ); + return self::send( $response ); + } } From a0be901ba738807d4e29c7d526c1c7051604379c Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Tue, 19 Mar 2024 13:03:04 +0600 Subject: [PATCH 29/53] feat: added course content api endpoint --- restapi/REST_Course.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 85c84758a2..d483d04d74 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -444,7 +444,7 @@ public function course_contents( WP_REST_Request $request ) { } $response = array( 'status' => 'not_found', - 'message' => __( 'Topic for this course with the given course id not found', 'tutor' ), + 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), 'data' => array(), ); return self::send( $response ); From f3b401a20e88050d2b7e471fe3d05f6ef96a011a Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Tue, 19 Mar 2024 14:19:56 +0600 Subject: [PATCH 30/53] Feat : API Post per page sync with settings --- restapi/REST_Course.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index d483d04d74..1b546cb5c0 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -74,14 +74,15 @@ public function __construct() { * @return WP_REST_Response */ public function course( WP_REST_Request $request ) { - $order = sanitize_text_field( $request->get_param( 'order' ) ); - $orderby = sanitize_text_field( $request->get_param( 'orderby' ) ); - $paged = sanitize_text_field( $request->get_param( 'paged' ) ); + $order = sanitize_text_field( $request->get_param( 'order' ) ); + $orderby = sanitize_text_field( $request->get_param( 'orderby' ) ); + $paged = sanitize_text_field( $request->get_param( 'paged' ) ); + $post_per_page = tutor_utils()->get_option( 'pagination_per_page' ); $args = array( 'post_type' => $this->post_type, 'post_status' => 'publish', - 'posts_per_page' => 10, + 'posts_per_page' => $post_per_page, 'paged' => $paged ? $paged : 1, 'order' => $order ? $order : 'ASC', 'orderby' => $orderby ? $orderby : 'title', @@ -336,13 +337,14 @@ public function course_sort_by_price( WP_REST_Request $request ) { $order = $request->get_param( 'order' ); $paged = $request->get_param( 'page' ); - $order = sanitize_text_field( $order ); - $paged = sanitize_text_field( $paged ); + $order = sanitize_text_field( $order ); + $paged = sanitize_text_field( $paged ); + $post_per_page = tutor_utils()->get_option( 'pagination_per_page' ); $args = array( 'post_type' => 'product', 'post_status' => 'publish', - 'posts_per_page' => 10, + 'posts_per_page' => $post_per_page, 'paged' => $paged ? $paged : 1, 'meta_key' => '_regular_price', From 2f2849b50fe15ae260d3978e1884b4598df00eea Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Tue, 16 Apr 2024 15:12:23 +0600 Subject: [PATCH 31/53] security issue resolved --- classes/User.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/classes/User.php b/classes/User.php index cdaa419545..6a31082d03 100644 --- a/classes/User.php +++ b/classes/User.php @@ -333,14 +333,16 @@ public function set_user_role( $user_id, $role, $old_roles ) { public function hide_notices() { $hide_notice = Input::get( 'tutor-hide-notice', '' ); $is_register_enabled = Input::get( 'tutor-registration', '' ); - if ( is_admin() && 'registration' === $hide_notice ) { + $has_manage_cap = current_user_can( 'manage_options' ); + + if ( $has_manage_cap && is_admin() && 'registration' === $hide_notice ) { tutor_utils()->checking_nonce( 'get' ); if ( 'enable' === $is_register_enabled ) { update_option( 'users_can_register', 1 ); } else { self::$hide_registration_notice = true; - setcookie( 'tutor_notice_hide_registration', 1, time() + ( 86400 * 30 ), tutor()->basepath ); + setcookie( 'tutor_notice_hide_registration', 1, time() + MONTH_IN_SECONDS, tutor()->basepath ); } } } From 0df10261623619e7c79d96fc103aa4f94f1b82b9 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Tue, 16 Apr 2024 15:36:17 +0600 Subject: [PATCH 32/53] endpoint updated --- classes/RestAPI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/RestAPI.php b/classes/RestAPI.php index 6ffb8bb13d..4b1a0adb5f 100644 --- a/classes/RestAPI.php +++ b/classes/RestAPI.php @@ -399,7 +399,7 @@ public function init_routes() { // Get course content by id. register_rest_route( $this->namespace, - '/course/contents/(?P\d+)', + '/course-contents/(?P\d+)', array( 'methods' => 'GET', 'callback' => array( From db572f63948ac50113b0c3e084c057a52a0dadf2 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Tue, 16 Apr 2024 15:47:49 +0600 Subject: [PATCH 33/53] api response updated --- restapi/REST_Course.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 1b546cb5c0..aba98305fd 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -419,18 +419,16 @@ public function course_contents( WP_REST_Request $request ) { $data = array(); foreach ( $topics->get_posts() as $post ) { $current_topic = array( - 'topic' => array( - 'id' => $post->ID, - 'summary' => $post->post_content, - 'contents' => array(), - ), + 'id' => $post->ID, + 'summary' => $post->post_content, + 'contents' => array(), ); $topic_contents = tutor_utils()->get_course_contents_by_topic( $post->ID, -1 ); if ( $topic_contents->have_posts() ) { foreach ( $topic_contents->get_posts() as $post ) { - array_push( $current_topic['topic']['contents'], $post ); + array_push( $current_topic['contents'], $post ); } } @@ -439,16 +437,18 @@ public function course_contents( WP_REST_Request $request ) { $response = array( 'status' => 'success', - 'message' => __( 'Course contents retreived sucessfully', 'tutor' ), + 'message' => __( 'Course contents retrieved successfully', 'tutor' ), 'data' => $data, ); return self::send( $response ); } + $response = array( 'status' => 'not_found', 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), 'data' => array(), ); + return self::send( $response ); } } From 236320845684152efd233356540d1a3644d38636 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Wed, 17 Apr 2024 15:16:20 +0600 Subject: [PATCH 34/53] api response consistency fixed --- restapi/REST_Course.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index aba98305fd..0a56e89884 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -395,9 +395,9 @@ function ( $post ) { } $response = array( - 'status' => 'not_found', - 'message' => __( 'Course not found', 'tutor' ), - 'data' => array(), + 'status_code' => 'not_found', + 'message' => __( 'Course not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); } @@ -436,17 +436,17 @@ public function course_contents( WP_REST_Request $request ) { } $response = array( - 'status' => 'success', - 'message' => __( 'Course contents retrieved successfully', 'tutor' ), - 'data' => $data, + 'status_code' => 'success', + 'message' => __( 'Course contents retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status' => 'not_found', - 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), - 'data' => array(), + 'status_code' => 'not_found', + 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); From 85970bf314aa5bff873f3288abc23eb728ad96c5 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Wed, 17 Apr 2024 15:17:48 +0600 Subject: [PATCH 35/53] spell correction --- restapi/REST_Course.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 0a56e89884..be3d2a042d 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -403,7 +403,7 @@ function ( $post ) { } /** - * Retreive the course contents for a given course id + * Retrieve the course contents for a given course id * * @since 2.6.3 * From 700bbb525df16029c73e2b9fd7760ceaba922f6b Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Wed, 17 Apr 2024 15:36:56 +0600 Subject: [PATCH 36/53] course content api response updated --- restapi/REST_Course.php | 1 + 1 file changed, 1 insertion(+) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index be3d2a042d..41a7e73779 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -420,6 +420,7 @@ public function course_contents( WP_REST_Request $request ) { foreach ( $topics->get_posts() as $post ) { $current_topic = array( 'id' => $post->ID, + 'title' => $post->post_title, 'summary' => $post->post_content, 'contents' => array(), ); From 0eb2343e62e1bed21d26659e138bb4019b4ac2ab Mon Sep 17 00:00:00 2001 From: Anindra Das Bivas Date: Wed, 17 Apr 2024 18:07:01 +0600 Subject: [PATCH 37/53] updated version from 2.6.2 to 2.7.0 --- restapi/REST_Course.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 41a7e73779..f3abde14e1 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -405,7 +405,7 @@ function ( $post ) { /** * Retrieve the course contents for a given course id * - * @since 2.6.3 + * @since 2.7.0 * * @param WP_REST_Request $request request params. * From 6b6a34461bb678688d0d374af31e75ecb698a74c Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Thu, 18 Apr 2024 10:22:12 +0600 Subject: [PATCH 38/53] Show each failed error on specific input key --- helpers/ValidationHelper.php | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/helpers/ValidationHelper.php b/helpers/ValidationHelper.php index aec6fbd6f0..cde8b17669 100644 --- a/helpers/ValidationHelper.php +++ b/helpers/ValidationHelper.php @@ -48,61 +48,61 @@ public static function validate( array $validation_rules, array $data ): object switch ( $nested_rule ) { case 'required': if ( ! self::has_key( $key, $data ) || self::is_empty( $data[ $key ] ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' is required', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' is required', 'tutor' ); } break; case 'numeric': if ( ! self::is_numeric( $data[ $key ] ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' is not numeric', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' is not numeric', 'tutor' ); } break; case 'min_length': if ( strlen( $data[ $key ] ) < $nested_rules[1] ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' minimum length is ', 'tutor' ) . $nested_rules[1]; + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' minimum length is ', 'tutor' ) . $nested_rules[1]; } break; case 'mimes': $extensions = explode( ',', $nested_rules[1] ); if ( ! self::in_array( $data[ $key ], $extensions ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' extension is not valid', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' extension is not valid', 'tutor' ); } break; case 'match_string': $strings = explode( ',', $nested_rules[1] ); if ( ! self::in_array( $data[ $key ], $strings ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' string is not valid', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' string is not valid', 'tutor' ); } break; case 'boolean': if ( ! self::is_boolean( $data[ $key ] ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' is not boolean', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' is not boolean', 'tutor' ); } break; case 'is_array': if ( ! self::is_array( $data[ $key ] ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' is not an array', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' is not an array', 'tutor' ); } break; case 'date_format': $format = $nested_rules[1]; if ( ! self::is_valid_date( $data[ $key ], $format ) ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' invalid date format', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' invalid date format', 'tutor' ); } break; case 'user_exists': $user_id = (int) $data[ $key ]; $is_exists = self::is_user_exists( $user_id ); if ( ! $is_exists ) { - $validation_pass = false; - $validation_errors[ $key ] = $key . __( ' user does not exist', 'tutor' ); + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' user does not exist', 'tutor' ); } break; default: @@ -112,10 +112,12 @@ public static function validate( array $validation_rules, array $data ): object } } } + $response = array( 'success' => $validation_pass, 'errors' => $validation_errors, ); + return (object) $response; } From 7f05751fbd998312461b50c75092c064ac1a6d0b Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Thu, 18 Apr 2024 10:22:56 +0600 Subject: [PATCH 39/53] optional input key validation --- helpers/ValidationHelper.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/helpers/ValidationHelper.php b/helpers/ValidationHelper.php index cde8b17669..be4090c4a6 100644 --- a/helpers/ValidationHelper.php +++ b/helpers/ValidationHelper.php @@ -44,6 +44,15 @@ public static function validate( array $validation_rules, array $data ): object foreach ( $rules as $rule ) { $nested_rules = explode( ':', $rule ); + /** + * Optional input validation. + */ + if ( isset( $nested_rules[0] ) && 'if_input' === $nested_rules[0] ) { + if ( ! self::has_key( $key, $data ) ) { + break; + } + } + foreach ( $nested_rules as $nested_rule ) { switch ( $nested_rule ) { case 'required': From 71bf5f468e432ba9ceed8aef98e2b4006918b1c0 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Thu, 18 Apr 2024 12:08:42 +0600 Subject: [PATCH 40/53] has record rule added --- helpers/ValidationHelper.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/helpers/ValidationHelper.php b/helpers/ValidationHelper.php index be4090c4a6..f9aca65cbd 100644 --- a/helpers/ValidationHelper.php +++ b/helpers/ValidationHelper.php @@ -106,6 +106,18 @@ public static function validate( array $validation_rules, array $data ): object $validation_errors[ $key ][] = $key . __( ' invalid date format', 'tutor' ); } break; + + case 'has_record': + list( $table, $column ) = explode( ',', $nested_rules[1], 2 ); + + $value = $data[ $key ]; + $has_record = self::has_record( $table, $column, $value ); + if ( ! $has_record ) { + $validation_pass = false; + $validation_errors[ $key ][] = $key . __( ' record not found', 'tutor' ); + } + break; + case 'user_exists': $user_id = (int) $data[ $key ]; $is_exists = self::is_user_exists( $user_id ); @@ -253,4 +265,26 @@ public static function is_user_exists( int $user_id ): bool { $user = get_user_by( 'id', $user_id ); return $user ? true : false; } + + /** + * Check a table has record. + * + * @since 2.7.0 + * + * @param string $table table name with prefix or without. + * @param string $column table column name. + * @param mixed $value table column value. + * + * @return boolean + */ + public static function has_record( $table, $column, $value ) { + global $wpdb; + $table_prefix = $wpdb->prefix; + if ( strpos( $table, $table_prefix ) !== 0 ) { + $table = $table_prefix . $table; + } + + $record = QueryHelper::get_row( $table, array( $column => $value ), $column ); + return $record ? true : false; + } } From 69cc51eefd5cdc07a1404bcad272dfd8aeb12d4f Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Thu, 18 Apr 2024 16:20:18 +0600 Subject: [PATCH 41/53] Fix - duplicate H1 tag on each single page --- classes/Assets.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/Assets.php b/classes/Assets.php index f04d78f85b..935b3c3f8e 100644 --- a/classes/Assets.php +++ b/classes/Assets.php @@ -183,9 +183,10 @@ public function frontend_scripts() { /** * We checked wp_enqueue_editor() in condition because it conflicting with Divi Builder * condition updated @since v.1.7.4 + * + * @since 2.7.0 is_user_logged_in() check added to remove duplicate H1 tag on each single post. */ - - if ( is_single() ) { + if ( is_single() && is_user_logged_in() ) { if ( function_exists( 'et_pb_is_pagebuilder_used' ) ) { $is_page_builder_used = et_pb_is_pagebuilder_used( get_the_ID() ); if ( ! $is_page_builder_used ) { From 0e1808ad4eed5f540c72d02a922be6a76bcfdb6c Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Sat, 20 Apr 2024 10:06:19 +0600 Subject: [PATCH 42/53] translation fix --- assets/react/front/tutor-front.js | 4 ++-- v2-library/_src/js/passwordStrengthChecker.js | 9 ++++----- .../src/components/datapicker/TutorDateRangePicker.js | 5 +++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/assets/react/front/tutor-front.js b/assets/react/front/tutor-front.js index da745f3718..cd3974a123 100644 --- a/assets/react/front/tutor-front.js +++ b/assets/react/front/tutor-front.js @@ -22,7 +22,7 @@ readyState_complete(() => { jQuery(document).ready(function($) { 'use strict'; /** - * wp.i18n translateable functions + * wp.i18n translatable functions * @since 1.9.0 */ const { __, _x, _n, _nx } = wp.i18n; @@ -293,7 +293,7 @@ jQuery(document).ready(function($) { if (completedPercentage < required_percentage) { const complete_lesson_btn = $('button[name="complete_lesson_btn"]'); complete_lesson_btn.attr('disabled', true); - complete_lesson_btn.wrap('
').after(`Watch at least ${video_data.required_percentage}% to complete the lesson.`); + complete_lesson_btn.wrap('
').after(`${__(`Watch at least ${video_data.required_percentage}% to complete the lesson.`, 'tutor')}`); } }, getPercentage: function(value, total) { diff --git a/v2-library/_src/js/passwordStrengthChecker.js b/v2-library/_src/js/passwordStrengthChecker.js index 9201230c94..3663366ab5 100644 --- a/v2-library/_src/js/passwordStrengthChecker.js +++ b/v2-library/_src/js/passwordStrengthChecker.js @@ -7,6 +7,7 @@ const weak = document.querySelector('.tutor-password-strength-hint .weak'); const medium = document.querySelector('.tutor-password-strength-hint .medium'); const strong = document.querySelector('.tutor-password-strength-hint .strong'); + const { __, _x, _n, _nx } = wp.i18n; let regExpWeak = /[a-z]/; let regExpMedium = /\d+/; @@ -53,15 +54,13 @@ weak.classList.add('active'); if (noticeText) { noticeText.style.display = 'block'; - noticeText.textContent = 'weak'; - // noticeText.classList.add('weak'); + noticeText.textContent = __('weak','tutor'); } } if (no == 2) { medium.classList.add('active'); if (noticeText) { - noticeText.textContent = 'medium'; - // noticeText.classList.add('medium'); + noticeText.textContent = __('medium','tutor'); } } else { medium.classList.remove('active'); @@ -74,7 +73,7 @@ medium.classList.add('active'); strong.classList.add('active'); if (noticeText) { - noticeText.textContent = 'strong'; + noticeText.textContent = __('strong','tutor'); // noticeText.classList.add('strong'); } } else { diff --git a/v2-library/src/components/datapicker/TutorDateRangePicker.js b/v2-library/src/components/datapicker/TutorDateRangePicker.js index ff935afbc9..658d48aeb6 100644 --- a/v2-library/src/components/datapicker/TutorDateRangePicker.js +++ b/v2-library/src/components/datapicker/TutorDateRangePicker.js @@ -2,6 +2,7 @@ import { differenceInDays } from 'date-fns'; import React, { useEffect, useState } from 'react'; import DatePicker, { CalendarContainer } from 'react-datepicker'; import { CustomInput } from '../CustomInput'; +const { __, _x, _n, _nx } = wp.i18n; const TutorDateRangePicker = () => { @@ -56,7 +57,7 @@ const TutorDateRangePicker = () => { {children}
- {dayCount ? (dayCount > 1 ? `${dayCount} days selected` : `${dayCount} day selected`) : '0 day selected'} + {dayCount ? (dayCount > 1 ? `${dayCount} days selected` : `${dayCount} day selected`) : __( '0 day selected', 'tutor' )}
From b1c443bfebcbc1177f9a48b78550eaa497cbf4b7 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Sat, 20 Apr 2024 12:47:07 +0600 Subject: [PATCH 43/53] translators comment added to improve wp translation instruction --- classes/Course.php | 8 ++++--- classes/Shortcode.php | 4 +++- classes/Tools_V2.php | 21 +++++++++++++----- classes/Tutor_Setup.php | 1 + classes/Withdraw.php | 3 ++- templates/dashboard/assignments/review.php | 7 +++++- .../dashboard/question-answer/answers.php | 5 ++++- templates/dashboard/withdraw.php | 22 +++++++++++++++---- templates/email/send-reset-password.php | 17 +++++++++++--- templates/single/course/reviews.php | 7 +++++- templates/single/lesson/required-enroll.php | 1 + templates/single/quiz/top.php | 4 +++- views/pages/tools/status.php | 2 +- 13 files changed, 79 insertions(+), 23 deletions(-) diff --git a/classes/Course.php b/classes/Course.php index bbae5fa161..956f4ceb9b 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -1499,15 +1499,17 @@ public function tutor_lms_hide_course_complete_btn( $html ) { $assignment_str = _n( 'assignment', 'assignments', $required_assignment_pass, 'tutor' ); if ( ! $is_quiz_pass && 0 == $required_assignment_pass ) { - /* translators: %s: number of quiz pass required */ + /* translators: %1$s: number of quiz/assignment pass required; %2$s: quiz/assignment string */ $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str ); } + if ( $is_quiz_pass && $required_assignment_pass > 0 ) { - /* translators: %s: number of assignment pass required */ + //phpcs:ignore $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_assignment_pass, $assignment_str ); } + if ( ! $is_quiz_pass && $required_assignment_pass > 0 ) { - /* translators: %s: number of quiz pass required */ + /* translators: %1$s: number of quiz pass required; %2$s: quiz string; %3$s: number of assignment pass required; %4$s: assignment string */ $_msg = sprintf( __( 'You have to pass %1$s %2$s and %3$s %4$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str, $required_assignment_pass, $assignment_str ); } diff --git a/classes/Shortcode.php b/classes/Shortcode.php index a33f57466e..c9b46bb99f 100644 --- a/classes/Shortcode.php +++ b/classes/Shortcode.php @@ -105,7 +105,9 @@ public function tutor_dashboard() { * @since 2.1.3 */ $login_url = tutor_utils()->get_option( 'enable_tutor_native_login', null, true, true ) ? '' : wp_login_url( tutor()->current_url ); - echo sprintf( __( 'Please %1$sSign-In%2$s to view this page', 'tutor' ), '' );//phpcs:ignore + $signin_link = ''; + /* translators: %s is anchor link for signin */ + echo sprintf( __( 'Please %s to view this page', 'tutor' ), $signin_link ); //phpcs:ignore } return apply_filters( 'tutor_dashboard/index', ob_get_clean() ); } diff --git a/classes/Tools_V2.php b/classes/Tools_V2.php index 3dc49a71e9..01940ca41b 100644 --- a/classes/Tools_V2.php +++ b/classes/Tools_V2.php @@ -1,5 +1,4 @@ ' . esc_html__( 'WordPress requirements', 'tutor' ) . '' ) + ? + /* translators: 1: MySQL version number, 2: WordPress requirements URL */ + sprintf( esc_html__( '%1$s - We recommend a minimum MySQL version of 5.6. See: %2$s', 'tutor' ), esc_html( $environment['mysql_version_string'] ), '' . esc_html__( 'WordPress requirements', 'tutor' ) . '' ) : esc_html( $environment['mysql_version_string'] ); $data['default_timezone_is_utc'] = ( 'UTC' !== $environment['default_timezone'] ) - ? sprintf( esc_html__( 'Default timezone is %s - it should be UTC', 'tutor' ), esc_html( $environment['default_timezone'] ) ) + ? + /* translators: %s: default timezone */ + sprintf( esc_html__( 'Default timezone is %s - it should be UTC', 'tutor' ), esc_html( $environment['default_timezone'] ) ) : '✓'; $data['fsockopen_curl'] = $environment['fsockopen_or_curl_enabled'] @@ -497,15 +500,21 @@ public function status( $type = '' ) { $data['dom_document'] = $environment['domdocument_enabled'] ? '✓' - : sprintf( esc_html__( 'Your server does not have the %s class enabled - HTML/Multipart emails, and also some extensions, will not work without DOMDocument.', 'tutor' ), 'DOMDocument' ); + : + /* translators: %s: DOMDocument class */ + sprintf( esc_html__( 'Your server does not have the %s class enabled - HTML/Multipart emails, and also some extensions, will not work without DOMDocument.', 'tutor' ), 'DOMDocument' ); $data['gzip'] = ( $environment['gzip_enabled'] ) ? '✓' - : sprintf( esc_html__( 'Your server does not support the %s function - this is required to use the GeoIP database from MaxMind.', 'tutor' ), 'gzopen' ); + : + /* translators: %s: gzopen function */ + sprintf( esc_html__( 'Your server does not support the %s function - this is required to use the GeoIP database from MaxMind.', 'tutor' ), 'gzopen' ); $data['multibyte_string'] = ( $environment['mbstring_enabled'] ) ? '✓' - : sprintf( esc_html__( 'Your server does not support the %s functions - this is required for better character encoding. Some fallbacks will be used instead for it.', 'tutor' ), 'mbstring' ); + : + /* translators: %s: mbstring functions */ + sprintf( esc_html__( 'Your server does not support the %s functions - this is required for better character encoding. Some fallbacks will be used instead for it.', 'tutor' ), 'mbstring' ); if ( ! null == $type ) { return $data[ $type ]; diff --git a/classes/Tutor_Setup.php b/classes/Tutor_Setup.php index d905d2d22e..a4c60a2027 100644 --- a/classes/Tutor_Setup.php +++ b/classes/Tutor_Setup.php @@ -455,6 +455,7 @@ public function tutor_setup_attributes() { 'type' => 'text', 'max' => 50, 'lable' => __( 'Lesson permalink', 'tutor' ), + /* translators: %s: sample permalink */ 'desc' => sprintf( __( 'Example: %s', 'tutor' ), get_home_url() . '/' . tutor()->course_post_type . '/sample-course/' . ( tutor_utils()->get_option( 'lesson_permalink_base', 'lessons' ) ) . '/sample-lesson/' ),//phpcs:ignore ), ), diff --git a/classes/Withdraw.php b/classes/Withdraw.php index ad487a93d0..93f1980f0e 100644 --- a/classes/Withdraw.php +++ b/classes/Withdraw.php @@ -236,6 +236,7 @@ public function tutor_make_an_withdraw() { wp_send_json_error( array( 'msg' => wp_sprintf( + /* translators: 1: total pending withdraw request 2: available for withdraw */ __( "You have total %1\$s pending withdraw request. You can't make more than %2\$s withdraw request at a time", 'tutor' ), $earning_summary->total_pending, $earning_summary->available_for_withdraw @@ -253,7 +254,7 @@ public function tutor_make_an_withdraw() { } if ( ( ! is_numeric( $withdraw_amount ) && ! is_float( $withdraw_amount ) ) || $withdraw_amount < $min_withdraw ) { - //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment + /* translators: 1: strong tag start 2: min withdrawal amount 3: strong tag end */ $required_min_withdraw = apply_filters( 'tutor_required_min_amount_msg', sprintf( __( 'Minimum withdrawal amount is %1$s %2$s %3$s ', 'tutor' ), '', $formatted_min_withdraw_amount, '' ) ); wp_send_json_error( array( 'msg' => $required_min_withdraw ) ); } diff --git a/templates/dashboard/assignments/review.php b/templates/dashboard/assignments/review.php index 883c8edaf3..30784fb654 100644 --- a/templates/dashboard/assignments/review.php +++ b/templates/dashboard/assignments/review.php @@ -139,7 +139,12 @@
-

{$max_mark}" );//phpcs:ignore ?>

+

+ {$max_mark}" );//phpcs:ignore + ?> +

diff --git a/templates/dashboard/question-answer/answers.php b/templates/dashboard/question-answer/answers.php index d9e29a3a8d..9d4471f233 100644 --- a/templates/dashboard/question-answer/answers.php +++ b/templates/dashboard/question-answer/answers.php @@ -36,7 +36,10 @@

display_name ); ?> - comment_date_gmt ) ) ) ); ?> + comment_date_gmt ) ) ) ); + ?>

diff --git a/templates/dashboard/withdraw.php b/templates/dashboard/withdraw.php index 098aeca9bb..97e4cd8696 100644 --- a/templates/dashboard/withdraw.php +++ b/templates/dashboard/withdraw.php @@ -66,18 +66,30 @@
-
+
+ +
", $available_for_withdraw_formatted, '' ) ); + /* translators: %s: available balance */ + echo wp_kses_post( sprintf( __( 'You have %s ready to withdraw now', 'tutor' ), "" . $available_for_withdraw_formatted . '' ) ); } else { - echo wp_kses_post( sprintf( __( 'You have %1$s %2$s %3$s and this is insufficient balance to withdraw', 'tutor' ), "", $available_for_withdraw_formatted, '' ) ); + /* translators: %s: available balance */ + echo wp_kses_post( sprintf( __( 'You have %s and this is insufficient balance to withdraw', 'tutor' ), "" . $available_for_withdraw_formatted . '' ) ); } ?>
total_pending > 0 ) : ?> -
tutor_price( $summary_data->total_pending ) ) ); ?>
+
+ tutor_price( $summary_data->total_pending ) ) ); + ?> +
@@ -102,8 +114,10 @@ get_tutor_dashboard_page_permalink( 'settings/withdraw-settings' ); + /* translators: %s: Withdraw Method Name */ echo esc_html( $withdraw_method_name ? sprintf( __( 'The preferred payment method is selected as %s. ', 'tutor' ), $withdraw_method_name ) : '' ); echo wp_kses( + /* translators: %1$s: a tag start, %2$s: a tag end */ sprintf( __( 'You can change your %1$s Withdraw Preference %2$s', 'tutor' ), "", '' ), array( 'a' => array( 'href' => true ), diff --git a/templates/email/send-reset-password.php b/templates/email/send-reset-password.php index 13d1ec88d0..fb233ea481 100644 --- a/templates/email/send-reset-password.php +++ b/templates/email/send-reset-password.php @@ -14,14 +14,25 @@

- +

- +

-

+

+ +

diff --git a/templates/single/course/reviews.php b/templates/single/course/reviews.php index ac60cb5115..5e75911ea2 100644 --- a/templates/single/course/reviews.php +++ b/templates/single/course/reviews.php @@ -106,7 +106,12 @@
- + + +
diff --git a/templates/single/lesson/required-enroll.php b/templates/single/lesson/required-enroll.php index b73ab7f0d3..9e688f3bb2 100644 --- a/templates/single/lesson/required-enroll.php +++ b/templates/single/lesson/required-enroll.php @@ -14,6 +14,7 @@ $args = array( 'headline' => __( 'Permission Denied', 'tutor' ), 'message' => __( 'Please enroll in this course to view course content.', 'tutor' ), + /* translators: %s: course name */ 'description' => sprintf( __( 'Course name : %s', 'tutor' ), get_the_title( $course_id ) ), 'button' => array( 'url' => get_permalink( $course_id ), diff --git a/templates/single/quiz/top.php b/templates/single/quiz/top.php index 355855c140..810f75033b 100644 --- a/templates/single/quiz/top.php +++ b/templates/single/quiz/top.php @@ -80,7 +80,9 @@
: - +
diff --git a/views/pages/tools/status.php b/views/pages/tools/status.php index 8189fe2167..4fd4f327a7 100644 --- a/views/pages/tools/status.php +++ b/views/pages/tools/status.php @@ -233,7 +233,7 @@ ' . sprintf( esc_html__( 'Default timezone is %s - it should be UTC', 'tutor' ), esc_html( $environment['default_timezone'] ) ) . ''; } else { echo ''; From 50cc289f6dbfa66f4afd0bb8c3908acd78236e64 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 10:17:36 +0600 Subject: [PATCH 44/53] sprintf helper added --- assets/react/helper/sprintf.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 assets/react/helper/sprintf.js diff --git a/assets/react/helper/sprintf.js b/assets/react/helper/sprintf.js new file mode 100644 index 0000000000..d56ffbfe85 --- /dev/null +++ b/assets/react/helper/sprintf.js @@ -0,0 +1,15 @@ +/** + * sprintf helper like as php + * + * @param {string} str string + * @param {...any} args + * + * @returns string + */ +function sprintf(str, ...args) { + return str.replace(/%s/g, function () { + return args.shift(); + }); +} + +export default sprintf; \ No newline at end of file From b83b3ac4c43a5a9a87143232eff9413937e71d33 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 10:18:09 +0600 Subject: [PATCH 45/53] translation issue fix --- assets/react/admin-dashboard/segments/withdraw.js | 10 ++++++++-- assets/react/front/tutor-front.js | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/assets/react/admin-dashboard/segments/withdraw.js b/assets/react/admin-dashboard/segments/withdraw.js index d52a69aad7..fe246b8a78 100644 --- a/assets/react/admin-dashboard/segments/withdraw.js +++ b/assets/react/admin-dashboard/segments/withdraw.js @@ -1,3 +1,5 @@ +const { default: sprintf } = require("../../helper/sprintf"); + document.addEventListener("DOMContentLoaded", function(){ const { __, _x, _n, _nx } = wp.i18n; // Approve and Reject button @@ -13,7 +15,9 @@ document.addEventListener("DOMContentLoaded", function(){ const amount = e.currentTarget.dataset.amount; const accountName = e.currentTarget.dataset.name; const content = document.getElementById('tutor-admin-withdraw-approve-content'); - content.innerHTML = `${__( 'You are approving '+ `${accountName}` + ' withdrawal request for '+ `${amount}` +'. Are you sure you want to approve?', 'tutor')}`; + content.innerHTML = `${ + sprintf( __( 'You are approving %s withdrawal request for %s. Are you sure you want to approve?', 'tutor'), `${accountName}`, `${amount}` ) + }`; } } @@ -26,7 +30,9 @@ document.addEventListener("DOMContentLoaded", function(){ const amount = e.currentTarget.dataset.amount; const accountName = e.currentTarget.dataset.name; const content = document.getElementById('tutor-admin-withdraw-reject-content'); - content.innerHTML = `${__( 'You are rejecting '+ `${accountName}` + ' withdrawal request for '+ `${amount}` +'. Are you sure you want to reject?', 'tutor')}`; + content.innerHTML = `${ + sprintf( __( 'You are rejecting %s withdrawal request for %s. Are you sure you want to reject?', 'tutor' ), `${accountName}`, `${amount}` ) + }`; } } } diff --git a/assets/react/front/tutor-front.js b/assets/react/front/tutor-front.js index cd3974a123..ce45bcc9b5 100644 --- a/assets/react/front/tutor-front.js +++ b/assets/react/front/tutor-front.js @@ -5,6 +5,7 @@ import './dashboard/export-csv'; import './pages/course-landing'; import './pages/instructor-list-filter'; import './_select_dd_search'; +import sprintf from '../helper/sprintf'; /** * Codes from this file should be decentralized according to relavent file/folder structure. * It's a legacy file. @@ -153,7 +154,7 @@ jQuery(document).ready(function($) { // Disallow moving forward if (newTime > max_seek_time) { e.preventDefault(); - tutor_toast(__('Warning', 'tutor'), __(`Forward seeking is disabled.`, 'tutor'), 'error'); + tutor_toast(__('Warning', 'tutor'), __('Forward seeking is disabled', 'tutor'), 'error'); return false; } return true; @@ -293,7 +294,7 @@ jQuery(document).ready(function($) { if (completedPercentage < required_percentage) { const complete_lesson_btn = $('button[name="complete_lesson_btn"]'); complete_lesson_btn.attr('disabled', true); - complete_lesson_btn.wrap('
').after(`${__(`Watch at least ${video_data.required_percentage}% to complete the lesson.`, 'tutor')}`); + complete_lesson_btn.wrap('
').after(`${ sprintf( __( 'Watch at least %s% to complete the lesson.', 'tutor' ), video_data.required_percentage ) }`); } }, getPercentage: function(value, total) { From eca2325408c8e6e0b6f760a4301121a4a2d7499b Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 10:18:47 +0600 Subject: [PATCH 46/53] gulp file updated to fix translation issue --- gulpfile.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 3e9be7175b..13b5ee50eb 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -88,7 +88,7 @@ for (let task in scss_blueprints) { var added_texts = []; const regex = /__\(\s*(['"])((?:(?!(? 'assets/js/' + f + '.js:1') .join(', '); function i18n_makepot(callback, target_dir) { @@ -110,7 +110,12 @@ function i18n_makepot(callback, target_dir) { } // Make sure only js extension file to process - if (stat.isFile() && path.extname(file_name) == '.js' && full_path.indexOf('assets/react') > -1) { + if (stat.isFile() && path.extname(file_name) == '.js' && + ( full_path.indexOf('assets/react') > -1 + || full_path.indexOf('v2-library/_src/js') > -1 + || full_path.indexOf('v2-library/src/components') > -1 + ) + ) { var codes = fs.readFileSync(full_path).toString(); var lines = codes.split('\n'); From 3b6fc85076b6eeabc30f243ece54b9938d6fe58c Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 12:18:56 +0600 Subject: [PATCH 47/53] Fix: instructor shortcode xss security issue --- templates/shortcode/instructor-filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shortcode/instructor-filter.php b/templates/shortcode/instructor-filter.php index 1c2f76e84f..41d9857b72 100644 --- a/templates/shortcode/instructor-filter.php +++ b/templates/shortcode/instructor-filter.php @@ -33,7 +33,7 @@ if ( is_array( $value ) ) { continue; } - echo esc_attr( 'data-' . $key . '="' . $value . '" ' ); + echo 'data-' . esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; } ?> > From a9cd75df5303ecee9eada06e01b9f7a803a9c752 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 15:38:31 +0600 Subject: [PATCH 48/53] version and changelog added --- readme.txt | 22 ++++++++++++++++++++-- tutor.php | 6 +++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/readme.txt b/readme.txt index b30cce8cb2..ed81cfc2ab 100644 --- a/readme.txt +++ b/readme.txt @@ -3,9 +3,9 @@ Contributors: themeum Donate link: https://www.themeum.com Tags: lms, course, elearning, education, learning management system Requires at least: 5.3 -Tested up to: 6.4 +Tested up to: 6.5 Requires PHP: 7.4 -Stable tag: 2.6.2 +Stable tag: 2.7.0 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -316,6 +316,24 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == += 2.7.0 - April 23, 2024 + +New: API for course content +New: API for student dashboard (Pro) +New: API for student calendar event list (Pro) +New: API for student enrolled courses (Pro) +New: API for quiz attempt list (Pro) +New: API for enrolled student list on a course (Pro) +New: API for become an instructor (Pro) +New: API for student order history (Pro) +New: APIs for profile management (Pro) +New: APIs for Q&A manage (Pro) +Update: WordPress 6.5 compatibility added +Update: Some enhancements to improve the overall experience +Fix: Duplicate H1 tag on each single page +Fix: Various translation related issues +Fix: Resolved known security issues + = 2.6.2 - March 11, 2024 New: APIs for enabling students to submit assignments (Pro) diff --git a/tutor.php b/tutor.php index e8cfbca53c..62ad8d2797 100644 --- a/tutor.php +++ b/tutor.php @@ -4,11 +4,11 @@ * Plugin URI: https://www.themeum.com/product/tutor-lms/ * Description: Tutor is a complete solution for creating a Learning Management System in WordPress way. It can help you to create small to large scale online education site very conveniently. Power features like report, certificate, course preview, private file sharing make Tutor a robust plugin for any educational institutes. * Author: Themeum - * Version: 2.6.2 + * Version: 2.7.0 * Author URI: https://themeum.com * Requires PHP: 7.4 * Requires at least: 5.3 - * Tested up to: 6.4 + * Tested up to: 6.5 * License: GPLv2 or later * Text Domain: tutor * @@ -24,7 +24,7 @@ /** * Defined the tutor main file */ -define( 'TUTOR_VERSION', '2.6.2' ); +define( 'TUTOR_VERSION', '2.7.0' ); define( 'TUTOR_FILE', __FILE__ ); /** From f55bac6f0a94d8e433ccce576839fb4e78ac7554 Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 16:44:28 +0600 Subject: [PATCH 49/53] status_code to code --- restapi/REST_Author.php | 12 ++-- restapi/REST_Course.php | 66 ++++++++++----------- restapi/REST_Course_Announcement.php | 16 +++-- restapi/REST_Lesson.php | 20 +++---- restapi/REST_Quiz.php | 89 ++++++++++++++-------------- restapi/REST_Rating.php | 12 ++-- restapi/REST_Topic.php | 16 +++-- restapi/RestAuth.php | 1 + 8 files changed, 116 insertions(+), 116 deletions(-) diff --git a/restapi/REST_Author.php b/restapi/REST_Author.php index 27eee61450..5385c741af 100644 --- a/restapi/REST_Author.php +++ b/restapi/REST_Author.php @@ -57,18 +57,18 @@ public function author_detail( WP_REST_Request $request ) { $author->courses = get_user_meta( $this->user_id, '_tutor_instructor_course_id', false ); $response = array( - 'status_code' => 'success', - 'message' => __( 'Author details retrieved successfully', 'tutor' ), - 'data' => $author, + 'code' => 'success', + 'message' => __( 'Author details retrieved successfully', 'tutor' ), + 'data' => $author, ); return self::send( $response ); } $response = array( - 'status_code' => 'invalid_id', - 'message' => __( 'Author not found', 'tutor' ), - 'data' => array(), + 'code' => 'invalid_id', + 'message' => __( 'Author not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index f3abde14e1..9cc8d4ae6d 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -139,18 +139,18 @@ function ( $post ) { } $response = array( - 'status_code' => 'success', - 'message' => __( 'Course retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Course not found', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Course not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); @@ -171,16 +171,16 @@ public function course_detail( WP_REST_Request $request ) { $detail = $this->course_additional_info( $post_id ); if ( $detail ) { $response = array( - 'status_code' => 'course_detail', - 'message' => __( 'Course detail retrieved successfully', 'tutor' ), - 'data' => $detail, + 'code' => 'course_detail', + 'message' => __( 'Course detail retrieved successfully', 'tutor' ), + 'data' => $detail, ); return self::send( $response ); } $response = array( - 'status_code' => 'course_detail', - 'message' => __( 'Detail not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'course_detail', + 'message' => __( 'Detail not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); @@ -238,9 +238,9 @@ public function course_by_terms( WP_REST_Request $request ) { // check array or not. if ( count( $validate_err ) > 0 ) { $response = array( - 'status_code' => 'validation_error', - 'message' => $validate_err, - 'data' => array(), + 'code' => 'validation_error', + 'message' => $validate_err, + 'data' => array(), ); return self::send( $response ); @@ -282,18 +282,18 @@ function ( $post ) { ); $response = array( - 'status_code' => 'success', - 'message' => __( 'Course retrieved successfully', 'tutor' ), - 'data' => $query->posts, + 'code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $query->posts, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Course not found for given terms', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Course not found for given terms', 'tutor' ), + 'data' => array(), ); return self::send( $response ); } @@ -386,18 +386,18 @@ function ( $post ) { ); $response = array( - 'status_code' => 'success', - 'message' => __( 'Course retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Course not found', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Course not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); } @@ -437,17 +437,17 @@ public function course_contents( WP_REST_Request $request ) { } $response = array( - 'status_code' => 'success', - 'message' => __( 'Course contents retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Course contents retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Contents for this course with the given course id not found', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/REST_Course_Announcement.php b/restapi/REST_Course_Announcement.php index 6337cc30d2..337e3ef139 100644 --- a/restapi/REST_Course_Announcement.php +++ b/restapi/REST_Course_Announcement.php @@ -54,26 +54,24 @@ public function course_announcement( WP_REST_Request $request ) { global $wpdb; - $table = $wpdb->prefix . 'posts'; - $result = $wpdb->get_results( - $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) + $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM {$wpdb->posts} WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) ); if ( count( $result ) > 0 ) { $response = array( - 'status_code' => 'success', - 'message' => __( 'Announcement retrieved successfully', 'tutor' ), - 'data' => $result, + 'code' => 'success', + 'message' => __( 'Announcement retrieved successfully', 'tutor' ), + 'data' => $result, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Announcement not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Announcement not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/REST_Lesson.php b/restapi/REST_Lesson.php index 70280f80fe..8337ef1579 100644 --- a/restapi/REST_Lesson.php +++ b/restapi/REST_Lesson.php @@ -25,15 +25,15 @@ class REST_Lesson { use REST_Response; /** - * Post type - * + * Post type + * * @var string $post_type */ private $post_type; /** - * Post parent ID - * + * Post parent ID + * * @var int $post_parent */ private $post_parent; @@ -94,18 +94,18 @@ public function topic_lesson( WP_REST_Request $request ) { } $response = array( - 'status_code' => 'success', - 'message' => __( 'Lesson retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Lesson retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Lesson not found for the given topic ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Lesson not found for the given topic ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/REST_Quiz.php b/restapi/REST_Quiz.php index e3e24f9765..3d67c612fc 100644 --- a/restapi/REST_Quiz.php +++ b/restapi/REST_Quiz.php @@ -10,7 +10,7 @@ namespace TUTOR; -use Themeum\Products\Helpers\QueryHelper; +use Tutor\Helpers\QueryHelper; use WP_REST_Request; if ( ! defined( 'ABSPATH' ) ) { @@ -80,8 +80,6 @@ public function quiz_with_settings( WP_REST_Request $request ) { global $wpdb; - $table = $wpdb->prefix . 'posts'; - $quizs = $wpdb->get_results( $wpdb->prepare( "SELECT @@ -89,7 +87,7 @@ public function quiz_with_settings( WP_REST_Request $request ) { post_title, post_content, post_name - FROM $table + FROM {$wpdb->posts} WHERE post_type = %s AND post_parent = %d ", @@ -107,18 +105,18 @@ public function quiz_with_settings( WP_REST_Request $request ) { array_push( $data, $quiz ); $response = array( - 'status_code' => 'success', - 'message' => __( 'Quiz retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Quiz retrieved successfully', 'tutor' ), + 'data' => $data, ); } return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Quiz not found for given ID', 'tutor' ), - 'data' => $data, + 'code' => 'not_found', + 'message' => __( 'Quiz not found for given ID', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } @@ -135,18 +133,19 @@ public function quiz_question_ans( WP_REST_Request $request ) { $this->post_parent = $request->get_param( 'id' ); - $q_t = $wpdb->prefix . $this->t_quiz_question; // question table + $wpdb->q_t = $wpdb->prefix . $this->t_quiz_question; // Question table. - $q_a_t = $wpdb->prefix . $this->t_quiz_ques_ans; // question answer table + $wpdb->q_a_t = $wpdb->prefix . $this->t_quiz_ques_ans; // Question answer table. $quizs = $wpdb->get_results( - $wpdb->prepare( "SELECT + $wpdb->prepare( + "SELECT question_id, question_title, question_description, question_type, question_mark, - question_settings FROM $q_t + question_settings FROM {$wpdb->q_t} WHERE quiz_id = %d ", $this->post_parent @@ -155,17 +154,18 @@ public function quiz_question_ans( WP_REST_Request $request ) { $data = array(); if ( count( $quizs ) > 0 ) { - // get question ans by question_id + // Get question ans by question_id. foreach ( $quizs as $quiz ) { - // un-serialized question settings. + // Un-serialized question settings. $quiz->question_settings = maybe_unserialize( $quiz->question_settings ); // question options with correct ans. $options = $wpdb->get_results( - $wpdb->prepare( "SELECT + $wpdb->prepare( + "SELECT answer_id, answer_title, - is_correct FROM $q_a_t + is_correct FROM {$wpdb->q_a_t} WHERE belongs_question_id = %d ", $quiz->question_id @@ -179,18 +179,18 @@ public function quiz_question_ans( WP_REST_Request $request ) { } $response = array( - 'status_code' => 'success', - 'message' => __( 'Question retrieved successfully', 'tutor' ), - 'data' => $data, + 'code' => 'success', + 'message' => __( 'Question retrieved successfully', 'tutor' ), + 'data' => $data, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Question not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Question not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); @@ -210,7 +210,7 @@ public function quiz_attempt_details( WP_REST_Request $request ) { $quiz_id = $request->get_param( 'id' ); - $quiz_attempt = $wpdb->prefix . $this->t_quiz_attempt; + $wpdb->quiz_attempt = $wpdb->prefix . $this->t_quiz_attempt; $attempts = $wpdb->get_results( $wpdb->prepare( @@ -226,7 +226,7 @@ public function quiz_attempt_details( WP_REST_Request $request ) { att.attempt_ended_at, att.is_manually_reviewed, att.manually_reviewed_at - FROM $quiz_attempt att + FROM {$wpdb->quiz_attempt} att WHERE att.quiz_id = %d ", $quiz_id @@ -248,18 +248,18 @@ public function quiz_attempt_details( WP_REST_Request $request ) { } $response = array( - 'status_code' => 'success', - 'message' => __( 'Quiz attempts retrieved successfully', 'tutor' ), - 'data' => $attempts, + 'code' => 'success', + 'message' => __( 'Quiz attempts retrieved successfully', 'tutor' ), + 'data' => $attempts, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Quiz attempts not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Quiz attempts not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); @@ -276,8 +276,8 @@ public function quiz_attempt_details( WP_REST_Request $request ) { */ protected function get_quiz_attempt_ans( $quiz_id ) { global $wpdb; - $quiz_attempt_ans = $wpdb->prefix . $this->t_quiz_attempt_ans; - $quiz_question = $wpdb->prefix . $this->t_quiz_question; + $wpdb->quiz_attempt_ans = $wpdb->prefix . $this->t_quiz_attempt_ans; + $wpdb->quiz_question = $wpdb->prefix . $this->t_quiz_question; // get attempt answers. $answers = $wpdb->get_results( @@ -288,8 +288,8 @@ protected function get_quiz_attempt_ans( $quiz_id ) { att_ans.question_mark, att_ans.achieved_mark, att_ans.minus_mark, - att_ans.is_correct FROM $quiz_attempt_ans as att_ans - JOIN $quiz_question q ON q.question_id = att_ans.question_id + att_ans.is_correct FROM {$wpdb->quiz_attempt_ans} as att_ans + JOIN {$wpdb->quiz_question} q ON q.question_id = att_ans.question_id WHERE att_ans.quiz_id = %d ", $quiz_id @@ -324,24 +324,27 @@ protected function get_quiz_attempt_ans( $quiz_id ) { */ protected function answer_titles_by_id( $id ) { global $wpdb; - $table = $wpdb->prefix . $this->t_quiz_ques_ans; + $wpdb->t_quiz_ques_ans = $wpdb->prefix . $this->t_quiz_ques_ans; if ( is_array( $id ) ) { - $array = QueryHelper::prepare_in_clause( $id ); + $array = QueryHelper::prepare_in_clause( $id ); $results = $wpdb->get_results( "SELECT answer_title - FROM $table + FROM {$wpdb->t_quiz_ques_ans} WHERE - answer_id IN ('" . $array . "')" + answer_id IN ('" . $array . "')"//phpcs:ignore ); } else { $results = $wpdb->get_results( - "SELECT + $wpdb->prepare( + "SELECT answer_title - FROM $table - WHERE answer_id = {$id}" + FROM {$wpdb->t_quiz_ques_ans} + WHERE answer_id = %d", + $id + ) ); } diff --git a/restapi/REST_Rating.php b/restapi/REST_Rating.php index 158aee17b0..ead4aadb6b 100644 --- a/restapi/REST_Rating.php +++ b/restapi/REST_Rating.php @@ -71,18 +71,18 @@ public function course_rating( WP_REST_Request $request ) { if ( ! empty( $ratings ) ) { $response = array( - 'status_code' => 'success', - 'message' => __( 'Course rating retrieved successfully', 'tutor' ), - 'data' => $ratings, + 'code' => 'success', + 'message' => __( 'Course rating retrieved successfully', 'tutor' ), + 'data' => $ratings, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Rating not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Rating not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/REST_Topic.php b/restapi/REST_Topic.php index b45f7e6391..54ea386dac 100644 --- a/restapi/REST_Topic.php +++ b/restapi/REST_Topic.php @@ -54,25 +54,23 @@ public function course_topic( WP_REST_Request $request ) { global $wpdb; - $table = $wpdb->prefix . 'posts'; - $result = $wpdb->get_results( - $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) + $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM {$wpdb->posts} WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) ); if ( count( $result ) > 0 ) { $response = array( - 'status_code' => 'get_topic', - 'message' => __( 'Topic retrieved successfully', 'tutor' ), - 'data' => $result, + 'code' => 'get_topic', + 'message' => __( 'Topic retrieved successfully', 'tutor' ), + 'data' => $result, ); return self::send( $response ); } $response = array( - 'status_code' => 'not_found', - 'message' => __( 'Topic not found for given ID', 'tutor' ), - 'data' => array(), + 'code' => 'not_found', + 'message' => __( 'Topic not found for given ID', 'tutor' ), + 'data' => array(), ); return self::send( $response ); diff --git a/restapi/RestAuth.php b/restapi/RestAuth.php index 2df5f59c80..3bd3a7f4ec 100644 --- a/restapi/RestAuth.php +++ b/restapi/RestAuth.php @@ -278,6 +278,7 @@ public static function process_api_request() { * @param string $key api key. * @param string $secret api secret. * @param string $permission authorization permission. + * @param string $description description. * * @return string */ From 41588a70c511eefb8aef95a9a8d76ff47f8cf0eb Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Mon, 22 Apr 2024 16:46:21 +0600 Subject: [PATCH 50/53] filter name changed --- restapi/REST_Response.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/restapi/REST_Response.php b/restapi/REST_Response.php index eea2b15eb9..63bc60383f 100644 --- a/restapi/REST_Response.php +++ b/restapi/REST_Response.php @@ -22,6 +22,7 @@ trait REST_Response { * Send response * * @since 1.7.1 + * @since 2.7.0 renamed filter to tutor_rest_api_response like as pro API response. * * @param array $response The response data. * @@ -29,6 +30,6 @@ trait REST_Response { */ public static function send( array $response ) { $response = new WP_REST_Response( $response ); - return apply_filters( 'tutor_rest_response', $response ); + return rest_ensure_response( apply_filters( 'tutor_rest_api_response', $response ) ); } } From 5a16445873d1511da542f3c068cd3e0ead60a6fe Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Tue, 23 Apr 2024 10:27:15 +0600 Subject: [PATCH 51/53] Q and A class name ref updated --- classes/Q_and_A.php | 63 ++++++++++++------------- classes/Tutor.php | 4 +- templates/dashboard/question-answer.php | 6 +-- views/pages/question_answer.php | 2 +- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/classes/Q_and_A.php b/classes/Q_and_A.php index 42116d0d2d..d3dfbad25a 100644 --- a/classes/Q_and_A.php +++ b/classes/Q_and_A.php @@ -2,8 +2,7 @@ /** * Manage Q & A * - * @package Tutor\Q_and_A - * @category Q_and_A + * @package Tutor\Q_And_A * @author Themeum * @link https://themeum.com * @since 1.0.0 @@ -26,33 +25,35 @@ class Q_And_A { /** * Register hooks * - * @param boolean $allow_hooks true/false to execute the hooks. + * @param boolean $register_hooks true/false to execute the hooks. */ - public function __construct( $allow_hooks = true ) { - if ( $allow_hooks ) { - add_action( 'wp_ajax_tutor_qna_create_update', array( $this, 'tutor_qna_create_update' ) ); - - /** - * Delete question - * - * @since v.1.6.4 - */ - add_action( 'wp_ajax_tutor_delete_dashboard_question', array( $this, 'tutor_delete_dashboard_question' ) ); - - /** - * Take action against single qna - * - * @since v2.0.0 - */ - add_action( 'wp_ajax_tutor_qna_single_action', array( $this, 'tutor_qna_single_action' ) ); - add_action( 'wp_ajax_tutor_qna_bulk_action', array( $this, 'process_bulk_action' ) ); - /** - * Q & A load more - * - * @since v2.0.6 - */ - add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' ); + public function __construct( $register_hooks = true ) { + if ( ! $register_hooks ) { + return; } + + add_action( 'wp_ajax_tutor_qna_create_update', array( $this, 'tutor_qna_create_update' ) ); + + /** + * Delete question + * + * @since v.1.6.4 + */ + add_action( 'wp_ajax_tutor_delete_dashboard_question', array( $this, 'tutor_delete_dashboard_question' ) ); + + /** + * Take action against single qna + * + * @since v2.0.0 + */ + add_action( 'wp_ajax_tutor_qna_single_action', array( $this, 'tutor_qna_single_action' ) ); + add_action( 'wp_ajax_tutor_qna_bulk_action', array( $this, 'process_bulk_action' ) ); + /** + * Q & A load more + * + * @since v2.0.6 + */ + add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' ); } /** @@ -116,7 +117,7 @@ public function tutor_qna_create_update() { $qna_object->user = $user; $qna_object->date = $date; - $this->inset_qna( $qna_object ); + $question_id = $this->inset_qna( $qna_object ); // Provide the html now. // phpcs:disable WordPress.Security.NonceVerification.Missing @@ -141,7 +142,7 @@ public function tutor_qna_create_update() { * Function to insert Q&A * * @param object $qna_object the object to insert. - * @return void|bool + * @return int */ public function inset_qna( $qna_object ) { $course_id = $qna_object->course_id; @@ -188,9 +189,7 @@ public function inset_qna( $qna_object ) { do_action( 'tutor_after_answer_to_question', $answer_id ); } - if ( tutor_is_rest() ) { - return ! empty( $question_id ) ? true : false; - } + return $question_id; } /** diff --git a/classes/Tutor.php b/classes/Tutor.php index 05be6341bf..e51b9a9258 100644 --- a/classes/Tutor.php +++ b/classes/Tutor.php @@ -202,7 +202,7 @@ final class Tutor { private $student; /** - * Q_and_A class object + * Q_And_A class object * * @since 1.1.0 * @@ -504,7 +504,7 @@ public function __construct() { $this->template = new Template(); $this->instructor = new Instructor(); $this->student = new Student(); - $this->q_and_a = new Q_and_A(); + $this->q_and_a = new Q_And_A(); $this->q_and_a_list = new Question_Answers_List(); $this->q_attempt = new Quiz_Attempts_List(); $this->quiz = new Quiz(); diff --git a/templates/dashboard/question-answer.php b/templates/dashboard/question-answer.php index e788a4b7fa..5da080b299 100644 --- a/templates/dashboard/question-answer.php +++ b/templates/dashboard/question-answer.php @@ -11,14 +11,14 @@ use TUTOR\Input; use TUTOR\Instructor; -use TUTOR\Q_and_A; +use TUTOR\Q_And_A; $question_id = Input::get( 'question_id', null, Input::TYPE_INT ); if ( $question_id ) { $question = tutor_utils()->get_qa_question( $question_id ); $user_id = get_current_user_id(); - if ( $question && ! Q_and_A::has_qna_access( $user_id, $question->comment_post_ID ) ) { + if ( $question && ! Q_And_A::has_qna_access( $user_id, $question->comment_post_ID ) ) { tutor_utils()->tutor_empty_state( tutor_utils()->error_message() ); return; } @@ -42,7 +42,7 @@ $view_as = $is_instructor ? ( $view_option ? $view_option : 'instructor' ) : 'student'; $as_instructor_url = add_query_arg( array( 'view_as' => 'instructor' ), tutor()->current_url ); $as_student_url = add_query_arg( array( 'view_as' => 'student' ), tutor()->current_url ); -$qna_tabs = \Tutor\Q_and_A::tabs_key_value( 'student' == $view_as ? get_current_user_id() : null ); +$qna_tabs = \Tutor\Q_And_A::tabs_key_value( 'student' == $view_as ? get_current_user_id() : null ); $active_tab = Input::get( 'tab', 'all' ); ?> diff --git a/views/pages/question_answer.php b/views/pages/question_answer.php index 075152b0d8..243bcdcf9a 100644 --- a/views/pages/question_answer.php +++ b/views/pages/question_answer.php @@ -47,7 +47,7 @@ $navbar_data = array( 'page_title' => __( 'Question & Answer', 'tutor' ), - 'tabs' => \Tutor\Q_and_A::tabs_key_value(), + 'tabs' => \Tutor\Q_And_A::tabs_key_value(), 'active' => $active_tab, ); ?> From d7558240f22c96e699472cdc3f4c81e7d9b74c0c Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Tue, 23 Apr 2024 10:29:22 +0600 Subject: [PATCH 52/53] class name changed --- classes/{Q_and_A.php => Q_And_A.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename classes/{Q_and_A.php => Q_And_A.php} (100%) diff --git a/classes/Q_and_A.php b/classes/Q_And_A.php similarity index 100% rename from classes/Q_and_A.php rename to classes/Q_And_A.php From 727204d5caf46a629b6fe755298b032982b70f5d Mon Sep 17 00:00:00 2001 From: "Md.Harun-Ur-Rashid" Date: Wed, 24 Apr 2024 10:20:42 +0600 Subject: [PATCH 53/53] changelog updated --- readme.txt | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/readme.txt b/readme.txt index ed81cfc2ab..2f1081b8bd 100644 --- a/readme.txt +++ b/readme.txt @@ -316,23 +316,23 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == -= 2.7.0 - April 23, 2024 - -New: API for course content -New: API for student dashboard (Pro) -New: API for student calendar event list (Pro) -New: API for student enrolled courses (Pro) -New: API for quiz attempt list (Pro) -New: API for enrolled student list on a course (Pro) -New: API for become an instructor (Pro) -New: API for student order history (Pro) -New: APIs for profile management (Pro) -New: APIs for Q&A manage (Pro) -Update: WordPress 6.5 compatibility added -Update: Some enhancements to improve the overall experience -Fix: Duplicate H1 tag on each single page -Fix: Various translation related issues -Fix: Resolved known security issues += 2.7.0 - April 24, 2024 + +New: Introduced API for accessing course content +New: Added API for student dashboard functionality (Pro) +New: Implemented API for student calendar event list (Pro) +New: Added API for accessing the student's enrolled courses (Pro) +New: Introduced API for retrieving quiz attempt lists (Pro) +New: Added API for accessing enrolled student lists on a course (Pro) +New: Implemented API for accepting instructor registration applications (Pro) +New: Added API for viewing student order history (Pro) +New: Introduced APIs for profile management (Pro) +New: Implemented APIs for Q&A management (Pro) +Update: Compatibility with WordPress 6.5 +Update: Implemented various enhancements to improve the overall user experience +Fix: Fixed the duplicate H1 tags issue on every single page +Fix: Resolved various translation-related issues +Fix: Enhanced security by solving a few vulnerabilities = 2.6.2 - March 11, 2024