File: /var/www/html/bwcdev/wp-content/plugins/quiz-master-next/php/classes/class-qsm-questions.php
<?php
/**
* File that contains class for questions.
*
* @package QSM
*/
/**
* Class that handles all creating, saving, and deleting of questions.
*
* @since 5.2.0
*/
class QSM_Questions {
/**
* Loads single question using question ID
*
* @since 5.2.0
* @param int $question_id The ID of the question.
* @return array The data for the question.
*/
public static function load_question( $question_id ) {
global $wpdb;
$question_id = intval( $question_id );
$question = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mlw_questions WHERE question_id = %d LIMIT 1", $question_id ), 'ARRAY_A' );
if ( ! is_null( $question ) ) {
$multicategories = array();
$multicategories_res = $wpdb->get_results( "SELECT `term_id` FROM `{$wpdb->prefix}mlw_question_terms` WHERE `question_id`='{$question['question_id']}' AND `taxonomy`='qsm_category'", ARRAY_A );
if ( ! empty( $multicategories_res ) ) {
foreach ( $multicategories_res as $cat ) {
$multicategories[] = $cat['term_id'];
}
}
$question['multicategories'] = $multicategories;
// Prepare answers.
$answers = maybe_unserialize( $question['answer_array'] );
if ( ! is_array( $answers ) ) {
$answers = array();
}
$question['answers'] = $answers;
$settings = maybe_unserialize( $question['question_settings'] );
if ( ! is_array( $settings ) ) {
$settings = array( 'required' => 1 );
}
$question['settings'] = $settings;
return apply_filters( 'qsm_load_question', $question, $question_id );
}
return array();
}
/**
*
*/
public static function load_question_data( $question_id, $question_data ) {
global $wpdb;
return $wpdb->get_var("SELECT {$question_data} FROM {$wpdb->prefix}mlw_questions WHERE question_id = {$question_id} LIMIT 1");
}
/**
* Loads questions for a quiz using the new page system
*
* @since 5.2.0
* @param int $quiz_id The ID of the quiz.
* @return array The array of questions.
*/
public static function load_questions_by_pages( $quiz_id ) {
// Prepares our variables.
global $wpdb;
global $mlwQuizMasterNext;
$quiz_id = intval( $quiz_id );
$question_ids = array();
$questions = array();
$page_for_ids = array();
// Gets the pages for the quiz.
$mlwQuizMasterNext->pluginHelper->prepare_quiz( $quiz_id );
$pages = $mlwQuizMasterNext->pluginHelper->get_quiz_setting( 'pages', array() );
// Get all question IDs needed.
if ( ! empty( $pages ) ) {
$total_pages = count( $pages );
for ( $i = 0; $i < $total_pages; $i++ ) {
foreach ( $pages[ $i ] as $question ) {
$question_id = intval( $question );
$question_ids[] = $question_id;
$page_for_ids[ $question_id ] = $i;
}
}
}
// If we have any question IDs, get the questions.
if ( count( $question_ids ) > 0 ) {
$question_sql = implode( ', ', $question_ids );
// Get all questions.
$question_array = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mlw_questions WHERE question_id IN (%1s)", $question_sql ), 'ARRAY_A' );
// Loop through questions and prepare serialized data.
foreach ( $question_array as $question ) {
$multicategories = self::get_question_categories( $question['question_id'] );
// get_question_categories
$question['multicategories'] = isset( $multicategories['category_tree'] ) && ! empty( $multicategories['category_tree'] ) ? array_keys( $multicategories['category_name'] ) : array();
$question['multicategoriesobject'] = isset( $multicategories['category_tree'] ) && ! empty( $multicategories['category_tree'] ) ? $multicategories['category_tree'] : array();
// Prepares settings.
$settings = maybe_unserialize( $question['question_settings'] );
if ( ! is_array( $settings ) ) {
$settings = array( 'required' => 1 );
}
$question['settings'] = $settings;
// Prepare answers.
$answers = maybe_unserialize( $question['answer_array'] );
if ( ! is_array( $answers ) ) {
$answers = array();
}
$question['answers'] = self::sanitize_answers( $answers, $settings );
// Get the page.
$question_id = intval( $question['question_id'] );
$question['page'] = intval( $page_for_ids[ $question_id ] );
$questions[ $question_id ] = $question;
}
} else {
// If we do not have pages on this quiz yet, use older load_questions and add page to them.
$questions = self::load_questions( $quiz_id );
foreach ( $questions as $key => $question ) {
$questions[ $key ]['page'] = isset( $question['page'] ) ? $question['page'] : 0;
}
}
return apply_filters( 'qsm_load_questions_by_pages', $questions, $quiz_id );
}
/**
* Loads questions for a quiz
*
* @since 5.2.0
* @param int $quiz_id The ID of the quiz.
* @return array The array of questions.
*/
public static function load_questions( $quiz_id ) {
global $wpdb;
$question_array = array();
// Get all questions.
if ( 0 !== $quiz_id ) {
$quiz_id = intval( $quiz_id );
$questions = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mlw_questions WHERE quiz_id=%d AND deleted='0' ORDER BY question_order ASC", $quiz_id ), 'ARRAY_A' );
} else {
$questions = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}mlw_questions WHERE deleted='0' ORDER BY question_order ASC", 'ARRAY_A' );
}
// Loop through questions and prepare serialized data.
foreach ( $questions as $question ) {
$multicategories = array();
$multicategories_res = $wpdb->get_results( "SELECT `term_id` FROM `{$wpdb->prefix}mlw_question_terms` WHERE `question_id`='{$question['question_id']}' AND `taxonomy`='qsm_category'", ARRAY_A );
if ( ! empty( $multicategories_res ) ) {
foreach ( $multicategories_res as $cat ) {
$multicategories[] = $cat['term_id'];
}
}
$question['multicategories'] = $multicategories;
// Prepare answers.
$answers = maybe_unserialize( $question['answer_array'] );
if ( ! is_array( $answers ) ) {
$answers = array();
}
$question['answers'] = $answers;
$settings = maybe_unserialize( $question['question_settings'] );
if ( ! is_array( $settings ) ) {
$settings = array( 'required' => 1 );
}
$question['settings'] = $settings;
$question_array[ $question['question_id'] ] = $question;
}
return apply_filters( 'qsm_load_questions', $question_array, $quiz_id );
}
/**
* Creates a new question
*
* @since 5.2.0
* @param array $data The question data.
* @param array $answers The answers for the question.
* @param array $settings Any settings for the question.
* @throws Exception Throws exception if wpdb query results in error.
* @return int The ID of the question that was created.
*/
public static function create_question( $data, $answers = array(), $settings = array() ) {
return self::create_save_question( $data, $answers, $settings );
}
/**
* Saves a question
*
* @since 5.2.0
* @param int $question_id The ID of the question to be saved.
* @param array $data The question data.
* @param array $answers The answers for the question.
* @param array $settings Any settings for the question.
* @throws Exception Throws exception if wpdb query results in error.
* @return int The ID of the question that was saved.
*/
public static function save_question( $question_id, $data, $answers = array(), $settings = array() ) {
$data['ID'] = intval( $question_id );
return self::create_save_question( $data, $answers, $settings, false );
}
/**
* Deletes a question
*
* @since 5.2.0
* @param int $question_id The ID for the question.
* @throws Exception Throws exception if wpdb query results in error.
* @return bool True if successful
*/
public static function delete_question( $question_id ) {
global $wpdb;
$results = $wpdb->update(
$wpdb->prefix . 'mlw_questions',
array(
'deleted' => 1,
),
array( 'question_id' => intval( $question_id ) ),
array(
'%d',
),
array( '%d' )
);
if ( false === $results ) {
$msg = $wpdb->last_error . ' from ' . $wpdb->last_query;
$mlwQuizMasterNext->log_manager->add( 'Error when deleting question', $msg, 0, 'error' );
throw new Exception( $msg );
}
return true;
}
/**
* Creates or saves a question
*
* This is used internally. Use create_question or save_question instead.
*
* @since 5.2.0
* @param array $data The question data.
* @param array $answers The answers for the question.
* @param array $settings Any settings for the question.
* @param bool $is_creating True if question is being created, false if being saved.
* @throws Exception Throws exception if wpdb query results in error.
* @return int The ID of the question that was created/saved.
*/
private static function create_save_question( $data, $answers, $settings, $is_creating = true ) {
global $wpdb, $mlwQuizMasterNext;
// Prepare defaults and parse.
$defaults = array(
'quiz_id' => 0,
'type' => '0',
'name' => '',
'answer_info' => '',
'comments' => '1',
'hint' => '',
'order' => 1,
'category' => '',
'multicategories' => '',
);
$data = wp_parse_args( $data, $defaults );
$defaults = array(
'required' => 1,
);
$settings = wp_parse_args( $settings, $defaults );
$sanitize_answers = self::sanitize_answers( $answers, $settings );
foreach ( $sanitize_answers as $key => $answer ) {
$answers_array = array(
htmlspecialchars( $answer[0], ENT_QUOTES ),
floatval( $answer[1] ),
intval( $answer[2] ),
);
if ( isset( $answer[3] ) ) {
array_push( $answers_array, htmlspecialchars( $answer[3], ENT_QUOTES ) );
}
$sanitize_answers[ $key ] = $answers_array;
}
$answers = apply_filters( 'qsm_answers_before_save', $sanitize_answers, $answers, $data, $settings );
$question_name = htmlspecialchars( $mlwQuizMasterNext->sanitize_html( $data['name'] ), ENT_QUOTES );
$trim_question_description = apply_filters( 'qsm_trim_question_description', true );
if ( $trim_question_description ) {
$question_name = trim( preg_replace( '/\s+/', ' ', $question_name ) );
}
$values = array(
'quiz_id' => intval( $data['quiz_id'] ),
'question_name' => $question_name,
'answer_array' => maybe_serialize( $answers ),
'question_answer_info' => $mlwQuizMasterNext->sanitize_html( $data['answer_info'] ),
'comments' => sanitize_text_field( $data['comments'] ),
'hints' => sanitize_text_field( $data['hint'] ),
'question_order' => intval( $data['order'] ),
'question_type_new' => sanitize_text_field( $data['type'] ),
'question_settings' => maybe_serialize( $settings ),
'category' => sanitize_text_field( $data['category'] ),
'deleted' => 0,
);
$values = apply_filters( 'qsm_save_question_data', $values );
$types = array(
'%d',
'%s',
'%s',
'%s',
'%d',
'%s',
'%d',
'%s',
'%s',
'%s',
'%d',
);
if ( $is_creating ) {
$results = $wpdb->insert(
$wpdb->prefix . 'mlw_questions',
$values,
$types
);
$question_id = $wpdb->insert_id;
} else {
$question_id = intval( $data['ID'] );
$results = $wpdb->update(
$wpdb->prefix . 'mlw_questions',
$values,
array( 'question_id' => $question_id ),
$types,
array( '%d' )
);
}
if ( false === $results ) {
$msg = $wpdb->last_error . ' from ' . $wpdb->last_query;
$mlwQuizMasterNext->log_manager->add( 'Error when creating/saving question', $msg, 0, 'error' );
throw new Exception( $msg );
}
/**
* Process Question Categories
*/
$question_terms_table = $wpdb->prefix . 'mlw_question_terms';
$wpdb->delete(
$question_terms_table,
array(
'question_id' => $question_id,
'taxonomy' => 'qsm_category',
)
);
if ( ! empty( $data['multicategories'] ) ) {
foreach ( $data['multicategories'] as $term_id ) {
$term_rel_data = array(
'question_id' => $question_id,
'quiz_id' => intval( $data['quiz_id'] ),
'term_id' => $term_id,
'taxonomy' => 'qsm_category',
);
// Check if the data already exists in the table
$data_exists = $wpdb->get_row($wpdb->prepare("SELECT * FROM $question_terms_table WHERE question_id = %s AND quiz_id = %s AND term_id = %s AND taxonomy = %s", $question_id, intval( $data['quiz_id'] ), $term_id, 'qsm_category' ));
if ( ! $data_exists ) {
$wpdb->insert( $question_terms_table, $term_rel_data );
}
}
}
/**
* Hook after saving question
*/
if ( $is_creating ) {
do_action( 'qsm_question_added', $question_id, $values );
} else {
do_action( 'qsm_question_updated', $question_id, $values );
}
do_action( 'qsm_saved_question', $question_id, $values );
return $question_id;
}
/**
* Creates or saves a question
*
* sanitizes answers
*
* @since 7.3.5
* @param array $answers The answers for the question.
* @return array sanitized $answers The answers for the question.
*/
public static function sanitize_answers( $answers, $settings ) {
global $mlwQuizMasterNext;
foreach ( $answers as $key => $answer ) {
if ( isset($answer[0]) ) {
if ( isset( $settings['answerEditor'] ) && 'rich' == $settings['answerEditor'] ) {
$answer[0] = $mlwQuizMasterNext->sanitize_html( $answer[0] );
} else {
$answer[0] = $mlwQuizMasterNext->sanitize_html( $answer[0], false );
}
$answers[ $key ] = $answer;
}
}
return $answers;
}
/**
* Get categories for a quiz
*
* @since 7.2.1
* @param int $quiz_id The ID of the quiz.
* @return array The array of categories.
*/
public static function get_quiz_categories( $quiz_id = 0 ) {
global $wpdb;
$categories = array();
if ( 0 !== $quiz_id ) {
$questions = self::load_questions_by_pages( $quiz_id );
$question_ids = array_column( $questions, 'question_id' );
$question_ids = implode( ',', $question_ids );
$question_terms = $wpdb->get_results( "SELECT `term_id` FROM `{$wpdb->prefix}mlw_question_terms` WHERE `question_id` IN ({$question_ids}) AND `taxonomy`='qsm_category'", ARRAY_A );
$term_ids = ! empty( $question_terms ) ? array_unique( array_column( $question_terms, 'term_id' ) ) : array();
$cat_array = self::get_question_categories_from_quiz_id( $quiz_id );
$enabled = get_option( 'qsm_multiple_category_enabled' );
if ( $enabled && 'cancelled' !== $enabled && ! empty( $cat_array ) ) {
$term_ids = array_unique( array_merge( $term_ids, $cat_array ) );
}
$categories = self::get_question_categories_from_term_ids( $term_ids );
}
return $categories;
}
/**
* Get categories from quiz id
*
* @since 7.3.3
* @param int $quiz_id The ID of the quiz.
* @return array The array of categories.
*/
public static function get_question_categories_from_quiz_id( $quiz_id ) {
$cat_array = array();
$questions = self::load_questions_by_pages( $quiz_id );
foreach ( $questions as $single_question ) {
if ( isset( $single_question['multicategories'] ) && is_array( $single_question['multicategories'] ) ) {
foreach ( $single_question['multicategories'] as $cat_id ) {
$cat_array[] = $cat_id;
}
}
}
return $cat_array;
}
/**
* Get categories from term ids
*
* @since 7.3.3
* @param int $term_ids Term IDs of the quiz.
* @return array The array of categories.
*/
public static function get_question_categories_from_term_ids( $term_ids ) {
$categories = array();
if ( ! empty( $term_ids ) ) {
$categories_names = array();
$categories_tree = array();
$terms = get_terms(
array(
'taxonomy' => 'qsm_category',
'include' => $term_ids,
'hide_empty' => false,
'orderby' => '',
'order' => '',
)
);
if ( ! empty( $terms ) ) {
foreach ( $terms as $tax ) {
$categories_names[ $tax->term_id ] = $tax->name;
$taxs[ $tax->parent ][] = $tax;
}
$categories_tree = self::create_terms_tree( $taxs, isset( $taxs[0] ) ? $taxs[0] : reset( $taxs ) );
}
$categories = array(
'list' => $categories_names,
'tree' => $categories_tree,
);
}
return $categories;
}
/**
* Get categories for a Question
*
* @since 7.2.1
* @param int $quiz_id The ID of the quiz.
* @return array The array of categories.
*/
public static function get_question_categories( $question_id = 0 ) {
global $wpdb;
$categories_tree = array();
$categories_names = array();
if ( 0 !== $question_id ) {
$question_terms = $wpdb->get_results( "SELECT `term_id` FROM `{$wpdb->prefix}mlw_question_terms` WHERE `question_id`='{$question_id}' AND `taxonomy`='qsm_category'", ARRAY_A );
if ( ! empty( $question_terms ) ) {
$term_ids = array_unique( array_column( $question_terms, 'term_id' ) );
if ( ! empty( $term_ids ) ) {
$terms = get_terms(
array(
'taxonomy' => 'qsm_category',
'include' => array_unique( $term_ids ),
'hide_empty' => false,
'orderby' => '',
'order' => '',
)
);
if ( ! empty( $terms ) ) {
foreach ( $terms as $tax ) {
$categories_names[ $tax->term_id ] = $tax->name;
$taxs[ $tax->parent ][] = $tax;
}
$categories_tree = self::create_terms_tree( $taxs, isset( $taxs[0] ) ? $taxs[0] : reset( $taxs ) );
}
}
}
}
return array(
'category_name' => $categories_names,
'category_tree' => $categories_tree,
);
}
/**
* Create tree structure of terms.
*
* @since 7.2.1
*/
public static function create_terms_tree( &$list, $parent ) {
$taxTree = array();
if ( is_array($parent) ) {
foreach ( $parent as $ind => $val ) {
if ( isset( $list[ $val->term_id ] ) ) {
$val->children = self::create_terms_tree( $list, $list[ $val->term_id ] );
}
$taxTree[] = $val;
}
}
return $taxTree;
}
}