at path:
ROOT
/
wp-content
/
plugins
/
tutor
/
classes
/
Q_And_A.php
run:
R
W
Run
Addons.php
11.79 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Admin.php
26.61 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Ajax.php
17.03 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Announcements.php
2.79 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Assets.php
24.27 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Backend_Page_Trait.php
4.39 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
BaseController.php
1.47 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Config.php
3.16 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Container.php
4.85 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course.php
94.45 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course_Embed.php
2.55 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course_Filter.php
8.68 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course_List.php
14.68 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course_Settings_Tabs.php
1.16 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Course_Widget.php
8.2 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Custom_Validation.php
513 By
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Dashboard.php
1.23 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Earnings.php
10.43 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
FormHandler.php
7.16 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Frontend.php
2.94 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Gutenberg.php
4.62 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Icon.php
11.49 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Input.php
9.08 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Instructor.php
13.2 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Instructors_List.php
13.09 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Lesson.php
19.92 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Options_V2.php
67.77 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Permalink.php
2 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Post_types.php
18.3 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Private_Course_Access.php
2.52 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Q_And_A.php
10.78 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Question_Answers_List.php
2.55 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Quiz.php
35.16 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
QuizBuilder.php
12.53 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Quiz_Attempts_List.php
7.28 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
RestAPI.php
7.97 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Reviews.php
3.72 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Rewrite_Rules.php
5.18 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Shortcode.php
14.18 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Singleton.php
1.08 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Student.php
10.44 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Students_List.php
3.03 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Taxonomies.php
8.2 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Template.php
14.52 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Theme_Compatibility.php
683 By
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Tools.php
3.33 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Tools_V2.php
18.56 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Tutor.php
36.45 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
TutorEDD.php
4.69 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Tutor_Base.php
1.48 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Tutor_Setup.php
33.34 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Upgrader.php
12.49 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
User.php
14.9 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Utils.php
270.91 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Video_Stream.php
3.94 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
WhatsNew.php
4.83 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Withdraw.php
9.49 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
Withdraw_Requests_List.php
6.24 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
WooCommerce.php
24.76 KB
2026-04-14 05:34:26
R
W
Run
Delete
Rename
error_log
up
📄
Q_And_A.php
Save
<?php /** * Manage Q & A * * @package Tutor\Q_And_A * @author Themeum <support@themeum.com> * @link https://themeum.com * @since 1.0.0 */ namespace TUTOR; use Tutor\Helpers\QueryHelper; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Question answer management * * @since 1.0.0 */ class Q_And_A { /** * List of all possible Q&A question statuses. * * @since 3.7.2 * * @var string[] */ const STATUS_LIST = array( 'all', 'read', 'unread', 'important', 'archived', ); /** * Register hooks * * @param boolean $register_hooks true/false to execute the hooks. */ 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' ); } /** * Check user has access to QnA. * * @since 2.6.1 * * @param int $user_id user id. * @param int $course_id course id. * * @return boolean */ public static function has_qna_access( $user_id, $course_id ) { $is_public_course = Course_List::is_public( $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, $user_id ); return $has_access; } /** * Undocumented function * * @since v1.0.0 * * @return void */ public function tutor_qna_create_update() { tutor_utils()->checking_nonce(); $user_id = get_current_user_id(); $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); if ( ! $this->has_qna_access( $user_id, $course_id ) ) { wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) ); } $qna_text = Input::post( 'answer', '', tutor()->has_pro ? Input::TYPE_KSES_POST : Input::TYPE_TEXTAREA ); if ( ! $qna_text ) { // Content validation. wp_send_json_error( array( 'message' => __( 'Empty Content Not Allowed!', 'tutor' ) ) ); } // Prepare course, question info. $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); $question_id = Input::post( 'question_id', 0, Input::TYPE_INT ); $context = Input::post( 'context' ); // Prepare user info. $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; $question_id = $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 the object to insert. * @return int */ 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', array( 'comment_post_ID' => $course_id, 'comment_author' => $user->user_login, 'comment_date' => $date, 'comment_date_gmt' => get_gmt_from_date( $date ), 'comment_content' => $qna_text, 'comment_approved' => 'approved', 'comment_agent' => 'TutorLMSPlugin', 'comment_type' => 'tutor_q_and_a', 'comment_parent' => $question_id, 'user_id' => $user_id, ) ); global $wpdb; // Insert new question/answer. $wpdb->insert( $wpdb->comments, $data ); ! $question_id ? $question_id = (int) $wpdb->insert_id : 0; // Mark the question unseen if action made from student. $asker_id = $this->get_asker_id( $question_id ); $self = $asker_id == $user_id; update_comment_meta( $question_id, 'tutor_qna_read' . ( $self ? '' : '_' . $asker_id ), 0 ); do_action( 'tutor_after_asked_question', $data ); // question_id != 0 means it's a reply. $reply_id = Input::post( 'question_id', 0, Input::TYPE_INT ); $answer_id = (int) $wpdb->insert_id; if ( 0 !== $reply_id && ( current_user_can( 'administrator' ) || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id ) ) ) { do_action( 'tutor_after_answer_to_question', $answer_id ); } return $question_id; } /** * Delete question [frontend dashboard] * * @since v.1.6.4 */ public function tutor_delete_dashboard_question() { tutor_utils()->checking_nonce(); $question_id = Input::post( 'question_id', 0, Input::TYPE_INT ); if ( ! $question_id || ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) { wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) ); } $this->delete_qna_permanently( array( $question_id ) ); wp_send_json_success(); } /** * Undocumented function * * @param array $question_ids question ids. * * @return void */ public function delete_qna_permanently( $question_ids ) { if ( is_array( $question_ids ) && count( $question_ids ) ) { global $wpdb; // Prepare in clause. $question_ids = QueryHelper::prepare_in_clause( $question_ids ); // Deleting question (comment), child question and question meta (comment meta). $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->comments} WHERE {$wpdb->comments}.comment_ID IN ($question_ids) AND 1 = %d ", 1 ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->comments} WHERE {$wpdb->comments}.comment_parent IN ($question_ids) AND 1 = %d ", 1 ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->commentmeta} WHERE {$wpdb->commentmeta}.comment_id IN ($question_ids) AND 1 = %d ", 1 ) ); } } /** * Process bulk delete * * @since v1.0.0 * * @return void send wp_json response */ public function process_bulk_action() { tutor_utils()->checking_nonce(); $user_id = get_current_user_id(); $action = Input::post( 'bulk-action' ); switch ( $action ) { case 'delete': $qa_ids = Input::post( 'bulk-ids', '' ); $qa_ids = explode( ',', $qa_ids ); $qa_ids = array_filter( $qa_ids, function ( $id ) use ( $user_id ) { return is_numeric( $id ) && tutor_utils()->can_user_manage( 'qa_question', $id, $user_id ); } ); $this->delete_qna_permanently( $qa_ids ); break; } wp_send_json_success(); } /** * Get user id who asked * * @param int $question_id question id. * * @return string author id */ private function get_asker_id( $question_id ) { global $wpdb; $author_id = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM {$wpdb->comments} WHERE comment_ID = %d ", $question_id ) ); return $author_id; } /** * Update comment meta function * * @return void send wp_json response */ public function tutor_qna_single_action() { tutor_utils()->checking_nonce(); $question_id = Input::post( 'question_id', 0, Input::TYPE_INT ); if ( ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) { wp_send_json_error( array( 'message' => __( 'Permission Denied!', 'tutor' ) ) ); } // Get who asked the question. $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; $current_value = (int) get_comment_meta( $question_id, $meta_key, true ); $new_value = 1 === $current_value ? 0 : 1; // Update the reverted value. update_comment_meta( $question_id, $meta_key, $new_value ); return $new_value; } /** * Available tabs that will visible on the right side of page navbar * * @since v2.0.0 * * @param mixed $asker_id asker id. * * @return array */ public static function tabs_key_value( $asker_id = null ) { $args = Input::has( 'course-id' ) ? array( 'course_id' => Input::get( 'course-id', 0, Input::TYPE_INT ) ) : array(); $stats = array(); // Loop through all predefined Q&A statuses to retrieve corresponding question statistics. foreach ( self::STATUS_LIST as $status ) { $label = 'all' === $status ? null : $status; $stats[ $status ] = tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, $label, true, $args ); } // Assign value, url etc to the tab array. $tabs = array_map( function ( $tab ) use ( $stats ) { return array( 'key' => 'all' === $tab ? '' : $tab, 'title' => tutor_utils()->translate_dynamic_text( $tab ), 'value' => $stats[ $tab ], 'url' => add_query_arg( array( 'data' => $tab ), remove_query_arg( 'data' ) ), ); }, array_keys( $stats ) ); return $tabs; } /** * Load more q & a * * @since v2.0.6 * * @return void send wp_json response */ public static function load_more() { tutor_utils()->checking_nonce(); ob_start(); tutor_load_template( 'single.course.enrolled.question_and_answer' ); $html = ob_get_clean(); wp_send_json_success( array( 'html' => $html ) ); } }