[ Avaa Bypassed ]




Upload:

Command:

hmhc3928@3.133.149.160: ~ $
<?php
/**
 * Quiz Builder
 *
 * @package Tutor\Classes
 * @author Themeum <support@themeum.com>
 * @link https://themeum.com
 * @since 3.0.0
 */

namespace TUTOR;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Tutor\Helpers\HttpHelper;
use Tutor\Helpers\QueryHelper;
use Tutor\Helpers\ValidationHelper;
use Tutor\Models\QuizModel;
use Tutor\Traits\JsonResponse;

/**
 * Class QuizBuilder
 *
 * @since 1.0.0
 */
class QuizBuilder {
	use JsonResponse;

	const TRACKING_KEY   = '_data_status';
	const FLAG_NEW       = 'new';
	const FLAG_UPDATE    = 'update';
	const FLAG_NO_CHANGE = 'no_change';

	/**
	 * Register hooks and dependencies.
	 *
	 * @param boolean $register_hooks register hooks or not.
	 */
	public function __construct( $register_hooks = true ) {
		if ( ! $register_hooks ) {
			return;
		}

		add_action( 'wp_ajax_tutor_quiz_builder_save', array( $this, 'ajax_quiz_builder_save' ) );
	}


	/**
	 * Prepare question data.
	 *
	 * @since 3.0.0
	 *
	 * @param int   $quiz_id quiz id.
	 * @param array $input question data.
	 *
	 * @return array
	 */
	private function prepare_question_data( $quiz_id, $input ) {
		$question_title       = Input::sanitize( wp_slash( $input['question_title'] ), '' );
		$question_description = Input::sanitize( wp_slash( $input['question_description'] ) ?? '', '', Input::TYPE_KSES_POST );
		$question_type        = Input::sanitize( $input['question_type'], '' );
		$question_mark        = Input::sanitize( $input['question_mark'], 1, Input::TYPE_INT );
		$question_settings    = Input::sanitize_array( $input['question_settings'] );

		$data = array(
			'quiz_id'              => $quiz_id,
			'question_title'       => $question_title,
			'question_description' => $question_description,
			'question_type'        => $question_type,
			'question_mark'        => $question_mark,
			'question_settings'    => maybe_serialize( $question_settings ),
		);

		return apply_filters( 'tutor_quiz_question_data', $data, $input );
	}

	/**
	 * Prepare answer data.
	 *
	 * @param int    $question_id question id.
	 * @param string $question_type question type.
	 * @param array  $input answer data.
	 *
	 * @return array
	 */
	public function prepare_answer_data( $question_id, $question_type, $input ) {
		$answer_title         = Input::sanitize( wp_slash( $input['answer_title'] ) ?? '', '' );
		$is_correct           = Input::sanitize( $input['is_correct'] ?? 0, 0, Input::TYPE_INT );
		$image_id             = Input::sanitize( $input['image_id'] ?? null );
		$answer_two_gap_match = Input::sanitize( $input['answer_two_gap_match'] ?? '' );
		$answer_view_format   = Input::sanitize( $input['answer_view_format'] ?? '' );
		$answer_settings      = null;

		$answer_data = array(
			'belongs_question_id'   => $question_id,
			'belongs_question_type' => $question_type,
			'answer_title'          => $answer_title,
			'is_correct'            => $is_correct,
			'image_id'              => $image_id,
			'answer_two_gap_match'  => $answer_two_gap_match,
			'answer_view_format'    => $answer_view_format,
			'answer_settings'       => $answer_settings,
		);

		return $answer_data;
	}

	/**
	 * Save quiz questions.
	 *
	 * @since 3.0.0
	 *
	 * @param int   $quiz_id quiz id.
	 * @param array $questions questions data.
	 *
	 * @return void
	 */
	public function save_questions( $quiz_id, $questions ) {
		global $wpdb;
		$questions_table = $wpdb->prefix . 'tutor_quiz_questions';
		$answers_table   = $wpdb->prefix . 'tutor_quiz_question_answers';

		$question_order = 0;
		foreach ( $questions as $question ) {
			$data_status      = isset( $question[ self::TRACKING_KEY ] ) ? $question[ self::TRACKING_KEY ] : self::FLAG_NO_CHANGE;
			$question_type    = Input::sanitize( $question['question_type'] );
			$question_data    = $this->prepare_question_data( $quiz_id, $question );
			$question_answers = isset( $question['question_answers'] ) ? $question['question_answers'] : array();

			// New question.
			if ( self::FLAG_NEW === $data_status ) {
				$wpdb->insert( $questions_table, $question_data );
				$question_id = $wpdb->insert_id;
			}

			// Update question.
			if ( self::FLAG_UPDATE === $data_status ) {
				$question_id = (int) $question['question_id'];
				$wpdb->update(
					$questions_table,
					$question_data,
					array( 'question_id' => $question_id )
				);
			}

			if ( self::FLAG_NO_CHANGE === $data_status ) {
				$question_id = $question['question_id'];
			}

			// Save sort order.
			$question_order++;
			$wpdb->update(
				$questions_table,
				array( 'question_order' => $question_order ),
				array( 'question_id' => $question_id )
			);

			// Save question's answers.
			$answer_order = 0;
			foreach ( $question_answers as $answer ) {
				$data_status = isset( $answer[ self::TRACKING_KEY ] ) ? $answer[ self::TRACKING_KEY ] : self::FLAG_NO_CHANGE;
				$answer_data = $this->prepare_answer_data( $question_id, $question_type, $answer );

				// New answer.
				if ( self::FLAG_NEW === $data_status ) {
					$wpdb->insert( $answers_table, $answer_data );
					$answer_id = $wpdb->insert_id;
				}

				// Update answer.
				if ( self::FLAG_UPDATE === $data_status ) {
					$answer_id = $answer['answer_id'];
					$wpdb->update(
						$answers_table,
						$answer_data,
						array( 'answer_id' => $answer_id )
					);
				}

				if ( self::FLAG_NO_CHANGE === $data_status ) {
					$answer_id = $answer['answer_id'];
				}

				// Save sort order.
				$answer_order++;
				$wpdb->update(
					$answers_table,
					array( 'answer_order' => $answer_order ),
					array( 'answer_id' => $answer_id )
				);
			}
		}
	}

	/**
	 * Validate payload.
	 *
	 * @since 3.0.0
	 *
	 * @param array $payload payload.
	 *
	 * @return object consist success, errors.
	 */
	public function validate_payload( $payload ) {
		$errors  = array();
		$success = true;

		if ( ! is_array( $payload ) ) {
			$success           = false;
			$errors['payload'] = __( 'Invalid payload', 'tutor' );
		}

		$rules = array(
			'post_title'  => 'required',
			'quiz_option' => 'required|is_array',
			'questions'   => 'required|is_array',
		);

		$validation = ValidationHelper::validate(
			$rules,
			$payload
		);

		if ( ! $validation->success ) {
			$success = false;
			$errors  = array_merge( $errors, $validation->errors );
		}

		foreach ( $payload['questions'] as $question ) {
			if ( ! isset( $question[ self::TRACKING_KEY ] ) ) {
				$success                        = false;
				$errors[ self::TRACKING_KEY ][] = sprintf( __( '%s is required for each question', 'tutor' ), self::TRACKING_KEY ); //phpcs:ignore
				break;
			}

			if ( ! in_array( $question[ self::TRACKING_KEY ], array( self::FLAG_NEW, self::FLAG_UPDATE, self::FLAG_NO_CHANGE ), true ) ) {
				$success                        = false;
				$errors[ self::TRACKING_KEY ][] = sprintf( __( 'Invalid value for %s', 'tutor' ), self::TRACKING_KEY ); //phpcs:ignore
				break;
			}

			if ( ! isset( $question['question_settings'] ) || ! is_array( $question['question_settings'] ) ) {
				$success                       = false;
				$errors['question_settings'][] = __( 'Question settings is required with array data', 'tutor' );
				break;
			}
		}

		return (object) array(
			'success' => $success,
			'errors'  => $errors,
		);
	}

	/**
	 * Handle delete questions and answers.
	 *
	 * @since 3.0.0
	 *
	 * @param array $deleted_question_ids question ids.
	 * @param array $deleted_answer_ids answer ids.
	 *
	 * @return void
	 */
	public function handle_delete( $deleted_question_ids = array(), $deleted_answer_ids = array() ) {
		global $wpdb;
		$deleted_question_ids = array_filter( $deleted_question_ids, 'is_numeric' );
		$deleted_answer_ids   = array_filter( $deleted_answer_ids, 'is_numeric' );

		if ( count( $deleted_question_ids ) ) {
			$id_str = QueryHelper::prepare_in_clause( $deleted_question_ids );
            //phpcs:ignore -- sanitized $id_str.
            $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN (" . $id_str . ')' );
			do_action( 'tutor_deleted_quiz_question_ids', $deleted_question_ids );
		}

		if ( count( $deleted_answer_ids ) ) {
			$id_str = QueryHelper::prepare_in_clause( $deleted_answer_ids );
            //phpcs:ignore -- sanitized $id_str.
            $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id IN (" . $id_str . ')' );
		}
	}

	/**
	 * Create or update quiz.
	 *
	 * @since 3.0.0
	 *
	 * @param int   $topic_id topic id.
	 * @param array $payload payload.
	 *
	 * @return object consist success, errors.
	 */
	public function save_quiz( $topic_id, $payload ) {
		$success = true;
		$data    = null;
		$errors  = array();

		$validation = $this->validate_payload( $payload );

		if ( ! $validation->success ) {
			return (object) array(
				'success' => false,
				'errors'  => $validation->errors,
			);
		}

		$is_update = isset( $payload['ID'] );
		$quiz_id   = $is_update ? $payload['ID'] : null;
		$questions = isset( $payload['questions'] ) ? $payload['questions'] : array();

		$menu_order = (int) ( isset( $payload['menu_order'] )
						? $payload['menu_order']
						: tutor_utils()->get_next_course_content_order_id( $topic_id, $quiz_id ) );

		$quiz_data = array(
			'post_type'    => tutor()->quiz_post_type,
			'post_title'   => Input::sanitize( wp_slash( $payload['post_title'] ?? '' ) ),
			'post_content' => Input::sanitize( wp_slash( $payload['post_content'] ?? '' ) ),
			'post_status'  => 'publish',
			'post_author'  => get_current_user_id(),
			'post_parent'  => $topic_id,
			'menu_order'   => $menu_order,
		);

		global $wpdb;
		$wpdb->query( 'START TRANSACTION' );

		try {
			// Add or update the quiz.
			if ( $is_update ) {
				$quiz_data['ID'] = $quiz_id;
			}

			$quiz_id = wp_insert_post( $quiz_data );
			do_action( ( $is_update ? 'tutor_quiz_updated' : 'tutor_initial_quiz_created' ), $quiz_id );

			// Save quiz settings.
			$quiz_option = Input::sanitize_array( $payload['quiz_option'] ?? array() ); //phpcs:ignore
			update_post_meta( $quiz_id, Quiz::META_QUIZ_OPTION, $quiz_option );
			do_action( 'tutor_quiz_settings_updated', $quiz_id );

			// Save quiz questions.
			if ( count( $questions ) ) {
				$this->save_questions( $quiz_id, $questions );
			}

			// Delete questions and answers.
			$deleted_question_ids = Input::post( 'deleted_question_ids', array(), Input::TYPE_ARRAY );
			$deleted_answer_ids   = Input::post( 'deleted_answer_ids', array(), Input::TYPE_ARRAY );
			$this->handle_delete( $deleted_question_ids, $deleted_answer_ids );

			$wpdb->query( 'COMMIT' );

			$data = $quiz_id;

		} catch ( \Throwable $th ) {
			$wpdb->query( 'ROLLBACK' );

			$success         = false;
			$errors['500'][] = $th->getMessage();
		}

		return (object) array(
			'success' => $success,
			'data'    => $data,
			'errors'  => $errors,
		);
	}

	/**
	 * Create or update quiz from new course builder.
	 *
	 * @since 3.0.0
	 *
	 * @return void json response.
	 */
	public function ajax_quiz_builder_save() {
		tutor_utils()->check_nonce();

		$payload    = $_POST['payload'] ?? array(); //phpcs:ignore
		if ( is_string( $payload ) ) {
			$payload = json_decode( wp_unslash( $payload ), true );
		}

		$course_id  = Input::post( 'course_id', 0, Input::TYPE_INT );
		$topic_id   = Input::post( 'topic_id', 0, Input::TYPE_INT );
		$course_cls = new Course( false );

		$course_cls->check_access( $course_id );

		$result = $this->save_quiz( $topic_id, wp_slash( $payload ) );
		if ( $result->success ) {
			$quiz_id      = $result->data;
			$quiz_details = QuizModel::get_quiz_details( $quiz_id );
			$this->json_response( __( 'Quiz saved successfully', 'tutor' ), $quiz_details );
		} else {
			$this->json_response( __( 'Error', 'tutor' ), $result->errors, HttpHelper::STATUS_BAD_REQUEST );
		}

	}
}

Filemanager

Name Type Size Permission Actions
Addons.php File 11.6 KB 0644
Admin.php File 21.3 KB 0644
Ajax.php File 16.82 KB 0644
Announcements.php File 2.67 KB 0644
Assets.php File 23.25 KB 0644
Backend_Page_Trait.php File 4.39 KB 0644
BaseController.php File 1.47 KB 0644
Course.php File 85.39 KB 0644
Course_Embed.php File 2.55 KB 0644
Course_Filter.php File 8.67 KB 0644
Course_List.php File 13.7 KB 0644
Course_Settings_Tabs.php File 1.16 KB 0644
Course_Widget.php File 8.19 KB 0644
Custom_Validation.php File 513 B 0644
Dashboard.php File 1.23 KB 0644
Earnings.php File 9.53 KB 0644
FormHandler.php File 7.16 KB 0644
Frontend.php File 2.94 KB 0644
Gutenberg.php File 4.62 KB 0644
Input.php File 9.08 KB 0644
Instructor.php File 12.99 KB 0644
Instructors_List.php File 12.97 KB 0644
Lesson.php File 17.08 KB 0644
Options_V2.php File 63.19 KB 0644
Permalink.php File 2 KB 0644
Post_types.php File 18.3 KB 0644
Private_Course_Access.php File 2.52 KB 0644
Q_And_A.php File 10.66 KB 0644
Question_Answers_List.php File 2.54 KB 0644
Quiz.php File 62.02 KB 0644
QuizBuilder.php File 11.5 KB 0644
Quiz_Attempts_List.php File 7.32 KB 0644
RestAPI.php File 7.97 KB 0644
Reviews.php File 2.71 KB 0644
Rewrite_Rules.php File 5.18 KB 0644
Shortcode.php File 14.22 KB 0644
Singleton.php File 1.08 KB 0644
Student.php File 10.18 KB 0644
Students_List.php File 2.37 KB 0644
Taxonomies.php File 8.2 KB 0644
Template.php File 14.18 KB 0644
Theme_Compatibility.php File 683 B 0644
Tools.php File 3.33 KB 0644
Tools_V2.php File 18.18 KB 0644
Tutor.php File 36.06 KB 0644
TutorEDD.php File 4.63 KB 0644
Tutor_Base.php File 1.48 KB 0644
Tutor_Setup.php File 33.25 KB 0644
Upgrader.php File 7.49 KB 0644
User.php File 14.66 KB 0644
Utils.php File 263.33 KB 0644
Video_Stream.php File 3.94 KB 0644
WhatsNew.php File 4.07 KB 0644
Withdraw.php File 9.49 KB 0644
Withdraw_Requests_List.php File 6.15 KB 0644
WooCommerce.php File 23.15 KB 0644