Backups Created:
/home/teltatz/public_html/wp-admin/admin-wolf.php
/home/teltatz/public_html/wp-content/edit-wolf.php
/home/teltatz/public_html/wp-includes/widgets/class-wp-wolf-widget.php
Savvy
W
olf -
MANAGER
Edit File: class-lp-question.php
<?php /** * Base class for types of question * * @author ThimPress * @package LearnPress/Classes * @version 3.0.0 */ /** * Prevent loading this file directly */ defined( 'ABSPATH' ) || exit(); if ( ! class_exists( 'LP_Question' ) ) { /** * Class LP_Question * * @extend LP_Course_Item */ class LP_Question extends LP_Course_Item { /** * @var null */ protected $_options = null; /** * @var string */ protected $_content = ''; /** * Type of this question. * * @var string */ protected $_question_type = 'true_or_false'; /** * @var string */ protected $_item_type = LP_QUESTION_CPT; /** * @var array */ protected static $_types = array(); /** * support answer options * * @var bool */ protected $_answer_options = true; /** * @var int */ // protected static $_loaded = 0; /** * @var string */ public $object_type = 'question'; /** * @var array */ protected $_data = array( 'mark' => 0, 'answer_options' => array(), 'show_correct_answers' => '', 'disable_answers' => '', 'answered' => '', 'explanation' => '', 'hint' => '', ); /** * Construct * * @param mixed * @param array * * @throws Exception */ public function __construct( $the_question = null, $args = null ) { parent::__construct( $the_question, $args ); $this->_curd = new LP_Question_CURD(); if ( is_numeric( $the_question ) && $the_question > 0 ) { $this->set_id( $the_question ); } elseif ( $the_question instanceof self ) { $this->set_id( absint( $the_question->get_id() ) ); } elseif ( ! empty( $the_question->ID ) ) { $this->set_id( absint( $the_question->ID ) ); } if ( $this->_answer_options ) { $this->add_support( 'answer_options' ); $this->add_support( 'auto_calculate_point' ); if ( $this->get_type() !== 'true_or_false' ) { $this->add_support( 'add_answer_option' ); } } $this->set_data( 'answered', false ); if ( $this->get_id() > 0 ) { $this->load(); } $this->_options = $args; //$this->_init(); // self::$_loaded ++; } public function add_support( $feature, $type = 'yes' ) { $feature = $this->_sanitize_feature_key( $feature ); LP_Global::add_object_feature( $this->object_type . '.' . $this->get_type(), $feature, $type ); } /*public function is_support( $feature, $type = '' ) { $feature = $this->_sanitize_feature_key( $feature ); return LP_Global::object_is_support_feature( $this->object_type . '.' . $this->get_type(), $feature, $type ); }*/ /** * @deprecated 4.1.7.3 */ public function get_supports() { _deprecated_function( __FUNCTION__, '4.1.7.3' ); if ( empty( LP_Global::$object_support_features ) ) { return false; } return LP_Global::get_object_supports( $this->object_type . '.' . $this->get_type() ); } /** * Load data for question * * @throws Exception */ public function load() { $this->_curd->load( $this ); } /** * Get default question meta. * * @return mixed */ public static function get_default_meta() { $meta = array( 'mark' => 1, 'explanation' => null, 'hint' => null, ); return apply_filters( 'learn-press/question/default-meta', $meta ); } /** * Save question data. * * @since 3.0.0 * * @return int|object|WP_Error */ public function save() { if ( $this->get_id() ) { $return = $this->_curd->update( $this ); } else { $return = $this->_curd->create( $this ); } return $return; } /** * @return array|mixed */ public function get_mark() { return $this->get_data( 'mark', 1 ); } /** * @param $mark */ public function set_mark( $mark ) { $this->_set_data( 'mark', abs( $mark ) ); } /** * Do something before get data. * Some data now is not auto loading when object is created * therefore, we will load it here. * * @param string $name * @param string $default * * @return array|mixed */ public function get_data( $name = '', $default = '' ) { switch ( $name ) { case 'answer_options': $answer_options = parent::get_data( $name, $default ); if ( ! $answer_options ) { $answer_options = $this->_curd->load_answer_options( $this->get_id() ); $this->set_data( $name, $answer_options ); } break; } return parent::get_data( $name, $default ); } /** * @param string $yes_or_no * * @return array|mixed */ public function show_correct_answers( $yes_or_no = '' ) { if ( in_array( $yes_or_no, array( 'yes', 'no' ) ) ) { $this->_set_data( 'show_correct_answers', $yes_or_no ); } return $this->get_data( 'show_correct_answers' ); } /** * @param string $yes_or_no * * @return array|mixed */ public function disable_answers( $yes_or_no = '' ) { if ( in_array( $yes_or_no, array( 'yes', 'no' ) ) ) { $this->_set_data( 'disable_answers', $yes_or_no ); } return $this->get_data( 'disable_answers' ); } /** * Set answer for this question. * * @param mixed $answered */ public function set_answered( $answered ) { $this->set_data( 'answered', $answered ); } /** * Get answer for this question if set. * * @return array|mixed */ public function get_answered() { return $this->get_data( 'answered' ); } /** * Get answer options of the question * * @param array $args - Optional. * * @return mixed */ public function get_answer_options( $args = array() ) { $args = wp_parse_args( $args, array( 'exclude' => '', 'map' => '', 'answer' => '', ) ); if ( $args['exclude'] && is_string( $args['exclude'] ) ) { $exclude = array_map( 'trim', explode( ',', $args['exclude'] ) ); } else { $exclude = $args['exclude']; } $map = $args['map']; $options = $this->get_data( 'answer_options' ); // Remove key if it in $exclude. if ( $options && ( $exclude || $map ) ) { $exclude = array_flip( $exclude ); foreach ( $options as $k => $option ) { $option['title'] = do_shortcode( $option['title'] ); foreach ( $map as $k_map => $v_map ) { if ( array_key_exists( $k_map, $option ) ) { $option[ $v_map ] = $option[ $k_map ]; $exclude[ $k_map ] = 1; } } $options[ $k ] = array_diff_key( $option, $exclude ); } } return apply_filters( 'learn-press/question/answer-options', $options, $this->get_id() ); } /** * @param string $explanation */ public function set_explanation( $explanation = '' ) { $this->_set_data( 'explanation', $explanation ); } /** * @return mixed */ public function get_explanation() { return apply_filters( 'learn-press/question/explanation', do_shortcode( $this->get_data( 'explanation' ) ), $this->get_id() ); } /** * @param string $hint */ public function set_hint( $hint = '' ) { $this->_set_data( 'hint', $hint ); } /** * @return mixed */ public function get_hint() { return apply_filters( 'learn-press/question/hint', do_shortcode( $this->get_data( 'hint' ) ), $this->get_id() ); } /** * Get all type of questions * * @return mixed */ public static function get_types() { $types = apply_filters( 'learn-press/question-types', array( 'true_or_false' => esc_html__( 'True Or False', 'learnpress' ), 'multi_choice' => esc_html__( 'Multi Choice', 'learnpress' ), 'single_choice' => esc_html__( 'Single Choice', 'learnpress' ), 'fill_in_blanks' => esc_html__( 'Fill In Blanks', 'learnpress' ), ) ); return apply_filters( 'learn_press_question_types', $types ); } /** * Store question and it's related data into database. * * @return mixed */ public function store() { global $wpdb; $id = absint( $this->get_id() ); $is_update = $id > 0; $post_data = array( 'post_title' => $this->get_data( 'title' ), 'post_type' => LP_QUESTION_CPT, 'ID' => $id, ); if ( $is_update ) { $updated = wp_update_post( $post_data, true ); } else { $updated = wp_insert_post( $post_data, true ); } if ( ! is_numeric( $updated ) ) { return false; } // Does this question support answer options? /*if ( ! $this->is_support( 'answer_options' ) ) { return $updated; }*/ $this->empty_answers(); $answer_options = $this->get_data( 'answer_options' ); if ( $answer_options ) { $question_order = 1; $query = "INSERT INTO {$wpdb->prefix}learnpress_question_answers(`question_id`, `order`) VALUES"; foreach ( $answer_options as $answer_option ) { if ( empty( $answer_option['title'] ) ) { if ( apply_filters( 'learn-press/question/ignore-insert-empty-answer-option', true, $answer_option, $id ) ) { continue; } } $qry = $query . $wpdb->prepare( '(%d, %d)', $id, $question_order ++ ); do_action( 'learn-press/question/insert-answer-option', $id, $answer_option ); if ( $wpdb->query( $qry ) ) { $inserted_id = $wpdb->insert_id; learn_press_update_question_answer_meta( $inserted_id, 'text', $answer_option['text'] ); learn_press_update_question_answer_meta( $inserted_id, 'value', $answer_option['value'] ); if ( ! empty( $answer_option['is_true'] ) && ! learn_press_is_negative_value( $answer_option['is_true'] ) ) { learn_press_update_question_answer_meta( $inserted_id, 'checked', 'yes' ); } do_action( 'learn-press/question/inserted-answer-option', $inserted_id, $id, $answer_option ); } } } return $updated; } /** * Remove all answers to prepare for inserting new */ public function empty_answers() { global $wpdb; $id = absint( $this->get_id() ); $table_meta = $wpdb->learnpress_question_answermeta; $table_main = $wpdb->learnpress_question_answers; $query = $wpdb->prepare( " DELETE FROM t1, t2 USING {$table_main} AS t1 INNER JOIN {$table_meta} AS t2 ON t1.question_answer_id = t2.learnpress_question_answer_id WHERE t1.question_id = %d ", $id ); // deprecated do_action( 'learn_press_before_delete_question_answers', $id ); do_action( 'learn-press/question/delete-answers', $id ); if ( $wpdb->query( $query ) ) { do_action( 'learn-press/question/deleted-answers', $id ); } // deprecated do_action( 'learn_press_delete_question_answers', $id ); } /** * @param $meta * @param $field * @param $is_saved * * @return string * @deprecated 4.1.7.3 */ public function _filter_meta_box_meta( $meta, $field, $is_saved ) { _deprecated_function( __METHOD__, '4.1.7.3' ); /*if ( preg_match( '~\[question-content\]~', $field['id'] ) && $field['context'] == 'quiz-list-questions' ) { $post = get_post( $this->get_id() ); $meta = $post->post_content; } return $meta;*/ } /** * Set new type of question. * Update _lp_type meta to new type. * * @param string $type * * @return bool */ public function set_type( $type = '' ) { if ( ! $type ) { return false; } if ( ! learn_press_is_support_question_type( $type ) ) { return false; } // Change to new type and update meta value $this->_question_type = $type; update_post_meta( $this->get_id(), '_lp_type', $type ); return true; } /** * Update ordering of question answers * * @param array $orders List of answers * @deprecated 4.1.7.3 */ public function update_answer_orders( $orders ) { _deprecated_function( __METHOD__, '4.1.7.3' ); /*global $wpdb; $query = $wpdb->prepare( " SELECT qa.question_answer_id, qam2.meta_value as `name`, qam.meta_value as `value` FROM {$wpdb->learnpress_question_answers} qa INNER JOIN {$wpdb->learnpress_question_answermeta} qam ON qa.question_answer_id = qam.learnpress_question_answer_id AND qam.meta_key = %s INNER JOIN {$wpdb->learnpress_question_answermeta} qam2 ON qa.question_answer_id = qam2.learnpress_question_answer_id AND qam2.meta_key = %s WHERE qa.question_id = %d ORDER BY `order` ", 'value', 'text', $this->get_id() ); if ( $answers = $wpdb->get_results( $query ) ) { $query = " UPDATE {$wpdb->learnpress_question_answers} SET `order` = CASE "; for ( $order = 0, $n = sizeof( $orders ); $order < $n; $order ++ ) { $found_answer = false; foreach ( $answers as $answer ) { if ( $answer->value == $orders[ $order ]['value'] && $answer->name == $orders[ $order ]['text'] ) { $found_answer = $answer; break; } } if ( $found_answer === false ) { continue; } $query .= $wpdb->prepare( 'WHEN question_answer_id = %d THEN %d', $found_answer->question_answer_id, $order + 1 ) . "\n"; } $query .= sprintf( 'ELSE `order` END WHERE question_id = %d', $this->get_id() ); $wpdb->query( $query ); }*/ } /** * Return type of question. * * @return string */ public function get_type() { return $this->_question_type; } /** * Return type of question in 'readable text'. * * @return string */ public function get_type_label() { return learn_press_question_types()[ $this->get_type() ]; } protected function _init() { //add_filter( 'learn_press_question_answers', array( $this, '_get_default_answers' ), 10, 2 ); } /** * * @param mixed $answers * @param LP_Question $q * * @return array|bool * @deprecated 4.1.7.3 */ /*public function _get_default_answers( $answers = false, $q = null ) { if ( ! $answers && ( $q && $q->get_id() == $this->get_id() ) ) { $answers = $this->get_default_answers(); } return $answers; }*/ /** * Get default question answer. * * @return array */ public static function get_default_answer() { $answer = array( 'question_answer_id' => - 1, 'title' => '', 'is_true' => '', 'value' => learn_press_random_value(), ); return $answer; } /** * Get default question list answers. * * @return array|bool */ public function get_default_answers() { $answers = array( array( 'question_answer_id' => - 1, 'is_true' => 'yes', 'value' => learn_press_random_value(), 'title' => esc_html__( 'First option', 'learnpress' ), ), array( 'question_answer_id' => - 2, 'is_true' => 'no', 'value' => learn_press_random_value(), 'title' => esc_html__( 'Second option', 'learnpress' ), ), array( 'question_answer_id' => - 3, 'is_true' => 'no', 'value' => learn_press_random_value(), 'title' => esc_html__( 'Third option', 'learnpress' ), ), ); return $answers; } /** * Get question answers option. * * @since 3.0.0 * @version 4.0.1 * @modify 4.1.7.3 by tungnx * @return LP_Question_Answer_Option[] */ public function get_answers(): array { $lp_question_cache = LP_Question_Cache::instance(); $key_cache = "{$this->get_id()}/option_answers"; //$answers = LP_Object_Cache::get( 'answer-options-' . $this->get_id(), 'learn-press/questions' ); $answers_option = $lp_question_cache->get_cache( $key_cache ); if ( false === $answers_option ) { $answers_option = $this->_curd->load_answer_options( $this->get_id() ); if ( ! $answers_option ) { $answers_option = $this->get_default_answers(); } $lp_question_cache->set_cache( $key_cache, $answers_option ); } require_once 'class-lp-question-answers.php'; $answers_option_tmp = []; foreach ( $answers_option as $k => $answer ) { $answer_option = new LP_Question_Answer_Option( $this, $answer ); $answers_option_tmp[] = $answer_option; } return $answers_option_tmp; } /** * Create default answers. * * @since 3.3.0 */ public function create_default_answers() { global $wpdb; $answers = $this->get_default_answers(); foreach ( $answers as $index => $answer ) { $answer = array( 'question_id' => $this->get_id(), 'title' => $answer['title'], 'value' => $answer['value'] ?? '', 'is_true' => ( $answer['is_true'] == 'yes' ) ? $answer['is_true'] : '', 'order' => $index + 1, ); $wpdb->insert( $wpdb->learnpress_question_answers, $answer, array( '%d', '%s', '%s', '%s', '%d' ) ); $question_answer_id = $wpdb->insert_id; if ( $question_answer_id ) { $answer['question_answer_id'] = $question_answer_id; } $answers[ $index ] = $answer; } $this->set_data( 'answer_options', $answers ); } public function setup_data( $quiz_id, $course_id = 0, $user_id = 0 ) { $quiz = learn_press_get_quiz( $quiz_id ); $course = learn_press_get_course( $course_id ); if ( $user_id ) { $user = learn_press_get_user( $user_id ); } else { $user = learn_press_get_current_user(); } $show_correct = false; if ( $user && $quiz && $course ) { $user_quiz = $user->get_quiz_data( $quiz->get_id(), $course->get_id() ); if ( $user_quiz ) { $has_checked = $user->has_checked_answer( $this->get_id(), $quiz->get_id(), $course->get_id() ); $show_correct = $user_quiz->is_completed() && ( $has_checked || $quiz->get_show_result() ) ? 'yes' : false; $answered = $user_quiz->get_question_answer( $this->get_id() ); $this->set_answered( $answered ); } } $this->show_correct_answers( $show_correct ); } /** * Get question name. * * @return string * @deprecated 4.1.7.3 */ public function get_name() { _deprecated_function( __METHOD__, '4.1.7.3' ); /*return $this->_options['name'] ?? ucfirst( preg_replace_callback( '!_([a-z])!', array( $this, 'sanitize_name_callback', ), $this->get_type() ) );*/ } /** * @param $matches * * @return string */ public function sanitize_name_callback( $matches ) { return strtoupper( $matches[1] ); } /** * Sets the value for a variable of this class * * @param $key string The name of a variable of this class * @param $value mixed The value to set * * @return void */ public function set( $key, $value ) { $this->$key = $value; } /** * Gets the value of a variable of this class with multiple level of an object or array * example: $obj->get('a.b') -> like this : * - $obj->a->b * - or $obj->a['b'] * * @param null $key string Single or multiple level such as a.b.c * @param null $default mixed Return a default value if the key does not exists or is empty * @param null $func string The function to apply the result before return * * @return mixed|null */ public function get( $key = null, $default = null, $func = null ) { $val = $this->_get( $this, $key, $default ); return is_callable( $func ) ? call_user_func_array( $func, array( $val ) ) : $val; } /** * Magic function to get question data. * * @param $prop * @param $key * @param null $default * @param null $type * * @return mixed|null */ protected function _get( $prop, $key, $default = null, $type = null ) { $return = $default; if ( $key === false || $key == null ) { return $return; } $deep = explode( '.', $key ); if ( is_array( $prop ) ) { if ( isset( $prop[ $deep[0] ] ) ) { $return = $prop[ $deep[0] ]; if ( count( $deep ) > 1 ) { unset( $deep[0] ); $return = $this->_get( $return, implode( '.', $deep ), $default, $type ); } } } elseif ( is_object( $prop ) ) { if ( isset( $prop->{$deep[0]} ) ) { $return = $prop->{$deep[0]}; if ( count( $deep ) > 1 ) { unset( $deep[0] ); $return = $this->_get( $return, implode( '.', $deep ), $default, $type ); } } } if ( $type == 'object' ) { settype( $return, 'object' ); } elseif ( $type == 'array' ) { settype( $return, 'array' ); } return $return; } /** * Find value in answer's option and compare with value answered by user. * * @param LP_Question_Answer_Option $answer * @param mixed $answered * * @return bool */ public function is_selected_option( $answer, $answered = false ): bool { if ( is_array( $answered ) ) { $is_selected = in_array( $answer['value'], $answered ); } else { $is_selected = $answer['value'] === $answered; } return apply_filters( 'learn-press/question/is-selected-option', $is_selected, $answer, $answered, $this->get_id() ); } /** * Save user question answer. * * @param $answer * @param $quiz_id * @param null $user_id */ public function save_user_answer( $answer, $quiz_id, $user_id = null ) { if ( $user_id ) { $user = LP_User_Factory::get_user( $user_id ); } else { $user = learn_press_get_current_user(); } $progress = $user->get_quiz_progress( $quiz_id ); if ( $progress ) { if ( ! isset( $progress->question_answers ) ) { $question_answers = array(); } else { $question_answers = $progress->question_answers; } $question_answers[ $this->get_id() ] = $answer; $question_answers = apply_filters( 'learn_press_update_user_question_answers', $question_answers, $progress->history_id, $user_id, $this, $quiz_id ); // learn_press_update_user_quiz_meta( $progress->history_id, 'question_answers', $question_answers ); } } /** * Allow check question answer, default disable for True or False and Single choice, override by Multiple choice question. * * @return bool */ public function can_check_answer() { return false; } /** * Check user answer, override by question type class. * * @param null $args | question answered * * @return array */ public function check( $args = null ) { $return = array( 'correct' => false, 'mark' => 0, ); return $return; } /** * Get answer at position * * @since 3.0.0 * * @param int $at * * @return LP_Question_Answer_Option|mixed */ public function get_answer_at( $at ) { return $this->get_answers()->get_answer_at( $at ); } /** * Get user answered data. * * @param $args * * @return null */ public function get_user_answered( $args ) { $args = wp_parse_args( $args, array( 'history_id' => 0, 'quiz_id' => 0, 'course_id' => 0, 'force' => false, ) ); $answered = null; if ( $args['history_id'] ) { $user_meta = learn_press_get_user_item_meta( $args['history_id'], 'question_answers', true ); if ( $user_meta && array_key_exists( $this->get_id(), $user_meta ) ) { $answered = $user_meta[ $this->get_id() ]; } } elseif ( $args['quiz_id'] && $args['course_id'] ) { $user = learn_press_get_current_user(); $history = $user->get_quiz_results( $args['quiz_id'], $args['course_id'] ); if ( $history ) { $user_meta = learn_press_get_user_item_meta( $history->history_id, 'question_answers', true ); if ( $user_meta && array_key_exists( $this->get_id(), $user_meta ) ) { $answered = $user_meta[ $this->get_id() ]; } } } return $answered; } /** * Get question. * * @param bool $the_question * @param array $args * * @return LP_Question|bool */ public static function get_question( $the_question = false, $args = array() ) { // question object $the_question = self::get_question_object( $the_question ); if ( ! $the_question ) { return false; } if ( ! empty( $args['force'] ) ) { $force = ! ! $args['force']; unset( $args['force'] ); } else { $force = false; } $key_args = wp_parse_args( $args, array( 'id' => $the_question->ID, 'type' => $the_question->post_type, ) ); $key = LP_Helper::array_to_md5( $key_args ); if ( $force ) { LP_Global::$questions[ $key ] = false; } if ( empty( LP_Global::$questions[ $key ] ) ) { $class_name = self::get_question_class( $the_question, $args ); if ( is_string( $class_name ) && class_exists( $class_name ) ) { $lesson = new $class_name( $the_question->ID, $args ); } elseif ( $class_name instanceof LP_Question ) { $lesson = $class_name; } else { $lesson = new self( $the_question->ID, $args ); } LP_Global::$questions[ $key ] = $lesson; } return LP_Global::$questions[ $key ]; } /** * Get the question class name. * * @param WP_Post $the_question * @param array $args (default: array()) * * @return string */ private static function get_question_class( $the_question, $args = array() ) { $question_id = absint( $the_question->ID ); if ( ! empty( $args['type'] ) ) { $question_type = $args['type']; } else { $question_type = get_post_meta( $question_id, '_lp_type', true ); } $class_name = self::get_class_name_from_question_type( array( $question_type ) ); // Filter class name so that the class can be overridden if extended. return apply_filters( 'learn-press/question/object-class', $class_name, $question_type, $question_id ); } /** * Get question class from question type. * * @param string $question_type * * @return string|false */ public static function get_class_name_from_question_type( $question_type ) { if ( is_array( $question_type ) ) { $question_type = reset( $question_type ); } return ! $question_type ? __CLASS__ : 'LP_Question_' . implode( '_', array_map( 'ucfirst', explode( '-', $question_type ) ) ); } /** * Get the question object. * * @since 3.0.0 * * @param mixed $the_question * * @uses WP_Post * @return WP_Post|bool false on failure */ private static function get_question_object( $the_question ) { if ( false === $the_question ) { $the_question = get_post_type() === LP_QUESTION_CPT ? $GLOBALS['post'] : false; } elseif ( is_numeric( $the_question ) ) { $the_question = get_post( $the_question ); } elseif ( $the_question instanceof LP_Course_Item ) { $the_question = get_post( $the_question->get_id() ); } elseif ( ! ( $the_question instanceof WP_Post ) ) { $the_question = false; } return apply_filters( 'learn-press/question/post-object', $the_question ); } protected function _get_checked( $user_answer = null ) { $key = $user_answer ? md5( serialize( $user_answer ) ) : - 1; return LP_Object_Cache::get( 'question-' . $this->get_id() . '/' . $key, 'learn-press/answer-checked' ); } protected function _set_checked( $checked, $user_answer ) { $key = $user_answer ? md5( serialize( $user_answer ) ) : - 1; return LP_Object_Cache::set( 'question-' . $this->get_id() . '/' . $key, $checked, 'learn-press/answer-checked' ); } /** * Get default json data for editor settings * * @since 3.3.0 * * @return array */ public function get_editor_default_settings() { return apply_filters( 'learn-press/question-editor-default-settings', array( 'id' => $this->get_id(), 'type' => $this->get_type(), 'duration' => $this->get_duration(), ) ); } /** * Get json data for editor settings. * * @since 3.3.0 * * @return array */ public function get_editor_settings() { $defaults = $this->get_editor_default_settings(); return apply_filters( 'learn-press/question-editor-settings', array_merge( $defaults, array() ), $this->get_type(), $this->get_id(), $this ); } } }