Adding new data field type, latlong, to HEAD
[moodle.git] / question / type / questiontype.php
CommitLineData
516cf3eb 1<?php // $Id$
2/**
3* The default questiontype class.
4*
5* @version $Id$
6* @author Martin Dougiamas and many others. This has recently been completely
7* rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
8* the Serving Mathematics project
9* {@link http://maths.york.ac.uk/serving_maths}
10* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
11* @package quiz
12*/
13
14/// Question type class //////////////////////////////////////////////
15
af3830ee 16class default_questiontype {
516cf3eb 17
18 /**
19 * Name of the question type
20 *
21 * The name returned should coincide with the name of the directory
22 * in which this questiontype is located
23 * @ return string
24 */
25 function name() {
26 return 'default';
27 }
28
29 /**
30 * Saves or updates a question after editing by a teacher
31 *
32 * Given some question info and some data about the answers
33 * this function parses, organises and saves the question
34 * It is used by {@link question.php} when saving new data from
35 * a form, and also by {@link import.php} when importing questions
36 * This function in turn calls {@link save_question_options}
37 * to save question-type specific options
38 * @return object A {@link question} object
39 * @param object $question The question object which should be updated
40 * @param object $form The form submitted by the teacher
41 * @param object $course The course we are in
42 */
43 function save_question($question, $form, $course) {
44 // This default implementation is suitable for most
45 // question types.
46
47 // First, save the basic question itself
48
49 $question->name = trim($form->name);
50 $question->questiontext = trim($form->questiontext);
51 $question->questiontextformat = $form->questiontextformat;
52 $question->parent = isset($form->parent)? $form->parent : 0;
53 $question->length = $this->actual_number_of_questions($question);
54 $question->penalty = isset($form->penalty) ? $form->penalty : 0;
55
56 if (empty($form->image)) {
57 $question->image = "";
58 } else {
59 $question->image = $form->image;
60 }
61
62 if (empty($question->name)) {
5433ee83 63 $question->name = substr(strip_tags($question->questiontext), 0, 15);
516cf3eb 64 if (empty($question->name)) {
65 $question->name = '-';
66 }
67 }
68
69 if ($question->penalty > 1 or $question->penalty < 0) {
70 $question->errors['penalty'] = get_string('invalidpenalty', 'quiz');
71 }
72
73 if (isset($form->defaultgrade)) {
74 $question->defaultgrade = $form->defaultgrade;
75 }
76
516cf3eb 77 if (!empty($question->id)) { // Question already exists
cbe20043 78 // keep existing unique stamp code
79 $question->stamp = get_field('question', 'stamp', 'id', $question->id);
4f48fb42 80 if (!update_record("question", $question)) {
516cf3eb 81 error("Could not update question!");
82 }
83 } else { // Question is a new one
cbe20043 84 // Set the unique code
85 $question->stamp = make_unique_id_code();
4f48fb42 86 if (!$question->id = insert_record("question", $question)) {
516cf3eb 87 error("Could not insert new question!");
88 }
89 }
90
91 // Now to save all the answers and type-specific options
92
93 $form->id = $question->id;
94 $form->qtype = $question->qtype;
95 $form->category = $question->category;
96
97 $result = $this->save_question_options($form);
98
99 if (!empty($result->error)) {
100 error($result->error);
101 }
102
103 if (!empty($result->notice)) {
104 notice($result->notice, "question.php?id=$question->id");
105 }
106
107 if (!empty($result->noticeyesno)) {
25ee4157 108 notice_yesno($result->noticeyesno, "question.php?id=$question->id&amp;courseid={$course->id}",
109 "edit.php?courseid={$course->id}");
516cf3eb 110 print_footer($course);
111 exit;
112 }
113
cbe20043 114 // Give the question a unique version stamp determined by question_hash()
115 if (!set_field('question', 'version', question_hash($question), 'id', $question->id)) {
116 error('Could not update question version field');
117 }
118
516cf3eb 119 return $question;
120 }
121
122 /**
123 * Saves question-type specific options
124 *
125 * This is called by {@link save_question()} to save the question-type specific data
126 * @return object $result->error or $result->noticeyesno or $result->notice
127 * @param object $question This holds the information from the editing form,
128 * it is not a standard question object.
129 */
130 function save_question_options($question) {
4eda4eec 131 return null;
516cf3eb 132 }
133
134 /**
135 * Changes all states for the given attempts over to a new question
136 *
137 * This is used by the versioning code if the teacher requests that a question
138 * gets replaced by the new version. In order for the attempts to be regraded
139 * properly all data in the states referring to the old question need to be
140 * changed to refer to the new version instead. In particular for question types
141 * that use the answers table the answers belonging to the old question have to
142 * be changed to those belonging to the new version.
143 *
144 * @param integer $oldquestionid The id of the old question
145 * @param object $newquestion The new question
146 * @param array $attempts An array of all attempt objects in whose states
147 * replacement should take place
148 */
149 function replace_question_in_attempts($oldquestionid, $newquestion, $attemtps) {
150 echo 'Not yet implemented';
151 return;
152 }
153
154 /**
155 * Loads the question type specific options for the question.
156 *
157 * This function loads any question type specific options for the
158 * question from the database into the question object. This information
159 * is placed in the $question->options field. A question type is
160 * free, however, to decide on a internal structure of the options field.
161 * @return bool Indicates success or failure.
162 * @param object $question The question object for the question. This object
163 * should be updated to include the question type
164 * specific information (it is passed by reference).
165 */
166 function get_question_options(&$question) {
167 if (!isset($question->options)) {
168 $question->options = new object;
169 }
170 // The default implementation attaches all answers for this question
ed28abca 171 $question->options->answers = get_records('question_answers', 'question',
172 $question->id);
516cf3eb 173 return true;
174 }
175
176 /**
0429cd86 177 * Deletes states from the question-type specific tables
178 *
179 * @param string $stateslist Comma separated list of state ids to be deleted
180 */
181 function delete_states($stateslist) {
182 /// The default question type does not have any tables of its own
183 // therefore there is nothing to delete
184
185 return true;
186 }
187
188 /**
189 * Deletes a question from the question-type specific tables
516cf3eb 190 *
191 * @return boolean Success/Failure
192 * @param object $question The question being deleted
193 */
90c3f310 194 function delete_question($questionid) {
516cf3eb 195 /// The default question type does not have any tables of its own
196 // therefore there is nothing to delete
197
198 return true;
199 }
200
201 /**
202 * Returns the number of question numbers which are used by the question
203 *
204 * This function returns the number of question numbers to be assigned
205 * to the question. Most question types will have length one; they will be
dfa47f96 206 * assigned one number. The 'description' type, however does not use up a
516cf3eb 207 * number and so has a length of zero. Other question types may wish to
208 * handle a bundle of questions and hence return a number greater than one.
209 * @return integer The number of question numbers which should be
210 * assigned to the question.
211 * @param object $question The question whose length is to be determined.
212 * Question type specific information is included.
213 */
214 function actual_number_of_questions($question) {
215 // By default, each question is given one number
216 return 1;
217 }
218
219 /**
220 * Creates empty session and response information for the question
221 *
222 * This function is called to start a question session. Empty question type
223 * specific session data (if any) and empty response data will be added to the
224 * state object. Session data is any data which must persist throughout the
225 * attempt possibly with updates as the user interacts with the
226 * question. This function does NOT create new entries in the database for
227 * the session; a call to the {@link save_session_and_responses} member will
228 * occur to do this.
229 * @return bool Indicates success or failure.
230 * @param object $question The question for which the session is to be
231 * created. Question type specific information is
232 * included.
233 * @param object $state The state to create the session for. Note that
234 * this will not have been saved in the database so
235 * there will be no id. This object will be updated
236 * to include the question type specific information
237 * (it is passed by reference). In particular, empty
238 * responses will be created in the ->responses
239 * field.
240 * @param object $cmoptions
241 * @param object $attempt The attempt for which the session is to be
242 * started. Questions may wish to initialize the
243 * session in different ways depending on the user id
244 * or time available for the attempt.
245 */
246 function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
247 // The default implementation should work for the legacy question types.
248 // Most question types with only a single form field for the student's response
249 // will use the empty string '' as the index for that one response. This will
250 // automatically be stored in and restored from the answer field in the
4f48fb42 251 // question_states table.
5a14d563 252 $state->responses = array(
253 '' => '',
254 );
516cf3eb 255 return true;
256 }
257
258 /**
259 * Restores the session data and most recent responses for the given state
260 *
261 * This function loads any session data associated with the question
262 * session in the given state from the database into the state object.
263 * In particular it loads the responses that have been saved for the given
264 * state into the ->responses member of the state object.
265 *
266 * Question types with only a single form field for the student's response
267 * will not need not restore the responses; the value of the answer
4f48fb42 268 * field in the question_states table is restored to ->responses['']
516cf3eb 269 * before this function is called. Question types with more response fields
270 * should override this method and set the ->responses field to an
271 * associative array of responses.
272 * @return bool Indicates success or failure.
273 * @param object $question The question object for the question including any
274 * question type specific information.
275 * @param object $state The saved state to load the session for. This
276 * object should be updated to include the question
277 * type specific session information and responses
278 * (it is passed by reference).
279 */
280 function restore_session_and_responses(&$question, &$state) {
281 // The default implementation does nothing (successfully)
282 return true;
283 }
284
285 /**
286 * Saves the session data and responses for the given question and state
287 *
288 * This function saves the question type specific session data from the
289 * state object to the database. In particular for most question types it saves the
290 * responses from the ->responses member of the state object. The question type
4f48fb42 291 * non-specific data for the state has already been saved in the question_states
516cf3eb 292 * table and the state object contains the corresponding id and
293 * sequence number which may be used to index a question type specific table.
294 *
295 * Question types with only a single form field for the student's response
296 * which is contained in ->responses[''] will not have to save this response,
4f48fb42 297 * it will already have been saved to the answer field of the question_states table.
516cf3eb 298 * Question types with more response fields should override this method and save
299 * the responses in their own database tables.
300 * @return bool Indicates success or failure.
301 * @param object $question The question object for the question including
302 * the question type specific information.
303 * @param object $state The state for which the question type specific
304 * data and responses should be saved.
305 */
306 function save_session_and_responses(&$question, &$state) {
307 // The default implementation does nothing (successfully)
308 return true;
309 }
310
311 /**
312 * Returns an array of values which will give full marks if graded as
313 * the $state->responses field
314 *
315 * The correct answer to the question in the given state, or an example of
316 * a correct answer if there are many, is returned. This is used by some question
317 * types in the {@link grade_responses()} function but it is also used by the
318 * question preview screen to fill in correct responses.
319 * @return mixed A response array giving the responses corresponding
320 * to the (or a) correct answer to the question. If there is
321 * no correct answer that scores 100% then null is returned.
322 * @param object $question The question for which the correct answer is to
323 * be retrieved. Question type specific information is
324 * available.
325 * @param object $state The state of the question, for which a correct answer is
326 * needed. Question type specific information is included.
327 */
328 function get_correct_responses(&$question, &$state) {
329 /* The default implementation returns the response for the first answer
330 that gives full marks. */
4eda4eec 331 if ($question->options->answers) {
332 foreach ($question->options->answers as $answer) {
333 if (((int) $answer->fraction) === 1) {
334 return array('' => $answer->answer);
335 }
516cf3eb 336 }
337 }
338 return null;
339 }
340
341 /**
342 * Return an array of values with the texts for all possible responses stored
343 * for the question
344 *
345 * All answers are found and their text values isolated
346 * @return object A mixed object
347 * ->id question id. Needed to manage random questions:
348 * it's the id of the actual question presented to user in a given attempt
349 * ->responses An array of values giving the responses corresponding
350 * to all answers to the question. Answer ids are used as keys.
351 * The text and partial credit are the object components
352 * @param object $question The question for which the answers are to
353 * be retrieved. Question type specific information is
354 * available.
355 */
356 // ULPGC ecastro
357 function get_all_responses(&$question, &$state) {
7baff8aa 358 if (isset($question->options->answers) && is_array($question->options->answers)) {
359 $answers = array();
516cf3eb 360 foreach ($question->options->answers as $aid=>$answer) {
7baff8aa 361 $r = new stdClass;
516cf3eb 362 $r->answer = $answer->answer;
363 $r->credit = $answer->fraction;
364 $answers[$aid] = $r;
365 }
7baff8aa 366 $result = new stdClass;
367 $result->id = $question->id;
368 $result->responses = $answers;
369 return $result;
516cf3eb 370 } else {
7baff8aa 371 return null;
516cf3eb 372 }
516cf3eb 373 }
374
375 /**
376 * Return the actual response to the question in a given state
377 * for the question
378 *
379 * @return mixed An array containing the response or reponses (multiple answer, match)
380 * given by the user in a particular attempt.
381 * @param object $question The question for which the correct answer is to
382 * be retrieved. Question type specific information is
383 * available.
384 * @param object $state The state object that corresponds to the question,
385 * for which a correct answer is needed. Question
386 * type specific information is included.
387 */
388 // ULPGC ecastro
8cc274de 389 function get_actual_response($question, $state) {
390 // change length to truncate responses here if you want
391 $lmax = 40;
392 if (!empty($state->responses)) {
393 $responses[] = (strlen($state->responses['']) > $lmax) ?
394 substr($state->responses[''], 0, $lmax).'...' : $state->responses[''];
395 } else {
396 $responses[] = '';
397 }
398 return $responses;
516cf3eb 399 }
400
401 // ULPGC ecastro
402 function get_fractional_grade(&$question, &$state) {
403 $maxgrade = $question->maxgrade;
404 $grade = $state->grade;
405 if ($maxgrade) {
406 return (float)($grade/$maxgrade);
407 } else {
408 return (float)$grade;
409 }
410 }
411
412
413 /**
414 * Checks if the response given is correct and returns the id
415 *
416 * @return int The ide number for the stored answer that matches the response
417 * given by the user in a particular attempt.
418 * @param object $question The question for which the correct answer is to
419 * be retrieved. Question type specific information is
420 * available.
421 * @param object $state The state object that corresponds to the question,
422 * for which a correct answer is needed. Question
423 * type specific information is included.
424 */
425 // ULPGC ecastro
426 function check_response(&$question, &$state){
427 return false;
428 }
429
430 /**
431 * Prints the question including the number, grading details, content,
432 * feedback and interactions
433 *
434 * This function prints the question including the question number,
435 * grading details, content for the question, any feedback for the previously
436 * submitted responses and the interactions. The default implementation calls
437 * various other methods to print each of these parts and most question types
438 * will just override those methods.
516cf3eb 439 * @param object $question The question to be rendered. Question type
440 * specific information is included. The
441 * maximum possible grade is in ->maxgrade. The name
442 * prefix for any named elements is in ->name_prefix.
443 * @param object $state The state to render the question in. The grading
444 * information is in ->grade, ->raw_grade and
445 * ->penalty. The current responses are in
446 * ->responses. This is an associative array (or the
447 * empty string or null in the case of no responses
448 * submitted). The last graded state is in
449 * ->last_graded (hence the most recently graded
450 * responses are in ->last_graded->responses). The
451 * question type specific information is also
452 * included.
453 * @param integer $number The number for this question.
454 * @param object $cmoptions
455 * @param object $options An object describing the rendering options.
456 */
457 function print_question(&$question, &$state, $number, $cmoptions, $options) {
458 /* The default implementation should work for most question types
459 provided the member functions it calls are overridden where required.
37a12367 460 The layout is determined by the template question.html */
516cf3eb 461 global $CFG;
462
463 // For editing teachers print a link to an editing popup window
464 $editlink = '';
465 if (isteacheredit($cmoptions->course)) {
466 $stredit = get_string('edit');
467 $linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" border="0" alt="'.$stredit.'" />';
e586cfb4 468 $editlink = link_to_popup_window('/question/question.php?id='.$question->id, $stredit, $linktext, 450, 550, $stredit, '', true);
516cf3eb 469 }
470
471 $grade = '';
472 if ($question->maxgrade and $options->scores) {
6b11a0e8 473 if ($cmoptions->optionflags & QUESTION_ADAPTIVE) {
f30bbcaf 474 $grade = (!question_state_is_graded($state->last_graded)) ? '--/' : round($state->last_graded->grade, $cmoptions->decimalpoints).'/';
516cf3eb 475 }
476 $grade .= $question->maxgrade;
477 }
b6e907a2 478
da1cc5a4 479 $comment = stripslashes($state->comment);
b6e907a2 480 $commentlink = '';
481 if (isset($options->questioncommentlink)) {
482 $strcomment = get_string('commentorgrade', 'quiz');
097bd3f5 483 $commentlink = '<div class="commentlink">'.link_to_popup_window ($options->questioncommentlink.'?attempt='.$state->attempt.'&amp;question='.$question->id,
484 'commentquestion', $strcomment, 450, 650, $strcomment, 'none', true).'</div>';
b6e907a2 485 }
516cf3eb 486
fe9b5cfd 487 $history = $this->history($question, $state, $number, $cmoptions, $options);
488
aaae75b0 489 include "$CFG->dirroot/question/type/question.html";
fe9b5cfd 490 }
491
492 /*
493 * Print history of responses
494 *
495 * Used by print_question()
496 */
497 function history($question, $state, $number, $cmoptions, $options) {
516cf3eb 498 $history = '';
499 if(isset($options->history) and $options->history) {
500 if ($options->history == 'all') {
501 // show all states
755bddf1 502 $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event > '0'", 'seq_number ASC');
516cf3eb 503 } else {
504 // show only graded states
755bddf1 505 $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTGRADE.','.QUESTION_EVENTCLOSEANDGRADE.")", 'seq_number ASC');
516cf3eb 506 }
507 if (count($states) > 1) {
508 $strreviewquestion = get_string('reviewresponse', 'quiz');
509 unset($table);
516cf3eb 510 $table->width = '100%';
d57cf4c1 511 if ($options->scores) {
512 $table->head = array (
513 get_string('numberabbr', 'quiz'),
514 get_string('action', 'quiz'),
515 get_string('response', 'quiz'),
516 get_string('time'),
517 get_string('score', 'quiz'),
518 //get_string('penalty', 'quiz'),
519 get_string('grade', 'quiz'),
520 );
521 } else {
522 $table->head = array (
523 get_string('numberabbr', 'quiz'),
524 get_string('action', 'quiz'),
525 get_string('response', 'quiz'),
526 get_string('time'),
527 );
3925a20a 528 }
529
516cf3eb 530 foreach ($states as $st) {
0a5b58af 531 $st->responses[''] = $st->answer;
532 $this->restore_session_and_responses($question, $st);
516cf3eb 533 $b = ($state->id == $st->id) ? '<b>' : '';
534 $be = ($state->id == $st->id) ? '</b>' : '';
fe9b5cfd 535 if ($state->id == $st->id) {
536 $link = '<b>'.$st->seq_number.'</b>';
537 } else {
538 if(isset($options->questionreviewlink)) {
539 $link = link_to_popup_window ($options->questionreviewlink.'?state='.$st->id.'&amp;number='.$number,
540 'reviewquestion', $st->seq_number, 450, 650, $strreviewquestion, 'none', true);
541 } else {
542 $link = $st->seq_number;
543 }
544 }
d57cf4c1 545 if ($options->scores) {
546 $table->data[] = array (
547 $link,
548 $b.get_string('event'.$st->event, 'quiz').$be,
549 $b.$this->response_summary($question, $st).$be,
550 $b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
551 $b.round($st->raw_grade, $cmoptions->decimalpoints).$be,
552 //$b.round($st->penalty, $cmoptions->decimalpoints).$be,
553 $b.round($st->grade, $cmoptions->decimalpoints).$be
554 );
555 } else {
556 $table->data[] = array (
557 $link,
558 $b.get_string('event'.$st->event, 'quiz').$be,
559 $b.$this->response_summary($question, $st).$be,
560 $b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
561 );
562 }
516cf3eb 563 }
564 $history = make_table($table);
565 }
566 }
fe9b5cfd 567 return $history;
516cf3eb 568 }
569
570
571 /**
572 * Prints the score obtained and maximum score available plus any penalty
573 * information
574 *
575 * This function prints a summary of the scoring in the most recently
576 * graded state (the question may not have been submitted for marking at
577 * the current state). The default implementation should be suitable for most
578 * question types.
579 * @param object $question The question for which the grading details are
580 * to be rendered. Question type specific information
581 * is included. The maximum possible grade is in
582 * ->maxgrade.
583 * @param object $state The state. In particular the grading information
584 * is in ->grade, ->raw_grade and ->penalty.
585 * @param object $cmoptions
586 * @param object $options An object describing the rendering options.
587 */
588 function print_question_grading_details(&$question, &$state, $cmoptions, $options) {
589 /* The default implementation prints the number of marks if no attempt
590 has been made. Otherwise it displays the grade obtained out of the
591 maximum grade available and a warning if a penalty was applied for the
592 attempt and displays the overall grade obtained counting all previous
593 responses (and penalties) */
594
f30bbcaf 595 if (QUESTION_EVENTDUPLICATE == $state->event) {
516cf3eb 596 echo ' ';
597 print_string('duplicateresponse', 'quiz');
598 }
599 if (!empty($question->maxgrade) && $options->scores) {
f30bbcaf 600 if (question_state_is_graded($state->last_graded)) {
516cf3eb 601 // Display the grading details from the last graded state
602 $grade->cur = round($state->last_graded->grade, $cmoptions->decimalpoints);
603 $grade->max = $question->maxgrade;
604 $grade->raw = round($state->last_graded->raw_grade, $cmoptions->decimalpoints);
605
606 // let student know wether the answer was correct
607 echo '<div class="correctness ';
608 if ($state->last_graded->raw_grade >= $question->maxgrade/1.01) { // We divide by 1.01 so that rounding errors dont matter.
609 echo ' correct">';
610 print_string('correct', 'quiz');
611 } else if ($state->last_graded->raw_grade > 0) {
612 echo ' partiallycorrect">';
613 print_string('partiallycorrect', 'quiz');
614 } else {
615 echo ' incorrect">';
616 print_string('incorrect', 'quiz');
617 }
618 echo '</div>';
619
620 echo '<div class="gradingdetails">';
621 // print grade for this submission
622 print_string('gradingdetails', 'quiz', $grade);
623 if ($cmoptions->penaltyscheme) {
624 // print details of grade adjustment due to penalties
625 if ($state->last_graded->raw_grade > $state->last_graded->grade){
626 print_string('gradingdetailsadjustment', 'quiz', $grade);
627 }
628 // print info about new penalty
629 // penalty is relevant only if the answer is not correct and further attempts are possible
f30bbcaf 630 if (($state->last_graded->raw_grade < $question->maxgrade) and (QUESTION_EVENTCLOSEANDGRADE !== $state->event)) {
516cf3eb 631 if ('' !== $state->last_graded->penalty && ((float)$state->last_graded->penalty) > 0.0) {
632 // A penalty was applied so display it
633 print_string('gradingdetailspenalty', 'quiz', $state->last_graded->penalty);
634 } else {
635 /* No penalty was applied even though the answer was
636 not correct (eg. a syntax error) so tell the student
637 that they were not penalised for the attempt */
638 print_string('gradingdetailszeropenalty', 'quiz');
639 }
640 }
641 }
642 echo '</div>';
643 }
644 }
645 }
646
647 /**
648 * Prints the main content of the question including any interactions
649 *
650 * This function prints the main content of the question including the
651 * interactions for the question in the state given. The last graded responses
652 * are printed or indicated and the current responses are selected or filled in.
653 * Any names (eg. for any form elements) are prefixed with $question->name_prefix.
654 * This method is called from the print_question method.
655 * @param object $question The question to be rendered. Question type
656 * specific information is included. The name
657 * prefix for any named elements is in ->name_prefix.
658 * @param object $state The state to render the question in. The grading
659 * information is in ->grade, ->raw_grade and
660 * ->penalty. The current responses are in
661 * ->responses. This is an associative array (or the
662 * empty string or null in the case of no responses
663 * submitted). The last graded state is in
664 * ->last_graded (hence the most recently graded
665 * responses are in ->last_graded->responses). The
666 * question type specific information is also
667 * included.
668 * The state is passed by reference because some adaptive
669 * questions may want to update it during rendering
670 * @param object $cmoptions
671 * @param object $options An object describing the rendering options.
672 */
673 function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
674 /* This default implementation prints an error and must be overridden
675 by all question type implementations, unless the default implementation
676 of print_question has been overridden. */
677
678 notify('Error: Question formulation and input controls has not'
679 .' been implemented for question type '.$this->name());
680 }
681
682 /**
683 * Prints the submit button(s) for the question in the given state
684 *
685 * This function prints the submit button(s) for the question in the
686 * given state. The name of any button created will be prefixed with the
687 * unique prefix for the question in $question->name_prefix. The suffix
4dca7e51 688 * 'submit' is reserved for the single question submit button and the suffix
516cf3eb 689 * 'validate' is reserved for the single question validate button (for
690 * question types which support it). Other suffixes will result in a response
691 * of that name in $state->responses which the printing and grading methods
692 * can then use.
693 * @param object $question The question for which the submit button(s) are to
694 * be rendered. Question type specific information is
695 * included. The name prefix for any
696 * named elements is in ->name_prefix.
697 * @param object $state The state to render the buttons for. The
698 * question type specific information is also
699 * included.
700 * @param object $cmoptions
701 * @param object $options An object describing the rendering options.
702 */
703 function print_question_submit_buttons(&$question, &$state, $cmoptions, $options) {
704 /* The default implementation should be suitable for most question
705 types. It prints a mark button in the case where individual marking is
706 allowed. */
707
6b11a0e8 708 if (($cmoptions->optionflags & QUESTION_ADAPTIVE) and !$options->readonly) {
516cf3eb 709 echo '<input type="submit" name="';
710 echo $question->name_prefix;
4dca7e51 711 echo 'submit" value="';
516cf3eb 712 print_string('mark', 'quiz');
5bc57211 713 echo '" class="submit btn"';
714 echo ' />';
516cf3eb 715 }
716 }
717
718
719 /**
720 * Return a summary of the student response
721 *
722 * This function returns a short string of no more than a given length that
723 * summarizes the student's response in the given $state. This is used for
724 * example in the response history table
725 * @return string The summary of the student response
0a5b58af 726 * @param object $question
516cf3eb 727 * @param object $state The state whose responses are to be summarized
728 * @param int $length The maximum length of the returned string
729 */
0a5b58af 730 function response_summary($question, $state, $length=80) {
516cf3eb 731 // This should almost certainly be overridden
755bddf1 732 return substr(implode(',', $this->get_actual_response($question, $state)), 0, $length);
516cf3eb 733 }
734
735 /**
736 * Renders the question for printing and returns the LaTeX source produced
737 *
738 * This function should render the question suitable for a printed problem
739 * or solution sheet in LaTeX and return the rendered output.
740 * @return string The LaTeX output.
741 * @param object $question The question to be rendered. Question type
742 * specific information is included.
743 * @param object $state The state to render the question in. The
744 * question type specific information is also
745 * included.
746 * @param object $cmoptions
747 * @param string $type Indicates if the question or the solution is to be
748 * rendered with the values 'question' and
749 * 'solution'.
750 */
751 function get_texsource(&$question, &$state, $cmoptions, $type) {
752 // The default implementation simply returns a string stating that
753 // the question is only available online.
754
755 return get_string('onlineonly', 'texsheet');
756 }
757
758 /**
759 * Compares two question states for equivalence of the student's responses
760 *
761 * The responses for the two states must be examined to see if they represent
762 * equivalent answers to the question by the student. This method will be
763 * invoked for each of the previous states of the question before grading
764 * occurs. If the student is found to have already attempted the question
765 * with equivalent responses then the attempt at the question is ignored;
766 * grading does not occur and the state does not change. Thus they are not
767 * penalized for this case.
768 * @return boolean
769 * @param object $question The question for which the states are to be
770 * compared. Question type specific information is
771 * included.
772 * @param object $state The state of the question. The responses are in
773 * ->responses.
774 * @param object $teststate The state whose responses are to be
775 * compared. The state will be of the same age or
776 * older than $state.
777 */
778 function compare_responses(&$question, $state, $teststate) {
779 // The default implementation performs a comparison of the response
780 // arrays. The ordering of the arrays does not matter.
781 // Question types may wish to override this (eg. to ignore trailing
782 // white space or to make "7.0" and "7" compare equal).
783 return $state->responses == $teststate->responses;
784 }
785
37a12367 786 /**
787 * Checks whether a response matches a given answer
788 *
789 * This method only applies to questions that use teacher-defined answers
790 *
791 * @return boolean
792 */
793 function test_response(&$question, &$state, $answer) {
794 $response = isset($state->responses['']) ? $state->responses[''] : '';
795 return ($response == $answer->answer);
796 }
797
516cf3eb 798 /**
799 * Performs response processing and grading
800 *
801 * This function performs response processing and grading and updates
802 * the state accordingly.
803 * @return boolean Indicates success or failure.
804 * @param object $question The question to be graded. Question type
805 * specific information is included.
806 * @param object $state The state of the question to grade. The current
807 * responses are in ->responses. The last graded state
808 * is in ->last_graded (hence the most recently graded
809 * responses are in ->last_graded->responses). The
810 * question type specific information is also
811 * included. The ->raw_grade and ->penalty fields
812 * must be updated. The method is able to
813 * close the question session (preventing any further
814 * attempts at this question) by setting
f30bbcaf 815 * $state->event to QUESTION_EVENTCLOSEANDGRADE
516cf3eb 816 * @param object $cmoptions
817 */
818 function grade_responses(&$question, &$state, $cmoptions) {
5a14d563 819 // The default implementation uses the test_response method to
820 // compare what the student entered against each of the possible
821 // answers stored in the question, and uses the grade from the
822 // first one that matches. It also sets the marks and penalty.
823 // This should be good enought for most simple question types.
516cf3eb 824
5a14d563 825 $state->raw_grade = 0;
516cf3eb 826 foreach($question->options->answers as $answer) {
5a14d563 827 if($this->test_response($question, $state, $answer)) {
828 $state->raw_grade = $answer->fraction;
516cf3eb 829 break;
830 }
831 }
5a14d563 832
833 // Make sure we don't assign negative or too high marks.
834 $state->raw_grade = min(max((float) $state->raw_grade,
835 0.0), 1.0) * $question->maxgrade;
836
837 // Update the penalty.
838 $state->penalty = $question->penalty * $question->maxgrade;
516cf3eb 839
f30bbcaf 840 // mark the state as graded
841 $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
842
516cf3eb 843 return true;
844 }
845
846
847 /**
848 * Includes configuration settings for the question type on the quiz admin
849 * page
850 *
7518b645 851 * TODO: It makes no sense any longer to do the admin for question types
852 * from the quiz admin page. This should be changed.
516cf3eb 853 * Returns an array of objects describing the options for the question type
854 * to be included on the quiz module admin page.
855 * Configuration options can be included by setting the following fields in
856 * the object:
857 * ->name The name of the option within this question type.
858 * The full option name will be constructed as
859 * "quiz_{$this->name()}_$name", the human readable name
860 * will be displayed with get_string($name, 'quiz').
861 * ->code The code to display the form element, help button, etc.
862 * i.e. the content for the central table cell. Be sure
863 * to name the element "quiz_{$this->name()}_$name" and
864 * set the value to $CFG->{"quiz_{$this->name()}_$name"}.
865 * ->help Name of the string from the quiz module language file
866 * to be used for the help message in the third column of
867 * the table. An empty string (or the field not set)
868 * means to leave the box empty.
869 * Links to custom settings pages can be included by setting the following
870 * fields in the object:
871 * ->name The name of the link text string.
872 * get_string($name, 'quiz') will be called.
873 * ->link The filename part of the URL for the link. The full URL
874 * is contructed as
aaae75b0 875 * "$CFG->wwwroot/question/type/{$this->name()}/$link?sesskey=$sesskey"
516cf3eb 876 * [but with the relavant calls to the s and rawurlencode
877 * functions] where $sesskey is the sesskey for the user.
878 * @return array Array of objects describing the configuration options to
879 * be included on the quiz module admin page.
880 */
881 function get_config_options() {
882 // No options by default
883
884 return false;
885 }
886
887 /**
dc1f00de 888 * Returns true if the editing wizard is finished, false otherwise.
889 *
890 * The default implementation returns true, which is suitable for all question-
516cf3eb 891 * types that only use one editing form. This function is used in
892 * question.php to decide whether we can regrade any states of the edited
893 * question and redirect to edit.php.
894 *
895 * The dataset dependent question-type, which is extended by the calculated
896 * question-type, overwrites this method because it uses multiple pages (i.e.
897 * a wizard) to set up the question and associated datasets.
898 *
899 * @param object $form The data submitted by the previous page.
900 *
901 * @return boolean Whether the wizard's last page was submitted or not.
902 */
903 function finished_edit_wizard(&$form) {
904 //In the default case there is only one edit page.
905 return true;
906 }
907
dc1f00de 908 /**
909 * Prints a table of course modules in which the question is used
910 *
7518b645 911 * TODO: This should be made quiz-independent
912 *
dc1f00de 913 * This function is used near the end of the question edit forms in all question types
914 * It prints the table of quizzes in which the question is used
915 * containing checkboxes to allow the teacher to replace the old question version
916 *
917 * @param object $question
918 * @param object $course
919 * @param integer $cmid optional The id of the course module currently being edited
920 */
921 function print_replacement_options($question, $course, $cmid='0') {
516cf3eb 922
923 // Disable until the versioning code has been fixed
516cf3eb 924 return;
925
926 // no need to display replacement options if the question is new
927 if(empty($question->id)) {
928 return true;
929 }
930
931 // get quizzes using the question (using the question_instances table)
932 $quizlist = array();
933 if(!$instances = get_records('quiz_question_instances', 'question', $question->id)) {
934 $instances = array();
935 }
936 foreach($instances as $instance) {
937 $quizlist[$instance->quiz] = $instance->quiz;
938 }
939 $quizlist = implode(',', $quizlist);
940 if(empty($quizlist) or !$quizzes = get_records_list('quiz', 'id', $quizlist)) {
941 $quizzes = array();
942 }
943
944 // do the printing
945 if(count($quizzes) > 0) {
946 // print the table
947 $strquizname = get_string('modulename', 'quiz');
948 $strdoreplace = get_string('replace', 'quiz');
949 $straffectedstudents = get_string('affectedstudents', 'quiz', $course->students);
950 echo "<tr valign=\"top\">\n";
951 echo "<td align=\"right\"><b>".get_string("replacementoptions", "quiz").":</b></td>\n";
952 echo "<td align=\"left\">\n";
953 echo "<table cellpadding=\"5\" align=\"left\" class=\"generalbox\" width=\"100%\">\n";
954 echo "<tr>\n";
955 echo "<th align=\"left\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\">$strquizname</th>\n";
956 echo "<th align=\"center\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\">$strdoreplace</th>\n";
957 echo "<th align=\"left\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\">$straffectedstudents</th>\n";
958 echo "</tr>\n";
959 foreach($quizzes as $quiz) {
960 // work out whethere it should be checked by default
961 $checked = '';
dc1f00de 962 if((int)$cmid === (int)$quiz->id
516cf3eb 963 or empty($quiz->usercount)) {
964 $checked = "checked=\"checked\"";
965 }
966
967 // find how many different students have already attempted this quiz
968 $students = array();
969 if($attempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'")) {
970 foreach($attempts as $attempt) {
4f48fb42 971 if (record_exists('question_states', 'attempt', $attempt->uniqueid, 'question', $question->id, 'originalquestion', 0)) {
516cf3eb 972 $students[$attempt->userid] = 1;
973 }
974 }
975 }
976 $studentcount = count($students);
977
978 $strstudents = $studentcount === 1 ? $course->student : $course->students;
979 echo "<tr>\n";
980 echo "<td align=\"left\" class=\"generaltablecell c0\">".format_string($quiz->name)."</td>\n";
981 echo "<td align=\"center\" class=\"generaltablecell c0\"><input name=\"q{$quiz->id}replace\" type=\"checkbox\" ".$checked." /></td>\n";
982 echo "<td align=\"left\" class=\"generaltablecell c0\">".(($studentcount) ? $studentcount.' '.$strstudents : '-')."</td>\n";
983 echo "</tr>\n";
984 }
985 echo "</table>\n";
986 }
987 echo "</td></tr>\n";
988 }
989
990 function print_question_form_end($question, $submitscript='') {
991 // This function is used at the end of the question edit forms in all question types
992 // It prints the submit, copy, and cancel buttons and the standard hidden form fields
993 global $USER;
994 echo '<tr valign="top">
995 <td colspan="2" align="center">
996 <input type="submit" '.$submitscript.' value="'.get_string('savechanges').'" /> ';
997 if ($question->id) {
76de2cdf 998 echo '<input type="submit" name="makecopy" '.$submitscript.' value="'.get_string("makecopy", "quiz").'" /> ';
516cf3eb 999 }
1000 echo '<input type="submit" name="cancel" value="'.get_string("cancel").'" />
1001 <input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />
1002 <input type="hidden" name="id" value="'.$question->id.'" />
1003 <input type="hidden" name="qtype" value="'.$question->qtype.'" />';
1004 // The following hidden field indicates that the versioning code should be turned on, i.e.,
1005 // that old versions should be kept if necessary
1006 echo '<input type="hidden" name="versioning" value="on" />
1007 </td></tr>';
1008 }
c5d94c41 1009
1010/// BACKUP FUNCTIONS ////////////////////////////
1011
1012 /*
1013 * Backup the data in the question
1014 *
1015 * This is used in question/backuplib.php
1016 */
1017 function backup($bf,$preferences,$question,$level=6) {
1018 // The default type has nothing to back up
1019 return true;
1020 }
1021
315559d3 1022/// RESTORE FUNCTIONS /////////////////
1023
1024 /*
1025 * Restores the data in the question
1026 *
1027 * This is used in question/restorelib.php
1028 */
1029 function restore($old_question_id,$new_question_id,$info,$restore) {
1030 // The default question type has nothing to restore
1031 return true;
1032 }
1033
1034 function restore_map($old_question_id,$new_question_id,$info,$restore) {
1035 // There is nothing to decode
1036 return true;
1037 }
1038
1039 function restore_recode_answer($state, $restore) {
1040 // There is nothing to decode
ba8f7ff0 1041 return $state->answer;
315559d3 1042 }
1043
1044 //This function restores the question_rqp_states
1045 function restore_state($state_id,$info,$restore) {
1046 // The default question type does not keep its own state information
1047 return true;
1048 }
516cf3eb 1049
1050}
1051
1052?>