/** * Convert a percentage to a percentage bar * @param String $percent The number to show in the progress bar. * @param String $title The optional title of the course. * * @return String The HTML to render the percentage bar. */ function WPCW_stats_convertPercentageToBar($percent, $title = false) { if ($title) { $title = sprintf('<span class="wpcw_progress_bar_title">%s</span>', $title); } return WPCW_content_progressBar($percent, false, $title); }
/** * Shows the quiz for the unit, based on the type being shown. * * @param Boolean $showErrorsOnForm If true, show the errors on the form, acts as an override to disable showing errors if they are not wanted. * * @return String The HTML that renders the quiz for answering. */ function render_quizzes_handleQuizRendering($showErrorsOnForm = true) { // Hopefully not needed, but just in case. if (!$this->unitQuizDetails) { return false; } // Reload all raw answers user has created so far (if we have anything so far) if (!empty($this->unitQuizProgress) && 'incomplete' == $this->unitQuizProgress->quiz_paging_status) { $this->fetch_quizzes_loadRawAnswersSoFarForThisQuiz($this->unitQuizProgress->quiz_data); } // Render the wrapper for the quiz using the pending message section // Use the Quiz ID and Unit ID to validate permissions. $html = sprintf('<div class="wpcw_fe_quiz_box_wrap" id="wpcw_fe_quiz_complete_%d_%d">', $this->unitPost->ID, $this->unitQuizDetails->quiz_id); // Are we going to show the answers on a single for as part of the review? if ($this->check_paging_shouldWeShowReviewPage_rightNow()) { // To do, show a message here about reviewing answers... $html .= WPCW_UnitFrontend::message_createMessage_warning(__('You can now review your answers before submitting them.', 'wp_courseware')); } // Use any raw selection data that we have so far to pre-fill answers. $resultsList = $this->unchecked_QuizAnswersToGrade; // enctype="multipart/form-data" for file uploads.. // data-wpcw_expired="false" - this is used by the timer to indicate if the timer has expired or not. $html .= sprintf('<form method="post" enctype="multipart/form-data" id="quiz_complete_%d_%d" data-wpcw_unit="%d" data-wpcw_quiz="%d" data-wpcw_expired="false">', $this->unitPost->ID, $this->unitQuizDetails->quiz_id, $this->unitPost->ID, $this->unitQuizDetails->quiz_id); $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">%s</div>', $this->unitQuizDetails->quiz_title); // #### 2 - Pass mark - just needed for blocking quizes if ('quiz_block' == $this->unitQuizDetails->quiz_type) { $totalQs = count($this->unitQuizDetails->questions); $passQs = ceil($this->unitQuizDetails->quiz_pass_mark / 100 * $totalQs); $html .= '<div class="wpcw_fe_quiz_pass_mark">'; $html .= sprintf(__('You\'ll need to correctly answer at least <b>%d of the %d</b> questions below (<b>at least %d%%</b>) to progress to the next unit.', 'wp_courseware'), $passQs, $totalQs, $this->unitQuizDetails->quiz_pass_mark); $html .= '</div>'; } // #### 3 - The actual question form. if (!empty($this->unitQuizDetails->questions)) { $questionNum = 1; $showQuestions = true; // Timer Mode if ('use_timer' == $this->unitQuizDetails->quiz_timer_mode) { // We've not started the quiz yet, so we show the begin test button. if (empty($this->unitQuizProgress) || 'retake_waiting' == $this->unitQuizProgress->quiz_next_step_type) { $showQuestions = false; $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>'; // Begin Quiz $html .= sprintf(' <div class="wpcw_fe_quiz_begin_quiz"> <div class="wpcw_fe_quiz_begin_quiz_hdr">%s</div> <a href="#" class="fe_btn fe_btn_completion" id="wpcw_fe_quiz_begin_quiz" data-wpcw_quiz="%d" data-wpcw_unit="%d" >%s</a> <div class="wpcw_fe_submit_loader wpcw_loader"> <img src="%simg/ajax_loader.gif" /> </div> </div>', sprintf(__('You have <b>%s</b> to complete this quiz...', 'wp_coursware'), WPCW_time_convertMinutesToHumanLabel($this->unitQuizDetails->quiz_timer_mode_limit)), $this->unitQuizDetails->quiz_id, $this->unitPost->ID, __('Begin Quiz...', 'wp_courseware'), WPCW_plugin_getPluginPath()); } else { // Total time in seconds for this quiz $timerDetails_secondsLeft = $allowedTime = 60 * $this->unitQuizDetails->quiz_timer_mode_limit; // What time do we have left? $timeSoFar = strtotime($this->unitQuizProgress->quiz_started_date); $timeNow = current_time('timestamp'); // We've started the quiz when the time so far is > 0 if ($timeSoFar > 0) { $timerDetails_secondsLeft = $allowedTime - ($timeNow - $timeSoFar); } // Show the timer if we have time left. $html .= sprintf('<div id="wpcw_fe_timer_countdown" data-time_left="%d">%s</div>', $timerDetails_secondsLeft, WPCW_time_convertSecondsToHumanLabel($timerDetails_secondsLeft)); } } if ($showQuestions) { // We're paging, so check what question or questions to show next. if ($this->check_paging_areWePagingQuestions()) { // Stores the index of the next question to show $questionToShowIndex = $this->fetch_paging_getQuestionIndex(); $question = $this->fetch_paging_getQuestion($questionToShowIndex); // Show progress of user through the questions. $html .= sprintf('<div class="wpcw_fe_paging_progress">%s</div>', sprintf(__('Question %d of %d', 'wp_courseware'), $questionToShowIndex + 1, $this->fetch_paging_getQuestionCount())); // Header before questions (but show progress before the header line). $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>'; // Show the answer later button. if ($this->check_paging_shouldWeShowAnswerLaterButton()) { $html .= sprintf('<div class="wpcw_fe_quiz_answer_later"><a href="#" class="fe_btn fe_btn_small fe_btn_navigation" id="wpcw_fe_quiz_answer_later">%s</a></div>', __('Answer Later...', 'wp_courseware')); } // Render just this question $html .= $this->render_quizzes_handleQuizRendering_singleQuestion($question, $resultsList, $showErrorsOnForm, $questionToShowIndex + 1, true, 0); // Work out what caption to show for the submit button. Change this if we're about to // submit the answers for the final question or review our answers. $buttonCaption = __('Save & Next Question »', 'wp_courseware'); // Are we showing the last incomplete question (and we're nearly complete) if ($this->unitQuizProgress && $this->unitQuizProgress->quiz_paging_incomplete <= 1 && $this->check_paging_isThisTheLastIncompleteQuestion($question->question_id)) { // We appear to be on the last question. Are we going to review too? if ($this->check_paging_shouldWeShowReviewPage()) { $buttonCaption = __('Save & Review Answers »', 'wp_courseware'); } else { $buttonCaption = __('Submit Answers', 'wp_courseware'); } } // Previous button - created if required. Using a button rather than a link to ensure the buttons look right when side-by-side. $buttonPreviousClicker = false; if ($this->check_paging_shouldWeShowPreviousButton()) { $buttonPreviousClicker = sprintf('<input type="submit" class="fe_btn fe_btn_completion btn_completion" id="fe_btn_quiz_previous" name="previous_question" value="%s">', __('« Previous Question', 'wp_courseware')); } // #### 4A - The navigation buttons $html .= sprintf('<div class="wpcw_fe_quiz_submit wpcw_fe_quiz_submit_data"> %s <div class="wpcw_fe_submit_loader wpcw_loader"> <img src="%simg/ajax_loader.gif" /> </div> %s <input type="submit" class="fe_btn fe_btn_completion btn_completion" id="fe_btn_quiz_next" name="submit" value="%s"> </div>', WPCW_content_progressBar(0, 'wpcw_fe_upload_progress'), WPCW_plugin_getPluginPath(), $buttonPreviousClicker, $buttonCaption); } else { // Header before questions $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>'; $questionIndex = 0; foreach ($this->unitQuizDetails->questions as $question) { $html .= $this->render_quizzes_handleQuizRendering_singleQuestion($question, $resultsList, $showErrorsOnForm, $questionNum++, false, $questionIndex++); } // #### 4B - The submit answers button. //<a href="#" class="fe_btn fe_btn_completion btn_completion" id="quiz_complete_%d_%d">%s</a> $html .= sprintf('<div class="wpcw_fe_quiz_submit wpcw_fe_quiz_submit_data"> %s <div class="wpcw_fe_submit_loader wpcw_loader"> <img src="%simg/ajax_loader.gif" /> </div> <input type="submit" class="fe_btn fe_btn_completion btn_completion" name="submit" value="%s"> </div>', WPCW_content_progressBar(0, 'wpcw_fe_upload_progress'), WPCW_plugin_getPluginPath(), __('Submit Answers', 'wp_courseware')); } // end of check for paging. } // end of check of showing questions (e.g. timer mode) } // end of question check $html .= '</div>'; // .wpcw_fe_quiz_box $html .= '</form>'; $html .= '</div>'; // .wpcw_fe_quiz_box_wrap return $html; }
/** * Creates widget that shows off the user's progress on their respective courses. * * e.g. [wpcourse_progress courses="2" user_progress="true" user_grade="true" /] */ function WPCW_shortcodes_showTrainingCourseProgress($atts, $content) { extract(shortcode_atts(array('courses' => 'all', 'user_progress' => true, 'user_grade' => true), $atts)); // Check flags to see what we're showing $showUserProgress = 'true' == strtolower($user_progress); $showUserGrade = 'true' == strtolower($user_grade); // Show a message to the user if they are not logged in. $user_id = get_current_user_id(); if (!$user_id) { return sprintf('<div class="wpcw_fe_progress_box_wrap"><div class="wpcw_fe_progress_box wpcw_fe_progress_box_error">%s</div></div>', apply_filters('wpcw_front_shortcode_wpcourse_progress_notloggedin', __('You need to be logged in to see your course progress.', 'wp_courseware'))); } // Get a list of all of the courses that the user is subscribed to. $courseList = WPCW_users_getUserCourseList($user_id); $selectedCourseList = array(); // Filter the list of courses to remove the ones that the trainer doesn't // want the user to see. 'all' means show all courses with no filtering. // Only do this check if we have any courses to check, to save time. if (!empty($courseList) && 'all' != strtolower($courses)) { $selectedCourseList = explode(',', $courses); // This is the list of courses we'll actually use. $chosenListOfCourses = array(); // We've got courses that have been specified, so we need to go through them now. if (!empty($selectedCourseList)) { foreach ($selectedCourseList as $potentialItem) { $potentialItem = trim($potentialItem); // Got a potential ID here. if (preg_match('/^([0-9]+)$/', $potentialItem)) { // Check each course we still have to see if the ID matches. // I know it's O(N), but it's simple at least. foreach ($courseList as $idx => $aSingleCourse) { // Got a match... if ($potentialItem == $aSingleCourse->course_id) { // Move the chosen course to the selected list. Doing // so makes subsequent searches faster. $chosenListOfCourses[] = $aSingleCourse; unset($courseList[$idx]); // Stop searching, we found it. break; } } // end foreach } // end ID check } // end foreach of potential IDs in list. } // Overwrite the list of courses to use. $courseList = $chosenListOfCourses; } // Handle when the list is empty if (empty($courseList)) { // Change message slightly based on how many courses are selected. $messageToShow = __('You are not currently enrolled on any courses.', 'wp_courseware'); if (!empty($selectedCourseList)) { $messageToShow = __('You are not currently enrolled on any of these courses.', 'wp_courseware'); } return sprintf('<div class="wpcw_fe_progress_box_wrap"><div class="wpcw_fe_progress_box wpcw_fe_progress_box_error">%s</div></div>', apply_filters('wpcw_front_shortcode_wpcourse_progress_no_courses', $messageToShow, count($courseList))); } // Used to determine how many columns we have in the table for showing the course details. $columnCount = 1; // Show the list of courses $html = '<table id="wpcw_fe_course_progress" class="wpcw_fe_table wpcw_fe_summary_course_progress">'; // The title bar for the course. $html .= '<thead><tr>'; // Course name $html .= sprintf('<th class="wpcw_fe_course_progress_course">%s</th>', __('Course', 'wp_courseware')); // Course progress if ($showUserProgress) { $columnCount++; $html .= sprintf('<th class="wpcw_fe_course_progress_pc">%s</th>', __('Your Progress', 'wp_courseware')); } // Overall grade so far if ($showUserGrade) { $columnCount++; $html .= sprintf('<th class="wpcw_fe_course_progress_grade">%s</th>', __('Your Overall Grade', 'wp_courseware')); } $html .= '</tr></thead><tbody>'; // The main body of the course information. foreach ($courseList as $aSingleCourse) { $html .= '<tr class="wpcw_fe_course_progress_row">'; // Course name $html .= sprintf('<td class="wpcw_fe_course_progress_course"><a href="#" data-toggle="wpcw_fe_course_progress_detail_%d">%s</a></td>', $aSingleCourse->course_id, $aSingleCourse->course_title); // Course progress if ($showUserProgress) { $html .= sprintf('<td class="wpcw_fe_course_progress_pc">%s</td>', WPCW_content_progressBar($aSingleCourse->course_progress)); } // Show the Overall grade so far if ($showUserGrade) { $html .= sprintf('<td class="wpcw_fe_course_progress_grade">%s</td>', WPCW_courses_getCourseCumulativeGrade($aSingleCourse->course_id, $user_id)); } $html .= '</tr>'; // Show full course details. This might be a setting at some point. $html .= sprintf('<tr><td class="wpcw_fe_course_progress_detail" id="wpcw_fe_course_progress_detail_%d" colspan="%d">', $aSingleCourse->course_id, $columnCount); $html .= WPCW_courses_renderCourseList($aSingleCourse->course_id, array('hide_credit_link' => true)); $html .= '</td></tr>'; } $html .= '</tbody></table>'; // end .wpcw_fe_summary_course_progress return $html; }