diff --git a/quiz.admin.inc b/quiz.admin.inc index bdd5db0..bedb16c 100644 --- a/quiz.admin.inc +++ b/quiz.admin.inc @@ -740,11 +740,12 @@ function _quiz_search_terms($start, $all = FALSE) { * HTML output to create page. */ function quiz_questions_form($form, $form_state, $quiz) { - if ($form_state['rebuild']) { - // Save the active filters in $_SESSION - $filters = $form_state['values']['browser']['table']['header']['filters']; - _quiz_questions_store_filters($filters); - } + $form = array( + '#attributes' => array( + 'id' => 'quiz-manage-questions-form', + ), + '#tree' => TRUE, + ); $types = _quiz_get_question_types(); @@ -757,51 +758,235 @@ function quiz_questions_form($form, $form_state, $quiz) { '#theme' => 'question_selection_table', '#collapsible' => TRUE, '#attributes' => array('id' => 'mq-fieldset'), - 'question_status' => array('#tree' => TRUE), ); - $form['#attached']['js'] = array(drupal_get_path('module', 'quiz') .'/theme/quiz_question_browser.js'); - // Add randomization settings if this quiz allows randomized questions _quiz_add_fields_for_random_quiz($form, $quiz); - // Build up a list of questions - $questions_to_add = array(); + if ($quiz->randomization == 2) { + $form['question_list']['random'] = array( + '#type' => 'value', + '#value' => TRUE, + ); + } + else { + $form['question_list']['random'] = array( + '#type' => 'value', + '#value' => FALSE, + ); + } - // We use $form_state[post] to avoid validation failures when questions are added using AJAX - if (isset($form_state['post']['weights'])) { - $questions = _quiz_get_questions_from_form_state($form_state, $questions_to_add); + $questions = array(); + if (isset($form_state['values']['question_list']['questions'])) { + // Use $form_state to build list of questions (after AJAX form refresh) + $qstns = $form_state['values']['question_list']['questions']; + foreach ($qstns as $key => $qstn) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + $questions[] = $qstn; + } + } } else { - // We are coming in fresh and fetches the questions currently on the quiz from the database... + // Fetch questions from the database $include_random = $quiz->randomization == 2; $questions = quiz_get_questions($quiz->nid, $quiz->vid, TRUE, FALSE, FALSE, $include_random); + foreach ($questions as $id => $question) { + $questions[$id] = get_object_vars($question); + } } - if (empty($questions)) { - $form['question_list']['no_questions'] = array( - '#markup' => '
' . t('There are currently no questions in this quiz. Assign existing questions by using the question browser below. You can also use the links above to create new questions.') . '
', + $question_ids = array(); + if (!empty($questions)) { + foreach ($questions as $question) { + $question_ids[] = $question['nid']; + $question_id = $question['nid'] . '-' . $question['vid']; + + if ($question['vid'] == $question['latest_vid']) { + $update_cell = array( + '#type' => 'markup', + '#markup' => t('Up to date'), + ); + } else { + $update_cell = array( + '#type' => 'checkbox', + '#title' => ( + l(t('Latest'), 'node/' . $question['nid'] . '/revisions/' . $question['latest_vid'] . '/view') + . ' of ' . + l(t('revisions'), 'node/' . $question['nid'] . '/revisions') + ), + '#default_value' => 0, + ); + } + if ($quiz->randomization == 2) { + $status = array( + '#type' => 'checkbox', + '#default_value' => isset($question['question_status']) ? ($question['question_status'] == QUESTION_ALWAYS) ? 1 : 0 : 0, + ); + } else { + $status = array( + '#type' => 'hidden', + '#value' => $question['question_status'], + ); + } + + $qstn = array(); + $qstn['nid'] = array( + '#type' => 'value', + '#value' => $question['nid'], + ); + $qstn['vid'] = array( + '#type' => 'value', + '#value' => $question['vid'], + ); + $qstn['title'] = array( + '#type' => 'hidden', + '#value' => $question['title'], + '#prefix' => $question['title'], + ); + $qstn['type'] = array( + '#type' => 'hidden', + '#value' => $question['type'], + '#prefix' => $types[$question['type']]['name'], + ); + $qstn['latest_vid'] = array( + '#type' => 'value', + '#value' => $question['latest_vid'], + ); + $qstn['update'] = $update_cell; + $qstn['question_status'] = $status; + + $qstn['max_score'] = array( + '#type' => 'textfield', + '#size' => 3, + '#default_value' => $question['max_score'], + ); + $qstn['weight'] = array( + '#type' => 'textfield', + '#size' => 3, + '#default_value' => $question['weight'], + ); + $qstn['remove'] = array( + '#type' => 'checkbox', + ); + + $form['question_list']['questions'][$question_id] = $qstn; + + } + } + else { + $form['additional_questions']['#collapsed'] = FALSE; + } + + // Add remove questions button to $form + $form['question_list']['questions']['update'] = array( + '#type' => 'button', + '#value' => t('Remove questions'), + '#ajax' => array( + 'callback' => 'quiz_questions_ajax', + 'wrapper' => 'quiz-manage-questions-form', + ), + ); + + // Add question browser for adding new questions to quiz. + $form['question_browser'] = array( + '#type' => 'fieldset', + '#title' => t('Other Questions'), + '#collapsible' => TRUE, + ); + $form['question_browser']['search'] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array('question-browser-search-wrapper'), + ), + ); + $form['question_browser']['search']['search-query'] = array( + '#type' => 'textfield', + '#title' => t('Search for a question to add to the quiz'), + '#default_value' => isset($form_state['values']['question_browser']['search']['search-query']) ? $form_state['values']['question_browser']['search']['search-query'] : '', + ); + $form['question_browser']['search']['search-button'] = array( + '#type' => 'button', + '#value' => t('Search'), + '#ajax' => array( + 'callback' => 'quiz_questions_ajax', + 'wrapper' => 'quiz-manage-questions-form', + ), + ); + + $form['question_browser']['questions'] = array( + '#type' => 'container', + '#attributes' => array( + 'id' => array('questionbrowser-wrapper'), + ), + ); + + if (isset($form_state['values']['question_browser']['search']['search-query']) && !empty($form_state['values']['question_browser']['search']['search-query'])) { + unset($form_state['values']['question_browser']['questions']); + unset($form['question_browser']['questions']); + $query = new EntityFieldQuery; + $query->entityCondition('entity_type', 'node') + ->propertyCondition('type', array_keys($types), 'IN') + ->propertyCondition('status', '1', '='); + if (!empty($question_ids)) { + $query->propertyCondition('nid', $question_ids, 'NOT IN'); + } + $query->propertyCondition('title', '%' . $form_state['values']['question_browser']['search']['search-query'] . '%', 'LIKE') + ->propertyOrderBy('title', 'ASC'); + $result = $query->execute(); + $question_search_raw = node_load_multiple(array_keys($result['node'])); + $question_search = array(); + foreach ($question_search_raw as $question) { + $question_search[$question->nid . '-' . $question->vid] = array( + 'add_to_quiz' => array( + '#type' => 'checkbox', + '#default_value' => 0, + ), + 'question' => array( + '#type' => 'markup', + '#markup' => $question->title, + ), + 'type' => array( + '#type' => 'markup', + '#markup' => $types[$question->type]['name'], + ), + 'nid' => array( + '#type' => 'hidden', + '#value' => $question->nid, + ), + ); + } + + $form['question_browser']['questions'] = $question_search; + $form['question_browser']['questions'] += array( + '#theme' => 'quiz_questions_browse', + ); + $form['question_browser']['questions']['update'] = array( + '#type' => 'button', + '#value' => t('Add questions'), + '#ajax' => array( + 'callback' => 'quiz_questions_ajax', + 'wrapper' => 'quiz-manage-questions-form', + ), ); } - // We add the browser and allows the browser to give us information on what questions are displayed in the browser... - $hidden_questions = array(); - $form['question_list']['browser'] = _quiz_question_browser_form($hidden_questions, $questions_to_add, $form_state, $quiz, $types); - // We add the questions from the browser as hidden question rows in the question list. Doing this we can have - // the question show up in the question list instantly when a question is chosen in the browser(using js). - _quiz_add_hidden_questions($questions, $hidden_questions, $form_state, $quiz); - // We add the questions to the form array - _quiz_add_questions_to_form($form, $questions, $quiz, $types); + + + + + + + // Show the number of questions in the table header. - $always_count = 0; - foreach ($form['question_list']['stayers'] as $stayer) { - if ($stayer['#default_value'] === 1) { - $always_count++; + $question_count = 0; + foreach ($form['question_list']['questions'] as $key => $question) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + $question_count++; } } - $form['question_list']['#title'] .= ' (' . $always_count . ')'; + $form['question_list']['#title'] .= ' (' . $question_count . ')'; // Give the user the option to create a new revision of the quiz _quiz_add_revision_checkbox($form, $quiz); @@ -817,6 +1002,23 @@ function quiz_questions_form($form, $form_state, $quiz) { return $form; } + +/** + * Handler for quiz_questions_form ajax. + * + * @param $form + * The form variable + * @param $form_state + * The form state variable + * @return + * Content for AJAX to use. + */ +function quiz_questions_ajax($form, $form_state) { + return $form; +} + + + /** * Fields for creating new questions are added to the quiz_questions_form * @@ -903,237 +1105,6 @@ function _quiz_add_fields_for_random_quiz(&$form, $quiz) { /** - * Returns the questions that was in the question list when the form was submitted using ajax. - * - * @param $form_state - * FAPI form_state(array) - * @return $questions - * Array of questions as objects - */ -function _quiz_get_questions_from_form_state(&$form_state, &$questions_to_add) { - $questions = array(); - // We first store all data from the post in a temporary array. - // Then we fetch more data for each question from the database. - $cur_questions = array(); - $vids = array(); - foreach ($form_state['post']['weights'] as $id => $value) { - $cur_question = new stdClass(); - - // Find nid and vid - $matches = array(); - preg_match('/([0-9]+)-([0-9]+)/', $id, $matches); - $cur_question->nid = $matches[1]; - if (!is_numeric($matches[2])) { - continue; - } - $vids[] = $cur_question->vid = $matches[2]; - $cur_question->max_score = intval($form_state['post']['max_scores'][$id]); - $cur_question->weight = intval($value); - $cur_question->staying = $form_state['post']['stayers'][$id] === '1'; - $cur_question->question_status = QUESTION_ALWAYS; - if ($cur_question->staying == TRUE) { - $questions_to_add[] = $id; - } - $cur_questions[$cur_question->nid] = $cur_question; - } - - $query = db_select('node_revision', 'r'); - $table_alias = $query->join('node', 'n', 'n.nid = r.nid'); - $res = $query->addField('n', 'nid') - ->addTag('node_access') - ->addField('n', 'type') - ->addField('n', 'vid', 'latest_vid') - ->addField('r', 'title') - ->condition('r.vid', $vids, 'IN') - ->execute(); - // TODO: Don't use db_fetch_object - while ($res_o = $res->fetch()) { - $cur_questions[$res_o->nid]->type = $res_o->type; - $cur_questions[$res_o->nid]->title = $res_o->title; - $cur_questions[$res_o->nid]->latest_vid = $res_o->latest_vid; - $questions[] = $cur_questions[$res_o->nid]; - } - return $questions; -} - -/** - * Adds all information about the hidden questions to the questions array. - * - * Hidden questions are used to avoid unnecessary ajax calls. - * - * @see quiz_questions_form - * - * @param $questions - * The questions already added to the question list(array) - * @param $hidden_questions - * The questions added to the browser(array) - * @param $form_state - * FAPI form_state(array) - * @param $quiz - * The quiz node - */ -function _quiz_add_hidden_questions(&$questions, &$hidden_questions, &$form_state, &$quiz) { - $cur_questions = array(); - $vids = array(); - foreach ($hidden_questions as $key => $id) { - $cur_question = new stdClass(); - $matches = array(); - - // Find nid and vid - preg_match('/([0-9]+)-([0-9]+)/', $id, $matches); - $nid = $matches[1]; - $vid = $matches[2]; - - // If a question already exists in the $questions array we won't add a new one... - $continue = FALSE; - foreach ($questions as $question) { - if ($question->vid == $vid) { - $continue = TRUE; - break; - } - } - if (!is_numeric($nid) || !is_numeric($vid) || $continue) { - continue; - } - - $cur_question->nid = $nid; - $vids[] = $cur_question->vid = $vid; - $cur_question->weight = 0; - $cur_question->question_status = ($quiz->randomization == 2) ? QUESTION_RANDOM : QUESTION_ALWAYS; - $cur_question->staying = isset($form_state['values']['stayers'][$id]) ? $form_state['values']['stayers'][$id] === '1' : FALSE; - $cur_questions[$cur_question->nid] = $cur_question; - } - if (count($vids) > 0) { - // We fetch the rest of the information for each question and adds node access security - $res = db_select('node', 'n'); - $res->fields('n', array('nid', 'type')); - $res->fields('r', array('title')); - $res->fields('p', array('max_score')); - $res->addField('n', 'vid', 'latest_vid'); - $res->join('node_revision', 'r', 'n.nid = r.nid'); - $res->join('quiz_question_properties', 'p', 'r.vid = p.vid'); - $res->condition('r.vid', $vids, 'in'); - - // TODO: Don't user db_fetch_object - foreach ($res->execute() as $res_o) { - $cur_questions[$res_o->nid]->type = $res_o->type; - $cur_questions[$res_o->nid]->title = $res_o->title; - $cur_questions[$res_o->nid]->max_score = $res_o->type == 'scale' ? 0 : $res_o->max_score; - $cur_questions[$res_o->nid]->latest_vid = $res_o->latest_vid; - $questions[] = $cur_questions[$res_o->nid]; - } - } -} - -/** - * Adds the questions in the $questions array to the form - * - * @param $form - * FAPI form(array) - * @param $questions - * The questions to be added to the question list(array) - * @param $quiz - * The quiz node(object) - * @param $question_types - * array of all available question types - */ -function _quiz_add_questions_to_form(&$form, &$questions, &$quiz, &$question_types) { - $form['question_list']['weights'] = array('#tree' => TRUE); - $form['question_list']['max_scores'] = array('#tree' => TRUE); - $form['question_list']['stayers'] = array('#tree' => TRUE); - $form['question_list']['revision'] = array('#tree' => TRUE); - if ($quiz->randomization == 2) { - $form['question_list']['compulsories'] = array('#tree' => TRUE); - } - - $my_dest = $_GET['q']; - foreach ($questions as $question) { - $fieldset = 'question_list'; - $id = $question->nid . '-' . $question->vid; - - $form[$fieldset]['weights'][$id] = array( - '#type' => 'textfield', - '#size' => 3, - '#maxlength' => 4, - '#default_value' => isset($question->weight) ? $question->weight : 0, - ); - // Quiz directions don't have scoring... - if ($question->type != 'quiz_directions') { - $form[$fieldset]['max_scores'][$id] = array( - '#type' => 'textfield', - '#size' => 2, - '#maxlength' => 2, - '#default_value' => isset($question->max_score) ? $question->max_score : 0, - ); - } - else { - $form[$fieldset]['max_scores'][$id] = array( - '#type' => 'value', - '#value' => isset($question->max_score) ? $question->max_score : 0, - ); - } - - // Add checkboxes to remove questions in js disabled browsers... - $form[$fieldset]['stayers'][$id] = array( - '#type' => 'checkbox', - '#default_value' => (isset($question->staying) && $question->staying === FALSE) ? 0 : 1, - '#attributes' => array('class' => array('q-staying')), - ); - - //Add checkboxes to mark compulsory questions for randomized quizzes. - if ($quiz->randomization == 2) { - $form[$fieldset]['compulsories'][$id] = array( - '#type' => 'checkbox', - '#default_value' => isset($question->question_status) ? ($question->question_status == QUESTION_ALWAYS) ? 1 : 0 : 0, - '#attributes' => array('class' => array('q-compulsory')), - ); - } - - $link_options = array( - 'attributes' => array('class' => array('handle-changes')), - ); - - $form[$fieldset]['titles'][$id] = array('#markup' => l($question->title, 'node/' . $question->nid, $link_options)); - - - $form[$fieldset]['types'][$id] = array( - '#markup' => $question_types[$question->type]['name'], - ); - - $form[$fieldset]['view_links'][$id] = array( - '#markup' => l( - t('Edit'), - 'node/' . $question->nid . '/edit', - array( - 'query' => array('destination' => $my_dest), - 'attributes' => array('class' => array('handle-changes')), - ) - ), - ); - // For js enabled browsers questions are removed by pressing a remove link - $form[$fieldset]['remove_links'][$id] = array( - '#markup' => '' . t('Remove') . '', - ); - // Add a checkbox to update to the latest revision of the question - if ($question->vid == $question->latest_vid) { - $update_cell = array( - '#markup' => t('Up to date'), - ); - } - else { - $update_cell = array( - '#type' => 'checkbox', - '#title' => (l(t('Latest'), 'node/' . $question->nid . '/revisions/' .$question->latest_vid . '/view') - . ' of ' . - l(t('revisions'), 'node/' . $question->nid . '/revisions') - ), - ); - } - $form[$fieldset]['revision'][$id] = $update_cell; - } -} - -/** * Adds checkbox for creating new revision. Checks it by default if answers exists. * * @param $form @@ -1167,377 +1138,146 @@ function _quiz_add_revision_checkbox(&$form, &$quiz) { } } -/** - * Creates the browser part of the quiz_questions_form - * - * @param $hidden_questions - * Array where we add the questions in the browser - * @param $questions - * Questions already added to the question list(array) - * @param $form_state - * FAPI form_state(array) - * @param $quiz - * Quiz node(object) - * @return form - * FAPI form(array) - */ -function _quiz_question_browser_form(&$hidden_questions, $questions, $form_state, $quiz, $question_types) { - if (!is_array($question_types) || count($question_types) == 0) { - return $form['no_questions'] = array( - '#markup' => t('No question types are enabled'), - ); - } - $form = array( - '#type' => 'fieldset', - '#title' => t('Browse for questions to add'), - '#description' => t('Mark all the questions you want to add.') . ' ' - . t('You can filter questions by using the textfields and select boxes.') . ' ' - . t('You can sort by pressing the table headers.'), - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#tree' => TRUE, - '#prefix' => '
', - '#suffix' => '
', - ); - $form['table'] = array('#theme' => 'quiz_browser'); - $browser = &$form['table']; - // Ajax use this field to send extra query strings to drupal - $browser['add_to_get'] = array( - '#type' => 'hidden', - '#default_value' => '', - ); - - $browser['header'] = array('#theme' => 'quiz_questions_browser_header'); - $browser['body'] = array('#theme' => 'quiz_questions_browser_body'); - - //Build filter part of form: - _quiz_question_browser_add_filter_fields($browser['header'], $question_types, $quiz); - - // Add querystring recieved via ajax to the $_GET array... - if (isset($form_state['values'])) { - _quiz_add_to_get($form_state['values']['browser']['table']['add_to_get']); - } - - // Browsers table header - $browser['header']['#header'] = array( - NULL, - array('data' => t('Title'), 'field' => 'n.title'), - array('data' => t('Type'), 'field' => 'n.type'), - array('data' => t('Changed'), 'field' => 'n.changed', 'sort' => 'desc'), - array('data' => t('Username'), 'field' => 'u.name'), - ); - - $child_nid = db_query('SELECT child_nid FROM {quiz_node_relationship} - WHERE parent_vid = :parent_vid', - array(':parent_vid' => $quiz->vid) - ) - ->fetchCol(); - - $query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort'); - $query->fields('n', array('nid', 'type', 'vid', 'title', 'changed')); - $query->fields('u', array('name')); - $query->leftJoin('users', 'u', 'n.uid = u.uid'); - $query->condition('type', array_keys($question_types), 'IN'); - if (count($child_nid)) { - $query->condition('nid', $child_nid, 'NOT IN'); - } - - // Apply filter conditions - // _quiz_question_browser_prepare_filter_sql($query); - $pre = 'quiz_question_browser_'; - $changed_timestamps = _quiz_get_interval_timestamps('changed'); - $filter_sql = ''; - if (isset($_SESSION[$pre . 'title']) && !empty($_SESSION[$pre . 'title'])) { - $query->condition('n.title', '%' . db_like($_SESSION[$pre . 'title']) . '%', 'LIKE'); - } - - if (isset($_SESSION[$pre . 'name']) && !empty($_SESSION[$pre . 'name'])) { - $query->condition('u.name', '%' . db_like($_SESSION[$pre . 'name']) . '%', 'LIKE'); - } - - if (isset($_SESSION[$pre . 'type']) && ($_SESSION[$pre . 'type'] !== '0')) { - $query->condition('type', array($_SESSION[$pre . 'type']), 'IN'); - } - - if (isset($_SESSION[$pre . 'changed'])) { - $changed_timestamps = _quiz_get_interval_timestamps('changed'); - if ($changed_timestamps[$_SESSION[$pre . 'changed']][0]) { - $query->condition('changed', $changed_timestamps[$_SESSION[$pre . 'changed']][0], '>'); - } - if ($changed_timestamps[$_SESSION[$pre . 'changed']][1]) { - $query->condition('changed', $changed_timestamps[$_SESSION[$pre . 'changed']][1], '<'); +function _quiz_remove_unwanted_questions_from_quiz(&$form_state) { + if (isset($form_state['values']['question_list']['questions'])) { + foreach ($form_state['values']['question_list']['questions'] as $id => $question) { + if (isset($question['remove']) && $question['remove'] == '1') { + unset($form_state['values']['question_list']['questions'][$id]); + } } } - $query->limit(10); - $query->orderByHeader($browser['header']['#header']); - $options = array(); - - foreach ($query->execute() as $res_o) { - $id = $res_o->nid . '-' . $res_o->vid; - // Add $id to hidden_questions, this way quiz_questions_form knows that it has to add a invisible row for this question. - $hidden_questions[] = $id; - - $options[$id] = check_plain($res_o->title); - $browser['body']['changed'][$id]['#value'] = format_date($res_o->changed, 'short'); - $browser['body']['types'][$id]['#value'] = $question_types[$res_o->type]['name']; - $browser['body']['names'][$id]['#value'] = check_plain($res_o->name); - } - - $browser['body']['titles'] = array( - '#title' => t('Titles'), - '#type' => 'checkboxes', - '#options' => $options, - '#attributes' => array('class' => array('quiz-browser-checkbox')), - '#default_value' => $questions, - ); - - $browser['pager'] = array( - '#markup' => '
' . theme('pager', array('tags' => NULL)) . '
', - ); - return $form; } /** - * adds filter fields to the question browser form + * Handler for quiz_questions_form ajax add questions button. * - * @param $browser - * FAPI form(array) - * @param $question_types - * Array of question types + * Adds questions to the $form_state array. + * + * @param $form_state + * The form state variable */ -function _quiz_question_browser_add_filter_fields(&$browser, &$question_types, $quiz) { - // Create options array for the type filter(select field) - $type_options = array(t('No filter')); - foreach (array_keys($question_types) as $type) { - $type_options[$type] = $question_types[$type]['name']; +function _quiz_add_new_questions_to_quiz(&$form_state) { + if (isset($form_state['values']['question_browser']['questions'])) { + // Use $form_state to add new questions (after AJAX form refresh) + $max_weight = 0; + foreach($form_state['values']['question_list']['questions'] as $key => $question) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + if (isset($question['weight']) && $question['weight'] > $max_weight) { + $max_weight = $question['weight']; + } + } + } + $nids = array(); + foreach ($form_state['values']['question_browser']['questions'] as $key => $new_question) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + if ($new_question['add_to_quiz'] == 1) { + $nids[] = $new_question['nid']; + } + } + } + $new_questions = quiz_get_added_questions($nids); + foreach($new_questions as $question) { + if (!isset($question->weight) || is_null($question->weight)) { + $max_weight++; + $weight = $max_weight; + } else { + $weight = $question->weight; + } + $form_state['values']['question_list']['questions'][$question->nid . '-' . $question->vid] = array( + 'title' => $question->title, + 'weight' => $weight, + 'type' => $question->type, + 'remove' => 0, + 'max_score' => isset($question->max_score) ? $question->max_score : 1, + 'nid' => $question->nid, + 'vid' => $question->vid, + 'latest_vid' => $question->latest_vid, + ); + } } +} - // Create options array for the changed filter - $changed_options = _quiz_get_time_interval_options(); - - // Create the filter form items - $browser['filters'] = array(); - $filters = &$browser['filters']; - $filters['all'] = array( - '#type' => 'checkbox', - ); - $pre = 'quiz_question_browser_'; - $filters['title'] = array( - '#type' => 'textfield', - '#size' => 20, - '#default_value' => isset($_SESSION[$pre . 'title']) ? $_SESSION[$pre . 'title'] : '', - '#ajax' => array( - 'callback' => 'quiz_questions_browser_body_callback', - 'effect' => 'slide', - 'wrapper' => 'quiz-browser-body', - 'method' => 'replace', - 'event' => 'doneTyping', // custom event - ), - ); - $filters['type'] = array( - '#type' => 'select', - '#options' => $type_options, - '#default_value' => isset($_SESSION[$pre . 'type']) ? $_SESSION[$pre . 'type'] : '', - '#ajax' => array( - 'callback' => 'quiz_questions_browser_body_callback', - 'effect' => 'slide', - 'wrapper' => 'quiz-browser-body', - 'method' => 'replace', - ), - ); - $filters['changed'] = array( - '#type' => 'select', - '#options' => $changed_options, - '#default_value' => isset($_SESSION[$pre . 'changed']) ? $_SESSION[$pre . 'changed'] : '', - '#ajax' => array( - 'callback' => 'quiz_questions_browser_body_callback', - 'effect' => 'slide', - 'wrapper' => 'quiz-browser-body', - 'method' => 'replace', - ), - ); - $filters['name'] = array( - '#type' => 'textfield', - '#size' => 10, - '#default_value' => isset($_SESSION[$pre . 'name']) ? $_SESSION[$pre . 'name'] : '', - '#ajax' => array( - 'callback' => 'quiz_questions_browser_body_callback', - 'effect' => 'slide', - 'wrapper' => 'quiz-browser-body', - 'method' => 'replace', - 'event' => 'doneTyping', // custom event - ), - ); -} /** - * Returns sql to be added in where clause in the browsers select statement + * Retrieve list of questions to be added to quiz. * - * @see _quiz_questions_browser_form() + * Used when on the manage questions page of a quiz. * - * @param $filter_params - * params to be sent as parameter to db_query. (array) - * @return $filter_sql - * sql to be added to where statement in browser(string) + * @param $nids + * Array of question nids + * @param $include_question + * Should the question(the node body) be included for the questions in the + * returned array? + * + * @return + * An array of questions. */ -function _quiz_question_browser_prepare_filter_sql(&$filter_params) { - $pre = 'quiz_question_browser_'; - $changed_timestamps = _quiz_get_interval_timestamps('changed'); - $filter_sql = ''; - if (isset($_SESSION[$pre .'title']) && drupal_strlen($_SESSION[$pre .'title']) > 0) { - $filter_sql .= ' AND n.title LIKE \'%s%%\''; - $filter_params[] = $_SESSION[$pre . 'title']; - } - if (isset($_SESSION[$pre .'name']) && drupal_strlen($_SESSION[$pre .'name']) > 0) { - $filter_sql .= ' AND u.name LIKE \'%s%%\''; - $filter_params[] = $_SESSION[$pre . 'name']; - } - if (isset($_SESSION[$pre . 'type']) && ($_SESSION[$pre . 'type'] !== '0')) { - $filter_sql .= ' AND n.type = \'%s\''; - $filter_params[] = $_SESSION[$pre . 'type']; - } - if (isset($_SESSION[$pre . 'changed'])) { - $filter_sql .= $changed_timestamps[$_SESSION[$pre . 'changed']]['sql']; +function quiz_get_added_questions($nids, $include_question = TRUE) { + $questions = array(); + if (empty($nids)) { + return array(); + } + if (!is_array($nids)) { + $nids = array($nids); + } + $query = db_select('node', 'n'); + $query->fields('n', array('nid', 'type')) + ->fields('nr', array('vid', 'title')); + $query->addField('n', 'vid', 'latest_vid'); + $query->join('node_revision', 'nr', 'n.nid = nr.nid'); + $query->condition('n.nid', $nids, 'IN') + ->condition('n.type', array_keys(_quiz_get_question_types()), 'IN') + ->condition('n.status', 1); + $results = $query->execute(); + foreach($results as $result) { + $node = $result; + $questions[] = quiz_node_map($node, $include_question); } - return $filter_sql; + return $questions; } + + + + + /** * Validate that the supplied questions are real. */ -function quiz_questions_form_validate($form, $form_state) { - if (_quiz_is_int(arg(1))) { - if (node_last_changed(intval(arg(1))) > $form_state['values']['timestamp']) { - form_set_error('changed', t('This content has been modified by another user, changes cannot be saved.')); - } - } - else { - form_set_error('changed', t('A critical error has occured. Please report error code 28 on the quiz project page.')); - return; +function quiz_questions_form_validate($form, &$form_state) { + if (node_last_changed(intval(arg(1))) > $form_state['values']['timestamp']) { + form_set_error('changed', t('This quiz has been modified by another user, changes cannot be saved.')); } - $already_checked = array(); - $weight_map = $form_state['values']['weights']; - // Make sure the number of random questions is a positive number - if (isset($form_state['values']['num_random_questions']) && !_quiz_is_int($form_state['values']['num_random_questions'], 0)) { - form_set_error('num_random_questions', 'The number of random questions needs to be a positive number'); + if (isset($form_state['values']['question_list']['random_settings']['num_random_questions']) && $form_state['values']['question_list']['random_settings']['num_random_questions'] < 0) { + form_set_error("question_list][random_settings][num_random_questions", 'The number of random questions needs to be greater than or equal to 0'); } // Make sure the max score for random questions is a positive number - if (isset($form_state['values']['max_score_for_random']) && !_quiz_is_int($form_state['values']['max_score_for_random'], 0)) { - form_set_error('max_score_for_random', 'The max score for random questions needs to be a positive number'); - } - - if (empty($weight_map)) { - form_set_error('none', 'No questions were included.'); - return; + if (isset($form_state['values']['question_list']['random_settings']['max_score_for_random']) && $form_state['values']['question_list']['random_settings']['max_score_for_random'] < 0) { + form_set_error("question_list][random_settings][max_score_for_random", 'The max score for random questions needs to be greater than or equal to 0'); } - $question_types = array_keys(_quiz_get_question_types()); - - foreach ($weight_map as $id => $weight) { - if ($form_state['values']['stayers'][$id] == 0) { - continue; - } // The question isn't to be added... - - list($nid, $vid) = explode('-', $id, 2); - - // If a node isn't one of the questionstypes we remove it from the question list - $has_questions = (Boolean) db_select('node', 'n') - ->fields('n', array('nid')) - ->condition('type', $question_types, 'IN') - ->addTag('node_access') - ->condition('n.nid', $nid) - ->execute() - ->fetchField(); - if (!$has_questions) { - form_set_error('none', 'One of the supplied questions was invalid. It has been removed from the quiz.'); - unset($form_state['values']['weights'][$id]); - } - - // We also make sure that we don't have duplicate questions in the quiz. - elseif (in_array($nid, $already_checked)) { - form_set_error('none', 'A duplicate question has been removed. You can only ask a question once per quiz.'); - unset($form_state['values']['weights'][$id]); - } - else { - $already_checked[] = $nid; - } - } + // Remove questions selected for removal + _quiz_remove_unwanted_questions_from_quiz($form_state); - // We make sure max score is a positive number - $max_scores = $form_state['values']['max_scores']; - foreach ($max_scores as $id => $max_score) { - if ($form_state['values']['stayers'][$id] == 0) { - continue; - } - if (!_quiz_is_int($max_score, 0)) { - form_set_error("max_scores][$id", t('Max score needs to be a positive number')); - } - } -} + // Make sure max score is a positive number -/** - * Update a quiz set of items with new weights and membership - * @param $quiz - * The quiz node - * @param $weight_map - * Weights for each question(determines the order in which the question will be taken by the quiz taker) - * @param $max_scores - * Array of max scores for each question - * @param $is_new_revision - * Array of boolean values determining if the question is to be updated to the newest revision - * @param $refreshes - * True if we are creating a new revision of the quiz - * @param $stayers - * Questions added to the quiz - * @param $compulsories - * Array of boolean values determining if the question is compulsory or not. - * @return array set of questions after updating - */ -function _quiz_update_items($quiz, $weight_map, $max_scores, $is_new_revision, $refreshes, $stayers, $compulsories = NULL) { - $questions = array(); - foreach ($weight_map as $id => $weight) { - // Do not add hidden questions to $questions - if ($stayers[$id] == 0) { - continue; - } - list($nid, $vid) = explode('-', $id, 2); - $nid = (int) $nid; - $vid = (int) $vid; - $question = new stdClass(); - $question->nid = $nid; - $question->vid = $vid; - if (isset($compulsories)) { - if ($compulsories[$id] == 1) { - $question->state = QUESTION_ALWAYS; + foreach ($form_state['values']['question_list']['questions'] as $key => $question) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + if ($question['max_score'] < 0) { + form_set_error("question_list][questions][$id][max_score", t('Max score needs to be greater than or equal to 0')); } - else { - $question->state = QUESTION_RANDOM; - $max_scores[$id] = $quiz->max_score_for_random; - } - } - else { - $question->state = QUESTION_ALWAYS; } - $question->weight = $weight; - $question->max_score = $max_scores[$id]; - $question->refresh = (isset($refreshes[$id]) && $refreshes[$id] == 1); - - // Add item as an object in the questions array. - $questions[] = $question; } - - // Save questions. - quiz_set_questions($quiz, $questions, $is_new_revision); - - return $questions; + // Add questions selected for addition + _quiz_add_new_questions_to_quiz($form_state); } /** @@ -1546,10 +1286,6 @@ function _quiz_update_items($quiz, $weight_map, $max_scores, $is_new_revision, $ * Updates from the "manage questions" tab. */ function quiz_questions_form_submit($form, &$form_state) { - if (isset($form_state['#from_ahah'])) { - return; - } - // Load the quiz node $quiz = node_load(intval(arg(1))); // Update the refresh latest quizzes table so that we know what the users latest quizzes are @@ -1560,23 +1296,56 @@ function quiz_questions_form_submit($form, &$form_state) { $is_new_revision = (bool) $form_state['values']['new_revision']; } - _quiz_question_browser_submit($form, $form_state); + $num_random = isset($form_state['values']['question_list']['random_settings']['num_random_questions']) ? $form_state['values']['question_list']['random_settings']['num_random_questions'] : 0; + $quiz->max_score_for_random = isset($form_state['values']['question_list']['random_settings']['max_score_for_random']) ? $form_state['values']['question_list']['random_settings']['max_score_for_random'] : 1; + $term_id = isset($form_state['values']['random_term_id']) ? (int) $form_state['values']['random_term_id'] : 0; + + + // Store what questions belong to the quiz + // $questions = _quiz_update_items($quiz, $weight_map, $max_scores, $is_new_revision, $refreshes, $stayers, $compulsories); + + + + + + $questions = array(); + foreach ($form_state['values']['question_list']['questions'] as $key => $question) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + $question = new StdClass(); + $question->nid = $form_state['values']['question_list']['questions'][$key]['nid']; + $question->vid = $form_state['values']['question_list']['questions'][$key]['vid']; + if (isset($form_state['values']['question_list']['questions'][$key]['question_status'])) { + if ($form_state['values']['question_list']['questions'][$key]['question_status'] == 1) { + $question->state = QUESTION_ALWAYS; + } + else { + $question->state = QUESTION_RANDOM; + $form_state['values']['question_list']['questions'][$key]['max_score'] = $quiz->max_score_for_random; + } + } + else { + $question->state = QUESTION_ALWAYS; + } + $question->weight = $form_state['values']['question_list']['questions'][$key]['weight']; + $question->max_score = $form_state['values']['question_list']['questions'][$key]['max_score']; + $question->refresh = (isset($form_state['values']['question_list']['questions'][$key]['update']) && $form_state['values']['question_list']['questions'][$id]['update'] == 1); + + // Add item as an object in the questions array. + $questions[] = $question; + } + } + // Save questions. + quiz_set_questions($quiz, $questions, $is_new_revision); + + $term_id = isset($form_state['values']['question_list']['random_settings']['random_term_id']) ? (int) $form_state['values']['question_list']['random_settings']['random_term_id'] : 0; + + - $weight_map = $form_state['values']['weights']; - $max_scores = $form_state['values']['max_scores']; - $refreshes = isset($form_state['values']['revision']) ? $form_state['values']['revision'] : NULL; - $stayers = $form_state['values']['stayers']; - $compulsories = isset($form_state['values']['compulsories']) ? $form_state['values']['compulsories'] : NULL; - $num_random = isset($form_state['values']['num_random_questions']) ? $form_state['values']['num_random_questions'] : 0; - $quiz->max_score_for_random = isset($form_state['values']['max_score_for_random']) ? $form_state['values']['max_score_for_random'] : 1; - $term_id = isset($form_state['values']['random_term_id']) ? (int) $form_state['values']['random_term_id'] : 0; - // Store what questions belong to the quiz - $questions = _quiz_update_items($quiz, $weight_map, $max_scores, $is_new_revision, $refreshes, $stayers, $compulsories); // If using random questions and no term ID is specified, make sure we have enough. if (empty($term_id)) { @@ -1806,45 +1575,6 @@ function theme_quiz_admin_summary($variables) { return $output; } -/** - * Theme a question selection table, adding drag and drop support. - */ -function theme_question_selection_table($variables) { - $form = $variables['form']; - drupal_add_tabledrag('question-list', 'order', 'sibling', 'question-list-weight', NULL, NULL, TRUE); - - // Building headers - $headers = array(t('Question'), t('Type'), t('Actions'), t('Update'), t('Max score')); - if (isset($form['compulsories'])) { - $headers[] = t('Compulsory'); - } - $headers[] = t('Weight'); - - // Building table body - $rows = array(); - if (!empty($form['titles'])) { - foreach (element_children($form['titles']) as $id) { - $form['weights'][$id]['#attributes']['class'] = array('question-list-weight'); - - $rows[] = _quiz_get_question_row($form, $id); - } - // Make sure the same fields aren't rendered twice - unset($form['types'], $form['view_links'], $form['remove_links'], $form['stayers']); - unset($form['max_scores'], $form['revision'], $form['weights'], $form['titles'], $form['compulsories']); - } - $html_attr = array('id' => 'question-list'); - - // We hide the table if no questions have been added so that jQuery can show it the moment the first question is beeing added. - if (isset($form['no_questions'])) { - $html_attr['style'] = "display:none;"; - } - - $table = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => $html_attr)); - - return drupal_render($form['random_settings']) - . $table - . drupal_render_children($form); -} // RESULT MANAGEMENT @@ -2333,47 +2063,6 @@ function _quiz_skip_validation(&$elements) { } } -/** - * Helper function for theme_question_selection_table - * - * TODO: DELETE - * - * @see quiz_questions_form() - * @see theme_question_selection_table() - * - * @param $sub_form - * Form definition array for a filtered questions list - * @param $id - * Identifier used in $sub_form - * @return table row - * Array defining a table row - */ -function _quiz_get_question_row($sub_form, $id) { - $question_types = _quiz_get_question_types(); - $type = $sub_form['types'][$id]['#markup']; - - // We add the class "hidden-class" to hide questions that haven't been added to the quiz yet. - $hidden_class = ($sub_form['stayers'][$id]['#default_value'] === 0) ? ' hidden-question' : ''; - - $data_array = array( - // The checkbox and the title - drupal_render($sub_form['stayers'][$id]) . drupal_render($sub_form['titles'][$id]), - - $type, - $sub_form['view_links'][$id]['#markup'] . '', - isset($sub_form['revision'][$id]) ? drupal_render($sub_form['revision'][$id]) : t("Up to date"), - drupal_render($sub_form['max_scores'][$id]) - ); - if (isset($sub_form['compulsories'])) { - $data_array[] = drupal_render($sub_form['compulsories'][$id]); - } - $data_array[] = drupal_render($sub_form['weights'][$id]); - return array( - 'class' => array('q-row draggable' . $hidden_class), - 'id' => 'q-' . $id, - 'data' => $data_array - ); -} /** * Finds and returns the last table rows(HTML) in a table(HTML) diff --git a/quiz.module b/quiz.module index 427502b..15a92df 100644 --- a/quiz.module +++ b/quiz.module @@ -568,10 +568,6 @@ function quiz_theme($existing, $type, $theme, $path) { 'file' => 'quiz.pages.inc', 'variables' => array('question_node' => NULL), ), - 'question_selection_table' => array( - 'file' => 'quiz.admin.inc', - 'render element' => 'form', - ), 'quiz_score_correct' => array( 'file' => 'quiz.pages.inc', 'variables' => array(), @@ -625,7 +621,17 @@ function quiz_theme($existing, $type, $theme, $path) { 'quiz_my_results_for_quiz' => array( 'variables' => array('rows' => array()), 'file' => 'quiz.admin.inc' - ) + ), + 'question_selection_table' => array( + 'file' => 'quiz.theme.inc', + 'path' => $path . '/theme', + 'render element' => 'form', + ), + 'quiz_questions_browse' => array( + 'file' => 'quiz.theme.inc', + 'path' => $path . '/theme', + 'render element' => 'form', + ), ); } diff --git a/theme/quiz.theme.inc b/theme/quiz.theme.inc new file mode 100644 index 0000000..89ec242 --- /dev/null +++ b/theme/quiz.theme.inc @@ -0,0 +1,94 @@ + $val) { + if (preg_match('/\d+-\d+/', $key) != FALSE) { + $questions[$key] = $val; + } + } + + // Building table body + $rows = array(); + foreach ($questions as $question) { + $this_row = array(); + $this_row[] = array('data' => drupal_render($question['title']), 'class' => 'question-list-title'); + if ($form['random']['#value'] == TRUE) { + $this_row[] = array('data' => drupal_render($question['question_status']), 'class' => 'question-list-compulsory'); + } + $this_row[] = array('data' => drupal_render($question['type']), 'class' => 'question-list-type'); + $this_row[] = array('data' => l('Edit', 'node/' . $question['nid']['#value'] . '/edit', array('query' => drupal_get_destination())), 'class' => 'question-list-edit'); + $this_row[] = array('data' => drupal_render($question['remove']), 'class' => 'question-list-remove'); + $this_row[] = array('data' => drupal_render($question['update']), 'class' => 'question-list-update'); + $this_row[] = array('data' => drupal_render($question['max_score']), 'class' => 'question-list-max-score'); + $this_row[] = array('data' => drupal_render($question['weight']), 'class' => 'question-list-weight'); + $rows[] = array('data' => $this_row, 'class' => array('draggable', 'question-' . $question['nid']['#value']));; + } + + // Building headers + if ($form['random']['#value'] == TRUE) { + $headers = array(t('Question'), t('Compulsory'), t('Type'), t('Edit'), t('Remove'), t('Update'), t('Max score'), t('Weight')); + } + else { + $headers = array(t('Question'), t('Type'), t('Edit'), t('Remove'), t('Update'), t('Max score'), t('Weight')); + } + + + if (!empty($questions)) { + $table = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => 'question-list'))); + drupal_add_tabledrag('question-list', 'order', 'sibling', 'question-list-weight', NULL, NULL, TRUE); + $form['questions']['#children'] = $table; + $form['questions']['#children'] .= drupal_render($form['questions']['update']); + } + else { + $form['questions']['#children'] = '
' . t('There are currently no questions in this quiz. Assign existing questions by using the question browser below. You can also use the links above to create new questions.') . '
'; + } + + return drupal_render_children($form); +} + + + + +/** + * Theme the quiz question browser question list. + * + * @param $form + * FAPI form array + * + */ +function theme_quiz_questions_browse($variables) { + $form = $variables['form']; + $table_rows = array(); + foreach($form as $id => $row) { + if (intval($id) || $id == '0') { + $this_row = array(); + $this_row[] = array('data' => drupal_render($row['add_to_quiz']), 'class' => 'question-select'); + $this_row[] = array('data' => $row['question']['#markup'], 'class' => 'question-name'); + $this_row[] = array('data' => $row['type']['#markup'], 'class' => 'question-type'); + $table_rows[] = array('data' => $this_row); + } + } + if (empty($table_rows)) { + $no_results = array( + '#type' => 'html_tag', + '#tag' => 'div', + '#attributes' => array( + 'id' => 'question-search-no-results', + ), + '#value' => t('Your search returned no results.'), + ); + return drupal_render($no_results); + } + + $headers = array(t('Add to quiz'), t('Question'), t('Type')); + $form['#children'] = theme('table', array('header' => $headers, 'rows' => $table_rows, 'attributes' => array('id'=>'questionlist'))); + $form['#children'] .= drupal_render($form['update']); + return $form['#children']; +}