You are here

quiz.admin.inc in Quiz 6.2

Administrator interface for Quiz module.

File

quiz.admin.inc
View source
<?php

/**
 * Administrator interface for Quiz module.
 *
 * @file
 */

// QUIZ ADMIN

/**
 * Quiz Admin.
 */
function quiz_admin() {
  $results = _quiz_get_results();
  return theme('quiz_admin', $results);
}

// QUIZ RESULTS ADMIN

/*
 * Quiz Results Admin.
 */
function quiz_admin_results($qid) {
  $result = db_fetch_object(db_query('SELECT qnp.nid FROM {quiz_node_properties} qnp, {quiz_node_results} qnrs WHERE qnrs.nid = qnp.nid AND qnrs.result_id = %d', $qid));
  if ($result->nid) {
    $quiz = node_load($result->nid);
    $questions = _quiz_get_answers($qid);
    $score = quiz_calculate_score($quiz, $qid);
    $summary = _quiz_get_summary_text($quiz, $score);
    return theme('quiz_admin_summary', $quiz, $questions, $score, $summary);
  }
  else {
    drupal_not_found();
  }
}

/**
 * Creates a form for quiz questions.
 *
 * Handles the manage questions tab.
 *
 * @param $qid
 *   ID of quiz to create
 * @return unknown
 */
function quiz_questions($node) {
  return drupal_get_form('quiz_questions_form', $node);
}

// EDIT QUIZ

/**
 * Handles "manage questions" tab.
 *
 * Displays form which allows questions to be assigned to the given quiz.
 *
 * @return
 *  HTML output to create page.
 */
function quiz_questions_form($context, $quiz) {

  // Set page title.
  drupal_set_title(check_plain($quiz->title));

  // Display links to create other questions.
  $form['additional_questions'] = array(
    '#type' => 'fieldset',
    '#title' => t('Create additional questions'),
    '#theme' => 'additional_questions',
  );
  foreach (_quiz_get_question_types() as $type) {
    $form['additional_questions'][$type] = array(
      '#type' => 'markup',
      '#value' => l(t($type), 'node/add/' . $type . '/' . $quiz->nid, array(
        'title' => t('Go to ' . $type . ' administration'),
      )) . ' ',
    );
  }

  // Display questions 'always' on this quiz.
  $form['filtered_question_list_always'] = array(
    '#type' => 'fieldset',
    '#title' => t("Questions 'always' on this quiz"),
    '#theme' => 'quiz_filtered_questions',
    '#collapsible' => TRUE,
    'question_status' => array(
      '#tree' => TRUE,
    ),
  );
  $form['filtered_random_question_list']['number_of_random_questions'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of questions to randomize'),
    '#size' => 3,
    '#default_value' => $quiz->number_of_random_questions,
    '#description' => t('The number of randomly selected questions to assign to this quiz.'),
  );
  if (function_exists('taxonomy_get_vocabularies')) {
    $form['filtered_random_question_list']['random_term_id'] = array(
      '#type' => 'select',
      '#title' => t('Terms'),
      '#size' => 1,
      '#options' => _quiz_taxonomy_select($quiz->tid),
      '#default_value' => $quiz->tid,
      '#description' => t('Randomly select from questions with this term, or choose from the random question pool below'),
    );
  }

  // Display questions 'random' on this quiz.
  $form['filtered_question_list_random'] = array(
    '#type' => 'fieldset',
    '#title' => t("Questions 'random' on this quiz"),
    '#theme' => 'quiz_filtered_questions',
    '#collapsible' => TRUE,
    'question_status' => array(
      '#tree' => TRUE,
    ),
  );

  // Display filtered question list.
  $form['filtered_question_list'] = array(
    '#type' => 'fieldset',
    '#title' => t("Questions 'never' on this quiz"),
    '#theme' => 'quiz_filtered_questions',
    '#collapsible' => TRUE,
    'question_status' => array(
      '#tree' => TRUE,
    ),
  );

  // Get all questions and their status in relation to this quiz.
  $questions = array_merge(_quiz_get_unused_questions($quiz->vid), _quiz_get_questions($quiz->vid));
  foreach ($questions as $question) {
    switch ($question->status) {
      case QUESTION_RANDOM:
        $_form =& $form['filtered_question_list_random'];
        break;
      case QUESTION_ALWAYS:
        $_form =& $form['filtered_question_list_always'];
        break;
      case QUESTION_NEVER:
        $_form =& $form['filtered_question_list'];
        break;
    }
    $_form['question_status'][$question->nid] = array(
      '#type' => 'radios',
      '#options' => array(
        QUESTION_RANDOM => '',
        QUESTION_ALWAYS => '',
        QUESTION_NEVER => '',
      ),
      '#default_value' => $question->status,
    );
    $_form['question'][$question->nid] = array(
      '#type' => 'markup',
      '#value' => $question->question,
    );
    $_form['type'][$question->nid] = array(
      '#type' => 'markup',
      '#value' => $question->type,
    );
  }

  // Show the number of 'always' questions in the 'always' table header.
  $form['filtered_question_list_always']['#title'] .= ' (' . count($form['filtered_question_list_always']['type']) . ')';
  $form['new_revision'] = array(
    '#type' => 'checkbox',
    '#default_value' => in_array('revision', variable_get('node_options_quiz', array())),
    '#title' => t('New revision'),
    '#description' => t('Allow question status changes to create a new revision of the quiz?'),
  );
  $form['timestamp'] = array(
    '#type' => 'hidden',
    '#value' => time(),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit questions'),
  );
  return $form;
}

/**
 * Submit function for quiz_questions.
 * 
 * Updates from the "manage questions" tab.
 * 
 * @param $form_id
 *  A string containing the form id.
 * @param $values
 *  Array containing the form values.
 */
function quiz_questions_form_submit($form, &$form_state) {

  // This is ugly and should be fixed.
  $quiz = node_load(arg(1));
  $new_revision = $form_state['values']['new_revision'];

  // Update quiz with selected question options.
  if (!quiz_update_questions($quiz, $form_state['values']['question_status'], $new_revision)) {
    form_set_error('', t('Either no questions were selected, or there was a problem updating your @quiz. Please try again.', array(
      '@quiz' => QUIZ_NAME,
    )));
    return;
  }

  // Check if selecting random question from pool, and not via term.
  if (empty($form_state['values']['random_term_id'])) {
    $assigned_random = 0;
    if (is_array($form_state['values']['question_status'])) {
      foreach ($form_state['values']['question_status'] as $id => $status) {
        if (QUESTION_RANDOM == $status) {
          $assigned_random++;
        }
      }
    }
    if ($form_state['values']['number_of_random_questions'] > $assigned_random) {
      $form_state['values']['number_of_random_questions'] = $assigned_random;
      drupal_set_message(t('The number of random questions for this @quiz have been lowered to %anum to match the number of questions you assigned.', array(
        '@quiz' => QUIZ_NAME,
        '%anum' => $assigned_random,
      ), 'warning'));
    }
  }
  else {

    // Warn user if not enough questions available with this term_id.

    //$available_random = count(_quiz_get_random_questions($form_state['values']['number_of_random_questions'], $form_state['values']['random_term_id']));
    $available_random = count(_quiz_get_random_taxonomy_question_ids($form_state['values']['random_term_id'], $form_state['values']['number_of_random_questions']));
    if ($form_state['values']['number_of_random_questions'] > $available_random) {
      $form_state['values']['number_of_random_questions'] = $available_random;
      drupal_set_message(t('There are currently not enough questions assigned to this term (@random). Please lower the number of random quetions or assign more questions to this taxonomy term before taking this @quiz.', array(
        '@random' => $available_random,
        '@quiz' => QUIZ_NAME,
      )), 'error');
    }
  }
  $result = db_query("UPDATE {quiz_node_properties} SET number_of_random_questions = %d, tid = %d WHERE vid = %d AND nid = %d", $form_state['values']['number_of_random_questions'], $form_state['values']['random_term_id'], $quiz->vid, $quiz->nid);
  if (!$result) {
    drupal_set_message(t('There was an error updating the @quiz.', array(
      '@quiz' => QUIZ_NAME,
    )), 'error');
  }
  else {
    drupal_set_message(t('Questions updated successfully.'));
  }
}

// Quiz Admin Settings

/**
 * Implementation of hook_settings().
 * 
 * This builds the main settings form for the quiz module.
 */
function quiz_admin_settings() {
  $form = array();
  $form['quiz_default_close'] = array(
    '#type' => 'textfield',
    '#title' => t('Default number of days before a @quiz is closed', array(
      '@quiz' => QUIZ_NAME,
    )),
    '#default_value' => variable_get('quiz_default_close', 30),
    '#description' => t('Supply a number of days to calculate the default close date for new quizzes.'),
  );
  $form['quiz_default_pass_rate'] = array(
    '#type' => 'textfield',
    '#title' => t('Default percentage needed to pass a @quiz', array(
      '@quiz' => QUIZ_NAME,
    )),
    '#default_value' => variable_get('quiz_default_pass_rate', 75),
    '#description' => t('Supply a number between 1 and 100 to set as the default percentage correct needed to pass a quiz. Set to 0 if you want to ignore pass/fail summary information by default.'),
  );
  $form['quiz_use_passfail'] = array(
    '#type' => 'checkbox',
    '#title' => t('Allow quiz creators to set a pass/fail option when creating a @quiz.', array(
      '@quiz' => strtolower(QUIZ_NAME),
    )),
    '#default_value' => variable_get('quiz_use_passfail', 1),
    '#description' => t('Check this to display the pass/fail options in the @quiz form. If you want to prohibit other quiz creators from changing the default pass/fail percentage set below, uncheck this option.', array(
      '@quiz' => QUIZ_NAME,
    )),
  );
  $form['quiz_look_feel'] = array(
    '#type' => 'fieldset',
    '#title' => t('Look and Feel Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#description' => t('Control aspects of the Quiz module\'s display'),
  );
  $form['quiz_look_feel']['quiz_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Display name'),
    '#default_value' => QUIZ_NAME,
    '#description' => t('Change the name of the quiz type. Do you call it <em>test</em> or <em>assessment</em> instead? Change the display name of the module to something else. Currently, it is called @quiz. By default, it is called <em>Quiz</em>.', array(
      '@quiz' => QUIZ_NAME,
    )),
    '#required' => TRUE,
  );
  $form['quiz_look_feel']['quiz_show_allowed_times'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show allowed times'),
    '#description' => t('When a user begins a new quiz, show the user the number of times they may take the test, and how many times they have already taken the test.'),
    '#default_value' => variable_get('quiz_show_allowed_times', TRUE),
  );
  return system_settings_form($form);
}

/**
 * Validation of the Form Settings form.
 * 
 * Checks the values for the form administration form for quiz settings.
 */
function quiz_settings_form_validate($form, &$form_state) {
  if (!is_numeric($form_state['values']['number_of_random_questions']) || $form_state['values']['number_of_random_questions'] < 0) {
    form_set_error('number_of_random_questions', t('The number of random questions must be at least 0.'));
  }
  if (!is_numeric($form_state['values']['quiz_default_close']) || $form_state['values']['quiz_default_close'] <= 0) {
    form_set_error('quiz_default_close', t('The default number of days before a quiz is closed must be a number greater than 0.'));
  }
  if (!is_numeric($form_state['values']['quiz_default_pass_rate'])) {
    form_set_error('quiz_default_pass_rate', t('The pass rate value must be a number between 0% and 100%.'));
  }
  if ($form_state['values']['quiz_default_pass_rate'] > 100) {
    form_set_error('quiz_default_pass_rate', t('The pass rate value must not be more than 100%.'));
  }
  if ($form_state['values']['quiz_default_pass_rate'] < 0) {
    form_set_error('quiz_default_pass_rate', t('The pass rate value must not be less than 0%.'));
  }
}

// DELETE QUIZ RESULTS

/**
 * Delete Result.
 */
function quiz_admin_result_delete() {
  return drupal_get_form('quiz_admin_result_delete_form');
}

/**
 * Creates a form used for deleting a set of quiz results.
 */
function quiz_admin_result_delete_form() {
  $form['del_rid'] = array(
    '#type' => 'hidden',
    '#value' => arg(2),
  );
  return confirm_form($form, t('Are you sure you want to delete this @quiz result?', array(
    '@quiz' => QUIZ_NAME,
  )), 'admin/quiz/results', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
function quiz_admin_result_delete_form_submit($form, &$form_state) {
  db_query("DELETE FROM {quiz_node_results} WHERE result_id = %d", $form_state['values']['del_rid']);
  db_query("DELETE FROM {quiz_node_results_answers} WHERE result_id = %d", $form_state['values']['del_rid']);
  drupal_set_message(t('Deleted result.'));
  $form_state['redirect'] = 'admin/quiz/results';
  $form_state['nid'] = $node->nid;
}

// THEME FUNCTIONS
// Remember to updated quiz_theme() in quiz.module

/**
 * Theme the admin results table.
 * 
 * @param $results
 *  As returned by _quiz_get_results().
 * 
 * @ingroup themeable
 */
function theme_quiz_admin($results) {
  $output = '';
  $rows = array();
  while (list($key, $result) = each($results)) {
    $rows[] = array(
      l('view', 'admin/quiz/' . $result['result_id'] . '/view') . ' | ' . l('delete', 'admin/quiz/' . $result['result_id'] . '/delete'),
      check_plain($result['title']),
      check_plain($result['name']),
      $result['result_id'],
      format_date($result['time_start'], 'small'),
      $result['time_end'] > 0 ? format_date($result['time_end'], 'small') : t('In Progress'),
    );
  }
  $header = array(
    t('Action'),
    t('@quiz Title', array(
      '@quiz' => QUIZ_NAME,
    )),
    t('Username'),
    t('Result<br />ID'),
    t('Time Started'),
    t('Finished?'),
  );
  if (!empty($rows)) {
    $output .= theme('table', $header, $rows);
  }
  else {
    $output .= t('No @quiz results found.', array(
      '@quiz' => QUIZ_NAME,
    ));
  }
  return $output;
}

/**
 * Theme the summary page for admins.
 * 
 * @param $quiz
 *  The quiz node object.
 * @param $questions
 *  The questions array as defined by _quiz_get_answers.
 * @param $score
 *  Array of score information as returned by quiz_calculate_score().
 * @param $summary
 *  Filtered text of the summary.
 * @return
 *  Themed html.
 * 
 * @ingroup themeable
 */
function theme_quiz_admin_summary($quiz, $questions, $score, $summary) {

  // Set the title here so themers can adjust.
  drupal_set_title(check_plain($quiz->title));

  // Display overall result.
  $output = '';
  $output .= '<div id="quiz_score_possible">' . t('This person got %num_correct of %question_count correct.', array(
    '%num_correct' => $score['num_correct'],
    '%question_count' => $score['question_count'],
  )) . '</div>' . "\n";
  $output .= '<div id="quiz_score_percent">' . t('Total score: @score%', array(
    '@score' => $score['percentage_score'],
  )) . '</div><br />' . "\n";
  $output .= '<div id="quiz_summary">' . check_markup($summary, $quiz->format) . '</div><br />' . "\n";

  // Get the feedback for all questions.
  $output .= theme('quiz_feedback', $questions, TRUE, TRUE);
  return $output;
}

Functions

Namesort descending Description
quiz_admin Quiz Admin.
quiz_admin_results
quiz_admin_result_delete Delete Result.
quiz_admin_result_delete_form Creates a form used for deleting a set of quiz results.
quiz_admin_result_delete_form_submit
quiz_admin_settings Implementation of hook_settings().
quiz_questions Creates a form for quiz questions.
quiz_questions_form Handles "manage questions" tab.
quiz_questions_form_submit Submit function for quiz_questions.
quiz_settings_form_validate Validation of the Form Settings form.
theme_quiz_admin Theme the admin results table.
theme_quiz_admin_summary Theme the summary page for admins.