Skip to content

Commit

Permalink
Merge pull request #2693 from QuizandSurveyMaster/CU-85ztnrg6g-link-m…
Browse files Browse the repository at this point in the history
…ultiple-questions-and-broadcating

Added functionality for link multiple question with broadcating channel
  • Loading branch information
zubairraeen authored Oct 30, 2024
2 parents f95b743 + 33e6fc7 commit 770f09c
Show file tree
Hide file tree
Showing 10 changed files with 730 additions and 55 deletions.
87 changes: 87 additions & 0 deletions css/qsm-admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,93 @@ td.scheduled_time_start {
margin: 0 10px;
width: auto;
}


#post-body-content .qsm-popup-upgrade-warning img {
width: auto;
height: 15px;
margin-right: 8px;
}

.qsm-linked-list-div-block {
padding: 4px;
margin: 5px 0;
margin-top: -20px;
width: max-content;
background-color: #ffe684;
border-radius: 2px;
}

span.qsm-linked-list-view-button {
cursor: pointer;
color: #135e96;
text-decoration: underline;
font-weight: 500;
}

span.qsm-linked-list-item {
margin: 0 0 5px 0;
padding: 5px;
background: #f0f0f1;
}

div.qsm-linked-list-inside span.qsm-unlink-the-question {
color: #DC3232;
font-weight: 500;
max-width: 60px;
cursor: pointer;
}

.qsm-linked-list-div-block p {
margin: 0;
font-size: 13px;
color: #856404;
}

.qsm-linked-list-container {
position: absolute;
}

.qsm-linked-list-div-block .qsm-linked-list-inside {
position: relative;
right: -155px;
top: 20px;
z-index: 999;
width: 350px;
padding: 10px;
box-sizing: border-box;
border-radius: 4px;
background: #ffffff;
border: 1px solid #dfd4d4;
box-shadow: 0 0 6px 2px #ddd;
display: grid;
grid-template-columns: 1fr;
}

.qsm-linked-list-div-block .qsm-linked-list-inside:before {
content: " ";
position: absolute;
top: -24px;
left: 28%;
margin-left: -12px;
border-width: 12px;
border-style: solid;
border-color: transparent transparent #ffffff transparent;
z-index: 1;
}

.qsm-linked-list-div-block .qsm-linked-list-inside:after {
content: " ";
position: absolute;
top: -26px;
left: 28%;
margin-left: -12px;
border-width: 12px;
border-style: solid;
border-color: transparent transparent #dfd4d4 transparent;
z-index: 0;
}

/** * Text Tab design */
.qsm-text-main-wrap {
display: inline-block;
Expand Down
268 changes: 264 additions & 4 deletions js/qsm-admin.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions mlw_quizmaster2.php
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,11 @@ public function qsm_admin_scripts_style( $hook ) {
'question_created' => __('Question created!', 'quiz-master-next'),
'new_question' => __('Your new question!', 'quiz-master-next'),
'creating_question' => __('Creating question...', 'quiz-master-next'),
'unlink_question' => __('Unlink', 'quiz-master-next'),
'duplicating_question' => __('Duplicating question...', 'quiz-master-next'),
'linking_question' => __('Linking...', 'quiz-master-next'),
'link_question' => __('Link', 'quiz-master-next'),
'creating_question' => __('Creating question...', 'quiz-master-next'),
'saving_question' => __('Saving question...', 'quiz-master-next'),
'question_saved' => __('Question was saved!', 'quiz-master-next'),
'load_more_quetions' => __('Load more questions', 'quiz-master-next'),
Expand Down
220 changes: 212 additions & 8 deletions php/admin/options-page-questions-tab.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ function qsm_options_questions_tab_content() {
$json_data = array(
'quizID' => $quiz_id,
'answerText' => __( 'Answer', 'quiz-master-next' ),
'linked_view' => __( 'View', 'quiz-master-next' ),
'linked_close' => __( 'Close', 'quiz-master-next' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'pages' => $pages,
'qpages' => $qpages,
'qsm_user_ve' => get_user_meta( $user_id, 'rich_editing', true ),
'saveNonce' => wp_create_nonce( 'ajax-nonce-sandy-page' ),
'unlinkNonce' => wp_create_nonce( 'ajax-nonce-unlink-question' ),
'categories' => $question_categories,
'form_type' => $form_type,
'quiz_system' => $quiz_system,
Expand Down Expand Up @@ -280,6 +283,12 @@ class="save-page-button button button-primary"><?php esc_html_e( 'Save Questions
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-2">
<div id="post-body-content" style="position: relative;">
<div class="qsm-linked-list-div-block">
<p><?php esc_attr_e( 'This question is linked with other quizzes ', 'quiz-master-next' ); ?> <span class="qsm-linked-list-view-button"><?php esc_attr_e( 'View', 'quiz-master-next' ); ?></span></p>
<div class="qsm-linked-list-container">
<div class="qsm-linked-list-inside"></div>
</div>
</div>
<div class="qsm-row">
<input type="text" id="question_title" class="question-title" name="question-title" value="" placeholder="<?php esc_attr_e( 'Type your question here', 'quiz-master-next' ); ?>">
</div>
Expand Down Expand Up @@ -876,6 +885,80 @@ class="save-page-button button button-primary"><?php esc_html_e( 'Save Questions
<?php
}

/**
* Unlinks a question from all quizzes it is associated with.
* This function checks for a valid nonce, retrieves the question ID from the request.
*
* @since 9.1.3
* @return void
*/
function qsm_ajax_unlink_question_from_list() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ajax-nonce-unlink-question' ) ) {
wp_send_json_error( array(
'message' => __(
'Nonce verification failed.',
'quiz-master-next'
),
));
}
$question_id = isset( $_POST['question_id'] ) ? intval( $_POST['question_id'] ) : 0;
if ( $question_id > 0 ) {
qsm_process_unlink_question_from_list_by_question_id($question_id);
wp_send_json_success( array(
'message' => __(
'Question is unlinked from all quizzes.',
'quiz-master-next'
),
));
} else {
wp_send_json_error( array(
'message' => __(
'Invalid question ID.',
'quiz-master-next'
),
));
}
}
add_action( 'wp_ajax_qsm_unlink_question_from_list', 'qsm_ajax_unlink_question_from_list' );

/**
* Unlinks a question from all quizzes it is associated with.
* @since 9.1.3
* @param int $question_id The ID of the question to unlink.
* @return void
*/
function qsm_process_unlink_question_from_list_by_question_id( $question_id ) {
global $wpdb;
$current_linked_questions = $wpdb->get_var( $wpdb->prepare(
"SELECT linked_question FROM {$wpdb->prefix}mlw_questions WHERE question_id = %d",
$question_id
) );

if ( $current_linked_questions ) {
$current_links = explode(',', $current_linked_questions);
$current_links = array_map('trim', $current_links);
$current_links = array_diff($current_links, [ $question_id ]);
$updated_linked_list = implode(',', array_filter($current_links));
$linked_ids = explode(',', $updated_linked_list);
foreach ( $linked_ids as $linked_id ) {
$wpdb->update(
$wpdb->prefix . 'mlw_questions',
array( 'linked_question' => $updated_linked_list ),
array( 'question_id' => intval($linked_id) ),
array( '%s' ),
array( '%d' )
);
}
$wpdb->update(
$wpdb->prefix . 'mlw_questions',
array( 'linked_question' => '' ),
array( 'question_id' => intval($question_id) ),
array( '%s' ),
array( '%d' )
);
}
}

add_action( 'wp_ajax_qsm_save_pages', 'qsm_ajax_save_pages' );

/**
Expand Down Expand Up @@ -1093,11 +1176,38 @@ function qsm_delete_question_from_database() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'delete_question_from_database' ) ) {
wp_send_json_error( __( 'Nonce verification failed.', 'quiz-master-next' ) );
}
$question_id = isset( $_POST['question_id'] ) ? intval( $_POST['question_id'] ) : 0;
$base_question_id = $question_id = isset( $_POST['question_id'] ) ? intval( $_POST['question_id'] ) : 0;
if ( $question_id ) {

global $wpdb, $mlwQuizMasterNext;
$results = $wpdb->delete( $wpdb->prefix . 'mlw_questions', array( 'question_id' => $question_id ) );
$update_qpages_after_delete = array();
$connected_question_ids = qsm_get_unique_linked_question_ids_to_remove( [ $question_id ] );
$question_ids_to_delete = array_merge($connected_question_ids, [ $question_id ]);
$question_ids_to_delete = array_unique( $question_ids_to_delete );
$placeholders = array_fill( 0, count( $question_ids_to_delete ), '%d' );

if ( ! empty($connected_question_ids) ) {
$connected_question_ids = array_diff($connected_question_ids, [ $base_question_id ] );
$update_qpages_after_delete = qsm_process_to_update_qpages_after_unlink($connected_question_ids);
}

// Construct the query with placeholders
$query = sprintf(
"DELETE FROM {$wpdb->prefix}mlw_questions WHERE question_id IN (%s)",
implode( ', ', $placeholders )
);

// Prepare the query
$query = $wpdb->prepare( $query, $question_ids_to_delete );
$results = $wpdb->query( $query );
if ( $results ) {
if ( ! empty($update_qpages_after_delete) ) {
foreach ( $update_qpages_after_delete as $quiz_id => $aftervalue ) {
$mlwQuizMasterNext->pluginHelper->prepare_quiz( $quiz_id );
$mlwQuizMasterNext->pluginHelper->update_quiz_setting( 'qpages', $aftervalue['qpages'] );
$mlwQuizMasterNext->pluginHelper->update_quiz_setting( 'pages', $aftervalue['pages'] );
}
}
wp_send_json_success( __( 'Question removed Successfully.', 'quiz-master-next' ) );
}else {
wp_send_json_error( __( 'Question delete failed!', 'quiz-master-next' ) );
Expand Down Expand Up @@ -1136,11 +1246,19 @@ function qsm_bulk_delete_question_from_database() {
} );

// Sanitize and validate the IDs
$question_id = array_map( 'intval', $question_id );

$base_question_ids = $question_id = array_map( 'intval', $question_id );
if ( ! empty( $question_id ) ) {
// Generate placeholders for each ID
$placeholders = array_fill( 0, count( $question_id ), '%d' );

$update_qpages_after_delete = array();
$connected_question_ids = qsm_get_unique_linked_question_ids_to_remove($question_id);
$question_ids_to_delete = array_merge($connected_question_ids, $question_id);
$question_ids_to_delete = array_unique( $question_ids_to_delete );
$placeholders = array_fill( 0, count( $question_ids_to_delete ), '%d' );
if ( ! empty($connected_question_ids) ) {
$connected_question_ids = array_diff($connected_question_ids, $base_question_ids );
$update_qpages_after_delete = qsm_process_to_update_qpages_after_unlink($connected_question_ids);
}

// Construct the query with placeholders
$query = sprintf(
Expand All @@ -1149,10 +1267,17 @@ function qsm_bulk_delete_question_from_database() {
);

// Prepare the query
$query = $wpdb->prepare( $query, $question_id );
$query = $wpdb->prepare( $query, $question_ids_to_delete );

$results = $wpdb->query( $query );
if ( $results ) {
if ( ! empty($update_qpages_after_delete) ) {
foreach ( $update_qpages_after_delete as $quiz_id => $aftervalue ) {
$mlwQuizMasterNext->pluginHelper->prepare_quiz( $quiz_id );
$mlwQuizMasterNext->pluginHelper->update_quiz_setting( 'qpages', $aftervalue['qpages'] );
$mlwQuizMasterNext->pluginHelper->update_quiz_setting( 'pages', $aftervalue['pages'] );
}
}
wp_send_json_success( __( 'Questions removed Successfully.', 'quiz-master-next' ) );
}else {
$mlwQuizMasterNext->log_manager->add( __('Error 0001 delete questions failed - question IDs:', 'quiz-master-next') . $question_id, '<br><b>Error:</b>' . $wpdb->last_error . ' from ' . $wpdb->last_query, 0, 'error' );
Expand All @@ -1164,6 +1289,82 @@ function qsm_bulk_delete_question_from_database() {
}
add_action( 'wp_ajax_qsm_bulk_delete_question_from_database', 'qsm_bulk_delete_question_from_database' );

/**
* returns pages and qpages for dependent question ids for update after deleting questions
*
* @param array $connected_question_ids An array of question IDs.
* @since 9.1.3
*/
function qsm_process_to_update_qpages_after_unlink( $connected_question_ids ) {
$comma_seprated_ids = implode( ',', array_unique($connected_question_ids) );
$qpages_array = array();
if ( ! empty($comma_seprated_ids) ) {
global $wpdb, $mlwQuizMasterNext;
$quiz_results = $wpdb->get_results( "SELECT `quiz_id`, `question_id` FROM `{$wpdb->prefix}mlw_questions` WHERE `question_id` IN (" .$comma_seprated_ids. ")" );
if ( ! empty($quiz_results) ) {
foreach ( $quiz_results as $single_quiz ) {
$quiz_id = $single_quiz->quiz_id;
$mlwQuizMasterNext->pluginHelper->prepare_quiz( $quiz_id );
$pages = $mlwQuizMasterNext->pluginHelper->get_quiz_setting( 'pages', array() );
$clone_qpages = $qpages = $mlwQuizMasterNext->pluginHelper->get_quiz_setting( 'qpages', array() );
if ( ! empty($clone_qpages) ) {
foreach ( $clone_qpages as $clonekey => $clonevalue ) {
if ( ! empty($clonevalue['questions']) && in_array($single_quiz->question_id, $clonevalue['questions']) ) {
$clone_qpages[ $clonekey ]['questions'] = array_diff($clonevalue['questions'], [ $single_quiz->question_id ]);
$pages[ $clonekey ] = array_diff($pages[ $clonekey ], [ $single_quiz->question_id ]);
}
}
$qpages = $clone_qpages;
}
//merge duplicate questions
$all_questions = array();
foreach ( $pages as $page_key => $questions ) {
$page_questions = array();
$questions = array_unique( $questions );
foreach ( $questions as $id ) {
if ( ! in_array( $id, $all_questions, true ) ) {
$page_questions[] = $id;
}
}
$all_questions = array_merge( $all_questions, $questions );
$pages[ $page_key ] = $page_questions;
if ( isset( $qpages[ $page_key ] ) ) {
$qpages[ $page_key ]['questions'] = $page_questions;
}
}
$qpages_array[ $quiz_id ] = array(
'pages' => $pages,
'qpages' => $qpages,
);
}
}
}
return $qpages_array;
}

/**
* Get Unique Linked Question IDs
*
* @param array $question_ids An array of question IDs to query for linked questions.
* @return array $unique_ids An array of unique question IDs extracted from the linked questions.
* @since 9.1.3
*/
function qsm_get_unique_linked_question_ids_to_remove( $question_ids ) {
global $wpdb;
$all_ids = array();
foreach ( $question_ids as $id ) {
$sql = $wpdb->prepare(
"SELECT linked_question FROM {$wpdb->prefix}mlw_questions WHERE question_id = %d",
$id
);
$linked_question = $wpdb->get_var($sql);
if ( ! empty($linked_question) ) {
$all_ids = array_merge($all_ids, explode(',', $linked_question));
}
}
return $all_ids;
}

add_action( 'wp_ajax_save_new_category', 'qsm_save_new_category' );
function qsm_save_new_category() {
$category = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
Expand Down Expand Up @@ -1243,7 +1444,10 @@ function qsm_options_questions_tab_template() {
<input type="checkbox" name="qsm-question-checkbox[]" class="qsm-question-checkbox" />
</div>
<div><p>{{{data.question}}}</p><p style="font-size: 12px;color: gray;font-style: italic;"><b>Quiz Name:</b> {{data.quiz_name}} <# if ( data.category != '' ) { #> <b>Category:</b> {{data.category}} <# } #></p></div>
<div><a href="javascript:void(0)" class="button import-button" data-question-id="{{data.id}}"><?php esc_html_e( 'Add Question', 'quiz-master-next' ); ?></a></div>
<div>
<a href="javascript:void(0)" class="button import-button" data-question-id="{{data.id}}"><?php esc_html_e( 'Add', 'quiz-master-next' ); ?></a>
<a href="javascript:void(0)" data-questions="{{data.linked_question}}" class="button link-question" data-question-id="{{data.id}}"><?php esc_html_e( 'Link', 'quiz-master-next' ); ?></a>
</div>
</div>
</script>

Expand Down
Loading

0 comments on commit 770f09c

Please sign in to comment.