芝麻web文件管理V1.00
编辑当前文件:/home/sditechnicalteam/socialdigivlms.com/wp-content/plugins/tutor/classes/QuizBuilder.php
* @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 ); } } }