function quiz_take_quiz in Quiz 8.4
Same name and namespace in other branches
- 5.2 quiz.module \quiz_take_quiz()
- 5 quiz.module \quiz_take_quiz()
- 6.6 quiz.module \quiz_take_quiz()
- 6.2 quiz.module \quiz_take_quiz()
- 6.3 quiz.module \quiz_take_quiz()
- 6.4 quiz.module \quiz_take_quiz()
- 6.5 quiz.module \quiz_take_quiz()
- 7.6 quiz.module \quiz_take_quiz()
- 7 quiz.module \quiz_take_quiz()
- 7.4 quiz.module \quiz_take_quiz()
- 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
Content array.
1 call to quiz_take_quiz()
- QuizController::quizTake in lib/
Drupal/ quiz/ Controller/ QuizController.php - Primary quiz-taking view on 'Take' tab.
File
- ./
quiz.module, line 1700 - Quiz Module
Code
function quiz_take_quiz(NodeInterface $quiz) {
$user = \Drupal::currentUser();
$allow_skipping = $quiz->allow_skipping;
if (!isset($quiz)) {
throw new NotFoundHttpException();
// Equivalent to drupal_not_found() in D7;
}
// If anonymous user and no unique hash, refresh with a unique string to
// prevent caching.
if (!$user
->id() && arg(4) != NULL) {
return new RedirectResponse(url('node/' . $quiz
->id() . '/take/' . md5(mt_rand() . time()), array(
'absolute' => TRUE,
)));
}
// Make sure we use the same revision of the quiz throughout the quiz taking
// session.
if (isset($_SESSION['quiz_' . $quiz
->id()]['quiz_vid']) && $quiz
->getRevisionId() != $_SESSION['quiz_' . $quiz
->id()]['quiz_vid'] && \Drupal::config('quiz.settings')
->get('quiz_auto_revisioning')) {
$quiz = node_load($quiz
->id(), $_SESSION['quiz_' . $quiz
->id()]['quiz_vid']);
}
// If the session has no data for this quiz.
if (!isset($_SESSION['quiz_' . $quiz
->id()]['quiz_questions'])) {
// We delete questions in progress from old revisions.
_quiz_delete_old_in_progress($quiz, $user
->id());
// See if the current user has progress for this revision of the quiz stored
// in the database
$rid = $user
->id() > 0 ? _quiz_active_result_id($user
->id(), $quiz
->id(), $quiz
->getRevisionId()) : 0;
// Are we resuming an in-progress quiz?
if ($quiz->allow_resume && $rid > 0) {
_quiz_resume_existing_quiz($quiz, $user
->id(), $rid);
}
elseif (quiz_start_check($quiz, $rid)) {
_quiz_take_quiz_init($quiz);
}
else {
return array(
'body' => array(
'#markup' => t('This quiz is closed'),
),
);
}
}
$q_passed_validation = FALSE;
if (quiz_availability($quiz) !== TRUE) {
drupal_set_message(t('This quiz is not available anymore.'), 'error');
return array(
'body' => array(
'#markup' => t('This quiz is closed'),
),
);
}
if (isset($_SESSION['quiz_' . $quiz
->id()]['question_duration'])) {
$_SESSION['quiz_' . $quiz
->id()]['question_duration'] -= REQUEST_TIME - $_SESSION['quiz_' . $quiz
->id()]['question_start_time'];
}
if (!isset($_POST['op'])) {
// @todo Starting new quiz... Do we need to show instructions here?
}
elseif (isset($_POST['question_nid']) && $_POST['question_nid'] != $_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0]['nid']) {
// The user has pressed the navigation buttons multiple times...
}
elseif (isset($_SESSION['quiz_' . $quiz
->id()]['question_duration']) && $_SESSION['quiz_' . $quiz
->id()]['question_duration'] < -2) {
// Timed quiz where the time has gone out 2 seconds ago. Do not store the users results...
}
elseif ($_POST['op'] == t('Next question')) {
$url = "node/{$quiz->id()}/take";
return new RedirectResponse($url);
}
elseif ($_POST['op'] == t('Finish') || $_POST['op'] == t('Next') || $_POST['op'] == t('Back') && $quiz->backwards_navigation) {
// 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
->id()]['quiz_questions'][0]['rid'] = $_SESSION['quiz_' . $quiz
->id()]['result_id'];
$_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0];
$former_question_array = array_shift($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']);
$former_question = node_load($former_question_array['nid'], $former_question_array['vid']);
// Call hook_evaluate_question().
$types = _quiz_get_question_types();
$module = $types[$former_question
->getType()]['module'];
$result = module_invoke($module, 'evaluate_question', $former_question, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
$q_passed_validation = $result->is_valid;
$check_jump = TRUE;
if ($q_passed_validation === TRUE) {
quiz_store_question_result($quiz, $result, array(
'set_msg' => TRUE,
'question_data' => $former_question_array,
));
}
elseif ($quiz->allow_jumping && _quiz_is_int($_POST['jump_to_question'])) {
$_POST['op'] = t('Leave blank');
$allow_skipping = TRUE;
$jumping = TRUE;
}
// Stash feedback in the session, since the $_POST gets cleared.
if ($quiz->feedback_time == QUIZ_FEEDBACK_QUESTION && $_POST['op'] != t('Back') && $q_passed_validation === TRUE) {
// Invoke hook_get_report().
$report = module_invoke($module, 'get_report', $former_question_array['nid'], $former_question_array['vid'], $_SESSION['quiz_' . $quiz
->id()]['result_id']);
module_load_include('pages.inc', 'quiz');
if ($report) {
$report_form = drupal_get_form('quiz_report_form', array(
$report,
), TRUE, TRUE, TRUE);
$report_form['op'] = array(
'#type' => 'submit',
'#value' => t('Next question'),
);
return $report_form;
}
}
if ($quiz->repeat_until_correct && $_POST['op'] != t('Back') && $q_passed_validation === TRUE) {
// If the question was answered incorrectly, repeat it
if ($result && !$result->is_correct && $result->is_evaluated) {
$last_q = array_pop($_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions']);
array_unshift($_SESSION['quiz_' . $quiz
->id()]['quiz_questions'], $last_q);
drupal_set_message(t('The answer was incorrect. Please try again.'), 'error');
unset($_SESSION['quiz_' . $quiz
->id()]['feedback']);
}
}
elseif ($_POST['op'] == t('Back') && $quiz->backwards_navigation) {
$quiz_id = 'quiz_' . $quiz
->id();
// We jump back two times. From the next question to the current, and then
// from the current to the previous.
for ($i = 0; $i < 2; $i++) {
$last_q = array_pop($_SESSION[$quiz_id]['previous_quiz_questions']);
array_unshift($_SESSION[$quiz_id]['quiz_questions'], $last_q);
}
}
// If anonymous user, refresh url with unique hash to prevent caching.
if (!$user
->id() && $q_passed_validation === TRUE) {
$url = url('node/' . $quiz
->id() . '/take', array(
'query' => array(
'quizkey' => md5(mt_rand() . REQUEST_TIME),
),
));
return new RedirectResponse($url);
}
}
// Check for a skip.
if (isset($_POST['op']) && ($_POST['op'] == t('Leave blank') || $_POST['op'] == t('Leave blank and finish')) && $allow_skipping) {
if (!isset($_SESSION['quiz_' . $quiz
->id()]['result_id'])) {
$_SESSION['quiz_' . $quiz
->id()]['result_id'] = quiz_create_rid($quiz);
}
$q_passed_validation = TRUE;
// Advance the question.
if (!isset($jumping) || isset($jumping) && !$jumping) {
$_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions'][] = $_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0];
// Load the last asked question.
$former_question_array = array_shift($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']);
$former_question = node_load($former_question_array['nid'], $former_question_array['vid']);
}
// Call hook_skip_question().
$module = quiz_question_module_for_type($former_question
->getType());
if (!$module) {
return array(
'body' => array(
'#markup' => ' ',
),
);
}
$result = module_invoke($module, 'skip_question', $former_question, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
// Store that the question was skipped:
quiz_store_question_result($quiz, $result, array(
'set_msg' => TRUE,
'question_data' => $former_question_array,
));
}
if (isset($check_jump) && $check_jump) {
if ($quiz->allow_jumping && _quiz_is_int($_POST['jump_to_question'])) {
quiz_jump_to($_POST['jump_to_question'], $quiz, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
}
}
$show_validation_message = FALSE;
// If this quiz is in progress, load the next questions and return it via the theme.
if (!empty($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']) || is_string($q_passed_validation)) {
//TODO:need to verify the condition
// If we got no error when validating the question
if (!is_string($q_passed_validation) || $_POST['op'] == t('Back') && $quiz->backwards_navigation) {
$question_node = node_load($_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0]['nid'], $_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0]['vid']);
if (isset($_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0]['rid'])) {
$question_node->rid = $_SESSION['quiz_' . $quiz
->id()]['quiz_questions'][0]['rid'];
}
// We got an error message when trying to validate the previous answer
}
else {
$question_node = $former_question;
$show_validation_message = TRUE;
array_unshift($_SESSION['quiz_' . $quiz
->id()]['quiz_questions'], $former_question_array);
if (is_array($_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions'])) {
array_pop($_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions']);
}
// Avoid caching for anonymous users
if (!$user
->id()) {
drupal_set_message($q_passed_validation, 'error');
$url = url('node/' . $quiz
->id() . '/take', array(
'query' => array(
'quizkey' => md5(mt_rand() . REQUEST_TIME),
),
));
return new RedirectResponse($url);
}
}
// Added the progress info to the view.
$number_of_questions = quiz_get_number_of_questions($quiz
->getRevisionId());
$question_number = $number_of_questions - count($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']);
$question_node->question_number = $question_number;
$content['progress']['#markup'] = theme('quiz_progress', array(
'question_number' => $question_number,
'num_questions' => $number_of_questions,
'allow_jumping' => $quiz->allow_jumping,
'time_limit' => $quiz->time_limit,
));
$content['progress']['#weight'] = -50;
if (count($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']) + count($_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions']) > $number_of_questions) {
drupal_set_message(t('At least one question have been deleted from the quiz after you started taking it. You will have to start over.'), 'warning', FALSE);
unset($_SESSION['quiz_' . $quiz
->id()]);
$url = url('node/' . $quiz
->id() . '/take');
return new RedirectResponse($url);
}
if (isset($_SESSION['quiz_' . $quiz
->id()]['question_duration'])) {
$time = $_SESSION['quiz_' . $quiz
->id()]['question_duration'];
if ($time < 1 && $time > -2) {
// The page was probably submitted by the js, we allow the data to be stored
$time = 1;
}
db_update('quiz_node_results')
->fields(array(
'time_left' => $time,
))
->condition('result_id', $_SESSION['quiz_' . $quiz
->id()]['result_id'])
->execute();
if ($time <= 1) {
// Quiz has been timed out, run a loop to mark the remaining questions
// as skipped.
quiz_jump_to(count($_SESSION['quiz_' . $quiz
->id()]['quiz_questions']) + count($_SESSION['quiz_' . $quiz
->id()]['previous_quiz_questions']) + 1, $quiz, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
$quiz_end = TRUE;
unset($content['progress']);
$show_validation_message = FALSE;
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') && \Drupal::config('quiz.settings')
->get('quiz_has_timer')) {
jquery_countdown_add('.countdown', array(
'until' => $time,
'onExpiry' => 'finished',
'compact' => TRUE,
'layout' => t('Time left') . ': {hnn}{sep}{mnn}{sep}{snn}',
));
// These are the two button op values that are accepted for answering
// questions.
$button_op1 = drupal_json_encode(t('Finish'));
$button_op2 = drupal_json_encode(t('Next'));
$js = "\n function finished() {\n // Find all buttons with a name of 'op'.\n var buttons = jQuery('input[type=submit][name=op], button[type=submit][name=op]');\n // Filter out the ones that don't have the right op value.\n buttons = buttons.filter(function() {\n return this.value == {$button_op1} || this.value == {$button_op2};\n });\n if (buttons.length == 1) {\n // Since only one button was found, this must be it.\n buttons.click();\n }\n else {\n // Zero, or more than one buttons were found; fall back on a page refresh.\n window.location = window.location.href;\n }\n }\n ";
drupal_add_js($js, array(
'type' => 'inline',
'scope' => JS_DEFAULT,
));
}
}
$_SESSION['quiz_' . $quiz
->id()]['question_start_time'] = REQUEST_TIME;
}
if ($show_validation_message) {
drupal_set_message($q_passed_validation, 'error');
}
// If we're not yet at the end.
if (empty($quiz_end)) {
$content['body']['question']['#markup'] = quiz_take_question_view($question_node, $quiz);
$content['body']['question']['#weight'] = 0;
// If we had feedback from the last question.
if (isset($_SESSION['quiz_' . $quiz
->id()]['feedback']) && $quiz->feedback_time == QUIZ_FEEDBACK_QUESTION) {
$content['body']['feedback']['#markup'] = rawurldecode($_SESSION['quiz_' . $quiz
->id()]['feedback']);
$content['body']['feedback']['#weight'] = -100;
}
drupal_set_title($quiz
->getTitle());
unset($_SESSION['quiz_' . $quiz
->id()]['feedback']);
}
}
else {
drupal_set_title($quiz
->getTitle());
$quiz_end = TRUE;
}
// If we're at the end of the quiz.
if (!empty($quiz_end) && isset($_SESSION['quiz_' . $quiz
->id()]['result_id'])) {
// IMPORTANT: Because of a bug _quiz_get_answers always have to be called before quiz_end_scoring... :/
$questions = _quiz_get_answers($quiz, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
$score = quiz_end_scoring($quiz, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
if ($quiz->feedback_time == QUIZ_FEEDBACK_NEVER) {
$content['body']['#markup'] = theme('quiz_no_feedback');
}
else {
// Get the results and summary text for this quiz.
$summary = _quiz_get_summary_text($quiz, $score);
$data = array(
'quiz' => $quiz,
'questions' => $questions,
'score' => $score,
'summary' => $summary,
'rid' => $_SESSION['quiz_' . $quiz
->id()]['result_id'],
);
// Get the themed summary page.
$content['body']['#markup'] = theme('quiz_take_summary', $data);
}
if ($score['is_evaluated']) {
_quiz_maintain_results($quiz, $_SESSION['quiz_' . $quiz
->id()]['result_id']);
}
// Remove session variables, save $rid
$session_data = $_SESSION['quiz_' . $quiz
->id()];
unset($_SESSION['quiz_' . $quiz
->id()]);
// NOTE: End actions might redirect the user somewhere. Code below this line might not get executed...
quiz_end_actions($quiz, $score, $session_data);
}
return $content;
}