/** * Render all of the correct answers for the user. * * @param $returnDataInArray Boolean If true, then return the answers as an array of formatted elements. */ function render_quizzes_showAllCorrectAnswers($returnDataInArray = false) { // Hopefully not needed, but just in case. if (!$this->unitQuizDetails) { return false; } // @since V2.90 // Working on a survey, so this is a little different. We only show a response, not giving // any indication if they are right or not. if ('survey' == $this->unitQuizDetails->quiz_type) { $setting_showCorrectAnswer = false; $setting_showOtherPossible = false; $setting_showUserAnswer = true; $setting_showExplanation = false; $setting_showMarkAnswers = false; $setting_showAnswersLater = $setting_showUserAnswer; } else { // Work out the settings for showing the answers $setting_allRaw = maybe_unserialize($this->unitQuizDetails->show_answers_settings); // Extract our settings. $setting_showCorrectAnswer = 'on' == WPCW_arrays_getValue($setting_allRaw, 'show_correct_answer'); $setting_showUserAnswer = 'on' == WPCW_arrays_getValue($setting_allRaw, 'show_user_answer'); $setting_showExplanation = 'on' == WPCW_arrays_getValue($setting_allRaw, 'show_explanation'); $setting_showOtherPossible = 'on' == WPCW_arrays_getValue($setting_allRaw, 'show_other_possible_answers'); $setting_showMarkAnswers = 'on' == WPCW_arrays_getValue($setting_allRaw, 'mark_answers'); } $html = false; $arrayToReturn = array(); // Create a simple DIV wrapper for the correct answers. if (!$returnDataInArray) { $html .= '<div class="wpcw_fe_quiz_box_wrap wpcw_fe_quiz_box_full_answers">'; $html .= '<div class="wpcw_fe_quiz_box wpcw_fe_quiz_box_pending">'; // #### 1 - Quiz Title - constant for all quizzes $html .= sprintf('<div class="wpcw_fe_quiz_title"><b>%s</b> %s</div>', __('Correct Answers for: ', 'wp_courseware'), $this->unitQuizDetails->quiz_title); // #### 2 - Header before questions $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>'; } // #### 3 - Extract the correct answer from the index of questions. if ($this->unitQuizDetails->questions && count($this->unitQuizDetails->questions) > 0) { $questionNum = 1; foreach ($this->unitQuizDetails->questions as $question) { // Clean out the html ready to add each item if ($returnDataInArray) { $html = false; } else { $html .= '<div class="wpcw_fe_quiz_q_single">'; } // ### 3a - Question title $html .= sprintf('<div class="wpcw_fe_quiz_q_title">%s #%d: %s</div>', __('Question', 'wp_courseware'), $questionNum++, nl2br(htmlspecialchars($question->question_question))); // ### 3b - The question image - If there's an image for this quiz, then render it. if ($question->question_image) { $html .= sprintf('<div class="wpcw_fe_quiz_q_image"><img src="%s" /></div>', $question->question_image); } // ### 3c - Their Mark (correct or incorrect) if ($setting_showMarkAnswers) { // See if they have an answer for this question first if (!empty($this->unitQuizProgress->quiz_data) && isset($this->unitQuizProgress->quiz_data[$question->question_id])) { $theirAnswerDetails = $this->unitQuizProgress->quiz_data[$question->question_id]; switch ($question->question_type) { // open-ended questions have grades. case 'open': case 'upload': // Check if question still needs to be marked. if (isset($this->unitQuizProgress->quiz_needs_marking_list) && is_array($this->unitQuizProgress->quiz_needs_marking_list) && in_array($question->question_id, $this->unitQuizProgress->quiz_needs_marking_list)) { $html .= sprintf('<div class="wpcw_fe_quiz_q_result wpcw_fe_quiz_q_user_grade"><b>%s:</b> %s</div>', __('Your Grade', 'wp_courseware'), __('Pending', 'wp_courseware')); } else { $gradePercentage = WPCW_arrays_getValue($theirAnswerDetails, 'their_grade'); $html .= sprintf('<div class="wpcw_fe_quiz_q_result wpcw_fe_quiz_q_user_grade"><b>%s:</b> %d%%</div>', __('Your Grade', 'wp_courseware'), $gradePercentage); } break; case 'multi': case 'truefalse': // Got it right... if ('yes' == WPCW_arrays_getValue($theirAnswerDetails, 'got_right')) { $html .= sprintf('<div class="wpcw_fe_quiz_q_result wpcw_fe_quiz_q_result_correct">%s</div>', __('Correct ', 'wp_courseware')); } else { $html .= sprintf('<div class="wpcw_fe_quiz_q_result wpcw_fe_quiz_q_result_incorrect">%s</div>', __('Incorrect ', 'wp_courseware')); } break; } } } // end if ($setting_showMarkAnswers) // Work out the correct answer $correctAnswer = $this->check_quizzes_getCorrectAnswer($question); // ### 3c - Answer - User's Answer if ($setting_showUserAnswer) { $theirAnswer = NULL; // See if they have an answer for this question first if (!empty($this->unitQuizProgress->quiz_data) && isset($this->unitQuizProgress->quiz_data[$question->question_id])) { $theirAnswerDetails = $this->unitQuizProgress->quiz_data[$question->question_id]; // Handle file types and open-ended questions switch ($question->question_type) { // File upload, so show link to file. case 'upload': $theirAnswerRaw = WPCW_arrays_getValue($theirAnswerDetails, 'their_answer'); $theirAnswer = sprintf('<a href="%s%s" target="_blank">%s .%s %s (%s)</a>', WP_CONTENT_URL, $theirAnswerRaw, __('Open', 'wp_courseware'), pathinfo($theirAnswerRaw, PATHINFO_EXTENSION), __('File', 'wp_courseware'), WPCW_files_getFileSize_human($theirAnswerRaw)); break; // Paragraph of text - show with <p> tags // Paragraph of text - show with <p> tags case 'open': $theirAnswer = str_replace(' ', '<br />', WPCW_arrays_getValue($theirAnswerDetails, 'their_answer')); break; default: $theirAnswer = WPCW_arrays_getValue($theirAnswerDetails, 'their_answer'); break; } } // We've got a valid answer. if (!is_null($theirAnswer)) { $html .= sprintf('<div class="wpcw_fe_quiz_q_your_answer"><b>%s:</b> %s</div>', __('Your Answer', 'wp_courseware'), $theirAnswer); // Show associated image for their answer if we have one. $imageURL = $this->fetch_quizzes_getImageForAnswer($question, WPCW_arrays_getValue($theirAnswerDetails, 'their_answer_raw')); if ($imageURL) { $html .= sprintf('<div class="wpcw_fe_quiz_a_image"><img src="%s" /></div>', $imageURL); } } else { $html .= sprintf('<div class="wpcw_fe_quiz_q_your_answer wpcw_fe_quiz_q_your_answer_none_found"><b>%s:</b> (%s)</div>', __('Your Answer', 'wp_courseware'), __('We don\'t have your answer for this question', 'wp_courseware')); } } // end if ($setting_showUserAnswer) // ### 3d - Answer - The Correct Answer (for fixed answer questions) if ($setting_showCorrectAnswer && in_array($question->question_type, array('truefalse', 'multi'))) { $html .= sprintf('<div class="wpcw_fe_quiz_q_correct"><b>%s:</b> %s</div>', __('Correct Answer', 'wp_courseware'), $correctAnswer); // Show image if there is one for this answer. $imageURL = $this->fetch_quizzes_getImageForAnswer($question, $question->question_correct_answer); if ($imageURL) { $html .= sprintf('<div class="wpcw_fe_quiz_a_image"><img src="%s" /></div>', $imageURL); } } // end if ($setting_showCorrectAnswer && $correctAnswer) // ### 3e - Answer - Other possible answers if ($setting_showOtherPossible && 'multi' == $question->question_type) { $html .= sprintf('<div class="wpcw_fe_quiz_q_possible"><b>%s:</b><ul class="wpcw_fe_quiz_q_possible_list">', __('Other Possible Answers', 'wp_courseware')); $answerList = WPCW_quizzes_decodeAnswers($question->question_data_answers); // Got to limit how many quiz answers we show, hence checking if ($question->question_multi_random_enable) { // Use object to select which answers to show. Set it up using answers from above. $quObjTemp = new WPCW_quiz_MultipleChoice($question); $quObjTemp->answerListRaw = $answerList; // Randomise and overwrite with just selected items. $quObjTemp->processAnswersWithRandomOption(); $answerList = $quObjTemp->answerListRaw; } if (!empty($answerList)) { foreach ($answerList as $singleAnswer) { // Don't show correct or user's answers.s if ($correctAnswer == $singleAnswer['answer'] || $theirAnswer == $singleAnswer['answer']) { continue; } $html .= sprintf('<li>%s</li>', $singleAnswer['answer']); } } // The closing .wpcw_fe_quiz_q_possible $html .= '</ul></div>'; } // end if ($setting_showOtherPossible && 'multi' == $question->question_type) // 3f - Quiz Explanation - If there's a quiz explanation, put it here. if ($setting_showExplanation && $question->question_answer_explanation) { $html .= sprintf('<div class="wpcw_fe_quiz_q_explanation"><b>%s:</b> %s</div>', __('Explanation', 'wp_courseware'), nl2br($question->question_answer_explanation)); } // Add this portion of data rather than use the div wrapper. if ($returnDataInArray) { $arrayToReturn[] = $html; } else { $html .= '</div>'; // wpcw_fe_quiz_q_single } } } // Non-array mode - return HTML if (!$returnDataInArray) { $html .= '</div>'; // .wpcw_fe_quiz_box $html .= '</div>'; // .wpcw_fe_quiz_box_wrap } else { return $arrayToReturn; } return $html; }
/** * Shows a detailed summary of the user's quiz or survey answers. */ function WPCW_showPage_UserProgess_quizAnswers_load() { global $wpcwdb, $wpdb; $wpdb->show_errors(); $page = new PageBuilder(false); $page->showPageHeader(__('Detailed User Quiz/Survey Results', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL()); $userID = WPCW_arrays_getValue($_GET, 'user_id') + 0; $unitID = WPCW_arrays_getValue($_GET, 'unit_id') + 0; $quizID = WPCW_arrays_getValue($_GET, 'quiz_id') + 0; // Create a link back to the detailed user progress, and back to all users. printf('<div class="wpcw_button_group">'); // Link back to all user summary printf('<a href="%s" class="button-secondary">%s</a> ', admin_url('users.php'), __('« Return to User Summary', 'wp_courseware')); if ($userDetails = get_userdata($userID)) { // Link back to user's personal summary printf('<a href="%s&user_id=%d" class="button-secondary">%s</a> ', admin_url('users.php?page=WPCW_showPage_UserProgess'), $userDetails->ID, sprintf(__('« Return to <b>%s\'s</b> Progress Report', 'wp_courseware'), $userDetails->display_name)); } // Try to get the full detailed results. $results = WPCW_quizzes_getUserResultsForQuiz($userID, $unitID, $quizID); // No results, so abort. if (!$results) { // Close the button wrapper for above early printf('</div>'); // .wpcw_button_group $page->showMessage(__('Sorry, but no results could be found.', 'wp_courseware'), true); $page->showPageFooter(); return; } // Could potentially have an issue where the quiz has been deleted // but the data exists.. small chance though. $quizDetails = WPCW_quizzes_getQuizDetails($quizID, true, true, $userID); // Extra button - return to gradebook printf('<a href="%s&course_id=%d" class="button-secondary">%s</a> ', admin_url('admin.php?page=WPCW_showPage_GradeBook'), $quizDetails->parent_course_id, __("« Return to Gradebook", 'wp_courseware')); printf('</div>'); // .wpcw_button_group // #### 1 - Handle grades being updated $results = WPCW_showPage_UserProgess_quizAnswers_handingGrading($quizDetails, $results, $page, $userID, $unitID); // #### 2A - Check if next action for user has been triggered by the admin. $results = WPCW_showPage_UserProgess_quizAnswers_whatsNext_savePreferences($quizDetails, $results, $page, $userID, $unitID); // #### 2B - Handle telling admin what's next WPCW_showPage_UserProgess_quizAnswers_whatsNext($quizDetails, $results, $page, $userID, $unitID); //Ê#### 3 - Handle sending emails if something has changed. if (isset($results->sendOutEmails) && $results->sendOutEmails) { $extraDetail = isset($results->extraEmailDetail) ? $results->extraEmailDetail : ''; // Only called if the quiz was graded. if (isset($results->quiz_has_just_been_graded) && $results->quiz_has_just_been_graded) { // Need to call the action anyway, but any functions hanging off this // should check if the admin wants users to have notifications or not. do_action('wpcw_quiz_graded', $userID, $quizDetails, number_format($results->quiz_grade, 1), $extraDetail); } $courseDetails = WPCW_courses_getCourseDetails($quizDetails->parent_course_id); if ($courseDetails->email_quiz_grade_option == 'send_email') { // Message is only if quiz has been graded. if (isset($results->quiz_has_just_been_graded) && $results->quiz_has_just_been_graded) { $page->showMessage(__('The user has been sent an email with their grade for this course.', 'wp_courseware')); } } } // #### - Table 1 - Overview printf('<h3>%s</h3>', __('Quiz/Survey Overview', 'wp_courseware')); $tbl = new TableBuilder(); $tbl->attributes = array('id' => 'wpcw_tbl_progress_quiz_info', 'class' => 'widefat wpcw_tbl'); $tblCol = new TableColumn(false, 'quiz_label'); $tblCol->cellClass = 'wpcw_tbl_label'; $tbl->addColumn($tblCol); $tblCol = new TableColumn(false, 'quiz_detail'); $tbl->addColumn($tblCol); // These are the base details for the quiz to show. $summaryData = array(__('Quiz Title', 'wp_courseware') => $quizDetails->quiz_title, __('Quiz Description', 'wp_courseware') => $quizDetails->quiz_desc, __('Quiz Type', 'wp_courseware') => WPCW_quizzes_getQuizTypeName($quizDetails->quiz_type), __('No. of Questions', 'wp_courseware') => $results->quiz_question_total, __('Completed Date', 'wp_courseware') => __('About', 'wp_courseware') . ' ' . human_time_diff($results->quiz_completed_date_ts) . ' ' . __('ago', 'wp_courseware') . '<br/><small>(' . date('D jS M Y \\a\\t H:i:s', $results->quiz_completed_date_ts) . ')</small>', __('Number of Quiz Attempts', 'wp_courseware') => $results->attempt_count, __('Permitted Quiz Attempts', 'wp_courseware') => -1 == $quizDetails->quiz_attempts_allowed ? __('Unlimited', 'wp_courseware') : $quizDetails->quiz_attempts_allowed); // Quiz details relating to score, etc. if ('survey' != $quizDetails->quiz_type) { $summaryData[__('Pass Mark', 'wp_courseware')] = $quizDetails->quiz_pass_mark . '%'; // Still got items to grade if ($results->quiz_needs_marking > 0) { $summaryData[__('No. of Questions to Grade', 'wp_courseware')] = '<span class="wpcw_status_info wpcw_icon_pending">' . $results->quiz_needs_marking . '</span>'; $summaryData[__('Overall Grade', 'wp_courseware')] = '<span class="wpcw_status_info wpcw_icon_pending">' . __('Awaiting Final Grading', 'wp_courseware') . '</span>'; } else { $summaryData[__('No. of Question to Grade', 'wp_courseware')] = '-'; // Show if PASSED or FAILED with the overall grade. $gradeData = false; if ($results->quiz_grade >= $quizDetails->quiz_pass_mark) { $gradeData = sprintf('<span class="wpcw_tbl_progress_quiz_overall wpcw_question_yesno_status wpcw_question_yes">%s%% %s</span>', number_format($results->quiz_grade, 1), __('Passed', 'wp_courseware')); } else { $gradeData = sprintf('<span class="wpcw_tbl_progress_quiz_overall wpcw_question_yesno_status wpcw_question_no">%s%% %s</span>', number_format($results->quiz_grade, 1), __('Failed', 'wp_courseware')); } $summaryData[__('Overall Grade', 'wp_courseware')] = $gradeData; } } foreach ($summaryData as $label => $data) { $tbl->addRow(array('quiz_label' => $label . ':', 'quiz_detail' => $data)); } echo $tbl->toString(); // ### 4 - Form Code - to allow instructor to send data back to printf('<form method="POST" id="wpcw_tbl_progress_quiz_grading_form">'); printf('<input type="hidden" name="grade_answers_submitted" value="true">'); // ### 5 - Table 2 - Each Specific Quiz $questionNumber = 0; if ($results->quiz_data && count($results->quiz_data) > 0) { foreach ($results->quiz_data as $questionID => $answer) { $data = $answer; // Get the question type if (isset($quizDetails->questions[$questionID])) { // Store as object for easy reference. $quObj = $quizDetails->questions[$questionID]; // Render the question as a table. printf('<h3>%s #%d - %s</h3>', __('Question', 'wp_courseware'), ++$questionNumber, $quObj->question_question); $tbl = new TableBuilder(); $tbl->attributes = array('id' => 'wpcw_tbl_progress_quiz_info', 'class' => 'widefat wpcw_tbl wpcw_tbl_progress_quiz_answers_' . $quObj->question_type); $tblCol = new TableColumn(false, 'quiz_label'); $tblCol->cellClass = 'wpcw_tbl_label'; $tbl->addColumn($tblCol); $tblCol = new TableColumn(false, 'quiz_detail'); $tbl->addColumn($tblCol); $theirAnswer = false; switch ($quObj->question_type) { case 'truefalse': case 'multi': $theirAnswer = $answer['their_answer']; break; // File Upload - create a download link // File Upload - create a download link case 'upload': $theirAnswer = sprintf('<a href="%s%s" target="_blank" class="button-primary">%s .%s %s (%s)</a>', WP_CONTENT_URL, $answer['their_answer'], __('Open', 'wp_courseware'), pathinfo($answer['their_answer'], PATHINFO_EXTENSION), __('File', 'wp_courseware'), WPCW_files_getFileSize_human($answer['their_answer'])); break; // Open Ended - Wrap in span tags, to cap the size of the field, and format new lines. // Open Ended - Wrap in span tags, to cap the size of the field, and format new lines. case 'open': $theirAnswer = '<span class="wpcw_q_answer_open_wrap"><textarea readonly>' . $data['their_answer'] . '</textarea></span>'; break; } // end of $theirAnswer check $summaryData = array(__('Type', 'wp_courseware') => array('data' => WPCW_quizzes_getQuestionTypeName($quObj->question_type), 'cssclass' => ''), __('Their Answer', 'wp_courseware') => array('data' => $theirAnswer, 'cssclass' => '')); // Just for quizzes - show answers/grade if ('survey' != $quizDetails->quiz_type) { switch ($quObj->question_type) { case 'truefalse': case 'multi': // The right answer... $summaryData[__('Correct Answer', 'wp_courseware')] = array('data' => $answer['correct'], 'cssclass' => ''); // Did they get it right? $getItRight = sprintf('<span class="wpcw_question_yesno_status wpcw_question_%s">%s</span>', $answer['got_right'], 'yes' == $answer['got_right'] ? __('Yes', 'wp_courseware') : __('No', 'wp_courseware')); $summaryData[__('Did they get it right?', 'wp_courseware')] = array('data' => $getItRight, 'cssclass' => ''); break; case 'upload': case 'open': $gradeHTML = false; $theirGrade = WPCW_arrays_getValue($answer, 'their_grade'); // Not graded - show select box. if ($theirGrade == 0) { $cssClass = 'wpcw_grade_needs_grading'; } else { $cssClass = 'wpcw_grade_already_graded'; $gradeHTML = sprintf('<span class="wpcw_grade_view">%d%% <a href="#">(%s)</a></span>', $theirGrade, __('Click to edit', 'wp_courseware')); } // Not graded yet, allow admin to grade the quiz, or change // the grading later if they want to. $gradeHTML .= WPCW_forms_createDropdown('grade_quiz_' . $quObj->question_id, WPCW_quizzes_getPercentageList(__('-- Select a grade --', 'wp_courseware')), $theirGrade, false, 'wpcw_tbl_progress_quiz_answers_grade'); $summaryData[__('Their Grade', 'wp_courseware')] = array('data' => $gradeHTML, 'cssclass' => $cssClass); break; } } // Check of showing the right answer. foreach ($summaryData as $label => $data) { $tbl->addRow(array('quiz_label' => $label . ':', 'quiz_detail' => $data['data']), $data['cssclass']); } echo $tbl->toString(); } // end if (isset($quizDetails->questions[$questionID])) } // foreach ($results->quiz_data as $questionID => $answer) } printf('</form>'); // Shows a bar that pops up, allowing the user to easily save all grades that have changed. ?> <div id="wpcw_sticky_bar" style="display: none"> <div id="wpcw_sticky_bar_inner"> <a href="#" id="wpcw_tbl_progress_quiz_grading_updated" class="button-primary"><?php _e('Save Changes to Grades', 'wp_courseware'); ?> </a> <span id="wpcw_sticky_bar_status" title="<?php _e('Grades have been changed. Ready to save changes?', 'wp_courseware'); ?> "></span> </div> </div> <br/><br/><br/><br/> <?php $page->showPageFooter(); }
/** * (non-PHPdoc) * @see WPCW_quiz_base::renderForm_toString() */ public function renderForm_toString($parentQuiz, $questionNum, $selectedAnswer, $showAsError, $errorToShow = false) { // Generate the ID of the field, also used for the CSS ID $fieldID = sprintf('question_%d_%s_%d', $parentQuiz->quiz_id, $this->questionType, $this->quizItem->question_id); // Have they already uploaded a file? If so, tell them with a link to open the file. if ($selectedAnswer) { // Shows the link for the existing file. $this->extraQuizHTML .= sprintf('<div class="wpcw_fe_quiz_q_upload_existing"> %s <b><a href="%s%s" target="_blank">.%s %s (%s)</a></b> %s </div>', __('You have uploaded a', 'wp_courseware'), WP_CONTENT_URL, $selectedAnswer, pathinfo($selectedAnswer, PATHINFO_EXTENSION), __('file', 'wp_courseware'), WPCW_files_getFileSize_human($selectedAnswer), __('for this answer.', 'wp_courseware')); // Shows the link to change the file $this->extraQuizHTML .= sprintf('<div class="wpcw_fe_quiz_q_upload_change_file_wrap"> <a href="#" class="wpcw_fe_quiz_q_upload_change_file" data-fieldid="%s">%s</a> <a href="#" class="wpcw_fe_quiz_q_upload_change_file_cancel">%s</a> <div class="wpcw_fe_quiz_q_upload_change_holder"></div> </div>', $fieldID, __('Click here to upload a different file...', 'wp_courseware'), __('Cancel uploading a different file', 'wp_courseware')); } else { // The file upload bit. $this->extraQuizHTML .= sprintf('<div class="wpcw_fe_quiz_q_upload_wrapper" id="%s">', $fieldID); $this->extraQuizHTML .= sprintf('<input type="file" name="%s" >', $fieldID); $this->extraQuizHTML .= '</div>'; } // Work out what file types are permitted $fileTypes = WPCW_files_cleanFileExtensionList($this->quizItem->question_answer_file_types); $permittedFiles = false; if (!empty($fileTypes)) { // Show message about permitted file types, which can be customised if needed. $permittedFiles = apply_filters('wpcw_front_quiz_upload_permitted_files', __('Allowed file types: ', 'wp_courseware') . implode(', ', $fileTypes) . '. ', $fileTypes); } // Add the hint if there is one if ($this->quizItem->question_answer_hint) { $this->extraQuizHTML .= sprintf('<div class="wpcw_fe_quiz_q_hint">%s</div>', $this->quizItem->question_answer_hint); } // Add the file type list if there are any if ($permittedFiles) { $this->extraQuizHTML .= sprintf('<div class="wpcw_fe_quiz_q_hint wpcw_fe_quiz_q_upload_permitted_files">%s</div>', $permittedFiles); } return parent::renderForm_toString_withClass($parentQuiz, $questionNum, $selectedAnswer, $showAsError, 'wpcw_fe_quiz_q_upload', $errorToShow); }