/**
  * The process grading function handles admin grading submissions.
  *
  * This function is hooked on to admin_init. It simply accepts
  * the grades as the Grader selected theme and saves the total grade and
  * individual question grades.
  *
  * @return bool
  */
 public function admin_process_grading_submission()
 {
     // NEEDS REFACTOR/OPTIMISING, such as combining the various meta data stored against the sensei_user_answer entry
     if (!isset($_POST['sensei_manual_grade']) || !wp_verify_nonce($_POST['_wp_sensei_manual_grading_nonce'], 'sensei_manual_grading') || !isset($_GET['quiz_id']) || $_GET['quiz_id'] != $_POST['sensei_manual_grade']) {
         return false;
         //exit and do not grade
     }
     $quiz_id = $_GET['quiz_id'];
     $user_id = $_GET['user'];
     global $woothemes_sensei;
     $questions = WooThemes_Sensei_Utils::sensei_get_quiz_questions($quiz_id);
     $quiz_lesson_id = $woothemes_sensei->quiz->get_lesson_id($quiz_id);
     $quiz_grade = 0;
     $count = 0;
     $quiz_grade_total = $_POST['quiz_grade_total'];
     $all_question_grades = array();
     $all_answers_feedback = array();
     foreach ($questions as $question) {
         ++$count;
         $question_id = $question->ID;
         if (isset($_POST['question_' . $question_id])) {
             $question_grade = 0;
             if ($_POST['question_' . $question_id] == 'right') {
                 $question_grade = $_POST['question_' . $question_id . '_grade'];
             }
             // add data to the array that will, after the loop, be stored on the lesson status
             $all_question_grades[$question_id] = $question_grade;
             // tally up the total quiz grade
             $quiz_grade += $question_grade;
         }
         // endif
         // Question answer feedback / notes
         $question_feedback = '';
         if (isset($_POST['questions_feedback'][$question_id])) {
             $question_feedback = wp_unslash($_POST['questions_feedback'][$question_id]);
         }
         $all_answers_feedback[$question_id] = $question_feedback;
     }
     // end for each $questions
     //store all question grades on the lesson status
     $woothemes_sensei->quiz->set_user_grades($all_question_grades, $quiz_lesson_id, $user_id);
     //store the feedback from grading
     $woothemes_sensei->quiz->save_user_answers_feedback($all_answers_feedback, $quiz_lesson_id, $user_id);
     // $_POST['all_questions_graded'] is set when all questions have been graded
     // in the class sensei grading user quiz -> display()
     if ($_POST['all_questions_graded'] == 'yes') {
         // set the users total quiz grade
         if (0 < intval($quiz_grade_total)) {
             $grade = abs(round(doubleval($quiz_grade) * 100 / $quiz_grade_total, 2));
         } else {
             $grade = 0;
         }
         WooThemes_Sensei_Utils::sensei_grade_quiz($quiz_id, $grade, $user_id);
         // Duplicating what Frontend->sensei_complete_quiz() does
         $pass_required = get_post_meta($quiz_id, '_pass_required', true);
         $quiz_passmark = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2));
         $lesson_metadata = array();
         if ($pass_required) {
             // Student has reached the pass mark and lesson is complete
             if ($quiz_passmark <= $grade) {
                 $lesson_status = 'passed';
             } else {
                 $lesson_status = 'failed';
             }
             // End If Statement
         } else {
             $lesson_status = 'graded';
         }
         $lesson_metadata['grade'] = $grade;
         // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz()" above
         WooThemes_Sensei_Utils::update_lesson_status($user_id, $quiz_lesson_id, $lesson_status, $lesson_metadata);
         if (in_array($lesson_status, array('passed', 'graded'))) {
             /**
              * Summary.
              *
              * Description.
              *
              * @since 1.7.0
              *
              * @param int  $user_id
              * @param int $quiz_lesson_id
              */
             do_action('sensei_user_lesson_end', $user_id, $quiz_lesson_id);
         }
         // end if in_array
     }
     // end if $_POST['all_que...
     if (isset($_POST['sensei_grade_next_learner']) && strlen($_POST['sensei_grade_next_learner']) > 0) {
         $load_url = add_query_arg(array('message' => 'graded'));
     } elseif (isset($_POST['_wp_http_referer'])) {
         $load_url = add_query_arg(array('message' => 'graded'), $_POST['_wp_http_referer']);
     } else {
         $load_url = add_query_arg(array('message' => 'graded'));
     }
     wp_safe_redirect(esc_url_raw($load_url));
     exit;
 }
 public function process_grading()
 {
     // NEEDS REFACTOR/OPTIMISING, such as combining the various meta data stored against the sensei_user_answer entry
     if (isset($_POST['sensei_manual_grade']) && isset($_GET['quiz_id'])) {
         //			error_log( __CLASS__ . ':' . __FUNCTION__ . ':' . print_r($_POST, true));
         $quiz_id = $_GET['quiz_id'];
         $user_id = $_GET['user'];
         $verify_nonce = wp_verify_nonce($_POST['_wp_sensei_manual_grading_nonce'], 'sensei_manual_grading');
         if ($verify_nonce && $quiz_id == $_POST['sensei_manual_grade']) {
             $questions = WooThemes_Sensei_Utils::sensei_get_quiz_questions($quiz_id);
             $quiz_grade = 0;
             $count = 0;
             $quiz_grade_total = $_POST['quiz_grade_total'];
             foreach ($questions as $question) {
                 ++$count;
                 $question_id = $question->ID;
                 if (isset($_POST['question_' . $question_id])) {
                     $correct = false;
                     $question_grade = 0;
                     $question_total_grade = $_POST['question_total_grade'];
                     if ($_POST['question_' . $question_id] == 'right') {
                         $correct = true;
                         $question_grade = $_POST['question_' . $question_id . '_grade'];
                     }
                     $activity_logged = WooThemes_Sensei_Utils::sensei_grade_question($question_id, $question_grade, $user_id);
                     $quiz_grade += $question_grade;
                 } else {
                     WooThemes_Sensei_Utils::sensei_delete_question_grade($question_id);
                 }
                 // WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), but
                 // as an answer note is not direct post_content it won't have slashes removed, so we need to do it
                 $answer_notes = wp_unslash($_POST['question_' . $question_id . '_notes']);
                 if (!$answer_notes || $answer_notes == '') {
                     $answer_notes = '';
                 }
                 WooThemes_Sensei_Utils::sensei_add_answer_notes($question_id, $user_id, $answer_notes);
             }
             if ($_POST['all_questions_graded'] == 'yes') {
                 $grade = abs(round(doubleval($quiz_grade) * 100 / $quiz_grade_total, 2));
                 $activity_logged = WooThemes_Sensei_Utils::sensei_grade_quiz($quiz_id, $grade, $user_id);
                 // Duplicating what Frontend->sensei_complete_quiz() does
                 $quiz_lesson_id = absint(get_post_meta($quiz_id, '_quiz_lesson', true));
                 $pass_required = get_post_meta($quiz_id, '_pass_required', true);
                 $quiz_passmark = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2));
                 $lesson_metadata = array();
                 if ($pass_required) {
                     // Student has reached the pass mark and lesson is complete
                     if ($quiz_passmark <= $grade) {
                         $lesson_status = 'passed';
                     } else {
                         $lesson_status = 'failed';
                     }
                     // End If Statement
                 } else {
                     $lesson_status = 'graded';
                 }
                 $lesson_metadata['grade'] = $grade;
                 // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz()" above
                 WooThemes_Sensei_Utils::update_lesson_status($user_id, $quiz_lesson_id, $lesson_status, $lesson_metadata);
                 switch ($lesson_status) {
                     case 'passed':
                     case 'graded':
                         do_action('sensei_user_lesson_end', $user_id, $quiz_lesson_id);
                         break;
                 }
             }
             if (isset($_POST['sensei_grade_next_learner']) && strlen($_POST['sensei_grade_next_learner']) > 0) {
                 $load_url = add_query_arg(array('message' => 'graded'));
             } elseif (isset($_POST['_wp_http_referer'])) {
                 $load_url = add_query_arg(array('message' => 'graded'), $_POST['_wp_http_referer']);
             } else {
                 $load_url = add_query_arg(array('message' => 'graded'));
             }
             wp_safe_redirect($load_url);
             exit;
         }
     }
 }
Exemple #3
0
 /**
  * This tests Sensei_Courses::get_completion_percentage
  * @since 1.8.0
  */
 public function testGetCompletionPercentage()
 {
     //does the function exist?
     $this->assertTrue(method_exists('WooThemes_Sensei_Course', 'get_completion_percentage'), 'The course class get_completion_percentage function does not exist.');
     // setup the test
     $test_user_id = wp_create_user('testGetCompletionPercentage', 'testGetCompletionPercentage', '*****@*****.**');
     $test_lessons = $this->factory->get_lessons();
     $test_course_id = $this->factory->get_random_course_id();
     remove_all_actions('sensei_user_course_start');
     WooThemes_Sensei_Utils::user_start_course($test_user_id, $test_course_id);
     // add lessons to the course
     foreach ($test_lessons as $lesson_id) {
         add_post_meta($lesson_id, '_lesson_course', intval($test_course_id));
     }
     // complete 3 lessons and check if the correct percentage returns
     $i = 0;
     for ($i = 0; $i < 3; $i++) {
         WooThemes_Sensei_Utils::update_lesson_status($test_user_id, $test_lessons[$i], 'complete');
     }
     $expected_percentage = 3 / count($test_lessons) * 100;
     $this->assertEquals($expected_percentage, Sensei()->course->get_completion_percentage($test_course_id, $test_user_id), 'Course completed percentage is not accurate');
     // complete all lessons
     foreach ($test_lessons as $lesson_id) {
         WooThemes_Sensei_Utils::update_lesson_status($test_user_id, $lesson_id, 'complete');
     }
     //all lessons should no be completed
     $this->assertEquals(100, Sensei()->course->get_completion_percentage($test_course_id, $test_user_id), 'Course completed percentage is not accurate');
 }
    public function sensei_complete_quiz()
    {
        global $post, $woothemes_sensei, $current_user;
        // Default grade
        $grade = 0;
        // Get Quiz Questions
        $lesson_quiz_questions = $woothemes_sensei->post_types->lesson->lesson_quiz_questions($post->ID);
        $quiz_lesson_id = absint(get_post_meta($post->ID, '_quiz_lesson', true));
        // Get quiz grade type
        $quiz_grade_type = get_post_meta($post->ID, '_quiz_grade_type', true);
        // Get quiz pass setting
        $pass_required = get_post_meta($post->ID, '_pass_required', true);
        // Get quiz pass mark
        $quiz_passmark = abs(round(doubleval(get_post_meta($post->ID, '_quiz_passmark', true)), 2));
        // Handle Quiz Completion
        if (isset($_POST['quiz_complete']) && wp_verify_nonce($_POST['woothemes_sensei_complete_quiz_noonce'], 'woothemes_sensei_complete_quiz_noonce')) {
            $sanitized_submit = esc_html($_POST['quiz_complete']);
            $questions_asked = array_filter(array_map('absint', $_POST['questions_asked']));
            $questions_asked_string = implode(',', $questions_asked);
            switch ($sanitized_submit) {
                case apply_filters('sensei_complete_quiz_text', __('Complete Quiz', 'woothemes-sensei')):
                    // Mark the Lesson as in-progress (if it isn't already), the entry is needed for WooThemes_Sensei_Utils::sensei_grade_quiz_auto() (optimise at some point?)
                    $activity_logged = WooThemes_Sensei_Utils::sensei_start_lesson($quiz_lesson_id);
                    $lesson_status = 'ungraded';
                    // Default when completing a quiz
                    // Save questions that were asked in this quiz
                    if (!empty($questions_asked_string)) {
                        update_comment_meta($activity_logged, 'questions_asked', $questions_asked_string);
                    }
                    // Save Quiz Answers
                    if (isset($_POST['sensei_question'])) {
                        WooThemes_Sensei_Utils::sensei_save_quiz_answers($_POST['sensei_question']);
                    }
                    // Grade quiz
                    // 3rd arg is count of total number of questions but it's not used by sensei_grade_quiz_auto()
                    $grade = WooThemes_Sensei_Utils::sensei_grade_quiz_auto($post->ID, $_POST['sensei_question'], count($lesson_quiz_questions), $quiz_grade_type);
                    $lesson_metadata = array();
                    // Get Lesson Grading Setting
                    if (is_wp_error($grade) || 'auto' != $quiz_grade_type) {
                        $lesson_status = 'ungraded';
                        // Quiz is manually graded and this was a user submission
                    } else {
                        // Quiz has been automatically Graded
                        if ($pass_required) {
                            // Student has reached the pass mark and lesson is complete
                            if ($quiz_passmark <= $grade) {
                                $lesson_status = 'passed';
                            } else {
                                $lesson_status = 'failed';
                            }
                            // End If Statement
                        } else {
                            $lesson_status = 'graded';
                        }
                        $lesson_metadata['grade'] = $grade;
                        // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz_auto()" above
                    }
                    WooThemes_Sensei_Utils::update_lesson_status($current_user->ID, $quiz_lesson_id, $lesson_status, $lesson_metadata);
                    switch ($lesson_status) {
                        case 'passed':
                        case 'graded':
                            do_action('sensei_user_lesson_end', $current_user->ID, $quiz_lesson_id);
                            break;
                    }
                    do_action('sensei_user_quiz_submitted', $current_user->ID, $post->ID, $grade, $quiz_passmark, $quiz_grade_type);
                    break;
                case apply_filters('sensei_save_quiz_text', __('Save Quiz', 'woothemes-sensei')):
                    $activity_logged = WooThemes_Sensei_Utils::sensei_start_lesson($quiz_lesson_id);
                    if ($activity_logged) {
                        // Save questions that were asked in this quiz
                        if (!empty($questions_asked_string)) {
                            update_comment_meta($activity_logged, 'questions_asked', $questions_asked_string);
                        }
                        if (isset($_POST['sensei_question'])) {
                            WooThemes_Sensei_Utils::sensei_save_quiz_answers($_POST['sensei_question']);
                        }
                    }
                    // Need message in case the data wasn't saved?
                    $this->messages = '<div class="sensei-message note">' . apply_filters('sensei_quiz_saved_text', __('Quiz Saved Successfully.', 'woothemes-sensei')) . '</div>';
                    break;
                case apply_filters('sensei_reset_quiz_text', __('Reset Quiz', 'woothemes-sensei')):
                    // Don't want to remove the lesson status (such as start meta data etc), just remove the answers, the questions asked meta and any grade meta
                    // Delete quiz answers, this auto deletes the corresponding meta data, such as the question/answer grade
                    WooThemes_Sensei_Utils::sensei_delete_quiz_answers($post->ID, $user_id);
                    WooThemes_Sensei_Utils::update_lesson_status($current_user->ID, $quiz_lesson_id, 'in-progress', array('questions_asked' => '', 'grade' => ''));
                    // Run any action on quiz/lesson reset (previously this didn't occur on resetting a quiz, see resetting a lesson in sensei_complete_lesson()
                    do_action('sensei_user_lesson_reset', $current_user->ID, $quiz_lesson_id);
                    $this->messages = '<div class="sensei-message note">' . apply_filters('sensei_quiz_reset_text', __('Quiz Reset Successfully.', 'woothemes-sensei')) . '</div>';
                    break;
                default:
                    // Nothing
                    break;
            }
            // End Switch Statement
            // Refresh page to avoid re-posting
            ?>
			<script type="text/javascript"> window.location = '<?php 
            echo get_permalink($post->ID);
            ?>
'; </script>
			<?php 
        }
        // End If Statement, submission of quiz
        $this->data = new stdClass();
        // Get latest quiz answers and grades
        $user_quizzes = $this->sensei_get_user_quiz_answers($post->ID);
        $user_lesson_status = WooThemes_Sensei_Utils::user_lesson_status($quiz_lesson_id, $current_user->ID);
        $user_quiz_grade = 0;
        if (isset($user_lesson_status->comment_ID)) {
            $user_quiz_grade = get_comment_meta($user_lesson_status->comment_ID, 'grade', true);
        }
        if (!is_array($user_quizzes)) {
            $user_quizzes = array();
        }
        // Check again that the lesson is complete
        $user_lesson_end = WooThemes_Sensei_Utils::user_completed_lesson($user_lesson_status);
        $user_lesson_complete = false;
        if ($user_lesson_end) {
            $user_lesson_complete = true;
        }
        // End If Statement
        $reset_allowed = get_post_meta($post->ID, '_enable_quiz_reset', true);
        // Build frontend data object
        $this->data->user_quizzes = $user_quizzes;
        $this->data->user_quiz_grade = $user_quiz_grade;
        $this->data->quiz_passmark = $quiz_passmark;
        $this->data->quiz_lesson = $quiz_lesson_id;
        $this->data->quiz_grade_type = $quiz_grade_type;
        $this->data->user_lesson_end = $user_lesson_end;
        $this->data->user_lesson_complete = $user_lesson_complete;
        $this->data->lesson_quiz_questions = $lesson_quiz_questions;
        $this->data->reset_quiz_allowed = $reset_allowed;
    }
 /**
  * Marked lesson as started for user
  * @param  integer $lesson_id ID of lesson
  * @return mixed boolean or comment_ID
  */
 public static function sensei_start_lesson($lesson_id = 0, $user_id = 0, $complete = false)
 {
     global $woothemes_sensei;
     if (intval($user_id) == 0) {
         $user_id = get_current_user_id();
     }
     $activity_logged = false;
     if (intval($lesson_id) > 0) {
         $course_id = get_post_meta($lesson_id, '_lesson_course', true);
         if ($course_id) {
             $is_user_taking_course = WooThemes_Sensei_Utils::user_started_course($course_id, $user_id);
             if (!$is_user_taking_course) {
                 WooThemes_Sensei_Utils::user_start_course($user_id, $course_id);
             }
         }
         $metadata = array();
         $status = 'in-progress';
         // Note: When this action runs the lesson status may not yet exist
         do_action('sensei_user_lesson_start', $user_id, $lesson_id);
         if ($complete) {
             $has_questions = get_post_meta($lesson_id, '_quiz_has_questions', true);
             if ($has_questions) {
                 $status = 'passed';
                 // Force a pass
                 $metadata['grade'] = 100;
             } else {
                 $status = 'complete';
             }
         }
         // Check if user is already taking the lesson
         $activity_logged = WooThemes_Sensei_Utils::user_started_lesson($lesson_id, $user_id);
         if (!$activity_logged) {
             $metadata['start'] = current_time('mysql');
             $activity_logged = WooThemes_Sensei_Utils::update_lesson_status($user_id, $lesson_id, $status, $metadata);
         }
         if ($complete) {
             // Run this *after* the lesson status has been created/updated
             do_action('sensei_user_lesson_end', $user_id, $lesson_id);
         }
     }
     return $activity_logged;
 }
 /**
  * Submit the users quiz answers for grading
  *
  * This function accepts users answers and stores it but also initiates the grading
  * if a quiz can be graded automatically it will, if not the answers can be graded by the teacher.
  *
  * @since 1.7.4
  * @access public
  *
  * @param array $quiz_answers
  * @param array $files from $_FILES
  * @param int $user_id
  * @param int $lesson_id
  *
  * @return bool $answers_submitted
  */
 public static function submit_answers_for_grading($quiz_answers, $files = array(), $lesson_id, $user_id = 0)
 {
     $answers_submitted = false;
     // get the user_id if none was passed in use the current logged in user
     if (!intval($user_id) > 0) {
         $user_id = get_current_user_id();
     }
     // make sure the parameters are valid before continuing
     if (empty($lesson_id) || empty($user_id) || 'lesson' != get_post_type($lesson_id) || !get_userdata($user_id) || !is_array($quiz_answers)) {
         return false;
     }
     global $post, $woothemes_sensei;
     // Default grade
     $grade = 0;
     // Get Quiz ID
     $quiz_id = $woothemes_sensei->lesson->lesson_quizzes($lesson_id);
     // Get quiz grade type
     $quiz_grade_type = get_post_meta($quiz_id, '_quiz_grade_type', true);
     // Get quiz pass setting
     $pass_required = get_post_meta($quiz_id, '_pass_required', true);
     // Get the minimum percentage need to pass this quiz
     $quiz_pass_percentage = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2));
     // Handle Quiz Completion submit for grading
     if (isset($_POST['questions_asked']) && is_array($_POST['questions_asked'])) {
         $questions_asked = array_filter(array_map('absint', $_POST['questions_asked']));
     } else {
         $questions_asked = array_keys($quiz_answers);
     }
     $questions_asked_string = implode(',', $questions_asked);
     // Save Quiz Answers for grading, the save function also calls the sensei_start_lesson
     self::save_user_answers($quiz_answers, $files, $lesson_id, $user_id);
     // Save questions that were asked in this quiz
     $user_lesson_status = WooThemes_Sensei_Utils::user_lesson_status($lesson_id, $user_id);
     if (isset($user_lesson_status->comment_ID)) {
         update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_string);
     }
     // Grade quiz
     $grade = WooThemes_Sensei_Utils::sensei_grade_quiz_auto($quiz_id, $quiz_answers, 0, $quiz_grade_type);
     // Get Lesson Grading Setting
     $lesson_metadata = array();
     $lesson_status = 'ungraded';
     // Default when completing a quiz
     // At this point the answers have been submitted
     $answers_submitted = true;
     // if this condition is false the quiz should manually be graded by admin
     if ('auto' == $quiz_grade_type && !is_wp_error($grade)) {
         // Quiz has been automatically Graded
         if ('on' == $pass_required) {
             // Student has reached the pass mark and lesson is complete
             if ($quiz_pass_percentage <= $grade) {
                 $lesson_status = 'passed';
             } else {
                 $lesson_status = 'failed';
             }
             // End If Statement
         } else {
             // Student only has to partake the quiz
             $lesson_status = 'graded';
         }
         $lesson_metadata['grade'] = $grade;
         // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz_auto()" above
     }
     // end if ! is_wp_error( $grade ...
     WooThemes_Sensei_Utils::update_lesson_status($user_id, $lesson_id, $lesson_status, $lesson_metadata);
     if ('passed' == $lesson_status || 'graded' == $lesson_status) {
         /**
          * Lesson end action hook
          *
          * This hook is fired after a lesson quiz has been graded and the lesson status is 'passed' OR 'graded'
          *
          * @param int $user_id
          * @param int $lesson_id
          */
         do_action('sensei_user_lesson_end', $user_id, $lesson_id);
     }
     /**
      * User quiz has been submitted
      *
      * Fires the end of the submit_answers_for_grading function. It will fire irrespective of the submission
      * results.
      *
      * @param int $user_id
      * @param int $quiz_id
      * @param string $grade
      * @param string $quiz_pass_percentage
      * @param string $quiz_grade_type
      */
     do_action('sensei_user_quiz_submitted', $user_id, $quiz_id, $grade, $quiz_pass_percentage, $quiz_grade_type);
     return $answers_submitted;
 }