Clean up whitespace.
[moodle.git] / question / type / questiontype.php
CommitLineData
516cf3eb 1<?php // $Id$
2/**
4323d029 3 * The default questiontype class.
4 *
5 * @author Martin Dougiamas and many others. This has recently been completely
6 * rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
7 * the Serving Mathematics project
8 * {@link http://maths.york.ac.uk/serving_maths}
9 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10 * @package questionbank
11 * @subpackage questiontypes
12 *//** */
516cf3eb 13
8d205441 14require_once($CFG->libdir . '/questionlib.php');
516cf3eb 15
32b0adfa 16/**
17 * This is the base class for Moodle question types.
271ffe3f 18 *
32b0adfa 19 * There are detailed comments on each method, explaining what the method is
20 * for, and the circumstances under which you might need to override it.
271ffe3f 21 *
32b0adfa 22 * Note: the questiontype API should NOT be considered stable yet. Very few
23 * question tyeps have been produced yet, so we do not yet know all the places
271ffe3f 24 * where the current API is insufficient. I would rather learn from the
32b0adfa 25 * experiences of the first few question type implementors, and improve the
26 * interface to meet their needs, rather the freeze the API prematurely and
27 * condem everyone to working round a clunky interface for ever afterwards.
4323d029 28 *
29 * @package questionbank
30 * @subpackage questiontypes
32b0adfa 31 */
af3830ee 32class default_questiontype {
516cf3eb 33
34 /**
a2156789 35 * Name of the question type
36 *
37 * The name returned should coincide with the name of the directory
38 * in which this questiontype is located
271ffe3f 39 *
32b0adfa 40 * @return string the name of this question type.
a2156789 41 */
516cf3eb 42 function name() {
43 return 'default';
44 }
45
a2156789 46 /**
271ffe3f 47 * The name this question should appear as in the create new question
a2156789 48 * dropdown.
271ffe3f 49 *
a2156789 50 * @return mixed the desired string, or false to hide this question type in the menu.
51 */
52 function menu_name() {
53 $name = $this->name();
54 $menu_name = get_string($name, 'qtype_' . $name);
55 if ($menu_name[0] == '[') {
271ffe3f 56 // Legacy behavior, if the string was not in the proper qtype_name
a2156789 57 // language file, look it up in the quiz one.
c2f8c4be 58 $menu_name = get_string($name, 'quiz');
a2156789 59 }
60 return $menu_name;
61 }
271ffe3f 62
a2156789 63 /**
64 * @return boolean true if this question can only be graded manually.
65 */
66 function is_manual_graded() {
67 return false;
68 }
69
70 /**
71 * @return boolean true if this question type can be used by the random question type.
72 */
73 function is_usable_by_random() {
74 return true;
75 }
76
c85607f0 77 /**
78 * @return whether the question_answers.answer field needs to have
79 * restore_decode_content_links_worker called on it.
80 */
81 function has_html_answers() {
82 return false;
83 }
84
36703ed7 85 /**
86 * Return an instance of the question editing form definition. This looks for a
87 * class called edit_{$this->name()}_question_form in the file
88 * {$CFG->docroot}/question/type/{$this->name()}/edit_{$this->name()}_question_form.php
89 * and if it exists returns an instance of it.
90 *
91 * @param string $submiturl passed on to the constructor call.
92 * @return object an instance of the form definition, or null if one could not be found.
93 */
271ffe3f 94 function create_editing_form($submiturl, $question) {
36703ed7 95 global $CFG;
96 require_once("{$CFG->dirroot}/question/type/edit_question_form.php");
271ffe3f 97 $definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php';
36703ed7 98 if (!(is_readable($definition_file) && is_file($definition_file))) {
99 return null;
100 }
101 require_once($definition_file);
271ffe3f 102 $classname = 'question_edit_'.$this->name().'_form';
36703ed7 103 if (!class_exists($classname)) {
104 return null;
105 }
271ffe3f 106 return new $classname($submiturl, $question);
36703ed7 107 }
108
99ba746d 109 /**
110 * @return string the full path of the folder this plugin's files live in.
111 */
112 function plugin_dir() {
113 global $CFG;
114 return $CFG->dirroot . '/question/type/' . $this->name();
115 }
116
117 /**
118 * @return string the URL of the folder this plugin's files live in.
119 */
120 function plugin_baseurl() {
121 global $CFG;
122 return $CFG->wwwroot . '/question/type/' . $this->name();
123 }
124
7b41a4a9 125 /**
126 * This method should be overriden if you want to include a special heading or some other
127 * html on a question editing page besides the question editing form.
128 *
129 * @param question_edit_form $mform a child of question_edit_form
130 * @param object $question
131 * @param string $wizardnow is '' for first page.
132 */
133 function display_question_editing_page(&$mform, $question, $wizardnow){
c2f8c4be 134 $name = $this->name();
135 $strheading = get_string('editing' . $name, 'qtype_' . $name);
136 if ($strheading[0] == '[') {
137 // Legacy behavior, if the string was not in the proper qtype_name
138 // language file, look it up in the quiz one.
139 $strheading = get_string('editing' . $name, 'quiz');
140 }
7b41a4a9 141
c2f8c4be 142 print_heading_with_help($strheading, $name, 'quiz');
7b41a4a9 143 $mform->display();
7b41a4a9 144 }
145
36703ed7 146 /**
147 *
148 *
149 * @param $question
150 */
151 function set_default_options(&$question) {
36703ed7 152 }
153
516cf3eb 154 /**
155 * Saves or updates a question after editing by a teacher
156 *
157 * Given some question info and some data about the answers
158 * this function parses, organises and saves the question
159 * It is used by {@link question.php} when saving new data from
160 * a form, and also by {@link import.php} when importing questions
161 * This function in turn calls {@link save_question_options}
162 * to save question-type specific options
32b0adfa 163 * @param object $question the question object which should be updated
164 * @param object $form the form submitted by the teacher
165 * @param object $course the course we are in
695d225c 166 * @return object On success, return the new question object. On failure,
271ffe3f 167 * return an object as follows. If the error object has an errors field,
32b0adfa 168 * display that as an error message. Otherwise, the editing form will be
695d225c 169 * redisplayed with validation errors, from validation_errors field, which
170 * is itself an object, shown next to the form fields.
516cf3eb 171 */
172 function save_question($question, $form, $course) {
173 // This default implementation is suitable for most
174 // question types.
271ffe3f 175
516cf3eb 176 // First, save the basic question itself
88495aa8 177 if (!record_exists('question_categories', 'id', $form->category)) {
178 print_error('categorydoesnotexist', 'question');
179 }
6000365e 180 $question->category = $form->category;
516cf3eb 181 $question->name = trim($form->name);
182 $question->questiontext = trim($form->questiontext);
183 $question->questiontextformat = $form->questiontextformat;
184 $question->parent = isset($form->parent)? $form->parent : 0;
185 $question->length = $this->actual_number_of_questions($question);
186 $question->penalty = isset($form->penalty) ? $form->penalty : 0;
187
188 if (empty($form->image)) {
189 $question->image = "";
190 } else {
191 $question->image = $form->image;
192 }
193
a4514d91 194 if (empty($form->generalfeedback)) {
195 $question->generalfeedback = '';
1b8a7434 196 } else {
a4514d91 197 $question->generalfeedback = trim($form->generalfeedback);
1b8a7434 198 }
199
516cf3eb 200 if (empty($question->name)) {
5433ee83 201 $question->name = substr(strip_tags($question->questiontext), 0, 15);
516cf3eb 202 if (empty($question->name)) {
203 $question->name = '-';
204 }
205 }
206
207 if ($question->penalty > 1 or $question->penalty < 0) {
208 $question->errors['penalty'] = get_string('invalidpenalty', 'quiz');
209 }
210
211 if (isset($form->defaultgrade)) {
212 $question->defaultgrade = $form->defaultgrade;
213 }
214
516cf3eb 215 if (!empty($question->id)) { // Question already exists
cbe20043 216 // keep existing unique stamp code
217 $question->stamp = get_field('question', 'stamp', 'id', $question->id);
4f48fb42 218 if (!update_record("question", $question)) {
516cf3eb 219 error("Could not update question!");
220 }
221 } else { // Question is a new one
cbe20043 222 // Set the unique code
223 $question->stamp = make_unique_id_code();
4f48fb42 224 if (!$question->id = insert_record("question", $question)) {
516cf3eb 225 error("Could not insert new question!");
226 }
227 }
228
229 // Now to save all the answers and type-specific options
230
6459b8fc 231 $form->id = $question->id;
232 $form->qtype = $question->qtype;
516cf3eb 233 $form->category = $question->category;
6459b8fc 234 $form->questiontext = $question->questiontext;
516cf3eb 235
236 $result = $this->save_question_options($form);
237
238 if (!empty($result->error)) {
239 error($result->error);
240 }
241
242 if (!empty($result->notice)) {
243 notice($result->notice, "question.php?id=$question->id");
244 }
245
246 if (!empty($result->noticeyesno)) {
25ee4157 247 notice_yesno($result->noticeyesno, "question.php?id=$question->id&amp;courseid={$course->id}",
248 "edit.php?courseid={$course->id}");
516cf3eb 249 print_footer($course);
250 exit;
251 }
252
cbe20043 253 // Give the question a unique version stamp determined by question_hash()
254 if (!set_field('question', 'version', question_hash($question), 'id', $question->id)) {
255 error('Could not update question version field');
256 }
257
516cf3eb 258 return $question;
259 }
271ffe3f 260
516cf3eb 261 /**
262 * Saves question-type specific options
263 *
264 * This is called by {@link save_question()} to save the question-type specific data
265 * @return object $result->error or $result->noticeyesno or $result->notice
266 * @param object $question This holds the information from the editing form,
267 * it is not a standard question object.
268 */
269 function save_question_options($question) {
4eda4eec 270 return null;
516cf3eb 271 }
272
273 /**
274 * Changes all states for the given attempts over to a new question
275 *
276 * This is used by the versioning code if the teacher requests that a question
277 * gets replaced by the new version. In order for the attempts to be regraded
278 * properly all data in the states referring to the old question need to be
279 * changed to refer to the new version instead. In particular for question types
280 * that use the answers table the answers belonging to the old question have to
281 * be changed to those belonging to the new version.
282 *
283 * @param integer $oldquestionid The id of the old question
284 * @param object $newquestion The new question
285 * @param array $attempts An array of all attempt objects in whose states
286 * replacement should take place
287 */
288 function replace_question_in_attempts($oldquestionid, $newquestion, $attemtps) {
289 echo 'Not yet implemented';
290 return;
291 }
292
293 /**
294 * Loads the question type specific options for the question.
295 *
296 * This function loads any question type specific options for the
297 * question from the database into the question object. This information
298 * is placed in the $question->options field. A question type is
299 * free, however, to decide on a internal structure of the options field.
300 * @return bool Indicates success or failure.
301 * @param object $question The question object for the question. This object
302 * should be updated to include the question type
303 * specific information (it is passed by reference).
304 */
305 function get_question_options(&$question) {
306 if (!isset($question->options)) {
307 $question->options = new object;
308 }
309 // The default implementation attaches all answers for this question
f07d1d31 310 $question->options->answers = get_records('question_answers', 'question', $question->id, 'id ASC');
516cf3eb 311 return true;
312 }
313
314 /**
0429cd86 315 * Deletes states from the question-type specific tables
316 *
317 * @param string $stateslist Comma separated list of state ids to be deleted
318 */
319 function delete_states($stateslist) {
320 /// The default question type does not have any tables of its own
321 // therefore there is nothing to delete
322
323 return true;
324 }
325
326 /**
327 * Deletes a question from the question-type specific tables
516cf3eb 328 *
329 * @return boolean Success/Failure
330 * @param object $question The question being deleted
331 */
90c3f310 332 function delete_question($questionid) {
516cf3eb 333 /// The default question type does not have any tables of its own
334 // therefore there is nothing to delete
335
336 return true;
337 }
338
339 /**
340 * Returns the number of question numbers which are used by the question
341 *
342 * This function returns the number of question numbers to be assigned
343 * to the question. Most question types will have length one; they will be
dfa47f96 344 * assigned one number. The 'description' type, however does not use up a
516cf3eb 345 * number and so has a length of zero. Other question types may wish to
346 * handle a bundle of questions and hence return a number greater than one.
347 * @return integer The number of question numbers which should be
348 * assigned to the question.
349 * @param object $question The question whose length is to be determined.
350 * Question type specific information is included.
351 */
352 function actual_number_of_questions($question) {
353 // By default, each question is given one number
354 return 1;
355 }
356
357 /**
358 * Creates empty session and response information for the question
359 *
360 * This function is called to start a question session. Empty question type
361 * specific session data (if any) and empty response data will be added to the
362 * state object. Session data is any data which must persist throughout the
363 * attempt possibly with updates as the user interacts with the
364 * question. This function does NOT create new entries in the database for
365 * the session; a call to the {@link save_session_and_responses} member will
366 * occur to do this.
367 * @return bool Indicates success or failure.
368 * @param object $question The question for which the session is to be
369 * created. Question type specific information is
370 * included.
371 * @param object $state The state to create the session for. Note that
372 * this will not have been saved in the database so
373 * there will be no id. This object will be updated
374 * to include the question type specific information
375 * (it is passed by reference). In particular, empty
376 * responses will be created in the ->responses
377 * field.
378 * @param object $cmoptions
379 * @param object $attempt The attempt for which the session is to be
380 * started. Questions may wish to initialize the
381 * session in different ways depending on the user id
382 * or time available for the attempt.
383 */
384 function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
385 // The default implementation should work for the legacy question types.
386 // Most question types with only a single form field for the student's response
387 // will use the empty string '' as the index for that one response. This will
388 // automatically be stored in and restored from the answer field in the
4f48fb42 389 // question_states table.
5a14d563 390 $state->responses = array(
391 '' => '',
392 );
516cf3eb 393 return true;
394 }
395
396 /**
397 * Restores the session data and most recent responses for the given state
398 *
399 * This function loads any session data associated with the question
400 * session in the given state from the database into the state object.
401 * In particular it loads the responses that have been saved for the given
402 * state into the ->responses member of the state object.
403 *
404 * Question types with only a single form field for the student's response
405 * will not need not restore the responses; the value of the answer
4f48fb42 406 * field in the question_states table is restored to ->responses['']
516cf3eb 407 * before this function is called. Question types with more response fields
408 * should override this method and set the ->responses field to an
409 * associative array of responses.
410 * @return bool Indicates success or failure.
411 * @param object $question The question object for the question including any
412 * question type specific information.
413 * @param object $state The saved state to load the session for. This
414 * object should be updated to include the question
415 * type specific session information and responses
416 * (it is passed by reference).
417 */
418 function restore_session_and_responses(&$question, &$state) {
419 // The default implementation does nothing (successfully)
420 return true;
421 }
422
423 /**
424 * Saves the session data and responses for the given question and state
425 *
426 * This function saves the question type specific session data from the
427 * state object to the database. In particular for most question types it saves the
428 * responses from the ->responses member of the state object. The question type
4f48fb42 429 * non-specific data for the state has already been saved in the question_states
516cf3eb 430 * table and the state object contains the corresponding id and
431 * sequence number which may be used to index a question type specific table.
432 *
433 * Question types with only a single form field for the student's response
434 * which is contained in ->responses[''] will not have to save this response,
4f48fb42 435 * it will already have been saved to the answer field of the question_states table.
516cf3eb 436 * Question types with more response fields should override this method and save
437 * the responses in their own database tables.
438 * @return bool Indicates success or failure.
439 * @param object $question The question object for the question including
440 * the question type specific information.
441 * @param object $state The state for which the question type specific
442 * data and responses should be saved.
443 */
444 function save_session_and_responses(&$question, &$state) {
445 // The default implementation does nothing (successfully)
446 return true;
447 }
448
449 /**
450 * Returns an array of values which will give full marks if graded as
451 * the $state->responses field
452 *
453 * The correct answer to the question in the given state, or an example of
454 * a correct answer if there are many, is returned. This is used by some question
455 * types in the {@link grade_responses()} function but it is also used by the
456 * question preview screen to fill in correct responses.
457 * @return mixed A response array giving the responses corresponding
458 * to the (or a) correct answer to the question. If there is
459 * no correct answer that scores 100% then null is returned.
460 * @param object $question The question for which the correct answer is to
461 * be retrieved. Question type specific information is
462 * available.
463 * @param object $state The state of the question, for which a correct answer is
464 * needed. Question type specific information is included.
465 */
466 function get_correct_responses(&$question, &$state) {
467 /* The default implementation returns the response for the first answer
468 that gives full marks. */
4eda4eec 469 if ($question->options->answers) {
470 foreach ($question->options->answers as $answer) {
471 if (((int) $answer->fraction) === 1) {
1f8db780 472 return array('' => addslashes($answer->answer));
4eda4eec 473 }
516cf3eb 474 }
475 }
476 return null;
477 }
478
479 /**
480 * Return an array of values with the texts for all possible responses stored
481 * for the question
482 *
483 * All answers are found and their text values isolated
484 * @return object A mixed object
485 * ->id question id. Needed to manage random questions:
486 * it's the id of the actual question presented to user in a given attempt
487 * ->responses An array of values giving the responses corresponding
488 * to all answers to the question. Answer ids are used as keys.
489 * The text and partial credit are the object components
490 * @param object $question The question for which the answers are to
491 * be retrieved. Question type specific information is
492 * available.
493 */
494 // ULPGC ecastro
495 function get_all_responses(&$question, &$state) {
7baff8aa 496 if (isset($question->options->answers) && is_array($question->options->answers)) {
497 $answers = array();
516cf3eb 498 foreach ($question->options->answers as $aid=>$answer) {
7baff8aa 499 $r = new stdClass;
516cf3eb 500 $r->answer = $answer->answer;
501 $r->credit = $answer->fraction;
502 $answers[$aid] = $r;
503 }
7baff8aa 504 $result = new stdClass;
505 $result->id = $question->id;
506 $result->responses = $answers;
507 return $result;
516cf3eb 508 } else {
7baff8aa 509 return null;
516cf3eb 510 }
516cf3eb 511 }
512
513 /**
514 * Return the actual response to the question in a given state
515 * for the question
516 *
517 * @return mixed An array containing the response or reponses (multiple answer, match)
518 * given by the user in a particular attempt.
519 * @param object $question The question for which the correct answer is to
520 * be retrieved. Question type specific information is
521 * available.
522 * @param object $state The state object that corresponds to the question,
523 * for which a correct answer is needed. Question
524 * type specific information is included.
525 */
526 // ULPGC ecastro
8cc274de 527 function get_actual_response($question, $state) {
528 // change length to truncate responses here if you want
529 $lmax = 40;
530 if (!empty($state->responses)) {
531 $responses[] = (strlen($state->responses['']) > $lmax) ?
532 substr($state->responses[''], 0, $lmax).'...' : $state->responses[''];
533 } else {
534 $responses[] = '';
535 }
536 return $responses;
516cf3eb 537 }
538
539 // ULPGC ecastro
540 function get_fractional_grade(&$question, &$state) {
541 $maxgrade = $question->maxgrade;
542 $grade = $state->grade;
543 if ($maxgrade) {
544 return (float)($grade/$maxgrade);
545 } else {
546 return (float)$grade;
547 }
548 }
549
550
551 /**
552 * Checks if the response given is correct and returns the id
553 *
554 * @return int The ide number for the stored answer that matches the response
555 * given by the user in a particular attempt.
556 * @param object $question The question for which the correct answer is to
557 * be retrieved. Question type specific information is
558 * available.
559 * @param object $state The state object that corresponds to the question,
560 * for which a correct answer is needed. Question
561 * type specific information is included.
562 */
563 // ULPGC ecastro
564 function check_response(&$question, &$state){
565 return false;
566 }
567
568 /**
99ba746d 569 * If this question type requires extra CSS or JavaScript to function,
570 * then this method will return an array of <link ...> tags that reference
571 * those stylesheets. This function will also call require_js()
572 * from ajaxlib.php, to get any necessary JavaScript linked in too.
573 *
574 * The two parameters match the first two parameters of print_question.
575 *
576 * @param object $question The question object.
577 * @param object $state The state object.
578 *
579 * @return an array of bits of HTML to add to the head of pages where
580 * this question is print_question-ed in the body. The array should use
581 * integer array keys, which have no significance.
582 */
583 function get_html_head_contributions(&$question, &$state) {
584 // By default, we link to any of the files styles.css, styles.php,
585 // script.js or script.php that exist in the plugin folder.
586 // Core question types should not use this mechanism. Their styles
587 // should be included in the standard theme.
588
589 // We only do this once
590 // for this question type, no matter how often this method is called.
591 static $already_done = false;
592 if ($already_done) {
593 return array();
594 }
595 $already_done = true;
596
597 $plugindir = $this->plugin_dir();
598 $baseurl = $this->plugin_baseurl();
599 $stylesheets = array();
600 if (file_exists($plugindir . '/styles.css')) {
601 $stylesheets[] = 'styles.css';
602 }
603 if (file_exists($plugindir . '/styles.php')) {
604 $stylesheets[] = 'styles.php';
605 }
606 if (file_exists($plugindir . '/script.js')) {
607 require_js($baseurl . '/script.js');
608 }
609 if (file_exists($plugindir . '/script.php')) {
610 require_js($baseurl . '/script.php');
611 }
612 $contributions = array();
613 foreach ($stylesheets as $stylesheet) {
614 $contributions[] = '<link rel="stylesheet" type="text/css" href="' .
615 $baseurl . '/' . $stylesheet . '" />"';
616 }
617 return $contributions;
618 }
619
620 /**
621 * Prints the question including the number, grading details, content,
622 * feedback and interactions
623 *
624 * This function prints the question including the question number,
625 * grading details, content for the question, any feedback for the previously
626 * submitted responses and the interactions. The default implementation calls
627 * various other methods to print each of these parts and most question types
628 * will just override those methods.
629 * @param object $question The question to be rendered. Question type
630 * specific information is included. The
631 * maximum possible grade is in ->maxgrade. The name
632 * prefix for any named elements is in ->name_prefix.
633 * @param object $state The state to render the question in. The grading
634 * information is in ->grade, ->raw_grade and
635 * ->penalty. The current responses are in
636 * ->responses. This is an associative array (or the
637 * empty string or null in the case of no responses
638 * submitted). The last graded state is in
639 * ->last_graded (hence the most recently graded
640 * responses are in ->last_graded->responses). The
641 * question type specific information is also
642 * included.
643 * @param integer $number The number for this question.
644 * @param object $cmoptions
645 * @param object $options An object describing the rendering options.
646 */
516cf3eb 647 function print_question(&$question, &$state, $number, $cmoptions, $options) {
648 /* The default implementation should work for most question types
649 provided the member functions it calls are overridden where required.
37a12367 650 The layout is determined by the template question.html */
271ffe3f 651
516cf3eb 652 global $CFG;
1b8a7434 653 $isgraded = question_state_is_graded($state->last_graded);
516cf3eb 654
647bcd41 655 // If this question is being shown in the context of a quiz
656 // get the context so we can determine whether some extra links
271ffe3f 657 // should be shown. (Don't show these links during question preview.)
d758f1e2 658 $cm = get_coursemodule_from_instance('quiz', $cmoptions->id);
647bcd41 659 if (!empty($cm->id)) {
660 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
ec6adca0 661 } else if (!empty($cm->course)) {
662 $context = get_context_instance(CONTEXT_COURSE, $cm->course);
647bcd41 663 } else {
ec6adca0 664 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
647bcd41 665 }
271ffe3f 666
516cf3eb 667 // For editing teachers print a link to an editing popup window
668 $editlink = '';
ec6adca0 669 if ($context && has_capability('moodle/question:manage', $context)) {
516cf3eb 670 $stredit = get_string('edit');
0fd5feef 671 $linktext = '<img src="'.$CFG->pixpath.'/t/edit.gif" alt="'.$stredit.'" />';
ee6c9355 672 $editlink = link_to_popup_window('/question/question.php?inpopup=1&amp;id='.$question->id, 'editquestion', $linktext, 450, 550, $stredit, '', true);
516cf3eb 673 }
674
a4514d91 675 $generalfeedback = '';
54074fb8 676 if ($options->generalfeedback) {
a4514d91 677 $generalfeedback = $this->format_text($question->generalfeedback,
1b8a7434 678 $question->questiontextformat, $cmoptions);
679 }
680
516cf3eb 681 $grade = '';
682 if ($question->maxgrade and $options->scores) {
6b11a0e8 683 if ($cmoptions->optionflags & QUESTION_ADAPTIVE) {
1b8a7434 684 $grade = !$isgraded ? '--/' : round($state->last_graded->grade, $cmoptions->decimalpoints).'/';
516cf3eb 685 }
686 $grade .= $question->maxgrade;
687 }
271ffe3f 688
3e3e5a40 689 $comment = stripslashes($state->manualcomment);
b6e907a2 690 $commentlink = '';
271ffe3f 691
647bcd41 692 if (isset($options->questioncommentlink) && $context && has_capability('mod/quiz:grade', $context)) {
b6e907a2 693 $strcomment = get_string('commentorgrade', 'quiz');
097bd3f5 694 $commentlink = '<div class="commentlink">'.link_to_popup_window ($options->questioncommentlink.'?attempt='.$state->attempt.'&amp;question='.$question->id,
695 'commentquestion', $strcomment, 450, 650, $strcomment, 'none', true).'</div>';
b6e907a2 696 }
516cf3eb 697
fe9b5cfd 698 $history = $this->history($question, $state, $number, $cmoptions, $options);
699
aaae75b0 700 include "$CFG->dirroot/question/type/question.html";
fe9b5cfd 701 }
702
703 /*
704 * Print history of responses
705 *
706 * Used by print_question()
707 */
708 function history($question, $state, $number, $cmoptions, $options) {
516cf3eb 709 $history = '';
710 if(isset($options->history) and $options->history) {
711 if ($options->history == 'all') {
712 // show all states
755bddf1 713 $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event > '0'", 'seq_number ASC');
516cf3eb 714 } else {
715 // show only graded states
755bddf1 716 $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTGRADE.','.QUESTION_EVENTCLOSEANDGRADE.")", 'seq_number ASC');
516cf3eb 717 }
718 if (count($states) > 1) {
719 $strreviewquestion = get_string('reviewresponse', 'quiz');
1b8a7434 720 $table = new stdClass;
516cf3eb 721 $table->width = '100%';
d57cf4c1 722 if ($options->scores) {
723 $table->head = array (
724 get_string('numberabbr', 'quiz'),
725 get_string('action', 'quiz'),
726 get_string('response', 'quiz'),
727 get_string('time'),
728 get_string('score', 'quiz'),
729 //get_string('penalty', 'quiz'),
730 get_string('grade', 'quiz'),
731 );
732 } else {
733 $table->head = array (
734 get_string('numberabbr', 'quiz'),
735 get_string('action', 'quiz'),
736 get_string('response', 'quiz'),
737 get_string('time'),
738 );
3925a20a 739 }
271ffe3f 740
516cf3eb 741 foreach ($states as $st) {
0a5b58af 742 $st->responses[''] = $st->answer;
743 $this->restore_session_and_responses($question, $st);
516cf3eb 744 $b = ($state->id == $st->id) ? '<b>' : '';
745 $be = ($state->id == $st->id) ? '</b>' : '';
fe9b5cfd 746 if ($state->id == $st->id) {
747 $link = '<b>'.$st->seq_number.'</b>';
748 } else {
749 if(isset($options->questionreviewlink)) {
750 $link = link_to_popup_window ($options->questionreviewlink.'?state='.$st->id.'&amp;number='.$number,
751 'reviewquestion', $st->seq_number, 450, 650, $strreviewquestion, 'none', true);
752 } else {
753 $link = $st->seq_number;
754 }
755 }
d57cf4c1 756 if ($options->scores) {
757 $table->data[] = array (
758 $link,
759 $b.get_string('event'.$st->event, 'quiz').$be,
760 $b.$this->response_summary($question, $st).$be,
761 $b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
762 $b.round($st->raw_grade, $cmoptions->decimalpoints).$be,
763 //$b.round($st->penalty, $cmoptions->decimalpoints).$be,
764 $b.round($st->grade, $cmoptions->decimalpoints).$be
765 );
766 } else {
767 $table->data[] = array (
768 $link,
769 $b.get_string('event'.$st->event, 'quiz').$be,
770 $b.$this->response_summary($question, $st).$be,
771 $b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
772 );
773 }
516cf3eb 774 }
775 $history = make_table($table);
776 }
777 }
fe9b5cfd 778 return $history;
516cf3eb 779 }
780
781
782 /**
783 * Prints the score obtained and maximum score available plus any penalty
784 * information
785 *
786 * This function prints a summary of the scoring in the most recently
787 * graded state (the question may not have been submitted for marking at
788 * the current state). The default implementation should be suitable for most
789 * question types.
790 * @param object $question The question for which the grading details are
791 * to be rendered. Question type specific information
792 * is included. The maximum possible grade is in
793 * ->maxgrade.
794 * @param object $state The state. In particular the grading information
795 * is in ->grade, ->raw_grade and ->penalty.
796 * @param object $cmoptions
797 * @param object $options An object describing the rendering options.
798 */
799 function print_question_grading_details(&$question, &$state, $cmoptions, $options) {
800 /* The default implementation prints the number of marks if no attempt
801 has been made. Otherwise it displays the grade obtained out of the
802 maximum grade available and a warning if a penalty was applied for the
803 attempt and displays the overall grade obtained counting all previous
804 responses (and penalties) */
805
f30bbcaf 806 if (QUESTION_EVENTDUPLICATE == $state->event) {
516cf3eb 807 echo ' ';
808 print_string('duplicateresponse', 'quiz');
809 }
810 if (!empty($question->maxgrade) && $options->scores) {
f30bbcaf 811 if (question_state_is_graded($state->last_graded)) {
516cf3eb 812 // Display the grading details from the last graded state
1b8a7434 813 $grade = new stdClass;
516cf3eb 814 $grade->cur = round($state->last_graded->grade, $cmoptions->decimalpoints);
815 $grade->max = $question->maxgrade;
816 $grade->raw = round($state->last_graded->raw_grade, $cmoptions->decimalpoints);
817
818 // let student know wether the answer was correct
819 echo '<div class="correctness ';
820 if ($state->last_graded->raw_grade >= $question->maxgrade/1.01) { // We divide by 1.01 so that rounding errors dont matter.
821 echo ' correct">';
822 print_string('correct', 'quiz');
823 } else if ($state->last_graded->raw_grade > 0) {
824 echo ' partiallycorrect">';
825 print_string('partiallycorrect', 'quiz');
826 } else {
827 echo ' incorrect">';
828 print_string('incorrect', 'quiz');
829 }
830 echo '</div>';
831
832 echo '<div class="gradingdetails">';
833 // print grade for this submission
834 print_string('gradingdetails', 'quiz', $grade);
835 if ($cmoptions->penaltyscheme) {
836 // print details of grade adjustment due to penalties
837 if ($state->last_graded->raw_grade > $state->last_graded->grade){
2962ad61 838 echo ' ';
516cf3eb 839 print_string('gradingdetailsadjustment', 'quiz', $grade);
840 }
841 // print info about new penalty
842 // penalty is relevant only if the answer is not correct and further attempts are possible
c7a99084 843 if (($state->last_graded->raw_grade < $question->maxgrade / 1.01)
844 and (QUESTION_EVENTCLOSEANDGRADE !== $state->event)) {
845
516cf3eb 846 if ('' !== $state->last_graded->penalty && ((float)$state->last_graded->penalty) > 0.0) {
847 // A penalty was applied so display it
2962ad61 848 echo ' ';
516cf3eb 849 print_string('gradingdetailspenalty', 'quiz', $state->last_graded->penalty);
850 } else {
851 /* No penalty was applied even though the answer was
852 not correct (eg. a syntax error) so tell the student
853 that they were not penalised for the attempt */
2962ad61 854 echo ' ';
516cf3eb 855 print_string('gradingdetailszeropenalty', 'quiz');
856 }
857 }
858 }
859 echo '</div>';
860 }
861 }
862 }
863
864 /**
865 * Prints the main content of the question including any interactions
866 *
867 * This function prints the main content of the question including the
868 * interactions for the question in the state given. The last graded responses
869 * are printed or indicated and the current responses are selected or filled in.
870 * Any names (eg. for any form elements) are prefixed with $question->name_prefix.
871 * This method is called from the print_question method.
872 * @param object $question The question to be rendered. Question type
873 * specific information is included. The name
874 * prefix for any named elements is in ->name_prefix.
875 * @param object $state The state to render the question in. The grading
876 * information is in ->grade, ->raw_grade and
877 * ->penalty. The current responses are in
878 * ->responses. This is an associative array (or the
879 * empty string or null in the case of no responses
880 * submitted). The last graded state is in
881 * ->last_graded (hence the most recently graded
882 * responses are in ->last_graded->responses). The
883 * question type specific information is also
884 * included.
885 * The state is passed by reference because some adaptive
886 * questions may want to update it during rendering
887 * @param object $cmoptions
888 * @param object $options An object describing the rendering options.
889 */
890 function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
891 /* This default implementation prints an error and must be overridden
892 by all question type implementations, unless the default implementation
893 of print_question has been overridden. */
894
895 notify('Error: Question formulation and input controls has not'
896 .' been implemented for question type '.$this->name());
897 }
898
899 /**
900 * Prints the submit button(s) for the question in the given state
901 *
902 * This function prints the submit button(s) for the question in the
903 * given state. The name of any button created will be prefixed with the
904 * unique prefix for the question in $question->name_prefix. The suffix
4dca7e51 905 * 'submit' is reserved for the single question submit button and the suffix
516cf3eb 906 * 'validate' is reserved for the single question validate button (for
907 * question types which support it). Other suffixes will result in a response
908 * of that name in $state->responses which the printing and grading methods
909 * can then use.
910 * @param object $question The question for which the submit button(s) are to
911 * be rendered. Question type specific information is
912 * included. The name prefix for any
913 * named elements is in ->name_prefix.
914 * @param object $state The state to render the buttons for. The
915 * question type specific information is also
916 * included.
917 * @param object $cmoptions
918 * @param object $options An object describing the rendering options.
919 */
920 function print_question_submit_buttons(&$question, &$state, $cmoptions, $options) {
921 /* The default implementation should be suitable for most question
922 types. It prints a mark button in the case where individual marking is
923 allowed. */
924
6b11a0e8 925 if (($cmoptions->optionflags & QUESTION_ADAPTIVE) and !$options->readonly) {
516cf3eb 926 echo '<input type="submit" name="';
927 echo $question->name_prefix;
4dca7e51 928 echo 'submit" value="';
516cf3eb 929 print_string('mark', 'quiz');
5bc57211 930 echo '" class="submit btn"';
931 echo ' />';
516cf3eb 932 }
933 }
934
935
936 /**
937 * Return a summary of the student response
938 *
939 * This function returns a short string of no more than a given length that
940 * summarizes the student's response in the given $state. This is used for
941 * example in the response history table
942 * @return string The summary of the student response
271ffe3f 943 * @param object $question
516cf3eb 944 * @param object $state The state whose responses are to be summarized
945 * @param int $length The maximum length of the returned string
946 */
0a5b58af 947 function response_summary($question, $state, $length=80) {
516cf3eb 948 // This should almost certainly be overridden
879caa51 949 $responses = $this->get_actual_response($question, $state);
950 if (empty($responses) || !is_array($responses)) {
951 $responses = array();
952 }
953 if (is_array($responses)) {
954 $responses = implode(',', $responses);
955 }
956 return substr($responses, 0, $length);
516cf3eb 957 }
958
959 /**
960 * Renders the question for printing and returns the LaTeX source produced
961 *
962 * This function should render the question suitable for a printed problem
963 * or solution sheet in LaTeX and return the rendered output.
964 * @return string The LaTeX output.
965 * @param object $question The question to be rendered. Question type
966 * specific information is included.
967 * @param object $state The state to render the question in. The
968 * question type specific information is also
969 * included.
970 * @param object $cmoptions
971 * @param string $type Indicates if the question or the solution is to be
972 * rendered with the values 'question' and
973 * 'solution'.
974 */
975 function get_texsource(&$question, &$state, $cmoptions, $type) {
976 // The default implementation simply returns a string stating that
977 // the question is only available online.
978
979 return get_string('onlineonly', 'texsheet');
980 }
981
982 /**
983 * Compares two question states for equivalence of the student's responses
984 *
985 * The responses for the two states must be examined to see if they represent
986 * equivalent answers to the question by the student. This method will be
987 * invoked for each of the previous states of the question before grading
988 * occurs. If the student is found to have already attempted the question
989 * with equivalent responses then the attempt at the question is ignored;
990 * grading does not occur and the state does not change. Thus they are not
991 * penalized for this case.
992 * @return boolean
993 * @param object $question The question for which the states are to be
994 * compared. Question type specific information is
995 * included.
996 * @param object $state The state of the question. The responses are in
ac249da5 997 * ->responses. This is the only field of $state
998 * that it is safe to use.
516cf3eb 999 * @param object $teststate The state whose responses are to be
1000 * compared. The state will be of the same age or
271ffe3f 1001 * older than $state. If possible, the method should
bb080d20 1002 * only use the field $teststate->responses, however
1003 * any field that is set up by restore_session_and_responses
1004 * can be used.
516cf3eb 1005 */
1006 function compare_responses(&$question, $state, $teststate) {
1007 // The default implementation performs a comparison of the response
1008 // arrays. The ordering of the arrays does not matter.
1009 // Question types may wish to override this (eg. to ignore trailing
1010 // white space or to make "7.0" and "7" compare equal).
271ffe3f 1011
c82f76d0 1012 return $state->responses === $teststate->responses;
516cf3eb 1013 }
1014
37a12367 1015 /**
1016 * Checks whether a response matches a given answer
1017 *
1018 * This method only applies to questions that use teacher-defined answers
1019 *
1020 * @return boolean
1021 */
1022 function test_response(&$question, &$state, $answer) {
1023 $response = isset($state->responses['']) ? $state->responses[''] : '';
1024 return ($response == $answer->answer);
1025 }
1026
516cf3eb 1027 /**
1028 * Performs response processing and grading
1029 *
1030 * This function performs response processing and grading and updates
1031 * the state accordingly.
1032 * @return boolean Indicates success or failure.
1033 * @param object $question The question to be graded. Question type
1034 * specific information is included.
1035 * @param object $state The state of the question to grade. The current
1036 * responses are in ->responses. The last graded state
1037 * is in ->last_graded (hence the most recently graded
1038 * responses are in ->last_graded->responses). The
1039 * question type specific information is also
1040 * included. The ->raw_grade and ->penalty fields
1041 * must be updated. The method is able to
1042 * close the question session (preventing any further
1043 * attempts at this question) by setting
f30bbcaf 1044 * $state->event to QUESTION_EVENTCLOSEANDGRADE
516cf3eb 1045 * @param object $cmoptions
1046 */
1047 function grade_responses(&$question, &$state, $cmoptions) {
5a14d563 1048 // The default implementation uses the test_response method to
1049 // compare what the student entered against each of the possible
271ffe3f 1050 // answers stored in the question, and uses the grade from the
5a14d563 1051 // first one that matches. It also sets the marks and penalty.
1052 // This should be good enought for most simple question types.
516cf3eb 1053
5a14d563 1054 $state->raw_grade = 0;
516cf3eb 1055 foreach($question->options->answers as $answer) {
5a14d563 1056 if($this->test_response($question, $state, $answer)) {
1057 $state->raw_grade = $answer->fraction;
516cf3eb 1058 break;
1059 }
1060 }
5a14d563 1061
1062 // Make sure we don't assign negative or too high marks.
1063 $state->raw_grade = min(max((float) $state->raw_grade,
1064 0.0), 1.0) * $question->maxgrade;
271ffe3f 1065
5a14d563 1066 // Update the penalty.
1067 $state->penalty = $question->penalty * $question->maxgrade;
516cf3eb 1068
f30bbcaf 1069 // mark the state as graded
1070 $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
1071
516cf3eb 1072 return true;
1073 }
1074
1075
1076 /**
1077 * Includes configuration settings for the question type on the quiz admin
1078 * page
1079 *
7518b645 1080 * TODO: It makes no sense any longer to do the admin for question types
1081 * from the quiz admin page. This should be changed.
516cf3eb 1082 * Returns an array of objects describing the options for the question type
1083 * to be included on the quiz module admin page.
1084 * Configuration options can be included by setting the following fields in
1085 * the object:
1086 * ->name The name of the option within this question type.
1087 * The full option name will be constructed as
1088 * "quiz_{$this->name()}_$name", the human readable name
1089 * will be displayed with get_string($name, 'quiz').
1090 * ->code The code to display the form element, help button, etc.
1091 * i.e. the content for the central table cell. Be sure
1092 * to name the element "quiz_{$this->name()}_$name" and
1093 * set the value to $CFG->{"quiz_{$this->name()}_$name"}.
1094 * ->help Name of the string from the quiz module language file
1095 * to be used for the help message in the third column of
1096 * the table. An empty string (or the field not set)
1097 * means to leave the box empty.
1098 * Links to custom settings pages can be included by setting the following
1099 * fields in the object:
1100 * ->name The name of the link text string.
1101 * get_string($name, 'quiz') will be called.
1102 * ->link The filename part of the URL for the link. The full URL
1103 * is contructed as
aaae75b0 1104 * "$CFG->wwwroot/question/type/{$this->name()}/$link?sesskey=$sesskey"
516cf3eb 1105 * [but with the relavant calls to the s and rawurlencode
1106 * functions] where $sesskey is the sesskey for the user.
1107 * @return array Array of objects describing the configuration options to
1108 * be included on the quiz module admin page.
1109 */
1110 function get_config_options() {
1111 // No options by default
1112
1113 return false;
1114 }
1115
1116 /**
dc1f00de 1117 * Returns true if the editing wizard is finished, false otherwise.
1118 *
1119 * The default implementation returns true, which is suitable for all question-
516cf3eb 1120 * types that only use one editing form. This function is used in
1121 * question.php to decide whether we can regrade any states of the edited
1122 * question and redirect to edit.php.
1123 *
1124 * The dataset dependent question-type, which is extended by the calculated
1125 * question-type, overwrites this method because it uses multiple pages (i.e.
1126 * a wizard) to set up the question and associated datasets.
1127 *
1128 * @param object $form The data submitted by the previous page.
1129 *
1130 * @return boolean Whether the wizard's last page was submitted or not.
1131 */
1132 function finished_edit_wizard(&$form) {
1133 //In the default case there is only one edit page.
1134 return true;
1135 }
1136
dc1f00de 1137 /**
1138 * Prints a table of course modules in which the question is used
1139 *
7518b645 1140 * TODO: This should be made quiz-independent
1141 *
dc1f00de 1142 * This function is used near the end of the question edit forms in all question types
1143 * It prints the table of quizzes in which the question is used
1144 * containing checkboxes to allow the teacher to replace the old question version
1145 *
1146 * @param object $question
1147 * @param object $course
1148 * @param integer $cmid optional The id of the course module currently being edited
1149 */
1150 function print_replacement_options($question, $course, $cmid='0') {
516cf3eb 1151
1152 // Disable until the versioning code has been fixed
20808266 1153 if (true) {
1154 return;
1155 }
271ffe3f 1156
516cf3eb 1157 // no need to display replacement options if the question is new
1158 if(empty($question->id)) {
1159 return true;
1160 }
1161
1162 // get quizzes using the question (using the question_instances table)
1163 $quizlist = array();
1164 if(!$instances = get_records('quiz_question_instances', 'question', $question->id)) {
1165 $instances = array();
1166 }
1167 foreach($instances as $instance) {
1168 $quizlist[$instance->quiz] = $instance->quiz;
1169 }
1170 $quizlist = implode(',', $quizlist);
1171 if(empty($quizlist) or !$quizzes = get_records_list('quiz', 'id', $quizlist)) {
1172 $quizzes = array();
1173 }
1174
1175 // do the printing
1176 if(count($quizzes) > 0) {
1177 // print the table
1178 $strquizname = get_string('modulename', 'quiz');
1179 $strdoreplace = get_string('replace', 'quiz');
1180 $straffectedstudents = get_string('affectedstudents', 'quiz', $course->students);
1181 echo "<tr valign=\"top\">\n";
1182 echo "<td align=\"right\"><b>".get_string("replacementoptions", "quiz").":</b></td>\n";
1183 echo "<td align=\"left\">\n";
1184 echo "<table cellpadding=\"5\" align=\"left\" class=\"generalbox\" width=\"100%\">\n";
1185 echo "<tr>\n";
077f3814 1186 echo "<th align=\"left\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\" scope=\"col\">$strquizname</th>\n";
1187 echo "<th align=\"center\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\" scope=\"col\">$strdoreplace</th>\n";
1188 echo "<th align=\"left\" valign=\"top\" nowrap=\"nowrap\" class=\"generaltableheader c0\" scope=\"col\">$straffectedstudents</th>\n";
516cf3eb 1189 echo "</tr>\n";
1190 foreach($quizzes as $quiz) {
1191 // work out whethere it should be checked by default
1192 $checked = '';
dc1f00de 1193 if((int)$cmid === (int)$quiz->id
516cf3eb 1194 or empty($quiz->usercount)) {
1195 $checked = "checked=\"checked\"";
1196 }
1197
1198 // find how many different students have already attempted this quiz
1199 $students = array();
1200 if($attempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'")) {
1201 foreach($attempts as $attempt) {
4f48fb42 1202 if (record_exists('question_states', 'attempt', $attempt->uniqueid, 'question', $question->id, 'originalquestion', 0)) {
516cf3eb 1203 $students[$attempt->userid] = 1;
1204 }
1205 }
1206 }
1207 $studentcount = count($students);
1208
1209 $strstudents = $studentcount === 1 ? $course->student : $course->students;
1210 echo "<tr>\n";
1211 echo "<td align=\"left\" class=\"generaltablecell c0\">".format_string($quiz->name)."</td>\n";
1212 echo "<td align=\"center\" class=\"generaltablecell c0\"><input name=\"q{$quiz->id}replace\" type=\"checkbox\" ".$checked." /></td>\n";
1213 echo "<td align=\"left\" class=\"generaltablecell c0\">".(($studentcount) ? $studentcount.' '.$strstudents : '-')."</td>\n";
1214 echo "</tr>\n";
1215 }
1216 echo "</table>\n";
1217 }
1218 echo "</td></tr>\n";
1219 }
1220
1b8a7434 1221 /**
1222 * Print the start of the question editing form, including the question category,
a4514d91 1223 * questionname, questiontext, image, defaultgrade, penalty and generalfeedback fields.
271ffe3f 1224 *
1b8a7434 1225 * Three of the fields, image, defaultgrade, penalty, are optional, and
271ffe3f 1226 * can be removed from the from using the $hidefields argument.
1227 *
1b8a7434 1228 * @param object $question The question object that the form we are printing is for.
1229 * @param array $err Array of optional error messages to display by each field.
1230 * Used when the form is being redisplayed after validation failed.
1231 * @param object $course The course object for the course this question belongs to.
1232 * @param boolean $usehtmleditor Whether the html editor should be used.
1233 * @param array $hidefields An array which may contain the strings,
1234 * 'image', 'defaultgrade' or 'penalty' to remove the corresponding field.
1235 */
1236 function print_question_form_start($question, $err, $course, $usehtmleditor, $hidefields = array()) {
1237 global $CFG;
1238
1239 // If you edit this function, you also need to edit random/editquestion.html.
271ffe3f 1240
1b8a7434 1241 if (!in_array('image', $hidefields)) {
1242 make_upload_directory("$course->id"); // Just in case
1243 $coursefiles = get_directory_list("$CFG->dataroot/$course->id", $CFG->moddata);
1244 foreach ($coursefiles as $filename) {
1245 if (mimeinfo("icon", $filename) == "image.gif") {
1246 $images["$filename"] = $filename;
1247 }
1248 }
516cf3eb 1249 }
271ffe3f 1250
1b8a7434 1251 include('editquestionstart.html');
1252 }
271ffe3f 1253
1b8a7434 1254 /**
1255 * Print the end of the question editing form, including the submit, copy,
1256 * and cancel button, and the standard hidden fields like the sesskey and
1257 * the question type.
271ffe3f 1258 *
1b8a7434 1259 * @param object $question The question object that the form we are printing is for.
1260 * @param string $submitscript Extra attributes, for example 'onsubmit="myfunction"',
1261 * that is added to the HTML of the submit button.
1262 * @param string $hiddenfields Extra hidden fields (actually any HTML)
1263 * to be added at the end of the form.
1264 */
1265 function print_question_form_end($question, $submitscript = '', $hiddenfields = '') {
1266 global $USER;
271ffe3f 1267
1b8a7434 1268 // If you edit this function, you also need to edit random/editquestion.html.
1269
1270 include('editquestionend.html');
1271 }
271ffe3f 1272
1b8a7434 1273 /**
1274 * Call format_text from weblib.php with the options appropriate to question types.
271ffe3f 1275 *
1b8a7434 1276 * @param string $text the text to format.
1277 * @param integer $text the type of text. Normally $question->questiontextformat.
1278 * @param object $cmoptions the context the string is being displayed in. Only $cmoptions->course is used.
1279 * @return string the formatted text.
1280 */
08eef20d 1281 function format_text($text, $textformat, $cmoptions = NULL) {
1b8a7434 1282 $formatoptions = new stdClass;
1283 $formatoptions->noclean = true;
1284 $formatoptions->para = false;
08eef20d 1285 return format_text($text, $textformat, $formatoptions, $cmoptions === NULL ? NULL : $cmoptions->course);
516cf3eb 1286 }
271ffe3f 1287
c5d94c41 1288/// BACKUP FUNCTIONS ////////////////////////////
1289
1290 /*
1291 * Backup the data in the question
1292 *
1293 * This is used in question/backuplib.php
1294 */
1295 function backup($bf,$preferences,$question,$level=6) {
1296 // The default type has nothing to back up
1297 return true;
1298 }
1299
315559d3 1300/// RESTORE FUNCTIONS /////////////////
1301
1302 /*
1303 * Restores the data in the question
1304 *
1305 * This is used in question/restorelib.php
1306 */
1307 function restore($old_question_id,$new_question_id,$info,$restore) {
1308 // The default question type has nothing to restore
1309 return true;
1310 }
1311
1312 function restore_map($old_question_id,$new_question_id,$info,$restore) {
1313 // There is nothing to decode
1314 return true;
1315 }
1316
1317 function restore_recode_answer($state, $restore) {
1318 // There is nothing to decode
ba8f7ff0 1319 return $state->answer;
271ffe3f 1320 }
315559d3 1321
1322 //This function restores the question_rqp_states
1323 function restore_state($state_id,$info,$restore) {
1324 // The default question type does not keep its own state information
1325 return true;
1326 }
516cf3eb 1327
1328}
1329
96ed722b 1330?>