/** * Fetch the answer data and clean it up based on the type of question * @param Array $potentialAnswers The potential answers that need checking. * @return Array A list of the results. */ function check_quizzes_canWeContinue_extractAnswerData($potentialAnswers) { $resultsList = array('answer_list' => array(), 'wrong_answer_list' => array(), 'error_answer_list' => array()); // #### 1 - Extract a list of actual answers from the potential answers. There will // be some noise in that data. Check the raw data to see what questions we have. foreach ($potentialAnswers as $key => $value) { // Only considering answers to questions. Format of answer field is: // question_16_truefalse_48 (first ID is quiz, 2nd ID is question, middle string // is the question type. if (preg_match('/^question_(\\d+)_([a-z]+)_(\\d+)$/', $key, $matches)) { $quizID = $matches[1]; $questionID = $matches[3]; $questionType = $matches[2]; // Again, check that answer matches quiz we're expecting. // Probably a little paranoid, but it's worth checking // to ensure there's nothing naughty going on. if ($quizID != $this->unitQuizDetails->quiz_id) { continue; } // Clean up the submitted data based on the type of quiz using the static checks in each // of the questions (to save loading whole class). If the data is valid, add the valid // answer to the list of fully validate danswers. switch ($questionType) { case 'multi': $resultsList['answer_list'][$questionID] = WPCW_quiz_MultipleChoice::sanitizeAnswerData($value); break; case 'truefalse': $resultsList['answer_list'][$questionID] = WPCW_quiz_TrueFalse::sanitizeAnswerData($value); break; case 'open': $resultsList['answer_list'][$questionID] = WPCW_quiz_OpenEntry::sanitizeAnswerData($value); break; // Ignore uploads as a $_POST field, simply because the files should be stored in $_FILES // not in $_POST. So if we have a file in $_FILES, that's clearly an issue. // Ignore uploads as a $_POST field, simply because the files should be stored in $_FILES // not in $_POST. So if we have a file in $_FILES, that's clearly an issue. case 'upload': break; } } // end of question check } // end of potential answers loop // ### 2 - Check for file uploads if the quiz requires them. Only check for uploads // if the quiz details say there should be some uploads. if ($this->unitQuizDetails->want_uploads) { $uploadResultList = WPCW_quiz_FileUpload::validateFiles($_FILES, $this->unitQuizDetails); // Merge the valid results. // Basically if a file has been uploaded correctly, that answer is marked as being set. if (count($uploadResultList['upload_valid']) > 0) { $resultsList['answer_list'] = $resultsList['answer_list'] + $uploadResultList['upload_valid']; } // Merge the error results if (count($uploadResultList['upload_errors']) > 0) { $resultsList['error_answer_list'] = $resultsList['error_answer_list'] + $uploadResultList['upload_errors']; } } return $resultsList; }
/** * Handle saving questions to the database. * @param Integer $quizID The quiz for which the questions apply to. * @param Object $page The associated page object for showing messages. */ function WPCW_showPage_ModifyQuiz_showQuizEntryForms_processSave($quizID, $page) { // No updates have been requested, so exit. if (!isset($_POST['survey_updated'])) { return; } global $wpdb, $wpcwdb; $wpdb->show_errors(); $questionsToSave = array(); $questionsToSave_New = array(); // Check $_POST data for the foreach ($_POST as $key => $value) { // #### 1 - Check if we're deleting a question? // Not interested in new questions that have been added and then deleted. Just the // ones that were added to the database first. if (preg_match('/^delete_wpcw_quiz_details_([0-9]+)$/', $key, $matches)) { $SQL = $wpdb->prepare("\n\t\t\t\tDELETE FROM {$wpcwdb->quiz_qs}\n\t\t\t\tWHERE question_id = %d\n\t\t\t", $matches[1]); $wpdb->query($SQL); } else { if (preg_match('/^question_question_(new_question_)?([0-9]+)$/', $key, $matches)) { // Got the ID of the question, now get answers and correct answer. $questionID = $matches[2]; // Store the extra string if we're adding a new question. $newQuestionPrefix = $matches[1]; $fieldName_Answers = 'question_answer_' . $newQuestionPrefix . $questionID; $fieldName_Correct = 'question_answer_sel_' . $newQuestionPrefix . $questionID; $fieldName_Type = 'question_type_' . $newQuestionPrefix . $questionID; $fieldName_Order = 'question_order_' . $newQuestionPrefix . $questionID; $fieldName_AnswerType = 'question_answer_type_' . $newQuestionPrefix . $questionID; $fieldName_AnswerHint = 'question_answer_hint_' . $newQuestionPrefix . $questionID; // Order should be a number $questionOrder = 0; if (isset($_POST[$fieldName_Order])) { $questionOrder = $_POST[$fieldName_Order] + 0; } // Default types $qAns = false; $qAnsCor = false; $qAnsType = false; // Just used for open question types. $qAnsFileTypes = false; // Just used for upload file types. // Get the hint - Just used for open and upload types. Allow HTML. $qAnsHint = trim(WPCW_arrays_getValue($_POST, $fieldName_AnswerHint)); // What type of question do we have? $questionType = WPCW_arrays_getValue($_POST, $fieldName_Type); switch ($questionType) { case 'multi': $qAns = WPCW_quiz_MultipleChoice::editSave_extractAnswerList($fieldName_Answers); $qAnsCor = WPCW_quiz_MultipleChoice::editSave_extractCorrectAnswer($qAns, $fieldName_Correct); // Provide the UI with at least once slot for an answer. if (!$qAns) { $qAns = array('', ''); } break; case 'open': // See if there's a question type that's been sent back to the server. $answerTypes = WPCW_quiz_OpenEntry::getValidAnswerTypes(); $thisAnswerType = WPCW_arrays_getValue($_POST, $fieldName_AnswerType); // Validate the answer type is in the list. Don't create a default so that user must choose. if (isset($answerTypes[$thisAnswerType])) { $qAnsType = $thisAnswerType; } // There's no correct answer for an open question. $qAnsCor = false; break; case 'upload': $fieldName_FileType = 'question_answer_file_types_' . $newQuestionPrefix . $questionID; // Check new file extension types, parsing them. $qAnsFileTypesRaw = WPCW_files_cleanFileExtensionList(WPCW_arrays_getValue($_POST, $fieldName_FileType)); $qAnsFileTypes = implode(',', $qAnsFileTypesRaw); break; case 'truefalse': $qAnsCor = WPCW_quiz_TrueFalse::editSave_extractCorrectAnswer($fieldName_Correct); break; // Not expecting anything here... so not handling the error case. // Not expecting anything here... so not handling the error case. default: break; } // ### 4 - Save new question data as a list ready for saving to the database. // New question - so no question ID as yet if ($newQuestionPrefix) { $questionsToSave_New[] = array('question_question' => stripslashes($value), 'question_answers' => $qAns ? implode("\n", $qAns) : $qAns, 'question_correct_answer' => $qAnsCor, 'parent_quiz_id' => $quizID, 'question_type' => $questionType, 'question_order' => $questionOrder, 'question_answer_type' => $qAnsType, 'question_answer_hint' => stripslashes($qAnsHint), 'question_answer_file_types' => $qAnsFileTypes); } else { $questionsToSave[$questionID] = array('question_id' => $questionID, 'question_question' => stripslashes($value), 'question_answers' => $qAns ? implode("\n", $qAns) : $qAns, 'question_correct_answer' => $qAnsCor, 'parent_quiz_id' => $quizID, 'question_type' => $questionType, 'question_order' => $questionOrder, 'question_answer_type' => $qAnsType, 'question_answer_hint' => stripslashes($qAnsHint), 'question_answer_file_types' => $qAnsFileTypes); } } } // end if question found. } // #### 5 - Check we have existing questions to save if (count($questionsToSave)) { // Now save all data back to the database. foreach ($questionsToSave as $questionID => $questionDetails) { $wpdb->query(arrayToSQLUpdate($wpcwdb->quiz_qs, $questionDetails, 'question_id')); } } // #### 6 - Save the new questions we have if (count($questionsToSave_New)) { // Now save all data back to the database. foreach ($questionsToSave_New as $questionDetails) { $wpdb->query(arrayToSQLInsert($wpcwdb->quiz_qs, $questionDetails)); } } // Show an error if questions are missing details. $page->showMessage(__('Questions were successfully updated.', 'wp_courseware')); }
/** * Having entered some details into the quiz, may the user progress to the next unit? If * there are any problems with the quiz, then they are dealt with via AJAX. * * @param Object $quizDetails The potential quiz details. * @param Array $potentialAnswers The potential answers that need checking. * @param Integer $userID The ID of the user that we're saving progress for. * * @return Boolean True if the user may progress, false otherwise. */ function WPCW_quizzes_handleQuizRendering_canUserContinueAfterQuiz($quizDetails, $potentialAnswers, $userID) { $resultsList = array('answer_list' => array(), 'wrong_answer_list' => array(), 'error_answer_list' => array()); $resultDetails = array('correct' => array(), 'wrong' => array()); // #### 1A Extract a list of actual answers from the potential answers. There will // be some noise in that data. foreach ($potentialAnswers as $key => $value) { // Only considering answers to questions. Format of answer field is: // question_16_truefalse_48 (first ID is quiz, 2nd ID is question, middle string // is the question type. if (preg_match('/^question_(\\d+)_([a-z]+)_(\\d+)$/', $key, $matches)) { $quizID = $matches[1]; $questionID = $matches[3]; $questionType = $matches[2]; // Again, check that answer matches quiz we're expecting. // Probably a little paranoid, but it's worth checking // to ensure there's nothing naughty going on. if ($quizID != $quizDetails->quiz_id) { continue; } // Clean up the submitted data based on the type of quiz using the static checks in each // of the questions (to save loading whole class). If the data is valid, add the valid // answer to the list of fully validate danswers. switch ($questionType) { case 'multi': $resultsList['answer_list'][$questionID] = WPCW_quiz_MultipleChoice::sanitizeAnswerData($value); break; case 'truefalse': $resultsList['answer_list'][$questionID] = WPCW_quiz_TrueFalse::sanitizeAnswerData($value); break; case 'open': $resultsList['answer_list'][$questionID] = WPCW_quiz_OpenEntry::sanitizeAnswerData($value); break; // Ignore uploads as a $_POST field, simply because the files should be stored in $_FILES // not in $_POST. So if we have a file in $_FILES, that's clearly an issue. // Ignore uploads as a $_POST field, simply because the files should be stored in $_FILES // not in $_POST. So if we have a file in $_FILES, that's clearly an issue. case 'upload': break; } } // end of question check } // end of potential answers loop // ### 1B Check for file uploads if the quiz requires them. Only check for uploads // if the quiz details say there should be some uploads. if ($quizDetails->want_uploads) { $uploadResultList = WPCW_quiz_FileUpload::validateFiles($_FILES, $quizDetails); // Merge the valid results. // Basically if a file has been uploaded correctly, that answer is marked as being set. if (count($uploadResultList['upload_valid']) > 0) { $resultsList['answer_list'] = $resultsList['answer_list'] + $uploadResultList['upload_valid']; } // Merge the error results if (count($uploadResultList['upload_errors']) > 0) { $resultsList['error_answer_list'] = $resultsList['error_answer_list'] + $uploadResultList['upload_errors']; } } // ### 2 - Check that we have enough answers given how many questions there are. // If there are not enough answers, then re-render the form with the answered questions // marked, and highlight the fields that have errors. if ($quizDetails->questions && count($resultsList['answer_list']) < count($quizDetails->questions)) { // Error - not all questions are answered echo WPCW_units_createErrorMessage(__('Please provide an answer for all of the questions.', 'wp_courseware')); // Show the form with the questions that have been completed already. echo WPCW_quizzes_handleQuizRendering($quizDetails->parent_unit_id, $quizDetails, $resultsList); // User may not continue - as quiz is not complete. return false; } // Flag to indicate if grading is needed before the user continues. $gradingNeeded = false; $gradingNeededBeforeContinue = false; // ### 3 - Do we need to check for correct answers? if ('survey' == $quizDetails->quiz_type) { // Never try to show answers. There aren't any. $quizDetails->quiz_show_answers = 'hide_answers'; // No answers to check. Say thanks echo WPCW_units_createSuccessMessage(__('Thank you for your responses. This unit is now complete.', 'wp_courseware')); } else { $resultDetails = WPCW_quizzes_checkForCorrectAnswers($quizDetails, $resultsList['answer_list']); // #### Step A - have open-ended questions that need marking. if (!empty($resultDetails['needs_marking'])) { $gradingNeeded = true; $courseDetails = WPCW_courses_getCourseDetails($quizDetails->parent_course_id); // Non-blocking quiz - so allowed to continue, but will be graded later. if ('quiz_noblock' == $quizDetails->quiz_type) { echo WPCW_units_createSuccessMessage($courseDetails->course_message_quiz_open_grading_non_blocking); } else { echo WPCW_units_createSuccessMessage($courseDetails->course_message_quiz_open_grading_blocking); // Grading is needed before they continue, but don't want them to re-take the quiz. $gradingNeededBeforeContinue = true; } } else { // Copy over the wrong answers. $resultsList['wrong_answer_list'] = $resultDetails['wrong']; // Some statistics $correctCount = count($resultDetails['correct']); $totalQuestions = count($quizDetails->questions); $correctPercentage = number_format($correctCount / $totalQuestions * 100, 1); // Non-blocking quiz. if ('quiz_noblock' == $quizDetails->quiz_type) { // Store user quiz results echo WPCW_units_createSuccessMessage(sprintf(__('You got <b>%d out of %d (%d%%)</b> questions correct! This unit is now complete.', 'wp_courseware'), $correctCount, $totalQuestions, $correctPercentage)); // Notify the user of their grade. do_action('wpcw_quiz_graded', $userID, $quizDetails, $correctPercentage, false); } else { $minPassQuestions = $totalQuestions * ($quizDetails->quiz_pass_mark / 100); // They've passed. Report how many they got right. if ($correctPercentage >= $quizDetails->quiz_pass_mark) { echo WPCW_units_createSuccessMessage(sprintf(__('You got <b>%d out of %d (%d%%)</b> questions correct! This unit is now complete.', 'wp_courseware'), $correctCount, $totalQuestions, $correctPercentage)); // Notify the user of their grade. do_action('wpcw_quiz_graded', $userID, $quizDetails, $correctPercentage, false); } else { echo WPCW_units_createErrorMessage(sprintf(__('Unfortunately, you only got <b>%d out of %d (%d%%)</b> questions correct. You need to correctly answer <b>at least %d questions (%d%%)</b>.', 'wp_courseware'), $correctCount, $totalQuestions, $correctPercentage, $minPassQuestions, $quizDetails->quiz_pass_mark)); // Show form with error answers. echo WPCW_quizzes_handleQuizRendering($quizDetails->parent_unit_id, $quizDetails, $resultsList); // Errors, so the user cannot progress yet. return false; } } // end of blocking quiz check } } // end of survey check // ### 4 - Show the correct answers to the user? if ('show_answers' == $quizDetails->quiz_show_answers) { echo WPCW_quizzes_showAllCorrectAnswers($quizDetails); } // ### 5 - Save the user progress WPCW_quizzes_saveUserProgress($userID, $quizDetails, $resultDetails, $resultsList['answer_list']); // Questions need grading, notify the admin if ($gradingNeeded) { // Notify the admin that questions need answering. do_action('wpcw_quiz_needs_grading', $userID, $quizDetails); } // Questions need grading, so don't allow user to continue if ($gradingNeededBeforeContinue) { return false; } // If we get this far, the user may progress to next unit return true; }
/** * Handle saving questions to the database. * * @param Integer $quizID The quiz for which the questions apply to. * @param Boolean $singleQuestionMode If true, then we're updating a single question, and we do things slightly differently. */ function WPCW_handler_questions_processSave($quizID, $singleQuestionMode = false) { global $wpdb, $wpcwdb; $wpdb->show_errors(); $questionsToSave = array(); $questionsToSave_New = array(); // Check $_POST data for the foreach ($_POST as $key => $value) { // #### 1 - Check if we're deleting a question from this quiz // We're not just deleting the question, just the association. This is because questions remain in the // pool now. if (preg_match('/^delete_wpcw_quiz_details_([0-9]+)$/', $key, $matches)) { // Remove mapping from the mapping table. $SQL = $wpdb->prepare("\n\t\t\t\tDELETE FROM {$wpcwdb->quiz_qs_mapping}\n\t\t\t\tWHERE question_id = %d\n\t\t\t\t AND parent_quiz_id = %d\n\t\t\t", $matches[1], $quizID); $wpdb->query($SQL); // Update usage counts WPCW_questions_updateUsageCount($matches[1]); // Just a deletion - move on to next array item to save processing time. continue; } // #### 2 - See if we have a question to check for. if (preg_match('/^question_question_(new_question_)?([0-9]+)$/', $key, $matches)) { // Got the ID of the question, now get answers and correct answer. $questionID = $matches[2]; // Store the extra string if we're adding a new question. $newQuestionPrefix = $matches[1]; $fieldName_Answers = 'question_answer_' . $newQuestionPrefix . $questionID; $fieldName_Answers_Img = 'question_answer_image_' . $newQuestionPrefix . $questionID; $fieldName_Correct = 'question_answer_sel_' . $newQuestionPrefix . $questionID; $fieldName_Type = 'question_type_' . $newQuestionPrefix . $questionID; $fieldName_Order = 'question_order_' . $newQuestionPrefix . $questionID; $fieldName_AnswerType = 'question_answer_type_' . $newQuestionPrefix . $questionID; $fieldName_AnswerHint = 'question_answer_hint_' . $newQuestionPrefix . $questionID; $fieldName_Explanation = 'question_answer_explanation_' . $newQuestionPrefix . $questionID; $fieldName_Image = 'question_image_' . $newQuestionPrefix . $questionID; // For Multi-Choice - Answer randomization $fieldName_Multi_Random_Enable = 'question_multi_random_enable_' . $newQuestionPrefix . $questionID; $fieldName_Multi_Random_Count = 'question_multi_random_count_' . $newQuestionPrefix . $questionID; // Order should be a number $questionOrder = 0; if (isset($_POST[$fieldName_Order])) { $questionOrder = $_POST[$fieldName_Order] + 0; } // Default types $qAns = false; $qAnsCor = false; $qAnsType = false; // Just used for open question types. $qAnsFileTypes = false; // Just used for upload file types. // Get the hint - Just used for open and upload types. Allow HTML. $qAnsHint = trim(WPCW_arrays_getValue($_POST, $fieldName_AnswerHint)); // Get the explanation - All questions. Allow HTML. $qAnsExplain = trim(WPCW_arrays_getValue($_POST, $fieldName_Explanation)); // The image URL to use. No HTML. Table record is 300 chars, hence cropping. $qQuesImage = trim(substr(strip_tags(WPCW_arrays_getValue($_POST, $fieldName_Image)), 0, 300)); // How many questions are there is this selection? 1 by default for non-random questions. $expandedQuestionCount = 1; // For Multi-Choice - Answer randomization $qMultiRandomEnable = false; $qMultiRandomCount = 5; // What type of question do we have? $questionType = WPCW_arrays_getValue($_POST, $fieldName_Type); switch ($questionType) { case 'multi': $qAns = WPCW_quiz_MultipleChoice::editSave_extractAnswerList($fieldName_Answers, $fieldName_Answers_Img); $qAnsCor = WPCW_quiz_MultipleChoice::editSave_extractCorrectAnswer($qAns, $fieldName_Correct); // Provide the UI with at least once slot for an answer. if (!$qAns) { $qAns = array('1' => array('answer' => ''), '2' => array('answer' => '')); } // Check randomization values (boolean will be 'on' to enable, as it's a checkbox) $qMultiRandomEnable = 'on' == WPCW_arrays_getValue($_POST, $fieldName_Multi_Random_Enable); $qMultiRandomCount = intval(WPCW_arrays_getValue($_POST, $fieldName_Multi_Random_Count)); break; case 'open': // See if there's a question type that's been sent back to the server. $answerTypes = WPCW_quiz_OpenEntry::getValidAnswerTypes(); $thisAnswerType = WPCW_arrays_getValue($_POST, $fieldName_AnswerType); // Validate the answer type is in the list. Don't create a default so that user must choose. if (isset($answerTypes[$thisAnswerType])) { $qAnsType = $thisAnswerType; } // There's no correct answer for an open question. $qAnsCor = false; break; case 'upload': $fieldName_FileType = 'question_answer_file_types_' . $newQuestionPrefix . $questionID; // Check new file extension types, parsing them. $qAnsFileTypesRaw = WPCW_files_cleanFileExtensionList(WPCW_arrays_getValue($_POST, $fieldName_FileType)); $qAnsFileTypes = implode(',', $qAnsFileTypesRaw); break; case 'truefalse': $qAnsCor = WPCW_quiz_TrueFalse::editSave_extractCorrectAnswer($fieldName_Correct); break; // Validate the the JSON data here... ensure all the tags are valid (not worried about the counts). // Then save back to database. // Validate the the JSON data here... ensure all the tags are valid (not worried about the counts). // Then save back to database. case 'random_selection': // Reset to zero for counting below. $expandedQuestionCount = 0; $decodedTags = WPCW_quiz_RandomSelection::decodeTagSelection(stripslashes($value)); // Capture just ID and count and resave back to database. $toSaveList = false; if (!empty($decodedTags)) { $toSaveList = array(); foreach ($decodedTags as $decodedKey => $decodedDetails) { $toSaveList[$decodedKey] = $decodedDetails['count']; // Track requested questions $expandedQuestionCount += $decodedDetails['count']; } } // Overwrite $value to use cleaned question $value = json_encode($toSaveList); break; // Not expecting anything here... so not handling the error case. // Not expecting anything here... so not handling the error case. default: break; } // ### 4a - Encode the answer data $encodedqAns = $qAns; if (!empty($qAns)) { foreach ($encodedqAns as $idx => $data) { $encodedqAns[$idx]['answer'] = base64_encode($data['answer']); } } // ### 4b - Save new question data as a list ready for saving to the database. $quDataToSave = array('question_answers' => false, 'question_question' => stripslashes($value), 'question_data_answers' => serialize($encodedqAns), 'question_correct_answer' => $qAnsCor, 'question_type' => $questionType, 'question_order' => $questionOrder, 'question_answer_type' => $qAnsType, 'question_answer_hint' => stripslashes($qAnsHint), 'question_answer_explanation' => stripslashes($qAnsExplain), 'question_answer_file_types' => $qAnsFileTypes, 'question_image' => $qQuesImage, 'question_expanded_count' => $expandedQuestionCount, 'question_multi_random_enable' => $qMultiRandomEnable, 'question_multi_random_count' => $qMultiRandomCount, 'taglist' => array()); // ### 5 - Check if there are any tags to save. Only happens for questions that // haven't been saved, so that we can save when we do a $_POST save. $tagFieldForNewQuestions = 'tags_to_add_' . $newQuestionPrefix . $questionID; if (isset($_POST[$tagFieldForNewQuestions])) { if (!empty($_POST[$tagFieldForNewQuestions])) { // Validate each tag ID we have, add to list to be stored for this question later. foreach ($_POST[$tagFieldForNewQuestions] as $idx => $tagText) { $tagText = trim(stripslashes($tagText)); if ($tagText) { $quDataToSave['taglist'][] = $tagText; } } } } // Not a new question - so not got question ID as yet if ($newQuestionPrefix) { $questionsToSave_New[] = $quDataToSave; } else { $quDataToSave['question_id'] = $questionID; $questionsToSave[$questionID] = $quDataToSave; } } // end if question found. } // Only need to adjust quiz settings when editing a quiz and not a single question. if (!$singleQuestionMode) { // #### 6 - Remove association of all questions for this quiz // as we're going to re-add them. $wpdb->query($wpdb->prepare("\n\t\t\t\t\tDELETE FROM {$wpcwdb->quiz_qs_mapping}\n\t\t\t\t\tWHERE parent_quiz_id = %d\n\t\t\t\t", $quizID)); } // #### 7 - Check we have existing questions to save if (count($questionsToSave)) { // Now save all data back to the database. foreach ($questionsToSave as $questionID => $questionDetails) { // Extract the question order, as can't save order with question in DB $questionOrder = $questionDetails['question_order']; unset($questionDetails['question_order']); // Tag list only used for new questions, so remove this field unset($questionDetails['taglist']); // Save question details back to database. $wpdb->query(arrayToSQLUpdate($wpcwdb->quiz_qs, $questionDetails, 'question_id')); // No need to update counts/associations when editing a single lone question if (!$singleQuestionMode) { // Create the association for this quiz/question. $wpdb->query($wpdb->prepare("\n\t\t\t\t\tINSERT INTO {$wpcwdb->quiz_qs_mapping} \n\t\t\t\t\t(question_id, parent_quiz_id, question_order)\n\t\t\t\t\tVALUES (%d, %d, %d)\n\t\t\t\t", $questionID, $quizID, $questionOrder)); // Update usage count for question. WPCW_questions_updateUsageCount($questionID); } } } // #### 8 - Save the new questions we have if (count($questionsToSave_New)) { // Now save all data back to the database. foreach ($questionsToSave_New as $questionDetails) { // Extract the question order, as can't save order with question in DB $questionOrder = $questionDetails['question_order']; unset($questionDetails['question_order']); // Extract the tags added for this question - we'll save manually. $tagsToAddList = $questionDetails['taglist']; unset($questionDetails['taglist']); // Create question in database $wpdb->query(arrayToSQLInsert($wpcwdb->quiz_qs, $questionDetails)); $newQuestionID = $wpdb->insert_id; // No need to update counts/associations when editing a single lone question if (!$singleQuestionMode) { // Create the association for this quiz/question. $wpdb->query($wpdb->prepare("\n\t\t\t\t\tINSERT INTO {$wpcwdb->quiz_qs_mapping} \n\t\t\t\t\t(question_id, parent_quiz_id, question_order)\n\t\t\t\t\tVALUES (%d, %d, %d)\n\t\t\t\t", $newQuestionID, $quizID, $questionOrder)); // Update usage WPCW_questions_updateUsageCount($newQuestionID); } // Add associations for tags for this unsaved question now we finally have a question ID. if (!empty($tagsToAddList)) { WPCW_questions_tags_addTags($newQuestionID, $tagsToAddList); } } } }