Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/type/questiontypebase.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 require_once($CFG->dirroot . '/question/engine/lib.php');
00030 
00031 
00048 class question_type {
00049     protected $fileoptions = array(
00050         'subdirs' => false,
00051         'maxfiles' => -1,
00052         'maxbytes' => 0,
00053     );
00054 
00055     public function __construct() {
00056     }
00057 
00061     public function name() {
00062         return substr(get_class($this), 6);
00063     }
00064 
00068     public function plugin_name() {
00069         return get_class($this);
00070     }
00071 
00076     public function local_name() {
00077         if (get_string_manager()->string_exists('pluginname', $this->plugin_name())) {
00078             return get_string('pluginname', $this->plugin_name());
00079         } else {
00080             return get_string($this->name(), $this->plugin_name());
00081         }
00082     }
00083 
00092     public function menu_name() {
00093         return $this->local_name();
00094     }
00095 
00101     public function is_real_question_type() {
00102         return true;
00103     }
00104 
00108     public function is_manual_graded() {
00109         return false;
00110     }
00111 
00117     public function is_question_manual_graded($question, $otherquestionsinuse) {
00118         return $this->is_manual_graded();
00119     }
00120 
00124     public function is_usable_by_random() {
00125         return true;
00126     }
00127 
00139     public function can_analyse_responses() {
00140         // This works in most cases.
00141         return !$this->is_manual_graded();
00142     }
00143 
00148     public function has_html_answers() {
00149         return false;
00150     }
00151 
00160     public function extra_question_fields() {
00161         return null;
00162     }
00163 
00168     public function questionid_column_name() {
00169         return 'questionid';
00170     }
00171 
00179     public function extra_answer_fields() {
00180         return null;
00181     }
00182 
00190     public function response_file_areas() {
00191         return array();
00192     }
00193 
00203     public function create_editing_form($submiturl, $question, $category,
00204             $contexts, $formeditable) {
00205         global $CFG;
00206         require_once($CFG->dirroot . '/question/type/edit_question_form.php');
00207         $definitionfile = $CFG->dirroot . '/question/type/' . $this->name() .
00208                 '/edit_' . $this->name() . '_form.php';
00209         if (!is_readable($definitionfile) || !is_file($definitionfile)) {
00210             throw new coding_exception($this->plugin_name() .
00211                     ' is missing the definition of its editing formin file ' .
00212                     $definitionfile . '.');
00213         }
00214         require_once($definitionfile);
00215         $classname = $this->plugin_name() . '_edit_form';
00216         if (!class_exists($classname)) {
00217             throw new coding_exception($this->plugin_name() .
00218                     ' does not define the class ' . $this->plugin_name() .
00219                     '_edit_form.');
00220         }
00221         return new $classname($submiturl, $question, $category, $contexts, $formeditable);
00222     }
00223 
00227     public function plugin_dir() {
00228         global $CFG;
00229         return $CFG->dirroot . '/question/type/' . $this->name();
00230     }
00231 
00235     public function plugin_baseurl() {
00236         global $CFG;
00237         return $CFG->wwwroot . '/question/type/' . $this->name();
00238     }
00239 
00248     public function display_question_editing_page($mform, $question, $wizardnow) {
00249         global $OUTPUT;
00250         $heading = $this->get_heading(empty($question->id));
00251 
00252         if (get_string_manager()->string_exists('pluginname_help', $this->plugin_name())) {
00253             echo $OUTPUT->heading_with_help($heading, 'pluginname', $this->plugin_name());
00254         } else {
00255             echo $OUTPUT->heading_with_help($heading, $this->name(), $this->plugin_name());
00256         }
00257 
00258         $permissionstrs = array();
00259         if (!empty($question->id)) {
00260             if ($question->formoptions->canedit) {
00261                 $permissionstrs[] = get_string('permissionedit', 'question');
00262             }
00263             if ($question->formoptions->canmove) {
00264                 $permissionstrs[] = get_string('permissionmove', 'question');
00265             }
00266             if ($question->formoptions->cansaveasnew) {
00267                 $permissionstrs[] = get_string('permissionsaveasnew', 'question');
00268             }
00269         }
00270         if (!$question->formoptions->movecontext  && count($permissionstrs)) {
00271             echo $OUTPUT->heading(get_string('permissionto', 'question'), 3);
00272             $html = '<ul>';
00273             foreach ($permissionstrs as $permissionstr) {
00274                 $html .= '<li>'.$permissionstr.'</li>';
00275             }
00276             $html .= '</ul>';
00277             echo $OUTPUT->box($html, 'boxwidthnarrow boxaligncenter generalbox');
00278         }
00279         $mform->display();
00280     }
00281 
00288     public function get_heading($adding = false) {
00289         if ($adding) {
00290             $string = 'pluginnameadding';
00291             $fallback = 'adding' . $this->name();
00292         } else {
00293             $string = 'pluginnameediting';
00294             $fallback = 'editing' . $this->name();
00295         }
00296         if (get_string_manager()->string_exists($string, $this->plugin_name())) {
00297             return get_string($string, $this->plugin_name());
00298         } else {
00299             return get_string($fallback, $this->plugin_name());
00300         }
00301     }
00302 
00311     public function set_default_options($questiondata) {
00312     }
00313 
00341     public function save_question($question, $form) {
00342         global $USER, $DB, $OUTPUT;
00343 
00344         list($question->category) = explode(',', $form->category);
00345         $context = $this->get_context_by_category_id($question->category);
00346 
00347         // This default implementation is suitable for most
00348         // question types.
00349 
00350         // First, save the basic question itself
00351         $question->name = trim($form->name);
00352         $question->parent = isset($form->parent) ? $form->parent : 0;
00353         $question->length = $this->actual_number_of_questions($question);
00354         $question->penalty = isset($form->penalty) ? $form->penalty : 0;
00355 
00356         if (empty($form->questiontext['text'])) {
00357             $question->questiontext = '';
00358         } else {
00359             $question->questiontext = trim($form->questiontext['text']);;
00360         }
00361         $question->questiontextformat = !empty($form->questiontext['format']) ?
00362                 $form->questiontext['format'] : 0;
00363 
00364         if (empty($form->generalfeedback['text'])) {
00365             $question->generalfeedback = '';
00366         } else {
00367             $question->generalfeedback = trim($form->generalfeedback['text']);
00368         }
00369         $question->generalfeedbackformat = !empty($form->generalfeedback['format']) ?
00370                 $form->generalfeedback['format'] : 0;
00371 
00372         if (empty($question->name)) {
00373             $question->name = shorten_text(strip_tags($form->questiontext['text']), 15);
00374             if (empty($question->name)) {
00375                 $question->name = '-';
00376             }
00377         }
00378 
00379         if ($question->penalty > 1 or $question->penalty < 0) {
00380             $question->errors['penalty'] = get_string('invalidpenalty', 'question');
00381         }
00382 
00383         if (isset($form->defaultmark)) {
00384             $question->defaultmark = $form->defaultmark;
00385         }
00386 
00387         // If the question is new, create it.
00388         if (empty($question->id)) {
00389             // Set the unique code
00390             $question->stamp = make_unique_id_code();
00391             $question->createdby = $USER->id;
00392             $question->timecreated = time();
00393             $question->id = $DB->insert_record('question', $question);
00394         }
00395 
00396         // Now, whether we are updating a existing question, or creating a new
00397         // one, we have to do the files processing and update the record.
00399         $question->modifiedby = $USER->id;
00400         $question->timemodified = time();
00401 
00402         if (!empty($question->questiontext) && !empty($form->questiontext['itemid'])) {
00403             $question->questiontext = file_save_draft_area_files($form->questiontext['itemid'],
00404                     $context->id, 'question', 'questiontext', (int)$question->id,
00405                     $this->fileoptions, $question->questiontext);
00406         }
00407         if (!empty($question->generalfeedback) && !empty($form->generalfeedback['itemid'])) {
00408             $question->generalfeedback = file_save_draft_area_files(
00409                     $form->generalfeedback['itemid'], $context->id,
00410                     'question', 'generalfeedback', (int)$question->id,
00411                     $this->fileoptions, $question->generalfeedback);
00412         }
00413         $DB->update_record('question', $question);
00414 
00415         // Now to save all the answers and type-specific options
00416         $form->id = $question->id;
00417         $form->qtype = $question->qtype;
00418         $form->category = $question->category;
00419         $form->questiontext = $question->questiontext;
00420         $form->questiontextformat = $question->questiontextformat;
00421         // current context
00422         $form->context = $context;
00423 
00424         $result = $this->save_question_options($form);
00425 
00426         if (!empty($result->error)) {
00427             print_error($result->error);
00428         }
00429 
00430         if (!empty($result->notice)) {
00431             notice($result->notice, "question.php?id=$question->id");
00432         }
00433 
00434         if (!empty($result->noticeyesno)) {
00435             throw new coding_exception(
00436                     '$result->noticeyesno no longer supported in save_question.');
00437         }
00438 
00439         // Give the question a unique version stamp determined by question_hash()
00440         $DB->set_field('question', 'version', question_hash($question),
00441                 array('id' => $question->id));
00442 
00443         return $question;
00444     }
00445 
00454     public function save_question_options($question) {
00455         global $DB;
00456         $extraquestionfields = $this->extra_question_fields();
00457 
00458         if (is_array($extraquestionfields)) {
00459             $question_extension_table = array_shift($extraquestionfields);
00460 
00461             $function = 'update_record';
00462             $questionidcolname = $this->questionid_column_name();
00463             $options = $DB->get_record($question_extension_table,
00464                     array($questionidcolname => $question->id));
00465             if (!$options) {
00466                 $function = 'insert_record';
00467                 $options = new stdClass();
00468                 $options->$questionidcolname = $question->id;
00469             }
00470             foreach ($extraquestionfields as $field) {
00471                 if (!isset($question->$field)) {
00472                     $result = new stdClass();
00473                     $result->error = "No data for field $field when saving " .
00474                             $this->name() . ' question id ' . $question->id;
00475                     return $result;
00476                 }
00477                 $options->$field = $question->$field;
00478             }
00479 
00480             if (!$DB->{$function}($question_extension_table, $options)) {
00481                 $result = new stdClass();
00482                 $result->error = 'Could not save question options for ' .
00483                         $this->name() . ' question id ' . $question->id;
00484                 return $result;
00485             }
00486         }
00487 
00488         $extraanswerfields = $this->extra_answer_fields();
00489         // TODO save the answers, with any extra data.
00490     }
00491 
00492     public function save_hints($formdata, $withparts = false) {
00493         global $DB;
00494         $context = $formdata->context;
00495 
00496         $oldhints = $DB->get_records('question_hints',
00497                 array('questionid' => $formdata->id), 'id ASC');
00498 
00499         if (!empty($formdata->hint)) {
00500             $numhints = max(array_keys($formdata->hint)) + 1;
00501         } else {
00502             $numhints = 0;
00503         }
00504 
00505         if ($withparts) {
00506             if (!empty($formdata->hintclearwrong)) {
00507                 $numclears = max(array_keys($formdata->hintclearwrong)) + 1;
00508             } else {
00509                 $numclears = 0;
00510             }
00511             if (!empty($formdata->hintshownumcorrect)) {
00512                 $numshows = max(array_keys($formdata->hintshownumcorrect)) + 1;
00513             } else {
00514                 $numshows = 0;
00515             }
00516             $numhints = max($numhints, $numclears, $numshows);
00517         }
00518 
00519         for ($i = 0; $i < $numhints; $i += 1) {
00520             if (html_is_blank($formdata->hint[$i]['text'])) {
00521                 $formdata->hint[$i]['text'] = '';
00522             }
00523 
00524             if ($withparts) {
00525                 $clearwrong = !empty($formdata->hintclearwrong[$i]);
00526                 $shownumcorrect = !empty($formdata->hintshownumcorrect[$i]);
00527             }
00528 
00529             if (empty($formdata->hint[$i]['text']) && empty($clearwrong) &&
00530                     empty($shownumcorrect)) {
00531                 continue;
00532             }
00533 
00534             // Update an existing hint if possible.
00535             $hint = array_shift($oldhints);
00536             if (!$hint) {
00537                 $hint = new stdClass();
00538                 $hint->questionid = $formdata->id;
00539                 $hint->hint = '';
00540                 $hint->id = $DB->insert_record('question_hints', $hint);
00541             }
00542 
00543             $hint->hint = $this->import_or_save_files($formdata->hint[$i],
00544                     $context, 'question', 'hint', $hint->id);
00545             $hint->hintformat = $formdata->hint[$i]['format'];
00546             if ($withparts) {
00547                 $hint->clearwrong = $clearwrong;
00548                 $hint->shownumcorrect = $shownumcorrect;
00549             }
00550             $DB->update_record('question_hints', $hint);
00551         }
00552 
00553         // Delete any remaining old hints.
00554         $fs = get_file_storage();
00555         foreach ($oldhints as $oldhint) {
00556             $fs->delete_area_files($context->id, 'question', 'hint', $oldhint->id);
00557             $DB->delete_records('question_hints', array('id' => $oldhint->id));
00558         }
00559     }
00560 
00569     protected function save_combined_feedback_helper($options, $formdata,
00570             $context, $withparts = false) {
00571         $options->correctfeedback = $this->import_or_save_files($formdata->correctfeedback,
00572                 $context, 'question', 'correctfeedback', $formdata->id);
00573         $options->correctfeedbackformat = $formdata->correctfeedback['format'];
00574 
00575         $options->partiallycorrectfeedback = $this->import_or_save_files(
00576                 $formdata->partiallycorrectfeedback,
00577                 $context, 'question', 'partiallycorrectfeedback', $formdata->id);
00578         $options->partiallycorrectfeedbackformat = $formdata->partiallycorrectfeedback['format'];
00579 
00580         $options->incorrectfeedback = $this->import_or_save_files($formdata->incorrectfeedback,
00581                 $context, 'question', 'incorrectfeedback', $formdata->id);
00582         $options->incorrectfeedbackformat = $formdata->incorrectfeedback['format'];
00583 
00584         if ($withparts) {
00585             $options->shownumcorrect = !empty($formdata->shownumcorrect);
00586         }
00587 
00588         return $options;
00589     }
00590 
00603     public function get_question_options($question) {
00604         global $CFG, $DB, $OUTPUT;
00605 
00606         if (!isset($question->options)) {
00607             $question->options = new stdClass();
00608         }
00609 
00610         $extraquestionfields = $this->extra_question_fields();
00611         if (is_array($extraquestionfields)) {
00612             $question_extension_table = array_shift($extraquestionfields);
00613             $extra_data = $DB->get_record($question_extension_table,
00614                     array($this->questionid_column_name() => $question->id),
00615                     implode(', ', $extraquestionfields));
00616             if ($extra_data) {
00617                 foreach ($extraquestionfields as $field) {
00618                     $question->options->$field = $extra_data->$field;
00619                 }
00620             } else {
00621                 echo $OUTPUT->notification('Failed to load question options from the table ' .
00622                         $question_extension_table . ' for questionid ' . $question->id);
00623                 return false;
00624             }
00625         }
00626 
00627         $extraanswerfields = $this->extra_answer_fields();
00628         if (is_array($extraanswerfields)) {
00629             $answer_extension_table = array_shift($extraanswerfields);
00630             $question->options->answers = $DB->get_records_sql("
00631                     SELECT qa.*, qax." . implode(', qax.', $extraanswerfields) . "
00632                     FROM {question_answers} qa, {{$answer_extension_table}} qax
00633                     WHERE qa.question = ? AND qax.answerid = qa.id
00634                     ORDER BY qa.id", array($question->id));
00635             if (!$question->options->answers) {
00636                 echo $OUTPUT->notification('Failed to load question answers from the table ' .
00637                         $answer_extension_table . 'for questionid ' . $question->id);
00638                 return false;
00639             }
00640         } else {
00641             // Don't check for success or failure because some question types do
00642             // not use the answers table.
00643             $question->options->answers = $DB->get_records('question_answers',
00644                     array('question' => $question->id), 'id ASC');
00645         }
00646 
00647         $question->hints = $DB->get_records('question_hints',
00648                 array('questionid' => $question->id), 'id ASC');
00649 
00650         return true;
00651     }
00652 
00659     public function make_question($questiondata) {
00660         $question = $this->make_question_instance($questiondata);
00661         $this->initialise_question_instance($question, $questiondata);
00662         return $question;
00663     }
00664 
00672     protected function make_question_instance($questiondata) {
00673         question_bank::load_question_definition_classes($this->name());
00674         $class = 'qtype_' . $this->name() . '_question';
00675         return new $class();
00676     }
00677 
00683     protected function initialise_question_instance(question_definition $question, $questiondata) {
00684         $question->id = $questiondata->id;
00685         $question->category = $questiondata->category;
00686         $question->contextid = $questiondata->contextid;
00687         $question->parent = $questiondata->parent;
00688         $question->qtype = $this;
00689         $question->name = $questiondata->name;
00690         $question->questiontext = $questiondata->questiontext;
00691         $question->questiontextformat = $questiondata->questiontextformat;
00692         $question->generalfeedback = $questiondata->generalfeedback;
00693         $question->generalfeedbackformat = $questiondata->generalfeedbackformat;
00694         $question->defaultmark = $questiondata->defaultmark + 0;
00695         $question->length = $questiondata->length;
00696         $question->penalty = $questiondata->penalty;
00697         $question->stamp = $questiondata->stamp;
00698         $question->version = $questiondata->version;
00699         $question->hidden = $questiondata->hidden;
00700         $question->timecreated = $questiondata->timecreated;
00701         $question->timemodified = $questiondata->timemodified;
00702         $question->createdby = $questiondata->createdby;
00703         $question->modifiedby = $questiondata->modifiedby;
00704 
00705         //Fill extra question fields values
00706         $extraquestionfields = $this->extra_question_fields();
00707         if (is_array($extraquestionfields)) {
00708             //omit table name
00709             array_shift($extraquestionfields);
00710             foreach($extraquestionfields as $field) {
00711                 $question->$field = $questiondata->options->$field;
00712             }
00713         }
00714 
00715         $this->initialise_question_hints($question, $questiondata);
00716     }
00717 
00723     protected function initialise_question_hints(question_definition $question, $questiondata) {
00724         if (empty($questiondata->hints)) {
00725             return;
00726         }
00727         foreach ($questiondata->hints as $hint) {
00728             $question->hints[] = $this->make_hint($hint);
00729         }
00730     }
00731 
00738     protected function make_hint($hint) {
00739         return question_hint::load_from_record($hint);
00740     }
00741 
00748     protected function initialise_combined_feedback(question_definition $question,
00749             $questiondata, $withparts = false) {
00750         $question->correctfeedback = $questiondata->options->correctfeedback;
00751         $question->correctfeedbackformat = $questiondata->options->correctfeedbackformat;
00752         $question->partiallycorrectfeedback = $questiondata->options->partiallycorrectfeedback;
00753         $question->partiallycorrectfeedbackformat =
00754                 $questiondata->options->partiallycorrectfeedbackformat;
00755         $question->incorrectfeedback = $questiondata->options->incorrectfeedback;
00756         $question->incorrectfeedbackformat = $questiondata->options->incorrectfeedbackformat;
00757         if ($withparts) {
00758             $question->shownumcorrect = $questiondata->options->shownumcorrect;
00759         }
00760     }
00761 
00772     protected function initialise_question_answers(question_definition $question,
00773             $questiondata, $forceplaintextanswers = true) {
00774         $question->answers = array();
00775         if (empty($questiondata->options->answers)) {
00776             return;
00777         }
00778         foreach ($questiondata->options->answers as $a) {
00779             $question->answers[$a->id] = new question_answer($a->id, $a->answer,
00780                     $a->fraction, $a->feedback, $a->feedbackformat);
00781             if (!$forceplaintextanswers) {
00782                 $question->answers[$a->id]->answerformat = $a->answerformat;
00783             }
00784         }
00785     }
00786 
00792     public function delete_question($questionid, $contextid) {
00793         global $DB;
00794 
00795         $this->delete_files($questionid, $contextid);
00796 
00797         $extraquestionfields = $this->extra_question_fields();
00798         if (is_array($extraquestionfields)) {
00799             $question_extension_table = array_shift($extraquestionfields);
00800             $DB->delete_records($question_extension_table,
00801                     array($this->questionid_column_name() => $questionid));
00802         }
00803 
00804         $extraanswerfields = $this->extra_answer_fields();
00805         if (is_array($extraanswerfields)) {
00806             $answer_extension_table = array_shift($extraanswerfields);
00807             $DB->delete_records_select($answer_extension_table,
00808                     'answerid IN (SELECT qa.id FROM {question_answers} qa WHERE qa.question = ?)',
00809                     array($questionid));
00810         }
00811 
00812         $DB->delete_records('question_answers', array('question' => $questionid));
00813 
00814         $DB->delete_records('question_hints', array('questionid' => $questionid));
00815     }
00816 
00830     public function actual_number_of_questions($question) {
00831         // By default, each question is given one number
00832         return 1;
00833     }
00834 
00840     public function get_random_guess_score($questiondata) {
00841         return 0;
00842     }
00843 
00871     public function get_possible_responses($questiondata) {
00872         return array();
00873     }
00874 
00880     public function find_standard_scripts() {
00881         global $PAGE;
00882 
00883         $plugindir = $this->plugin_dir();
00884         $plugindirrel = 'question/type/' . $this->name();
00885 
00886         if (file_exists($plugindir . '/script.js')) {
00887             $PAGE->requires->js('/' . $plugindirrel . '/script.js');
00888         }
00889         if (file_exists($plugindir . '/script.php')) {
00890             $PAGE->requires->js('/' . $plugindirrel . '/script.php');
00891         }
00892     }
00893 
00910     public function finished_edit_wizard($form) {
00911         //In the default case there is only one edit page.
00912         return true;
00913     }
00914 
00916 
00917     /*
00918      * Imports question from the Moodle XML format
00919      *
00920      * Imports question using information from extra_question_fields function
00921      * If some of you fields contains id's you'll need to reimplement this
00922      */
00923     public function import_from_xml($data, $question, $format, $extra=null) {
00924         $question_type = $data['@']['type'];
00925         if ($question_type != $this->name()) {
00926             return false;
00927         }
00928 
00929         $extraquestionfields = $this->extra_question_fields();
00930         if (!is_array($extraquestionfields)) {
00931             return false;
00932         }
00933 
00934         //omit table name
00935         array_shift($extraquestionfields);
00936         $qo = $format->import_headers($data);
00937         $qo->qtype = $question_type;
00938 
00939         foreach ($extraquestionfields as $field) {
00940             $qo->$field = $format->getpath($data, array('#', $field, 0, '#'), '');
00941         }
00942 
00943         // run through the answers
00944         $answers = $data['#']['answer'];
00945         $a_count = 0;
00946         $extraanswersfields = $this->extra_answer_fields();
00947         if (is_array($extraanswersfields)) {
00948             array_shift($extraanswersfields);
00949         }
00950         foreach ($answers as $answer) {
00951             $ans = $format->import_answer($answer);
00952             if (!$this->has_html_answers()) {
00953                 $qo->answer[$a_count] = $ans->answer['text'];
00954             } else {
00955                 $qo->answer[$a_count] = $ans->answer;
00956             }
00957             $qo->fraction[$a_count] = $ans->fraction;
00958             $qo->feedback[$a_count] = $ans->feedback;
00959             if (is_array($extraanswersfields)) {
00960                 foreach ($extraanswersfields as $field) {
00961                     $qo->{$field}[$a_count] =
00962                         $format->getpath($answer, array('#', $field, 0, '#'), '');
00963                 }
00964             }
00965             ++$a_count;
00966         }
00967         return $qo;
00968     }
00969 
00970     /*
00971      * Export question to the Moodle XML format
00972      *
00973      * Export question using information from extra_question_fields function
00974      * If some of you fields contains id's you'll need to reimplement this
00975      */
00976     public function export_to_xml($question, $format, $extra=null) {
00977         $extraquestionfields = $this->extra_question_fields();
00978         if (!is_array($extraquestionfields)) {
00979             return false;
00980         }
00981 
00982         //omit table name
00983         array_shift($extraquestionfields);
00984         $expout='';
00985         foreach ($extraquestionfields as $field) {
00986             $exportedvalue = $format->xml_escape($question->options->$field);
00987             $expout .= "    <$field>{$exportedvalue}</$field>\n";
00988         }
00989 
00990         $extraanswersfields = $this->extra_answer_fields();
00991         if (is_array($extraanswersfields)) {
00992             array_shift($extraanswersfields);
00993         }
00994         foreach ($question->options->answers as $answer) {
00995             // TODO this should be re-factored to use $format->write_answer().
00996             $percent = 100 * $answer->fraction;
00997             $expout .= "    <answer fraction=\"$percent\" {$format->format($answer->answerformat)}>\n";
00998             $expout .= $format->writetext($answer->answer, 3, false);
00999             $expout .= "      <feedback {$format->format($answer->feedbackformat)}>\n";
01000             $expout .= $format->writetext($answer->feedback, 4, false);
01001             $expout .= "      </feedback>\n";
01002             if (is_array($extraanswersfields)) {
01003                 foreach ($extraanswersfields as $field) {
01004                     $exportedvalue = $format->xml_escape($answer->$field);
01005                     $expout .= "      <{$field}>{$exportedvalue}</{$field}>\n";
01006                 }
01007             }
01008 
01009             $expout .= "    </answer>\n";
01010         }
01011         return $expout;
01012     }
01013 
01019     public function generate_test($name, $courseid=null) {
01020         $form = new stdClass();
01021         $form->name = $name;
01022         $form->questiontextformat = 1;
01023         $form->questiontext = 'test question, generated by script';
01024         $form->defaultmark = 1;
01025         $form->penalty = 0.3333333;
01026         $form->generalfeedback = "Well done";
01027 
01028         $context = get_context_instance(CONTEXT_COURSE, $courseid);
01029         $newcategory = question_make_default_categories(array($context));
01030         $form->category = $newcategory->id . ',1';
01031 
01032         $question = new stdClass();
01033         $question->courseid = $courseid;
01034         $question->qtype = $this->qtype;
01035         return array($form, $question);
01036     }
01037 
01043     protected function get_context_by_category_id($category) {
01044         global $DB;
01045         $contextid = $DB->get_field('question_categories', 'contextid', array('id'=>$category));
01046         $context = get_context_instance_by_id($contextid);
01047         return $context;
01048     }
01049 
01065     protected function import_or_save_files($field, $context, $component, $filearea, $itemid) {
01066         if (!empty($field['itemid'])) {
01067             // This is the normal case. We are safing the questions editing form.
01068             return file_save_draft_area_files($field['itemid'], $context->id, $component,
01069                     $filearea, $itemid, $this->fileoptions, trim($field['text']));
01070 
01071         } else if (!empty($field['files'])) {
01072             // This is the case when we are doing an import.
01073             foreach ($field['files'] as $file) {
01074                 $this->import_file($context, $component,  $filearea, $itemid, $file);
01075             }
01076         }
01077         return trim($field['text']);
01078     }
01079 
01086     public function move_files($questionid, $oldcontextid, $newcontextid) {
01087         $fs = get_file_storage();
01088         $fs->move_area_files_to_new_context($oldcontextid,
01089                 $newcontextid, 'question', 'questiontext', $questionid);
01090         $fs->move_area_files_to_new_context($oldcontextid,
01091                 $newcontextid, 'question', 'generalfeedback', $questionid);
01092     }
01093 
01103     protected function move_files_in_answers($questionid, $oldcontextid,
01104             $newcontextid, $answerstoo = false) {
01105         global $DB;
01106         $fs = get_file_storage();
01107 
01108         $answerids = $DB->get_records_menu('question_answers',
01109                 array('question' => $questionid), 'id', 'id,1');
01110         foreach ($answerids as $answerid => $notused) {
01111             if ($answerstoo) {
01112                 $fs->move_area_files_to_new_context($oldcontextid,
01113                         $newcontextid, 'question', 'answer', $answerid);
01114             }
01115             $fs->move_area_files_to_new_context($oldcontextid,
01116                     $newcontextid, 'question', 'answerfeedback', $answerid);
01117         }
01118     }
01119 
01129     protected function move_files_in_combined_feedback($questionid, $oldcontextid,
01130             $newcontextid) {
01131         global $DB;
01132         $fs = get_file_storage();
01133 
01134         $fs->move_area_files_to_new_context($oldcontextid,
01135                 $newcontextid, 'question', 'correctfeedback', $questionid);
01136         $fs->move_area_files_to_new_context($oldcontextid,
01137                 $newcontextid, 'question', 'partiallycorrectfeedback', $questionid);
01138         $fs->move_area_files_to_new_context($oldcontextid,
01139                 $newcontextid, 'question', 'incorrectfeedback', $questionid);
01140     }
01141 
01147     protected function delete_files($questionid, $contextid) {
01148         $fs = get_file_storage();
01149         $fs->delete_area_files($contextid, 'question', 'questiontext', $questionid);
01150         $fs->delete_area_files($contextid, 'question', 'generalfeedback', $questionid);
01151     }
01152 
01160     protected function delete_files_in_answers($questionid, $contextid, $answerstoo = false) {
01161         global $DB;
01162         $fs = get_file_storage();
01163 
01164         $answerids = $DB->get_records_menu('question_answers',
01165                 array('question' => $questionid), 'id', 'id,1');
01166         foreach ($answerids as $answerid => $notused) {
01167             if ($answerstoo) {
01168                 $fs->delete_area_files($contextid, 'question', 'answer', $answerid);
01169             }
01170             $fs->delete_area_files($contextid, 'question', 'answerfeedback', $answerid);
01171         }
01172     }
01173 
01181     protected function delete_files_in_combined_feedback($questionid, $contextid) {
01182         global $DB;
01183         $fs = get_file_storage();
01184 
01185         $fs->delete_area_files($contextid,
01186                 'question', 'correctfeedback', $questionid);
01187         $fs->delete_area_files($contextid,
01188                 'question', 'partiallycorrectfeedback', $questionid);
01189         $fs->delete_area_files($contextid,
01190                 'question', 'incorrectfeedback', $questionid);
01191     }
01192 
01193     public function import_file($context, $component, $filearea, $itemid, $file) {
01194         $fs = get_file_storage();
01195         $record = new stdClass();
01196         if (is_object($context)) {
01197             $record->contextid = $context->id;
01198         } else {
01199             $record->contextid = $context;
01200         }
01201         $record->component = $component;
01202         $record->filearea  = $filearea;
01203         $record->itemid    = $itemid;
01204         $record->filename  = $file->name;
01205         $record->filepath  = '/';
01206         return $fs->create_file_from_string($record, $this->decode_file($file));
01207     }
01208 
01209     protected function decode_file($file) {
01210         switch ($file->encoding) {
01211             case 'base64':
01212             default:
01213                 return base64_decode($file->content);
01214         }
01215     }
01216 }
01217 
01218 
01226 class question_possible_response {
01232     public $responseclass;
01234     public $fraction;
01241     public function __construct($responseclass, $fraction) {
01242         $this->responseclass = $responseclass;
01243         $this->fraction = $fraction;
01244     }
01245 
01246     public static function no_response() {
01247         return new question_possible_response(get_string('noresponse', 'question'), 0);
01248     }
01249 }
 All Data Structures Namespaces Files Functions Variables Enumerations