/[drupal]/contributions/modules/quiz/quiz.admin.inc
ViewVC logotype

Contents of /contributions/modules/quiz/quiz.admin.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Thu Aug 13 16:48:17 2009 UTC (3 months, 1 week ago) by sivaji
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +1 -1 lines
File MIME type: text/x-php
Merging GSoC code with HEAD.
1 <?php
2 // $Id: quiz.admin.inc,v 1.6 2009/08/13 16:37:12 sivaji Exp $
3
4 /**
5 * Administrator interface for Quiz module.
6 *
7 * @file
8 */
9
10
11 // QUIZ ADMIN
12
13 /**
14 * Quiz Admin.
15 */
16 function quiz_admin($nid) {
17 $breadcrumb = drupal_get_breadcrumb();
18 //lets add the Quiz Results to the breadcrumb array
19 $breadcrumb[] = l(t('Quiz Results'), 'admin/quiz/results');
20 drupal_set_breadcrumb($breadcrumb);
21 $results = _quiz_get_results($nid);
22 return theme('quiz_admin', $results);
23 }
24
25 /**
26 * Displays the quizzes by title with a link to the appropriate results
27 * for that specific quiz.
28 *
29 * @return
30 * Formatted data.
31 */
32 function quiz_admin_quizzes() {
33 $results = _quiz_get_quizzes();
34 return theme('quiz_admin_quizzes', $results);
35 }
36
37 // QUIZ RESULTS ADMIN
38
39 /**
40 * Quiz Results Admin.
41 *
42 * @param $qid
43 * The quiz result ID for a particular result.
44 */
45 function quiz_admin_results($qid) {
46 $breadcrumb = drupal_get_breadcrumb();
47 // Lets add the Quiz Results to the breadcrumb array.
48 $breadcrumb[] = l(t('Quiz Results'), 'admin/quiz/results');
49
50 // We join against node because we might have multiple versions
51 // of a quiz, and joining against quiz_node_properties would
52 // return multiple rows with the same quiz result. Since we are
53 // only hitting the {node} index, this should be fast.
54 $result = db_fetch_object(db_query(
55 "SELECT qnrs.nid
56 FROM {quiz_node_results} qnrs
57 INNER JOIN {node} n ON qnrs.nid = n.nid
58 WHERE qnrs.result_id = %d",
59 $qid
60 ));
61 if ($result->nid) {
62 $quiz = node_load($result->nid);
63 $questions = _quiz_get_answers($qid);
64 $score = quiz_calculate_score($quiz, $qid);
65 $summary = _quiz_get_summary_text($quiz, $score);
66
67 // Lets add the quiz title to the breadcrumb array.
68 $breadcrumb[] = l($quiz->title, 'admin/quiz/' . $result->nid . '/view');
69 drupal_set_breadcrumb($breadcrumb);
70 return theme('quiz_admin_summary', $quiz, $questions, $score, $summary);
71 }
72 else {
73 // Set the breadcrumb without the title since there isn't one and show error page.
74 drupal_set_breadcrumb($breadcrumb);
75 drupal_not_found();
76 }
77 }
78
79 /**
80 * Creates a form for quiz questions.
81 *
82 * Handles the manage questions tab.
83 *
84 * @param $qid
85 * ID of quiz to create
86 * @return
87 * String containing the form.
88 */
89 function quiz_questions($node) {
90 // Set page title.
91 drupal_set_title(check_plain($node->title));
92
93 // Add JS
94 $path = drupal_get_path('module', 'quiz') . '/quiz.admin.js';
95 drupal_add_js($path, 'module');
96
97 return drupal_get_form('quiz_questions_form', $node);
98 }
99
100 // EDIT QUIZ
101
102 /**
103 * Handles "manage questions" tab.
104 *
105 * Displays form which allows questions to be assigned to the given quiz.
106 *
107 * @param $context
108 * The form context
109 * @param $quiz
110 * The quiz node.
111 * @return
112 * HTML output to create page.
113 */
114 function quiz_questions_form($context, $quiz) {
115 // This is a target for AHAH callbacks. Do not remove.
116 $form['ahah_target'] = array(
117 '#type' => 'markup',
118 '#value' => '<div id="questions-always-target"></div>',
119 );
120
121 // Display links to create other questions.
122 $form['additional_questions'] = array(
123 '#type' => 'fieldset',
124 '#title' => t('Create additional questions'),
125 '#theme' => 'additional_questions',
126 );
127 $types = _quiz_get_question_types();
128 foreach ($types as $type => $info) {
129 $url_type = str_replace('_', '-', $type);
130 $form['additional_questions'][$type] = array(
131 '#type' => 'markup',
132 // FIXME: This looks broken:
133 '#value' => '<div class="add-questions">' . l(t($info['name']), 'node/add/'. $url_type .'/'. $quiz->nid, array('title' => t('Go to @name administration', array('@name' => $info['name'])))) .'</div>',
134 );
135 }
136
137 // Display questions 'always' on this quiz.
138 $form['filtered_question_list_always'] = array(
139 '#type' => 'fieldset',
140 '#title' => t('Questions always on this quiz'),
141 // '#theme' => 'quiz_filtered_questions',
142 '#theme' => 'question_selection_table',
143 '#collapsible' => TRUE,
144 'question_status' => array('#tree' => TRUE),
145 );
146
147 $form['filtered_question_list_always']['always_box'] = array(
148 '#type' => 'fieldset',
149 '#title' => '<strong>' . t('Find and add a question') . '</strong>',
150 '#description' => t('Begin typing a question title or keyword. Suggestions will be presented based on your typing. ') .
151 '<strong>' . t('You must choose one of the suggested questions.') . '</strong> ' .
152 t('To add a new question, use the "Create additional questions" section above.'),
153 '#collapsible' => FALSE,
154 '#collapsed' => FALSE,
155 );
156
157 $form['filtered_question_list_always']['always_box']['always_autocomplete'] = array(
158 '#type' => 'textfield',
159 //'#title' => t('Find a question'),
160 // '#description' => t('Begin typing a question title or keyword. Suggestions will be presented based on your typing. ') .
161 // '<strong>' . t('You must choose one of the suggested questions.') . '</strong> ' .
162 // t('To add a new question, use the "Create additional questions" section above.'),
163 '#default_value' => '',
164 '#size' => 60,
165 '#maxlength' => 256,
166 '#required' => FALSE,
167 '#autocomplete_path' => 'admin/quiz/listquestions',
168 );
169 $form['filtered_question_list_always']['always_box']['add_to_list'] = array(
170 '#type' => 'submit',
171 '#value' => t('Add to quiz'),
172 '#submit' => 'quiz_questions_form_submit',
173 '#ahah' => array(
174 'path' => 'admin/quiz/newquestion',
175 'wrapper' => 'questions-always-target',
176 'progress' => array('type' => 'bar', 'message' => t('Adding question...')),
177 ),
178 );
179 $form['filtered_question_list_always']['remove_from_quiz'] = array(
180 '#type' => 'hidden',
181 '#default_value' => '',
182 );
183
184 // Display questions 'random' on this quiz.
185 $form['filtered_question_list_random'] = array(
186 '#type' => 'fieldset',
187 '#title' => t('Pool of questions randomly selected for this quiz'),
188 '#theme' => 'question_selection_table',
189 '#collapsible' => TRUE,
190 '#collapsed' => FALSE, // Setting this to TRUE breaks tableDrag. What a drag.
191 'question_status' => array('#tree' => TRUE),
192 );
193
194 $form['filtered_question_list_random']['number_of_random_questions'] = array(
195 '#type' => 'textfield',
196 '#title' => t('Number of questions to randomize'),
197 '#size' => 3,
198 '#default_value' => $quiz->number_of_random_questions,
199 '#description' => t('The number of randomly selected questions that should be assigned to this quiz.'),
200 '#weight' => 1,
201 );
202
203 $form['filtered_question_list_random']['random_box'] = array(
204 '#type' => 'fieldset',
205 '#title' => '<strong>' . t('Find and add a question') . '</strong>',
206 '#description' => t('Begin typing a question title or keyword. Suggestions will be presented based on your typing. ') .
207 '<strong>' . t('You must choose one of the suggested questions.') . '</strong> ' .
208 t('To add a new question, use the "Create additional questions" section above.'),
209 '#collapsible' => FALSE,
210 '#collapsed' => FALSE,
211 '#weight' => -1,
212 );
213
214 $form['filtered_question_list_random']['random_box']['random_autocomplete'] = array(
215 '#type' => 'textfield',
216 '#default_value' => '',
217 '#size' => 60,
218 '#maxlength' => 256,
219 '#required' => FALSE,
220 '#weight' => -2,
221 '#autocomplete_path' => 'admin/quiz/listquestions',
222 );
223 $form['filtered_question_list_random']['random_box']['add_to_random'] = array(
224 '#type' => 'submit',
225 '#value' => t('Add to quiz'),
226 '#submit' => 'quiz_questions_form_submit',
227 '#weight' => -1,
228 '#ahah' => array(
229 'path' => 'admin/quiz/newquestion',
230 'wrapper' => 'questions-always-target',
231 'progress' => array('type' => 'bar', 'message' => t('Adding question...')),
232 ),
233 );
234
235
236 $terms = _quiz_taxonomy_select($quiz->tid);
237 if (!empty($terms) && function_exists('taxonomy_get_vocabularies')) {
238 $form['filtered_question_list_random']['random_term_id'] = array(
239 '#type' => 'select',
240 '#title' => t('Terms'),
241 '#size' => 1,
242 '#options' => _quiz_taxonomy_select($quiz->tid),
243 '#default_value' => $quiz->tid,
244 '#description' => t('Randomly select from questions with this term, or choose from the random question pool below'),
245 '#weight' => 2,
246 );
247 }
248
249
250
251 // Build up a list of questions, sorted into those that are random and those that are
252 // always on the quiz.
253 $questions = _quiz_get_questions($quiz->nid, $quiz->vid);
254 $rows = array();
255 $form['filtered_question_list_random']['weights'] = array('#tree' => TRUE);
256 $form['filtered_question_list_always']['weights'] = array('#tree' => TRUE);
257 foreach ($questions as $question) {
258 $id_mod = ($question->question_status == QUESTION_RANDOM ? 'random' : 'always');
259 $fieldset = 'filtered_question_list_' . $id_mod;
260 $id = $id_mod . '-' . $question->nid;
261
262 $form[$fieldset]['weights'][$id] = array(
263 //'#type' => 'weight',
264 //'#delta' => 60,
265 '#type' => 'textfield',
266 '#size' => 3,
267 '#maxlength' => 4,
268 '#default_value' => isset($question->weight) ? $question->weight : 0,
269 );
270 $form[$fieldset]['titles'][$id] = array(
271 // '#value' => $question->question, // Question is too long for drag and drop.
272 '#value' => $question->title,
273 );
274 $form[$fieldset]['types'][$id] = array(
275 '#value' => $question->type,
276 );
277 $form[$fieldset]['view_links'][$id] = array(
278 '#value' => l('View', 'node/' . $question->nid),
279 );
280 $form[$fieldset]['remove_links'][$id] = array(
281 // FIXME: This does not degrade for non-JS browsers.
282 //'#value' => l('Remove', 'node/' . $question->nid .'/questions/remove', array('attributes' => array('class' => 'rem-link'))),
283 '#value' => '<a href="#" class="rem-link">' . t('Remove') . '</a>',
284 );
285 }
286
287 // Show the number of 'always' questions in the 'always' table header.
288 $always_count = isset($form['filtered_question_list_always']['titles']) ? count($form['filtered_question_list_always']['titles']) : 0;
289 $form['filtered_question_list_always']['#title'] .= ' ('. $always_count .')';
290
291 $form['new_revision'] = array(
292 '#type' => 'checkbox',
293 '#default_value' => in_array('revision', variable_get('node_options_quiz', array())),
294 '#title' => t('New revision'),
295 '#description' => t('Allow question status changes to create a new revision of the quiz?'),
296 );
297
298 $form['timestamp'] = array('#type' => 'hidden', '#value' => time());
299
300 $form['submit'] = array(
301 '#type' => 'submit',
302 '#value' => t('Submit questions'),
303 );
304
305 return $form;
306 }
307
308 /**
309 * Validate that the supplied questions are real.
310 */
311 function quiz_questions_form_validate($form, $form_state) {
312 $question_types = array_keys(_quiz_get_question_types());
313 $removed = explode(',', $form_state['values']['remove_from_quiz']);
314 $placeholders = db_placeholders($question_types, 'varchar');
315 $sql = 'SELECT COUNT(nid) FROM {node} WHERE type IN (' . $placeholders . ') AND nid = %d';
316 $already_checked = array();
317
318 $weight_map = $form_state['values']['weights'];
319 if (empty($weight_map)) {
320 form_set_error('none', 'No questions were included.');
321 return;
322 }
323
324 foreach ($weight_map as $id => $weight) {
325 if (in_array($id, $removed)) {
326 // Ignore items that are going to be removed anyway.
327 continue;
328 }
329 list($state, $nid) = explode('-', $id, 2);
330 $params = $question_types; // Copy array.
331 $params[] = $nid;
332
333 if (db_result(db_query($sql, $params)) == 0) {
334 form_set_error('none', 'One of the supplied questions was invalid. It has been removed from the quiz.');
335 unset($form_state['values']['weights'][$id]);
336 }
337 elseif (in_array($nid, $already_checked)) {
338 form_set_error('none', 'A duplicate question has been removed. You can only ask a question once per quiz.');
339 unset($form_state['values']['weights'][$id]);
340 }
341 else {
342 $already_checked[] = $nid;
343 }
344 }
345 }
346
347 /**
348 * Update a quiz or qcollection set of items with new weights and membership
349 * @param $quiz
350 * @param $weight_map
351 * @param $removed_set
352 * @param $is_new_revision
353 * @return array set of questions after updating
354 */
355 function _quiz_update_items($quiz, $weight_map, $removed_set, $is_new_revision) {
356 // Get quiz questions that will always be on the quiz:
357 $questions = array();
358 foreach ($weight_map as $id => $weight) {
359 if (in_array($id, $removed_set)) {
360 // Skip items that should be removed.
361 continue;
362 }
363 list($state, $nid) = explode('-', $id, 2);
364 $nid = (int)$nid;
365 $question['nid'] = $nid;
366 $question['state'] = ($state == 'always' ? QUESTION_ALWAYS : QUESTION_RANDOM);
367 $question['weight'] = $weight;
368
369 // Add item as an object in the questions array.
370 $questions[] = (object)$question;
371 }
372
373 // Save questions.
374 quiz_set_questions($quiz, $questions, $new_revision);
375
376 return $questions;
377 }
378
379 /**
380 * Submit function for quiz_questions.
381 *
382 * Updates from the "manage questions" tab.
383 *
384 * @param $form_id
385 * A string containing the form id.
386 * @param $values
387 * Array containing the form values.
388 */
389 function quiz_questions_form_submit($form, &$form_state) {
390 // This is ugly and should be fixed.
391 $quiz = node_load(arg(1));
392
393 $is_new_revision = (bool) $form_state['values']['new_revision'];
394
395 $removed = explode(',', $form_state['values']['remove_from_quiz']);
396 $weight_map = $form_state['values']['weights'];
397
398 $questions = _quiz_update_items($quiz, $weight_map, $removed, $is_new_revision);
399
400 $num_random = $form_state['values']['number_of_random_questions'];
401 $term_id = isset($form_state['values']['random_term_id']) ? $form_state['values']['random_term_id'] : 0;
402
403 // If using random questions and no term ID is specified, make sure we have enough.
404 if (empty($term_id)) {
405 $assigned_random = 0;
406
407 foreach ($questions as $question) {
408 if ($question->state == QUESTION_RANDOM) {
409 ++$assigned_random;
410 }
411 }
412
413 // Adjust number of random questions downward to match number of selected questions..
414 if ($num_random > $assigned_random) {
415 $num_random = $assigned_random;
416 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'));
417 }
418 }
419 else {
420 // Warn user if not enough questions available with this term_id.
421 $available_random = count(_quiz_get_random_taxonomy_question_ids($term_id, $num_random));
422 if ($num_random > $available_random) {
423 $num_random = $available_random;
424 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');
425 }
426 }
427
428 // Update the quiz properties.
429 $sql = "UPDATE {quiz_node_properties} SET number_of_random_questions = %d, tid = %d WHERE vid = %d AND nid = %d";
430 if (db_query($sql, $num_random, $term_id, $quiz->vid, $quiz->nid) == FALSE) {
431 drupal_set_message(t('There was an error updating the @quiz.', array('@quiz' => QUIZ_NAME)), 'error');
432 }
433 else {
434 drupal_set_message(t('Questions updated successfully.'));
435 }
436 }
437
438 /**
439 * Autocompletion function for quiz questions.
440 */
441 function quiz_admin_list_questions_ac($vid, $string = '') {
442 $type_map = _quiz_get_question_types();
443 $values = array_keys($type_map);
444 $placeholder = db_placeholders($values, 'varchar');
445 $query = strtolower($string);
446
447 $sql = "SELECT n.nid, n.title, n.type
448 FROM {node} n
449 INNER JOIN {node_revisions} r ON n.vid = r.vid
450 WHERE n.type IN ($placeholder)
451 AND n.status = 1
452 AND (LOWER(n.title) LIKE '%%%s%%' OR LOWER(r.body) LIKE '%%%s%%')";
453
454 array_push($values, $query, $query);
455 $result = db_query_range(db_rewrite_sql($sql), $values, 0, 20);
456
457 $matches = array();
458 while ($node = db_fetch_object($result)) {
459 $type = $type_map[$node->type]['name'];
460 $key = sprintf('%s [type:%s, id:%d]', check_plain($node->title), check_plain($type), $node->nid);
461 $matches[$key] = $key;
462 }
463
464 drupal_json($matches);
465 }
466
467 /**
468 * AHAH form completion for adding new questions.
469 */
470 function quiz_admin_add_question_ahah() {
471 $cached_form_state = array();
472 $form_id = $_POST['form_build_id'];
473
474 // Make sure the form exists
475 if (!($cached_form = form_get_cache($form_id, $cached_form_state))) {
476 form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
477 $output = theme('status_messages');
478 print drupal_to_js(array('status' => TRUE, 'data' => $output));
479 exit();
480 }
481
482 // Just remember that $form_state is not checked, here. It's raw POST.
483 $form_state = array('values' => $_POST);
484
485 if (!empty($form_state['values']['always_autocomplete'])) {
486 $new_question = $form_state['values']['always_autocomplete'];
487 $frequency = 'always';
488 }
489 elseif (!empty($form_state['values']['random_autocomplete'])) {
490 drupal_set_message('Adding random question', 'status');
491 $new_question = $form_state['values']['random_autocomplete'];
492 $frequency = 'random';
493 }
494 else {
495 form_set_error('form_token', t('No question was found.'));
496 $output = theme('status_messages');
497 print drupal_to_js(array('status' => TRUE, 'data' => $output));
498 exit();
499 }
500
501 $matches = array();
502 $reg = '/id:([0-9]+)\]$/';
503 preg_match($reg, $new_question, $matches);
504
505 // Modify the form to add a new entry to weights.
506 if (!empty($matches[1]) && empty($cached_form['filtered_question_list_' . $frequency]['weights'][$frequency . '-' . $matches[1]])) {
507 $cached_form['filtered_question_list_' . $frequency]['weights'][$frequency . '-' . $matches[1]] = array(
508 '#type' => 'textfield',
509 '#size' => 3,
510 '#maxlength' => 4,
511 '#default_value' => 0,
512 );
513 }
514
515 // Re-save the cache.
516 form_set_cache($form_id, $cached_form, $cached_form_state);
517
518 // Because rendering a new table row is not possible from the server side, we
519 // need to launch a one-time JavaScript to add the item to the table.
520 $output = theme('status_messages') . '<script>Quiz.addQuestion("' . $frequency . '");</script>';
521
522 print drupal_to_js(array('status' => TRUE, 'data' => $output));
523 exit;
524 }
525
526 // Quiz Admin Settings
527
528 /**
529 * Implementation of hook_settings().
530 *
531 * This builds the main settings form for the quiz module.
532 */
533 function quiz_admin_settings() {
534 $form = array();
535
536 $form['quiz_global_settings'] = array(
537 '#type' => 'fieldset',
538 '#title' => t('Global Configuration'),
539 '#collapsible' => TRUE,
540 '#collapsed' => TRUE,
541 '#description' => t('Control aspects of the Quiz module\'s display'),
542 );
543
544 $form['quiz_global_settings']['quiz_default_close'] = array(
545 '#type' => 'textfield',
546 '#title' => t('Default number of days before a @quiz is closed', array('@quiz' => QUIZ_NAME)),
547 '#default_value' => variable_get('quiz_default_close', 30),
548 '#description' => t('Supply a number of days to calculate the default close date for new quizzes.'),
549 );
550
551 $form['quiz_global_settings']['quiz_default_pass_rate'] = array(
552 '#type' => 'textfield',
553 '#title' => t('Default percentage needed to pass a @quiz', array('@quiz' => QUIZ_NAME)),
554 '#default_value' => variable_get('quiz_default_pass_rate', 75),
555 '#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.'),
556 );
557
558 $form['quiz_global_settings']['quiz_use_passfail'] = array(
559 '#type' => 'checkbox',
560 '#title' => t('Allow quiz creators to set a pass/fail option when creating a @quiz.', array('@quiz' => strtolower(QUIZ_NAME))),
561 '#default_value' => variable_get('quiz_use_passfail', 1),
562 '#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)),
563 );
564
565 $form['quiz_global_settings']['quiz_max_result_options'] = array(
566 '#type' => 'textfield',
567 '#title' => t('Maximum Result Options'),
568 '#description' => t('Set the maximum number of result options (categorizations for scoring a quiz).'),
569 '#default_value' => variable_get('quiz_max_result_options', 5),
570 '#size' => 2,
571 '#maxlength' => 2,
572 '#required' => FALSE,
573 );
574
575 // Added for support of actions and allowing the user to filter the actions dropdown by a value.
576 $form['quiz_global_settings']['quiz_action_type'] = array(
577 '#type' => 'textfield',
578 '#title' => t('Default actions type'),
579 '#size' => 25,
580 '#default_value' => variable_get('quiz_action_type', 'all'),
581 '#description' => t('Filter the actions dropdown by a specific type.'),
582 );
583
584 $form['quiz_global_settings']['quiz_remove_partial_quiz_record'] = array(
585 '#type' => 'select',
586 '#title' => t('Remove Incomplete Quiz Records (older than)'),
587 '#options' => quiz_remove_partial_quiz_record_value(),
588 '#description' => t('Number of days that you like to keep the incomplete quiz records'),
589 '#default_value' => variable_get('quiz_remove_partial_quiz_record', quiz_remove_partial_quiz_record_value()),
590 );
591
592 $form['quiz_global_settings']['quiz_autotitle_length'] = array(
593 '#type' => 'textfield',
594 '#title' => t('Length of automatically set question titles'),
595 '#size' => 3,
596 '#maxlength' => 3,
597 '#description' => t('Integer between 0 and 128. If the question creator doesn\'t set a question title the system will make a title automatically. Here you can deside how long the autotitle can be.'),
598 '#default_value' => variable_get('quiz_autotitle_length', 50),
599 );
600
601 $target = array(
602 'attributes' => array(
603 'target' => '_blank'
604 ),
605 );
606
607 $links = array(
608 '!views' => l(t('Views'), 'http://drupal.org/project/views', $target),
609 '!cck' => l(t('CCK'), 'http://drupal.org/project/cck', $target),
610 '!jquery_countdown' => l(t('JQuery Countdown'), 'http://drupal.org/project/jquery_countdown', $target),
611 '!userpoints' => l(t('UserPoints'), 'http://drupal.org/project/userpoints', $target),
612 '@quiz' => QUIZ_NAME,
613 );
614
615 $form['quiz_addons'] = array(
616 '#type' => 'fieldset',
617 '#title' => t('Addons Configuration'),
618 '#description' => t('Quiz module can integrate with other d.o modules like !views, !cck, !userpoints, !jquery_countdown. Here you can configure the way quiz module integrates to other modules. Disabled checkbox indicates modules are not enabled/installed', $links),
619 '#collapsible' => TRUE,
620 '#collapsed' => TRUE,
621 );
622
623 $form['quiz_addons']['quiz_has_userpoints'] = array(
624 '#type' => 'checkbox',
625 '#title' => t('Enable UserPoints Module Integration'),
626 '#default_value' => variable_get('quiz_has_userpoints', 0),
627 '#description' => t('!userpoints an *optional* module for quiz, provides way for users to gain or lose points for performing certain actions on your site like attending @quiz. you will need to install the !userpoints module to use this feature.', $links),
628 '#disabled' => !module_exists('userpoints'),
629 );
630
631 $form['quiz_addons']['quiz_has_timer'] = array(
632 '#type' => 'checkbox',
633 '#title' => t('Display Timer for Timed Quiz'),
634 '#default_value' => variable_get('quiz_has_timer', 0),
635 '#description' => t('!jquery_countdown an *optional* module for quiz used for the timer to be displayed on the user\'s page, you will need to install the !jquery_countdown module. Without this timer, the user will not know how long he or she has to complete the @quiz', $links),
636 '#disabled' => !function_exists('jquery_countdown_add'),
637 );
638
639 $form['quiz_look_feel'] = array(
640 '#type' => 'fieldset',
641 '#title' => t('Look and Feel Settings'),
642 '#collapsible' => TRUE,
643 '#collapsed' => TRUE,
644 '#description' => t('Control aspects of the Quiz module\'s display'),
645 );
646
647 $form['quiz_look_feel']['quiz_name'] = array(
648 '#type' => 'textfield',
649 '#title' => t('Display name'),
650 '#default_value' => QUIZ_NAME,
651 '#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>.',
652 array('@quiz' => QUIZ_NAME)),
653 '#required' => TRUE,
654 );
655
656 $form['quiz_look_feel']['quiz_show_allowed_times'] = array(
657 '#type' => 'checkbox',
658 '#title' => t('Show allowed times'),
659 '#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.'),
660 '#default_value' => variable_get('quiz_show_allowed_times', TRUE),
661
662 );
663
664 $form['quiz_email_settings'] = array(
665 '#type' => 'fieldset',
666 '#title' => t('Email Settings'),
667 '#description' => t('Allows to send results to quiz author/ attendee via e-mail and configuration e-mail subject and body.'),
668 '#collapsible' => TRUE,
669 '#collapsed' => TRUE,
670 );
671 $form['quiz_email_settings']['quiz_email_results'] = array(
672 '#type' => 'checkbox',
673 '#title' => t('Enable Sending Quiz Results to Attendees', array('@quiz' => strtolower(QUIZ_NAME))),
674 '#default_value' => variable_get('quiz_email_results', 1),
675 '#description' => t('Check this to send users quiz results over email at the end of quiz.')
676 );
677 $form['quiz_email_settings']['quiz_results_to_quiz_author'] = array(
678 '#type' => 'checkbox',
679 '#title' => t('Enable Sending Quiz Results Author', array('@quiz' => strtolower(QUIZ_NAME))),
680 '#default_value' => variable_get('quiz_results_to_quiz_author', 1),
681 '#description' => t('Check this to send users quiz results over email to quiz author (<em>when the attendee is an anonymous user</em>).'),
682 );
683 $form['quiz_email_settings']['quiz_email_results_subject'] = array(
684 '#type' => 'textfield',
685 '#title' => t('Configure Email subject'),
686 '#description' => t('This format will be used to send Email to notify results at the end of quiz.'),
687 '#default_value' => variable_get('quiz_email_results_subject', quiz_email_results_format('subject')),
688 );
689
690 $form['quiz_email_settings']['quiz_email_results_body'] = array(
691 '#type' => 'textarea',
692 '#title' => t('Configure Email format'),
693 '#description' => t('This format will be used to send Email to notify results at the end of quiz. !title !sitename !username !date !desc !correct !total !percentage !url are placeholder.'),
694 '#default_value' => variable_get('quiz_email_results_body', quiz_email_results_format('body')),
695 );
696
697 $form['#validate'][] = 'quiz_settings_form_validate';
698 return system_settings_form($form);
699 }
700
701 /**
702 * Validation of the Form Settings form.
703 *
704 * Checks the values for the form administration form for quiz settings.
705 */
706 function quiz_settings_form_validate($form, &$form_state) {
707 if (!is_numeric($form_state['values']['quiz_default_close']) || $form_state['values']['quiz_default_close'] <= 0) {
708 form_set_error('quiz_default_close', t('The default number of days before a quiz is closed must be a number greater than 0.'));
709 }
710 if (!is_numeric($form_state['values']['quiz_default_pass_rate'])) {
711 form_set_error('quiz_default_pass_rate', t('The pass rate value must be a number between 0% and 100%.'));
712 }
713 if (!is_numeric($form_state['values']['quiz_autotitle_length'])) {
714 form_set_error('quiz_autotitle_length', t('The autotitle length value must be an integer between 0 and 128.'));
715 }
716 if ($form_state['values']['quiz_default_pass_rate'] > 100) {
717 form_set_error('quiz_default_pass_rate', t('The pass rate value must not be more than 100%.'));
718 }
719 if ($form_state['values']['quiz_default_pass_rate'] < 0) {
720 form_set_error('quiz_default_pass_rate', t('The pass rate value must not be less than 0%.'));
721 }
722 if ($form_state['values']['quiz_autotitle_length'] > 128) {
723 form_set_error('quiz_autotitle_length', t('The autotitle length value must not be bigger than 128.'));
724 }
725 if ($form_state['values']['quiz_autotitle_length'] < 0) {
726 form_set_error('quiz_autotitle_length', t('The autotitle length value must not be smaller than 0.'));
727 }
728 }
729
730 // DELETE QUIZ RESULTS
731
732 /**
733 * Delete Result.
734 */
735 function quiz_admin_result_delete() {
736 return drupal_get_form('quiz_admin_result_delete_form');
737 }
738
739 /**
740 * Creates a form used for deleting a set of quiz results.
741 */
742 function quiz_admin_result_delete_form() {
743 $form['del_rid'] = array('#type' => 'hidden', '#value' => arg(2));
744 return confirm_form($form,
745 t('Are you sure you want to delete this @quiz result?', array('@quiz' => QUIZ_NAME)),
746 'admin/quiz/results',
747 t('This action cannot be undone.'),
748 t('Delete'),
749 t('Cancel')
750 );
751 }
752
753 function quiz_admin_result_delete_form_submit($form, &$form_state) {
754 $nid = db_result(db_query("SELECT nid FROM {quiz_node_results} WHERE result_id = %d",$form_state['values']['del_rid']));
755 db_query("DELETE FROM {quiz_node_results} WHERE result_id = %d", $form_state['values']['del_rid']);
756 db_query("DELETE FROM {quiz_node_results_answers} WHERE result_id = %d", $form_state['values']['del_rid']);
757 drupal_set_message(t('Result has been Deleted.'));
758 $form_state['redirect'] = 'admin/quiz/'. intval($nid) .'/view';
759 $form_state['nid'] = $nid;
760 }
761
762
763
764 // THEME FUNCTIONS
765 // Remember to updated quiz_theme() in quiz.module
766
767 /**
768 * Theme the admin quizzes table.
769 *
770 * @param $results
771 * As returned by _quiz_get_quizzes().
772 *
773 * @ingroup themeable
774 */
775 function theme_quiz_admin_quizzes($results) {
776 $output = '';
777 $rows = array();
778
779 while (list($key, $result) = each($results)) {
780 $rows[] = array(
781 l($result['title'], 'admin/quiz/'. $result['nid'] .'/view'),
782 check_plain($result['name']),
783 format_date($result['created'], 'small'),
784 );
785 }
786
787 $header = array(
788 t('@quiz title', array('@quiz' => QUIZ_NAME)),
789 t('Created by'),
790 t('Created on')
791 );
792 $output = (!empty($rows)) ? theme('table', $header, $rows) : '<p>' . t('No @quizzes found.', array('@quiz' => QUIZ_NAME)) . '</p>';
793 return $output;
794 }
795
796
797 /**
798 * Theme the admin results table.
799 *
800 * @param $results
801 * As returned by _quiz_get_results().
802 *
803 * @ingroup themeable
804 */
805 function theme_quiz_admin($results) {
806 $output = '';
807 $quiz = current($results);
808 drupal_set_title(t('@current Results', array('@current' => check_plain($quiz['title']))));
809 // generates <img src="foo.bar.png" /> tag
810 $path_to_module_quiz = drupal_get_path('module', 'quiz');
811 $png = array(
812 'view' => theme('image', $path_to_module_quiz . '/images/view.png', t('View User Answers and Correct Answers'), t('View User Answers and Correct Answers')),
813 'delete' => theme('image', $path_to_module_quiz . '/images/delete.png', t('Delete this Result Record'), t('Delete this Result Record')),
814 'html' => theme('image', $path_to_module_quiz . '/images/html.png', t('Export as HTML'), t('Export as HTML')),
815 'xml' => theme('image', $path_to_module_quiz . '/images/xml.png', t('Export as XML'), t('Export as XML')),
816 'csv' => theme('image', $path_to_module_quiz . '/images/csv.png', t('Export as CSV'), t('Export as CSV')),
817 );
818 if (module_exists('results_export')) {
819 $export_teaser_view = array(
820 'html_png' => l($png['html'], 'admin/quiz/results_export_teaser_view/'. $quiz['nid'] . '/html', array('html' => TRUE)),
821 'xml_png' => l($png['xml'], 'admin/quiz/results_export_teaser_view/'. $quiz['nid'] . '/xml', array('html' => TRUE)),
822 'csv_png' => l($png['csv'], 'admin/quiz/results_export_teaser_view/'. $quiz['nid'] . '/csv', array('html' => TRUE)),
823 );
824 }
825 while (list($key, $result) = each($results)) {
826 $action = array(
827 'view_png' => l($png['view'], 'admin/quiz/reports/' . $result['result_id'] . '/results', array('html' => TRUE)),
828 'delete_png' => l($png['delete'], 'admin/quiz/' . $result['result_id'] . '/delete', array('html' => TRUE)),
829 );
830 if (module_exists('results_export')) {
831 $export_full_view = array(
832 'html_png' => l($png['html'], 'admin/quiz/results_export_full_view/'. $result['result_id'] . '/html', array('html' => TRUE)),
833 //'xml_png' => l($png['xml'], 'admin/quiz/results_export_full_view/'. $result['result_id'] . '/xml', array('html' => TRUE)),
834 //'csv_png' => l($png['csv'], 'admin/quiz/results_export_full_view/'. $result['result_id'] . '/csv', array('html' => TRUE)),
835 );
836 }
837 $rows[] = array(
838 implode(' ', $action),
839 check_plain($result['name']),
840 $result['result_id'],
841 format_date($result['time_start'], 'small'),
842 ($result['time_end'] > 0) ? format_date($result['time_end'], 'small') : t('In Progress'),
843 ($result['time_end'] > 0) ? quiz_get_time_taken_in_minutes($result['time_end'] - $result['time_start']) : quiz_get_time_taken_in_minutes(time() - $result['time_start']),
844 ($result['time_end'] > 0) ? $result['score'] : t('--'),
845 module_exists('results_export') ? implode(' ', $export_full_view) : '',
846 );
847 }
848
849 $header = array(
850 t('Action'),
851 t('Username'),
852 t('Result<br />ID'),
853 t('Time <br />Started <br /> (m/d/y - h/m)'),
854 t('Finished? <br /> (m/d/y - h/m)'),
855 t('Time <br />Taken <br /> (min:sec)'),
856 t('Score'),
857 module_exists('results_export') ? t('Export') : '',
858 );
859
860 if (!empty($rows)) {
861 $output .= module_exists('results_export') ? '<div id="export-table"><p>' . t('Export this Table') . '</p>'. implode(' ', $export_teaser_view) . '</div>' : '';
862 $output .= theme('table', $header, $rows);
863 }
864 else {
865 $output .= t('No @quiz results found.', array('@quiz' => QUIZ_NAME));
866 }
867 return $output;
868 }
869
870 /*
871 * @param $time_in_sec
872 * Integers time in seconds
873 * @return
874 * String time in min : sec format
875 */
876 function quiz_get_time_taken_in_minutes($time_in_sec) {
877 $min = intval($time_in_sec / 60);
878 $sec = $time_in_sec % 60;
879 return "$min : $sec";
880 }
881
882
883 /**
884 * Theme the summary page for admins.
885 *
886 * @param $quiz
887 * The quiz node object.
888 * @param $questions
889 * The questions array as defined by _quiz_get_answers.
890 * @param $score
891 * Array of score information as returned by quiz_calculate_score().
892 * @param $summary
893 * Filtered text of the summary.
894 * @return
895 * Themed html.
896 *
897 * @ingroup themeable
898 */
899 function theme_quiz_admin_summary($quiz, $questions, $score, $summary) {
900 // Set the title here so themers can adjust.
901 drupal_set_title(check_plain($quiz->title));
902
903 if (!$score['is_evaluated']) {
904 drupal_set_message(t('This quiz has not been scored yet.'), 'error');
905 }
906
907 // Display overall result.
908 $output = '';
909 $params = array('%num_correct' => $score['numeric_score'], '%question_count' => $score['possible_score']);
910 $output .= '<div id="quiz_score_possible">'. t('This person got %num_correct of %question_count possible points.', $params) .'</div>'."\n";
911 $output .= '<div id="quiz_score_percent">'. t('Total score: @score%', array('@score' => $score['percentage_score'])) .'</div><br />'."\n";
912 $output .= '<div id="quiz_summary">'. check_markup($summary, $quiz->format) .'</div><br />'."\n";
913 // Get the feedback for all questions.
914 $output .= theme('quiz_feedback', $questions, TRUE, TRUE);
915 return $output;
916 }
917
918 /**
919 * Theme a question selection table, adding drag and drop support.
920 */
921 function theme_question_selection_table($form) {
922 // This is a temporary hack.
923 static $table_counter = 0;
924 ++$table_counter;
925
926 $question_types = _quiz_get_question_types();
927
928 //drupal_add_tabledrag('questions-order-' . $table_counter, 'order', 'sibling', 'question-order-weight', 'question-order-weight-' . $table_counter, NULL, TRUE);
929 drupal_add_tabledrag('questions-order-' . $table_counter, 'order', 'sibling', 'question-order-weight-' . $table_counter, NULL, NULL, TRUE);
930
931 $headers = array(t('Question'), t('Type'), t('Actions'), t('Weight'));
932 $rows = array();
933 if (!empty($form['titles'])) {
934 foreach (element_children($form['titles']) as $nid) {
935 $form['weights'][$nid]['#attributes']['class'] = 'question-order-weight question-order-weight-' . $table_counter;
936 $type = $form['types'][$nid]['#value'];
937
938 $rows[] = array(
939 'class' => 'draggable',
940 'data' => array(
941 drupal_render($form['titles'][$nid]),
942 $question_types[$type]['name'],
943 $form['view_links'][$nid]['#value'] . ' | ' . $form['remove_links'][$nid]['#value'],
944 drupal_render($form['weights'][$nid]),
945 ),
946 );
947 unset($form['types'][$nid], $form['view_links'][$nid], $form['remove_links'][$nid]);
948 }
949
950 }
951 $table = theme('table', $headers, $rows, array('id' => 'questions-order-' . $table_counter));
952 return $table . drupal_render($form);
953 }
954
955 function quiz_remove_partial_quiz_record_value() {
956 /*$list = array();
957 $list[0] = t('Never');
958 for ($i=1; $i<31; $i++) {
959 $list[$i * 86400] = $i . ' ' . t('Day(s)');
960 }
961 for ($i=35; $i<125; $i+=5) {
962 $list[$i * 86400] = $i . ' ' . t('Day(s)');
963 }*/
964 //return $list;
965 return array(
966 '0' => t('Never'),
967 '86400' => t('1 Day'),
968 '172800' => t('2 Days'),
969 '259200' => t('3 Days'),
970 '345600' => t('4 Days'),
971 '432000' => t('5 Days'),
972 '518400' => t('6 Days'),
973 '604800' => t('7 Days'),
974 '691200' => t('8 Days'),
975 '777600' => t('9 Days'),
976 '864000' => t('10 Days'),
977 '950400' => t('11 Days'),
978 '1036800' => t('12 Days'),
979 '1123200' => t('13 Days'),
980 '1209600' => t('14 Days'),
981 '1296000' => t('15 Days'),
982 '1382400' => t('16 Days'),
983 '1468800' => t('17 Days'),
984 '1555200' => t('18 Days'),
985 '1641600' => t('19 Days'),
986 '1728000' => t('20 Days'),
987 '1814400' => t('21 Days'),
988 '1900800' => t('22 Days'),
989 '1987200' => t('23 Days'),
990 '2073600' => t('24 Days'),
991 '2160000' => t('25 Days'),
992 '2246400' => t('26 Days'),
993 '2332800' => t('27 Days'),
994 '2419200' => t('28 Days'),
995 '2505600' => t('29 Days'),
996 '2592000' => t('30 Days'),
997 '3024000' => t('35 Days'),
998 '3456000' => t('40 Days'),
999 '3888000' => t('45 Days'),
1000 '4320000' => t('50 Days'),
1001 '4752000' => t('55 Days'),
1002 '5184000' => t('60 Days'),
1003 '5616000' => t('65 Days'),
1004 '6048000' => t('70 Days'),
1005 '6480000' => t('75 Days'),
1006 '6912000' => t('80 Days'),
1007 '7344000' => t('85 Days'),
1008 '7776000' => t('90 Days'),
1009 '8208000' => t('95 Days'),
1010 '8640000' => t('100 Days'),
1011 '9072000' => t('105 Days'),
1012 '9504000' => t('110 Days'),
1013 '9936000' => t('115 Days'),
1014 '10368000' => t('120 Days'),
1015 );
1016 }
1017
1018 /*
1019 * Adds inline js to automatically set the question's node title.
1020 */
1021 function quiz_set_auto_title() {
1022 drupal_add_js('
1023 $(document).ready(function () {
1024 function quizUpdateTitle() {
1025 $("#edit-title").val($("#edit-body").val().substring(0, '. variable_get('quiz_autotitle_length', 50) .'));
1026 }
1027 $("#edit-body").keyup(quizUpdateTitle);
1028 if($("#edit-title").val().length > 0){
1029 $("#edit-body").unbind("keyup", quizUpdateTitle);
1030 }
1031 $("#edit-title").keyup(function() {
1032 $("#edit-body").unbind("keyup", quizUpdateTitle);
1033 });
1034 });
1035 ', 'inline');
1036
1037 }

  ViewVC Help
Powered by ViewVC 1.1.2