You are here

function quiz_take_quiz in Quiz 6.6

Same name and namespace in other branches
  1. 8.4 quiz.module \quiz_take_quiz()
  2. 5.2 quiz.module \quiz_take_quiz()
  3. 5 quiz.module \quiz_take_quiz()
  4. 6.2 quiz.module \quiz_take_quiz()
  5. 6.3 quiz.module \quiz_take_quiz()
  6. 6.4 quiz.module \quiz_take_quiz()
  7. 6.5 quiz.module \quiz_take_quiz()
  8. 7.6 quiz.module \quiz_take_quiz()
  9. 7 quiz.module \quiz_take_quiz()
  10. 7.4 quiz.module \quiz_take_quiz()
  11. 7.5 quiz.module \quiz_take_quiz()

Handles quiz taking.

This gets executed when the main quiz node is first loaded.

Parameters

$quiz: The quiz node.

Return value

HTML output for page.

1 call to quiz_take_quiz()
quiz_view in ./quiz.module
Implementation of hook_view().

File

./quiz.module, line 986
Quiz Module

Code

function quiz_take_quiz($quiz) {
  global $user;
  $allow_skipping = TRUE;

  // If no access, fail.
  if (!user_access('access quiz')) {
    drupal_access_denied();
    return;
  }
  if (!isset($quiz)) {
    drupal_not_found();
    return;
  }

  // If anonymous user and no unique hash, refresh with a unique string to prevent caching.
  if (!$quiz->name && arg(4) == NULL) {
    drupal_goto('node/' . $quiz->nid . '/quiz/start/' . md5(mt_rand() . time()));
  }
  if (!isset($_SESSION['quiz_' . $quiz->nid]['quiz_questions'])) {
    $rid = _quiz_active_result_id($user->uid, $quiz->nid, $quiz->vid);

    // Are we resuming an in-progress quiz?
    if ($rid > 0) {
      _quiz_resume_existing_quiz($quiz, $user->uid, $rid);
    }
    elseif ($rid = quiz_start_actions($quiz)) {

      // Create question list.
      $questions = quiz_build_question_list($quiz);
      if ($questions === FALSE) {
        drupal_set_message(t('Not enough random questions were found. Please !add_more_questions before trying to take this @quiz.', array(
          '@quiz' => QUIZ_NAME,
          '!add_more_questions' => l(t('add more questions'), 'node/' . arg(1) . '/questions'),
        )), 'error');
        return array(
          'body' => array(
            '#value' => '',
          ),
        );
      }
      if (count($questions) == 0) {
        drupal_set_message(t('No questions were found. Please !assign_questions before trying to take this @quiz.', array(
          '@quiz' => QUIZ_NAME,
          '!assign_questions' => l(t('assign questions'), 'node/' . arg(1) . '/questions'),
        )), 'error');
        return array(
          'body' => array(
            '#value' => '',
          ),
        );
      }

      // Initialize session variables.
      $_SESSION['quiz_' . $quiz->nid]['quiz_questions'] = $questions;
      $_SESSION['quiz_' . $quiz->nid]['result_id'] = $rid;
      $_SESSION['quiz_' . $quiz->nid]['question_number'] = 0;
      $_SESSION['quiz_' . $quiz->nid]['question_start_time'] = time();
      $_SESSION['quiz_' . $quiz->nid]['question_duration'] = $quiz->time_limit;
    }
    else {
      return array(
        'body' => array(
          '#value' => '',
        ),
      );
    }
  }
  if (!isset($_POST['op'])) {

    // Starting new quiz... Do we need to show instructions here?
  }
  elseif ($_POST['op'] == t('Back')) {
    unset($_POST['tries']);

    // We maintain two lists -- previous questions and upcomming questions.
    // When we go backward, we pop one from the previous and prepend it to
    // the upcomming.
    // TODO: This can be maintained more efficiently with a single array of
    // all questions and then a pointer to the current question. That makes
    // rewinding much easier.
    $quiz_id = 'quiz_' . $quiz->nid;
    $last_q = array_pop($_SESSION[$quiz_id]['previous_quiz_questions']);
    array_unshift($_SESSION[$quiz_id]['quiz_questions'], $last_q);
  }
  elseif ($_POST['op'] == t('Submit') || $_POST['op'] == t('Next')) {
    if (!isset($_POST['tries'])) {

      // Moving skip logic here...
      if ($allow_skipping) {

        // Advance the question.
        $_SESSION['quiz_' . $quiz->nid]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz->nid]['quiz_questions'][0];

        // Load the last asked question.
        $former_question_array = array_shift($_SESSION['quiz_' . $quiz->nid]['quiz_questions']);
        $former_question = node_load(array(
          'nid' => $former_question_array['nid'],
        ));

        // Call hook_skip_question().
        $module = quiz_module_for_type($former_question->type);
        $result = module_invoke($module, 'skip_question', $former_question, $_SESSION['quiz_' . $quiz->nid]['result_id']);

        // Report that the question was skipped:

        //quiz_store_question_result($former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_'. $quiz->nid]['result_id'], $result);
        quiz_store_question_result($result, array(
          'set_msg' => TRUE,
        ));
      }
      else {
        drupal_set_message(t('You must select an answer before you can progress to the next question!'), 'error');
      }
    }
    else {

      //unset($_SESSION['quiz_'. $quiz->nid]['previous_quiz_questions']);

      // Previous quiz questions: Questions that have been asked already. We save a record of all of them
      // so that a user can navigate backward all the way to the beginning of the quiz.
      $_SESSION['quiz_' . $quiz->nid]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz->nid]['quiz_questions'][0];
      $former_question_array = array_shift($_SESSION['quiz_' . $quiz->nid]['quiz_questions']);
      $former_question = node_load(array(
        'nid' => $former_question_array['nid'],
      ));

      // Call hook_evaluate_question().
      $types = _quiz_get_question_types();
      $module = $types[$former_question->type]['module'];

      //drupal_set_message($module . ' evaluate_question called');
      $result = module_invoke($module, 'evaluate_question', $former_question, $_SESSION['quiz_' . $quiz->nid]['result_id']);

      //$result stdClass Object ( [score] => 0 [nid] => 3 [vid] => 3 [rid] => 27 [is_correct] => [is_evaluated] => 1 [is_skipped] => )
      quiz_store_question_result($result, array(
        'set_msg' => TRUE,
      ));

      // Stash feedback in the session, since the $_POST gets cleared.
      if ($quiz->feedback_time == QUIZ_FEEDBACK_QUESTION) {

        // Invoke hook_get_report().

        //$report = module_invoke($former_question->type, 'get_report', $former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_'. $quiz->nid]['result_id']);
        $report = module_invoke($module, 'get_report', $former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_' . $quiz->nid]['result_id']);
        $_SESSION['quiz_' . $quiz->nid]['feedback'] = rawurlencode(quiz_get_feedback($quiz, $report));
      }

      // If anonymous user, refresh url with unique hash to prevent caching.
      if (!$user->uid) {

        //drupal_goto('node/'. $quiz->nid .'/quiz/start/'. md5(mt_rand() . time())); #460550
        drupal_goto('node/' . $quiz->nid, array(
          'quizkey' => md5(mt_rand() . time()),
        ));
      }
    }
  }
  elseif ($_POST['op'] == t('Skip') && $allow_skipping) {

    // Advance the question.
    $_SESSION['quiz_' . $quiz->nid]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz->nid]['quiz_questions'][0];

    // Load the last asked question.
    $former_question_array = array_shift($_SESSION['quiz_' . $quiz->nid]['quiz_questions']);
    $former_question = node_load(array(
      'nid' => $former_question_array['nid'],
    ));

    // Call hook_skip_question().
    $module = quiz_module_for_type($former_question->type);
    $result = module_invoke($module, 'skip_question', $former_question, $_SESSION['quiz_' . $quiz->nid]['result_id']);

    // Report that the question was skipped:

    //quiz_store_question_result($former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_'. $quiz->nid]['result_id'], $result);
    quiz_store_question_result($result, array(
      'set_msg' => TRUE,
    ));
  }

  // The content array:

  //$content['body'] = array();

  // If we had feedback from the last question.
  if (isset($_SESSION['quiz_' . $quiz->nid]['feedback']) && $quiz->feedback_time == QUIZ_FEEDBACK_QUESTION) {
    $content['body']['feedback']['#value'] = rawurldecode($_SESSION['quiz_' . $quiz->nid]['feedback']);

    //$output .= rawurldecode($_SESSION['quiz_'. $quiz->nid]['feedback']);
  }

  // If this quiz is in progress, load the next questions and return it via the theme.
  if (!empty($_SESSION['quiz_' . $quiz->nid]['quiz_questions'])) {
    $question_node = node_load(array(
      'nid' => $_SESSION['quiz_' . $quiz->nid]['quiz_questions'][0]['nid'],
    ));

    //$output .= theme('quiz_take_question', $quiz, $question_node);

    // Start mods...
    $number_of_questions = quiz_get_number_of_questions($quiz->vid, $quiz->nid);
    $question_number = $number_of_questions - count($_SESSION['quiz_' . $quiz->nid]['quiz_questions']);
    $question_node->question_number = $question_number;
    $content['progress']['#value'] = theme('quiz_progress', $question_number, $number_of_questions);
    $content['progress']['#weight'] = -50;
    if ($_SESSION['quiz_' . $quiz->nid]['question_duration']) {
      $_SESSION['quiz_' . $quiz->nid]['question_duration'] -= time() - $_SESSION['quiz_' . $quiz->nid]['question_start_time'];

      /*
      if ($_SESSION['quiz_'. $quiz->nid]['question_duration'] < -10) {
        unset($_SESSION['quiz_'. $quiz->nid]);
        drupal_set_message('You have left this quiz partially complete.');
        return ;
      }
      */
      $time = $_SESSION['quiz_' . $quiz->nid]['question_duration'] > 0 ? $_SESSION['quiz_' . $quiz->nid]['question_duration'] : 1;
      db_query("UPDATE {quiz_node_results} SET time_left = %d WHERE result_id = %d", $time, $_SESSION['quiz_' . $quiz->nid]['result_id']);
      if ($time == 1) {

        /* Quiz has been timed out, run a loop to mark the remaining questions
         * as skipped */
        while (!empty($_SESSION['quiz_' . $quiz->nid]['quiz_questions'])) {
          $_SESSION['quiz_' . $quiz->nid]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz->nid]['quiz_questions'][0];
          $former_question_array = array_shift($_SESSION['quiz_' . $quiz->nid]['quiz_questions']);
          $former_question = node_load(array(
            'nid' => $former_question_array['nid'],
          ));

          // Call hook_skip_question().
          $module = quiz_module_for_type($former_question->type);
          $result = module_invoke($module, 'skip_question', $former_question, $_SESSION['quiz_' . $quiz->nid]['result_id']);

          // Report that the question was skipped:

          //quiz_store_question_result($former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_'. $quiz->nid]['result_id'], $result);
          quiz_store_question_result($result, array(
            'set_mesg' => FALSE,
          ));
        }
        $_SESSION['quiz_' . $quiz->nid]['quiz_questions'] = array();

        // empty the quiz questions
        // We're at the end of the quiz, so set a flag saying so.
        $quiz_end = TRUE;
        drupal_set_message(t('You have run out of time.'), 'error');
      }
      else {

        // There is still time left, so let's go ahead and insert the countdown javascript.
        if (function_exists('jquery_countdown_add') && variable_get('quiz_has_timer', 1)) {
          jquery_countdown_add('.countdown', array(
            'until' => $time,
            'onExpiry' => 'finished',
          ));
          drupal_add_js('function finished() { window.location = window.location.href; }', 'inline');

          //window.location.reload() method doesn't works here.
        }
      }
      $_SESSION['quiz_' . $quiz->nid]['question_start_time'] = time();
    }

    // If we're not yet at the end.
    if (empty($quiz_end)) {

      // Basically, we store the results of what would normally be a node_view() of the question node in
      // the $content for the quiz node. Since we want to override the default theme, we use the
      // quiz_node_view() function, which performs the steps in node_view(), but in a way more
      // specific to our needs.
      $content['body']['#value'] = quiz_node_view($question_node, TRUE, FALSE);
      drupal_set_title(check_plain($quiz->title));
      unset($_SESSION['quiz_' . $quiz->nid]['feedback']);
    }
  }
  else {
    $quiz_end = TRUE;
  }

  // If we're at the end of the quiz.
  if (!empty($quiz_end)) {
    $score = quiz_end_actions($quiz, $_SESSION['quiz_' . $quiz->nid]['result_id']);

    //print_r($score);exit;Array ( [question_count] => 2 [possible_score] => 2 [numeric_score] => 2 [percentage_score] => 100 [is_evaluated] => 1 )
    if ($quiz->feedback_time == QUIZ_FEEDBACK_NEVER) {
      $content['body']['#value'] = theme('quiz_no_feedback');
    }
    else {

      // Get the results and summary text for this quiz.
      $questions = _quiz_get_answers($_SESSION['quiz_' . $quiz->nid]['result_id']);
      $summary = _quiz_get_summary_text($quiz, $score);

      // Get the themed summary page.
      $content['body']['#value'] = theme('quiz_take_summary', $quiz, $questions, $score, $summary);
    }

    // Remove session variables.
    unset($_SESSION['quiz_' . $quiz->nid]);
  }

  //return $output;
  return $content;
}