/**
  * 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>&nbsp;&nbsp;%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>&nbsp;&nbsp;%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('&#13;', '<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>&nbsp;&nbsp;%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>&nbsp;&nbsp;(%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>&nbsp;&nbsp;%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>&nbsp;&nbsp;%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>&nbsp;&nbsp;', admin_url('users.php'), __('&laquo; 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>&nbsp;&nbsp;', admin_url('users.php?page=WPCW_showPage_UserProgess'), $userDetails->ID, sprintf(__('&laquo; 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>&nbsp;&nbsp;', admin_url('admin.php?page=WPCW_showPage_GradeBook'), $quizDetails->parent_course_id, __("&laquo; 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);
    }