From cde2709a88fc11c4f029ab3aa4ef2feb7a7aab24 Mon Sep 17 00:00:00 2001 From: Dongsheng Cai Date: Fri, 5 Nov 2010 06:34:00 +0000 Subject: [PATCH] MDL-15573, question export rewrite --- lib/questionlib.php | 81 ++- question/export.php | 69 +-- question/export_form.php | 6 - question/format.php | 167 +++--- question/format/xml/format.php | 541 +++++++++++++----- question/import.php | 30 +- question/type/calculated/questiontype.php | 26 +- .../type/calculatedmulti/questiontype.php | 25 +- .../type/calculatedsimple/questiontype.php | 16 +- question/type/essay/questiontype.php | 9 +- question/type/match/questiontype.php | 15 +- question/type/multichoice/questiontype.php | 30 +- question/type/numerical/questiontype.php | 43 +- question/type/questiontype.php | 24 + question/type/shortanswer/questiontype.php | 17 +- question/type/truefalse/questiontype.php | 22 +- 16 files changed, 796 insertions(+), 325 deletions(-) diff --git a/lib/questionlib.php b/lib/questionlib.php index dc04838c94c..05183b5e56e 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -3270,10 +3270,73 @@ function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filea function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) { global $DB, $CFG; + list($context, $course, $cm) = get_context_info_array($context->id); + require_login($course, false, $cm); + + if ($filearea === 'export') { + require_once($CFG->dirroot . '/question/editlib.php'); + $contexts = new question_edit_contexts($context); + // check export capability + $contexts->require_one_edit_tab_cap('export'); + $category_id = (int)array_shift($args); + $format = array_shift($args); + $cattofile = array_shift($args); + $contexttofile = array_shift($args); + $filename = array_shift($args); + + // load parent class for import/export + require_once($CFG->dirroot . '/question/format.php'); + require_once($CFG->dirroot . '/question/editlib.php'); + require_once($CFG->dirroot . '/question/format/' . $format . '/format.php'); + + $classname = 'qformat_' . $format; + if (!class_exists($classname)) { + send_file_not_found(); + } + + $qformat = new $classname(); + + if (!$category = $DB->get_record('question_categories', array('id' => $category_id))) { + send_file_not_found(); + } + + $qformat->setCategory($category); + $qformat->setContexts($contexts->having_one_edit_tab_cap('export')); + $qformat->setCourse($course); + + if ($cattofile == 'withcategories') { + $qformat->setCattofile(true); + } else { + $qformat->setCattofile(false); + } + + if ($contexttofile == 'withcontexts') { + $qformat->setContexttofile(true); + } else { + $qformat->setContexttofile(false); + } + + if (!$qformat->exportpreprocess()) { + send_file_not_found(); + print_error('exporterror', 'question', $thispageurl->out()); + } + + // export data to moodle file pool + if (!$content = $qformat->exportprocess(true)) { + send_file_not_found(); + } + + //DEBUG + //echo ''; + //die; + send_file($content, $filename, 0, 0, true, true); + } + $attemptid = (int)array_shift($args); $questionid = (int)array_shift($args); - require_login($course, false); if ($attemptid === 0) { // preview @@ -3321,3 +3384,19 @@ function question_check_file_access($question, $state, $options, $contextid, $co return $QTYPES[$question->qtype]->check_file_access($question, $state, $options, $contextid, $component, $filearea, $args, $forcedownload); } + +/** + * Create url for question export + * + * @param int $contextid, current context + * @param int $categoryid, categoryid + * @param string $format + * @param string $withcategories + * @param string $ithcontexts + * @param moodle_url export file url + */ +function question_make_export_url($contextid, $categoryid, $format, $withcategories, $withcontexts) { + global $CFG; + $urlbase = "$CFG->httpswwwroot/pluginfile.php"; + return moodle_url::make_file_url($urlbase, "/$contextid/question/export/{$categoryid}/{$format}/{$withcategories}/{$withcontexts}/export.xml", true); +} diff --git a/question/export.php b/question/export.php index fd965af004b..d4772f05ac8 100644 --- a/question/export.php +++ b/question/export.php @@ -37,68 +37,30 @@ $PAGE->set_heading($COURSE->fullname); echo $OUTPUT->header(); - $exportfilename = default_export_filename($COURSE, $category); - $export_form = new question_export_form($thispageurl, array('contexts'=>$contexts->having_one_edit_tab_cap('export'), 'defaultcategory'=>$pagevars['cat'], - 'defaultfilename'=>$exportfilename)); + $export_form = new question_export_form($thispageurl, array('contexts'=>$contexts->having_one_edit_tab_cap('export'), 'defaultcategory'=>$pagevars['cat'])); - if ($from_form = $export_form->get_data()) { /// Filename - - - if (! is_readable("format/$from_form->format/format.php")) { + if ($from_form = $export_form->get_data()) { + $thiscontext = $contexts->lowest(); + if (!is_readable("format/$from_form->format/format.php")) { print_error('unknowformat', '', '', $from_form->format); } - - // load parent class for import/export - require_once("format.php"); - - // and then the class for the selected format - require_once("format/$from_form->format/format.php"); - - $classname = "qformat_$from_form->format"; - $qformat = new $classname(); - $qformat->setContexts($contexts->having_one_edit_tab_cap('export')); - $qformat->setCategory($category); - $qformat->setCourse($COURSE); - - if (empty($from_form->exportfilename)) { - $from_form->exportfilename = default_export_filename($COURSE, $category); + $withcategories = 'nocategories'; + if (!empty($from_form->cattofile)) { + $withcategories = 'withcategories'; } - $qformat->setFilename($from_form->exportfilename); - $canaccessbackupdata = has_capability('moodle/backup:backupcourse', $contexts->lowest()); - $qformat->set_can_access_backupdata($canaccessbackupdata); - $qformat->setCattofile(!empty($from_form->cattofile)); - $qformat->setContexttofile(!empty($from_form->contexttofile)); - - if (! $qformat->exportpreprocess()) { // Do anything before that we need to - print_error('exporterror', 'question', $thispageurl->out()); + $withcontexts = 'nocontexts'; + if (!empty($from_form->contexttofile)) { + $withcontexts = 'withcontexts'; } - if (! $qformat->exportprocess()) { // Process the export data - print_error('exporterror', 'question', $thispageurl->out()); - } + $export_url = question_make_export_url($thiscontext->id, $category->id, $from_form->format, $withcategories, $withcontexts); - if (! $qformat->exportpostprocess()) { // In case anything needs to be done after - print_error('exporterror', 'question', $thispageurl->out()); - } - echo "
"; + echo $OUTPUT->box_start(); + echo get_string('yourfileshoulddownload', 'question', $export_url->out()); + echo $OUTPUT->box_end(); - // link to download the finished file - $file_ext = $qformat->export_file_extension(); - $filename = $from_form->exportfilename . $file_ext; - if ($canaccessbackupdata) { - $efile = get_file_url($qformat->question_get_export_dir() . '/' . $filename, - array('forcedownload' => 1)); - echo '

' . - get_string('download', 'quiz') . '

'; - echo '

' . - get_string('downloadextra', 'quiz') . '

'; - } else { - $efile = get_file_url($filename, null, 'questionfile'); - echo '

' . - get_string('yourfileshoulddownload', 'question', $efile) . '

'; - $PAGE->requires->js_function_call('document.location.replace', array($efile), false, 1); - } + $PAGE->requires->js_function_call('document.location.replace', array($export_url->out()), false, 1); echo $OUTPUT->continue_button(new moodle_url('edit.php', $thispageurl->params())); echo $OUTPUT->footer(); @@ -111,4 +73,3 @@ $export_form->display(); echo $OUTPUT->footer(); - diff --git a/question/export_form.php b/question/export_form.php index 3528ed2f2a2..f76a991b6fb 100644 --- a/question/export_form.php +++ b/question/export_form.php @@ -13,7 +13,6 @@ class question_export_form extends moodleform { $defaultcategory = $this->_customdata['defaultcategory']; $contexts = $this->_customdata['contexts']; - $defaultfilename = $this->_customdata['defaultfilename']; //-------------------------------------------------------------------------------- $mform->addElement('header','fileformat',get_string('fileformat','quiz')); $fileformatnames = get_import_export_formats('export'); @@ -46,10 +45,6 @@ class question_export_form extends moodleform { // $mform->addElement('select', 'format', get_string('fileformat','quiz'), $fileformatnames); // $mform->setDefault('format', 'gift'); - $mform->addElement('text', 'exportfilename', get_string('exportname', 'quiz'), array('size'=>40)); - $mform->setDefault('exportfilename', $defaultfilename); - $mform->setType('exportfilename', PARAM_FILE); - // set a template for the format select elements $renderer =& $mform->defaultRenderer(); $template = "{help} {element}\n"; @@ -60,4 +55,3 @@ class question_export_form extends moodleform { //-------------------------------------------------------------------------------- } } - diff --git a/question/format.php b/question/format.php index d8c52be36d0..3748307deef 100644 --- a/question/format.php +++ b/question/format.php @@ -27,6 +27,7 @@ class qformat_default { var $translator = null; var $canaccessbackupdata = true; + protected $importcontext = null; // functions to indicate import/export functionality // override to return true if implemented @@ -225,13 +226,17 @@ class qformat_default { /** * Process the file * This method should not normally be overidden + * @param object $context * @return boolean success */ - function importprocess() { - global $USER, $DB, $OUTPUT; + function importprocess($category) { + global $USER, $DB, $OUTPUT, $QTYPES; + + $context = $category->context; + $this->importcontext = $context; // reset the timer in case file upload was slow - @set_time_limit(0); + set_time_limit(0); // STAGE 1: Parse the file echo $OUTPUT->notification( get_string('parsingquestions','quiz') ); @@ -241,7 +246,7 @@ class qformat_default { return false; } - if (! $questions = $this->readquestions($lines)) { // Extract all the questions + if (!$questions = $this->readquestions($lines)) { // Extract all the questions echo $OUTPUT->notification( get_string('noquestionsinfile','quiz') ); return false; } @@ -264,6 +269,7 @@ class qformat_default { $gradeerrors = 0; $goodquestions = array(); foreach ($questions as $question) { + if (!empty($question->fraction) and (is_array($question->fraction))) { $fractions = $question->fraction; $answersvalid = true; // in case they are! @@ -314,26 +320,35 @@ class qformat_default { } continue; } + $question->context = $context; $count++; echo "

$count. ".$this->format_question_text($question)."

"; - $question->category = $this->category->id; + $question->category = $category->id; $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) $question->createdby = $USER->id; $question->timecreated = time(); - $question->id = $DB->insert_record("question", $question); + $question->id = $DB->insert_record('question', $question); + if (isset($question->questiontextfiles)) { + foreach ($question->questiontextfiles as $file) { + $QTYPES[$question->qtype]->import_file($context, 'question', 'questiontext', $question->id, $file); + } + } + if (isset($question->generalfeedbackfiles)) { + foreach ($question->generalfeedbackfiles as $file) { + $QTYPES[$question->qtype]->import_file($context, 'question', 'generalfeedback', $question->id, $file); + } + } $this->questionids[] = $question->id; // Now to save all the answers and type-specific options - global $QTYPES; - $result = $QTYPES[$question->qtype] - ->save_question_options($question); + $result = $QTYPES[$question->qtype]->save_question_options($question); if (!empty($result->error)) { echo $OUTPUT->notification($result->error); @@ -455,9 +470,10 @@ class qformat_default { * then you will need to override this method. Even then * try to use readquestion for each question * @param array lines array of lines from readdata + * @param object $context * @return array array of question objects */ - function readquestions($lines) { + function readquestions($lines, $context) { $questions = array(); $currentquestion = array(); @@ -477,7 +493,7 @@ class qformat_default { } if (!empty($currentquestion)) { // There may be a final question - if ($question = $this->readquestion($currentquestion)) { + if ($question = $this->readquestion($currentquestion, $context)) { $questions[] = $question; } } @@ -548,50 +564,6 @@ class qformat_default { return true; } - /** - * Import an image file encoded in base64 format - * @param string path path (in course data) to store picture - * @param string base64 encoded picture - * @return string filename (nb. collisions are handled) - */ - function importimagefile( $path, $base64 ) { - global $CFG; - - //TODO: MDL-16094 - throw new coding_exception('importimagefile() was not converted to new file api yet, sorry - see MDL-16094'); - - // all this to get the destination directory - // and filename! - $fullpath = "{$CFG->dataroot}/{$this->course->id}/$path"; - $path_parts = pathinfo( $fullpath ); - $destination = $path_parts['dirname']; - $file = clean_filename( $path_parts['basename'] ); - - // check if path exists - check_dir_exists($destination, true, true ); - - // detect and fix any filename collision - get unique filename - $newfiles = resolve_filename_collisions( $destination, array($file) ); - $newfile = $newfiles[0]; - - // convert and save file contents - if (!$content = base64_decode( $base64 )) { - return ''; - } - $newfullpath = "$destination/$newfile"; - if (!$fh = fopen( $newfullpath, 'w' )) { - return ''; - } - if (!fwrite( $fh, $content )) { - return ''; - } - fclose( $fh ); - - // return the (possibly) new filename - $newfile = preg_replace("~{$CFG->dataroot}/{$this->course->id}/~", '',$newfullpath); - return $newfile; - } - /******************* * EXPORT FUNCTIONS @@ -654,26 +626,20 @@ class qformat_default { /** * Do the export * For most types this should not need to be overrided - * @return boolean success + * @return stored_file */ function exportprocess() { - global $CFG, $OUTPUT; - - // create a directory for the exports (if not already existing) - if (! $export_dir = make_upload_directory($this->question_get_export_dir())) { - print_error('cannotcreatepath', 'quiz', $export_dir); - } - $path = $CFG->dataroot.'/'.$this->question_get_export_dir(); + global $CFG, $OUTPUT, $DB, $USER; // get the questions (from database) in this category // only get q's with no parents (no cloze subquestions specifically) - if ($this->category){ + if ($this->category) { $questions = get_questions_category( $this->category, true ); } else { $questions = $this->questions; } - echo $OUTPUT->notification( get_string('exportingquestions','quiz') ); + //echo $OUTPUT->notification( get_string('exportingquestions','quiz') ); $count = 0; // results are first written into string (and then to a file) @@ -685,8 +651,13 @@ class qformat_default { // file if selected. 0 means that it will get printed before the 1st question $trackcategory = 0; + $fs = get_file_storage(); + // iterate through questions foreach($questions as $question) { + // used by file api + $contextid = $DB->get_field('question_categories', 'contextid', array('id'=>$question->category)); + $question->contextid = $contextid; // do not export hidden questions if (!empty($question->hidden)) { @@ -711,15 +682,35 @@ class qformat_default { $dummyquestion->name = 'Switch category to ' . $categoryname; $dummyquestion->id = 0; $dummyquestion->questiontextformat = ''; + $dummyquestion->contextid = 0; $expout .= $this->writequestion($dummyquestion) . "\n"; } } // export the question displaying message $count++; - echo "

$count. ".$this->format_question_text($question)."

"; - if (question_has_capability_on($question, 'view', $question->category)){ - $expout .= $this->writequestion( $question ) . "\n"; + + //echo '
'; + //echo $OUTPUT->container_start(); + //echo '' . $count . '. '; + //echo $this->format_question_text($question); + //echo $OUTPUT->container_end(); + + if (question_has_capability_on($question, 'view', $question->category)) { + // files used by questiontext + $files = $fs->get_area_files($contextid, 'question', 'questiontext', $question->id); + $question->questiontextfiles = $files; + // files used by generalfeedback + $files = $fs->get_area_files($contextid, 'question', 'generalfeedback', $question->id); + $question->generalfeedbackfiles = $files; + if (!empty($question->options->answers)) { + foreach ($question->options->answers as $answer) { + $files = $fs->get_area_files($contextid, 'question', 'answerfeedback', $answer->id); + $answer->feedbackfiles = $files; + } + } + + $expout .= $this->writequestion($question, $contextid) . "\n"; } } @@ -733,18 +724,8 @@ class qformat_default { } // final pre-process on exported data - $expout = $this->presave_process( $expout ); - - // write file - $filepath = $path."/".$this->filename . $this->export_file_extension(); - if (!$fh=fopen($filepath,"w")) { - print_error( 'cannotopen','quiz',$continuepath,$filepath ); - } - if (!fwrite($fh, $expout, strlen($expout) )) { - print_error( 'cannotwrite','quiz',$continuepath,$filepath ); - } - fclose($fh); - return true; + $expout = $this->presave_process($expout); + return $expout; } /** @@ -876,4 +857,30 @@ class qformat_default { $text = $question->questiontext; return format_text(html_to_text($text), $format, $formatoptions); } + + /** + * convert files into text output in the given format. + * @param array + * @param string encoding method + * @return string $string + */ + function writefiles($files, $encoding='base64') { + if (empty($files)) { + return ''; + } + $string = ''; + foreach ($files as $file) { + if ($file->is_directory()) { + continue; + } + $string .= 'get_filename() . '"'); + $string .= (' encoding="' . $encoding . '"'); + $string .= '>'; + $string .= base64_encode($file->get_content()); + //$string .= 'base64'; + $string .= ''; + } + return $string; + } } diff --git a/question/format/xml/format.php b/question/format/xml/format.php index b5a3b91d627..a922d03502d 100755 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -116,7 +116,7 @@ class qformat_xml extends qformat_default { * @param string error if set value must exist, return false and issue message if not * @return mixed value */ - function getpath( $xml, $path, $default, $istext=false, $error='' ) { + function getpath($xml, $path, $default, $istext=false, $error='') { foreach ($path as $index) { if (!isset($xml[$index])) { if (!empty($error)) { @@ -132,7 +132,7 @@ class qformat_xml extends qformat_default { if (!is_string($xml)) { $this->error( get_string('invalidxml','qformat_xml') ); } - $xml = trim( $xml ); + $xml = trim($xml); } return $xml; @@ -144,25 +144,49 @@ class qformat_xml extends qformat_default { * @param $question array question question array from xml tree * @return object question object */ - function import_headers( $question ) { + function import_headers($question) { // get some error strings - $error_noname = get_string( 'xmlimportnoname','quiz' ); - $error_noquestion = get_string( 'xmlimportnoquestion','quiz' ); + $error_noname = get_string('xmlimportnoname','quiz'); + $error_noquestion = get_string('xmlimportnoquestion','quiz'); // this routine initialises the question object $qo = $this->defaultquestion(); // question name $qo->name = $this->getpath( $question, array('#','name',0,'#','text',0,'#'), '', true, $error_noname ); - $qo->questiontext = $this->getpath( $question, array('#','questiontext',0,'#','text',0,'#'), '', true ); - $qo->questiontextformat = $this->trans_format($this->getpath( - $question, array('#','questiontext',0,'@','format'), '')); - $qo->image = $this->getpath( $question, array('#','image',0,'#'), $qo->image ); + $qo->questiontext = $this->getpath($question, array('#','questiontext',0,'#','text',0,'#'), '', true ); + $qo->questiontextformat = $this->trans_format($this->getpath($question, array('#','questiontext',0,'@','format'), '')); + $qo->image = $this->getpath($question, array('#','image',0,'#'), $qo->image ); $image_base64 = $this->getpath( $question, array('#','image_base64','0','#'),'' ); if (!empty($image_base64)) { - $qo->image = $this->importimagefile( $qo->image, $image_base64); + $qo->image = $this->importimagefile($qo->image, $image_base64); } - $qo->generalfeedback = $this->getpath( $question, array('#','generalfeedback',0,'#','text',0,'#'), $qo->generalfeedback, true ); + + $qo->questiontextfiles = array(); + + // restore files in questiontext + $files = $this->getpath($question, array('#', 'questiontext', 0, '#','file'), array(), false); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->questiontextfiles[] = $data; + } + + // restore files in generalfeedback + $qo->generalfeedback = $this->getpath($question, array('#','generalfeedback',0,'#','text',0,'#'), $qo->generalfeedback, true); + $qo->generalfeedbackfiles = array(); + $qo->generalfeedbackformat = $this->trans_format($this->getpath($question, array('#', 'generalfeedback', 0, '@', 'format'), '')); + $files = $this->getpath($question, array('#', 'generalfeedback', 0, '#', 'file'), array(), false); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->generalfeedbackfiles[] = $data; + } + $qo->defaultgrade = $this->getpath( $question, array('#','defaultgrade',0,'#'), $qo->defaultgrade ); $qo->penalty = $this->getpath( $question, array('#','penalty',0,'#'), $qo->penalty ); @@ -174,15 +198,45 @@ class qformat_xml extends qformat_default { * @param array answer xml tree for single answer * @return object answer object */ - function import_answer( $answer ) { - $fraction = $this->getpath( $answer, array('@','fraction'),0 ); - $text = $this->getpath( $answer, array('#','text',0,'#'), '', true ); - $feedback = $this->getpath( $answer, array('#','feedback',0,'#','text',0,'#'), '', true ); + function import_answer($answer) { + $fraction = $this->getpath($answer, array('@', 'fraction'), 0); + $answertext = $this->getpath($answer, array('#', 'text', 0, '#'), '', true); + $answerformat = $this->trans_format($this->getpath($answer, array('#', 'text', 0, '#'), '')); + $answerfiles = array(); + $files = $this->getpath($answer, array('#', 'answer', 0, '#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->name = $file['@']['name']; + $data->encoding = $file['@']['encoding']; + $answerfiles[] = $data; + } + + $feedbacktext = $this->getpath($answer, array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true); + $feedbackformat = $this->trans_format($this->getpath($answer, array('#', 'feedback', 0, '@', 'format'), '')); + $feedbackfiles = array(); + $files = $this->getpath($answer, array('#', 'feedback', 0, '#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->name = $file['@']['name']; + $data->encoding = $file['@']['encoding']; + $feedbackfiles[] = $data; + } + + $ans = new stdclass; + + $ans->answer = array(); + $ans->answer['text'] = $answertext; + $ans->answer['format'] = $answerformat; + $ans->answer['files'] = $answerfiles; + + $ans->feedback = array(); + $ans->feedback['text'] = $feedbacktext; + $ans->feedback['format'] = $feedbackformat; + $ans->feedback['files'] = $feedbackfiles; - $ans = null; - $ans->answer = $text; $ans->fraction = $fraction / 100; - $ans->feedback = $feedback; return $ans; } @@ -191,9 +245,9 @@ class qformat_xml extends qformat_default { * @param array question question array from xml tree * @return object question object */ - function import_multichoice( $question ) { + function import_multichoice($question) { // get common parts - $qo = $this->import_headers( $question ); + $qo = $this->import_headers($question); // 'header' parts particular to multichoice $qo->qtype = MULTICHOICE; @@ -202,9 +256,48 @@ class qformat_xml extends qformat_default { $shuffleanswers = $this->getpath( $question, array('#','shuffleanswers',0,'#'), 'false' ); $qo->answernumbering = $this->getpath( $question, array('#','answernumbering',0,'#'), 'abc' ); $qo->shuffleanswers = $this->trans_single($shuffleanswers); - $qo->correctfeedback = $this->getpath( $question, array('#','correctfeedback',0,'#','text',0,'#'), '', true ); - $qo->partiallycorrectfeedback = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); - $qo->incorrectfeedback = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); + + $qo->correctfeedback = array(); + $qo->correctfeedback['text'] = $this->getpath($question, array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true); + $qo->correctfeedback['format'] = $this->trans_format($this->getpath($question, array('#', 'correctfeedback', 0, '@', 'format'), '')); + $qo->correctfeedback['files'] = array(); + // restore files in correctfeedback + $files = $this->getpath($question, array('#', 'correctfeedback', 0, '#','file'), array(), false); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->correctfeedback['files'][] = $data; + } + + $qo->partiallycorrectfeedback = array(); + $qo->partiallycorrectfeedback['text'] = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); + $qo->partiallycorrectfeedback['format'] = $this->trans_format($this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '@', 'format'), '')); + $qo->partiallycorrectfeedback['files'] = array(); + // restore files in partiallycorrectfeedback + $files = $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '#','file'), array(), false); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->partiallycorrectfeedback['files'][] = $data; + } + + $qo->incorrectfeedback = array(); + $qo->incorrectfeedback['text'] = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); + $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath($question, array('#', 'incorrectfeedback', 0, '@', 'format'), '')); + $qo->incorrectfeedback['files'] = array(); + // restore files in incorrectfeedback + $files = $this->getpath($question, array('#', 'incorrectfeedback', 0, '#','file'), array(), false); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->incorrectfeedback['files'][] = $data; + } // There was a time on the 1.8 branch when it could output an empty answernumbering tag, so fix up any found. if (empty($qo->answernumbering)) { @@ -215,12 +308,13 @@ class qformat_xml extends qformat_default { $answers = $question['#']['answer']; $a_count = 0; foreach ($answers as $answer) { - $ans = $this->import_answer( $answer ); + $ans = $this->import_answer($answer); $qo->answer[$a_count] = $ans->answer; $qo->fraction[$a_count] = $ans->fraction; $qo->feedback[$a_count] = $ans->feedback; ++$a_count; } + return $qo; } @@ -268,7 +362,17 @@ class qformat_xml extends qformat_default { $warning = false; foreach ($question['#']['answer'] as $answer) { $answertext = $this->getpath( $answer, array('#','text',0,'#'), '', true ); - $feedback = $this->getpath($answer, array('#','feedback',0,'#','text',0,'#'), '', true ); + $feedback = $this->getpath($answer, array('#','feedback',0,'#','text',0,'#'), '', true); + $feedbackformat = $this->getpath($answer, array('#','feedback',0, '@', 'format'), '', true); + $feedbackfiles = $this->getpath($answer, array('#', 'feedback', 0, '#', 'file'), array(), false); + $files = array(); + foreach ($feedbackfiles as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $files[] = $data; + } if ($answertext != 'true' && $answertext != 'false') { $warning = true; $answertext = $first ? 'true' : 'false'; // Old style file, assume order is true/false. @@ -276,11 +380,19 @@ class qformat_xml extends qformat_default { if ($answertext == 'true') { $qo->answer = ($answer['@']['fraction'] == 100); $qo->correctanswer = $qo->answer; - $qo->feedbacktrue = $feedback; + $qo->feedbacktrue = array(); + $qo->feedbacktrue['text'] = $feedback; + $qo->feedbacktrue['format'] = $this->trans_format($feedbackformat); + $qo->feedbacktrue['itemid'] = null; + $qo->feedbacktruefiles = $files; } else { $qo->answer = ($answer['@']['fraction'] != 100); $qo->correctanswer = $qo->answer; - $qo->feedbackfalse = $feedback; + $qo->feedbackfalse = array(); + $qo->feedbackfalse['text'] = $feedback; + $qo->feedbackfalse['format'] = $this->trans_format($feedbackformat); + $qo->feedbackfalse['itemid'] = null; + $qo->feedbackfalsefiles = $files; } $first = false; } @@ -358,12 +470,12 @@ class qformat_xml extends qformat_default { $qo->tolerance = array(); foreach ($answers as $answer) { // answer outside of is deprecated - $answertext = trim( $this->getpath( $answer, array('#',0), '' ) ); - $qo->answer[] = $this->getpath( $answer, array('#','text',0,'#'), $answertext, true ); + $obj = $this->import_answer($answer); + $qo->answer[] = $obj->answer; if (empty($qo->answer)) { $qo->answer = '*'; } - $qo->feedback[] = $this->getpath( $answer, array('#','feedback',0,'#','text',0,'#'), '', true ); + $qo->feedback[] = $obj->feedback; $qo->tolerance[] = $this->getpath( $answer, array('#','tolerance',0,'#'), 0 ); // fraction as a tag is deprecated @@ -381,6 +493,21 @@ class qformat_xml extends qformat_default { $qo->unit[] = $this->getpath( $unit, array('#','unit_name',0,'#'), '', true ); } } + $instructions = $this->getpath($question, array('#', 'instructions'), array()); + if (!empty($instructions)) { + $qo->instructions = array(); + $qo->instructions['text'] = $instructions[0]['#']['text'][0]['#']; + $qo->instructions['format'] = $this->trans_format($instructions[0]['@']['format']); + $files = $instructions[0]['#']['file']; + $qo->instructionsfiles = array(); + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->instructionsfiles[] = $data; + } + } return $qo; } @@ -404,8 +531,22 @@ class qformat_xml extends qformat_default { // run through subquestions foreach ($subquestions as $subquestion) { - $qo->subquestions[] = $this->getpath( $subquestion, array('#','text',0,'#'), '', true ); - $qo->subanswers[] = $this->getpath( $subquestion, array('#','answer',0,'#','text',0,'#'), '', true); + $question = array(); + $question['text'] = $this->getpath($subquestion, array('#', 'text', 0, '#'), '', true); + $question['format'] = $this->trans_format($this->getpath($subquestion, array('@', 'format'), '', true)); + $question['files'] = array(); + + $files = $this->getpath($subquestion, array('#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass(); + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $question['files'][] = $data; + } + $qo->subquestions[] = $question; + $answers = $this->getpath($subquestion, array('#', 'answer'), array()); + $qo->subanswers[] = $this->getpath($subquestion, array('#','answer',0,'#','text',0,'#'), '', true); } return $qo; } @@ -422,8 +563,11 @@ class qformat_xml extends qformat_default { // header parts particular to essay $qo->qtype = ESSAY; + $answers = $question['#']['answer']; + $answer = array_pop($answers); + $answer = $this->import_answer($answer); // get feedback - $qo->feedback = $this->getpath( $question, array('#','answer',0,'#','feedback',0,'#','text',0,'#'), '', true ); + $qo->feedback = $answer->feedback; // get fraction - tag is deprecated $qo->fraction = $this->getpath( $question, array('@','fraction'), 0 ) / 100; @@ -432,7 +576,7 @@ class qformat_xml extends qformat_default { return $qo; } - function import_calculated( $question ) { + function import_calculated($question) { // import calculated question // get common parts @@ -446,16 +590,59 @@ class qformat_xml extends qformat_default { $shuffleanswers = $this->getpath( $question, array('#','shuffleanswers',0,'#'), 'false' ); $qo->answernumbering = $this->getpath( $question, array('#','answernumbering',0,'#'), 'abc' ); $qo->shuffleanswers = $this->trans_single($shuffleanswers); - $qo->correctfeedback = $this->getpath( $question, array('#','correctfeedback',0,'#','text',0,'#'), '', true ); - $qo->partiallycorrectfeedback = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); - $qo->incorrectfeedback = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); + + $qo->correctfeedback = array(); + $qo->correctfeedback['text'] = $this->getpath( $question, array('#','correctfeedback',0,'#','text',0,'#'), '', true ); + $qo->correctfeedback['format'] = $this->trans_format($this->getpath( $question, array('#', 'correctfeedback', 0, '@', 'formath'), '', true )); + $qo->correctfeedback['files'] = array(); + + $files = $this->getpath($question, array('#', 'correctfeedback', '0', '#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass(); + $data->content = $file['#']; + $data->name = $file['@']['name']; + $data->encoding = $file['@']['encoding']; + $qo->correctfeedback['files'][] = $data; + } + + $qo->partiallycorrectfeedback = array(); + $qo->partiallycorrectfeedback['text'] = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); + $qo->partiallycorrectfeedback['format'] = $this->trans_format($this->getpath($question, array('#','partiallycorrectfeedback', 0, '@','format'), '', true )); + $qo->partiallycorrectfeedback['files'] = array(); + + $files = $this->getpath($question, array('#', 'partiallycorrectfeedback', '0', '#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass(); + $data->content = $file['#']; + $data->name = $file['@']['name']; + $data->encoding = $file['@']['encoding']; + $qo->partiallycorrectfeedback['files'][] = $data; + } + + $qo->incorrectfeedback = array(); + $qo->incorrectfeedback['text'] = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); + $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath($question, array('#','incorrectfeedback', 0, '@','format'), '', true )); + $qo->incorrectfeedback['files'] = array(); + + $files = $this->getpath($question, array('#', 'incorrectfeedback', '0', '#', 'file'), array()); + foreach ($files as $file) { + $data = new stdclass(); + $data->content = $file['#']; + $data->name = $file['@']['name']; + $data->encoding = $file['@']['encoding']; + $qo->incorrectfeedback['files'][] = $data; + } + $qo->unitgradingtype = $this->getpath( $question, array('#','unitgradingtype',0,'#'), 0 ); $qo->unitpenalty = $this->getpath( $question, array('#','unitpenalty',0,'#'), 0 ); $qo->showunits = $this->getpath( $question, array('#','showunits',0,'#'), 0 ); $qo->unitsleft = $this->getpath( $question, array('#','unitsleft',0,'#'), 0 ); $qo->instructions = $this->getpath( $question, array('#','instructions',0,'#','text',0,'#'), '', true ); + + $files = $this->getpath($question, array('#', 'instructions', 0, '#', 'file', 0, '@'), '', false); + // get answers array - // echo "
 question";print_r($question);echo "
"; + // echo "
 question";print_r($question);echo "
"; $answers = $question['#']['answer']; $qo->answers = array(); $qo->feedback = array(); @@ -466,25 +653,18 @@ class qformat_xml extends qformat_default { $qo->correctanswerlength = array(); $qo->feedback = array(); foreach ($answers as $answer) { + $ans = $this->import_answer($answer); // answer outside of is deprecated - if (!empty( $answer['#']['text'] )) { - $answertext = $this->import_text( $answer['#']['text'] ); + if (empty($ans->answer['text'])) { + $ans->answer['text'] = '*'; } - else { - $answertext = trim($answer['#'][0]); - } - if ($answertext == '') { - $qo->answers[] = '*'; - } else { - $qo->answers[] = $answertext; - } - $qo->feedback[] = $this->import_text( $answer['#']['feedback'][0]['#']['text'] ); + $qo->answers[] = $ans->answer; + $qo->feedback[] = $ans->feedback; $qo->tolerance[] = $answer['#']['tolerance'][0]['#']; // fraction as a tag is deprecated if (!empty($answer['#']['fraction'][0]['#'])) { $qo->fraction[] = $answer['#']['fraction'][0]['#']; - } - else { + } else { $qo->fraction[] = $answer['@']['fraction'] / 100; } $qo->tolerancetype[] = $answer['#']['tolerancetype'][0]['#']; @@ -501,9 +681,26 @@ class qformat_xml extends qformat_default { $qo->unit[] = $unit['#']['unit_name'][0]['#']; } } - $datasets = $question['#']['dataset_definitions'][0]['#']['dataset_definition']; - $qo->dataset = array(); - $qo->datasetindex= 0 ; + $instructions = $this->getpath($question, array('#', 'instructions'), array()); + if (!empty($instructions)) { + $qo->instructions = array(); + $qo->instructions['text'] = $instructions[0]['#']['text'][0]['#']; + $qo->instructions['format'] = $this->trans_format($instructions[0]['@']['format']); + $qo->instructionsfiles = array(); + if (isset($instructions[0]['#']['file'])) { + $files = $instructions[0]['#']['file']; + foreach ($files as $file) { + $data = new stdclass; + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $qo->instructionsfiles[] = $data; + } + } + } + $datasets = $question['#']['dataset_definitions'][0]['#']['dataset_definition']; + $qo->dataset = array(); + $qo->datasetindex= 0 ; foreach ($datasets as $dataset) { $qo->datasetindex++; $qo->dataset[$qo->datasetindex] = new stdClass(); @@ -522,14 +719,13 @@ class qformat_xml extends qformat_default { $datasetitems = $dataset['#']['dataset_items'][0]['#']['dataset_item']; foreach ($datasetitems as $datasetitem) { $qo->dataset[$qo->datasetindex]->itemindex++; - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex] = new stdClass(); - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->itemnumber = $datasetitem['#']['number'][0]['#']; //[0]['#']['number'][0]['#'] ; // [0]['numberitems'] ;//['#']['number'][0]['#'];// $datasetitems['#']['number'][0]['#']; - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->value = $datasetitem['#']['value'][0]['#'] ;//$datasetitem['#']['value'][0]['#']; - } + $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex] = new stdClass(); + $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->itemnumber = $datasetitem['#']['number'][0]['#']; //[0]['#']['number'][0]['#'] ; // [0]['numberitems'] ;//['#']['number'][0]['#'];// $datasetitems['#']['number'][0]['#']; + $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->value = $datasetitem['#']['value'][0]['#'] ;//$datasetitem['#']['value'][0]['#']; + } } - // echo "
loaded qo";print_r($qo);echo "
"; - + // echo "
loaded qo";print_r($qo);echo "
"; return $qo; } @@ -558,12 +754,12 @@ class qformat_xml extends qformat_default { function readquestions($lines) { // we just need it as one big string $text = implode($lines, " "); - unset( $lines ); + unset($lines); // this converts xml to big nasty data structure // the 0 means keep white space as it is (important for markdown format) // print_r it if you want to see what it looks like! - $xml = xmlize( $text, 0 ); + $xml = xmlize($text, 0); // set up array to hold all our questions $questions = array(); @@ -742,7 +938,7 @@ class qformat_xml extends qformat_default { * @param boolean short stick it on one line * @return string formatted text */ - function writetext( $raw, $ilev=0, $short=true) { + function writetext($raw, $ilev=0, $short=true) { $indent = str_repeat( " ",$ilev ); // if required add CDATA tags @@ -790,35 +986,17 @@ class qformat_xml extends qformat_default { return $content; } - /** - * Include an image encoded in base 64 - * @param string imagepath The location of the image file - * @return string xml code segment - */ - function writeimage( $imagepath ) { - global $CFG; - - if (empty($imagepath)) { - return ''; - } - - $courseid = $this->course->id; - if (!$binary = file_get_contents( "{$CFG->dataroot}/$courseid/$imagepath" )) { - return ''; - } - - $content = " \n".base64_encode( $binary )."\n". - "\n \n"; - return $content; - } - /** * Turns question into an xml segment - * @param array question question array + * @param object question object + * @param int context id * @return string xml segment */ - function writequestion( $question ) { - global $CFG,$QTYPES, $OUTPUT; + function writequestion($question) { + global $CFG, $QTYPES, $OUTPUT; + + $fs = get_file_storage(); + $contextid = $question->contextid; // initial string; $expout = ""; @@ -841,28 +1019,32 @@ class qformat_xml extends qformat_default { $expout .= " \n"; $expout .= " \n"; return $expout; - } - elseif ($question->qtype != MULTIANSWER) { + } elseif ($question->qtype != MULTIANSWER) { // for all question types except Close - $name_text = $this->writetext( $question->name ); + $name_text = $this->writetext($question->name); $qtformat = $this->get_format($question->questiontextformat); - $question_text = $this->writetext( $question->questiontext ); - $generalfeedback = $this->writetext( $question->generalfeedback ); + $generalfeedbackformat = $this->get_format($question->generalfeedbackformat); + + $question_text = $this->writetext($question->questiontext); + $question_text_files = $this->writefiles($question->questiontextfiles); + + $generalfeedback = $this->writetext($question->generalfeedback); + $generalfeedback_files = $this->writefiles($question->generalfeedbackfiles); + $expout .= " \n"; $expout .= " $name_text\n"; $expout .= " \n"; $expout .= $question_text; + $expout .= $question_text_files; $expout .= " \n"; - $expout .= " {$question->image}\n"; - $expout .= $this->writeimage($question->image); - $expout .= " \n"; + $expout .= " \n"; $expout .= $generalfeedback; + $expout .= $generalfeedback_files; $expout .= " \n"; $expout .= " {$question->defaultgrade}\n"; $expout .= " {$question->penalty}\n"; $expout .= " {$question->hidden}\n"; - } - else { + } else { // for Cloze type only $name_text = $this->writetext( $question->name ); $question_text = $this->writetext( $question->questiontext ); @@ -899,8 +1081,10 @@ class qformat_xml extends qformat_default { } $expout .= " \n"; $expout .= $this->writetext($answertext, 3) . "\n"; - $expout .= " \n"; - $expout .= $this->writetext( $answer->feedback,4,false ); + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback,4,false); + $expout .= $this->writefiles($answer->feedbackfiles); $expout .= " \n"; $expout .= " \n"; } @@ -908,28 +1092,51 @@ class qformat_xml extends qformat_default { case MULTICHOICE: $expout .= " ".$this->get_single($question->options->single)."\n"; $expout .= " ".$this->get_single($question->options->shuffleanswers)."\n"; - $expout .= " ".$this->writetext($question->options->correctfeedback, 3)."\n"; - $expout .= " ".$this->writetext($question->options->partiallycorrectfeedback, 3)."\n"; - $expout .= " ".$this->writetext($question->options->incorrectfeedback, 3)."\n"; + + $textformat = $this->get_format($question->options->correctfeedbackformat); + $files = $fs->get_area_files($contextid, 'qtype_multichoice', 'correctfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->correctfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $textformat = $this->get_format($question->options->partiallycorrectfeedbackformat); + $files = $fs->get_area_files($contextid, 'qtype_multichoice', 'partiallycorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->partiallycorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $textformat = $this->get_format($question->options->incorrectfeedbackformat); + $files = $fs->get_area_files($contextid, 'qtype_multichoice', 'incorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->incorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + $expout .= " ".$this->writetext($question->options->answernumbering, 3)."\n"; foreach($question->options->answers as $answer) { $percent = $answer->fraction * 100; $expout .= " \n"; - $expout .= $this->writetext( $answer->answer,4,false ); - $expout .= " \n"; - $expout .= $this->writetext( $answer->feedback,5,false ); + $expout .= $this->writetext($answer->answer,4,false); + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback,5,false); + $expout .= $this->writefiles($answer->feedbackfiles); $expout .= " \n"; $expout .= " \n"; } break; case SHORTANSWER: - $expout .= " {$question->options->usecase}\n "; + $expout .= " {$question->options->usecase}\n "; foreach($question->options->answers as $answer) { $percent = 100 * $answer->fraction; $expout .= " \n"; $expout .= $this->writetext( $answer->answer,3,false ); - $expout .= " \n"; - $expout .= $this->writetext( $answer->feedback,4,false ); + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback); + $expout .= $this->writefiles($answer->feedbackfiles); $expout .= " \n"; $expout .= " \n"; } @@ -942,7 +1149,11 @@ class qformat_xml extends qformat_default { // tags are an added feature, old filed won't have them $expout .= " {$answer->answer}\n"; $expout .= " $tolerance\n"; - $expout .= " ".$this->writetext( $answer->feedback )."\n"; + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback); + $expout .= $this->writefiles($answer->feedbackfiles); + $expout .= " \n"; // fraction tag is deprecated // $expout .= " {$answer->fraction}\n"; $expout .= "\n"; @@ -959,12 +1170,25 @@ class qformat_xml extends qformat_default { } $expout .= "\n"; } + if (!empty($question->options->instructionsformat)) { + $textformat = $this->get_format($question->options->instructionsformat); + $files = $fs->get_area_files($contextid, 'qtype_numerical', 'instruction', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->instructions, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + } break; case MATCH: foreach($question->options->subquestions as $subquestion) { - $expout .= "\n"; - $expout .= $this->writetext( $subquestion->questiontext ); - $expout .= "".$this->writetext( $subquestion->answertext )."\n"; + $files = $fs->get_area_files($contextid, 'qtype_match', 'subquestion', $subquestion->id); + $textformat = $this->get_format($subquestion->questiontextformat); + $expout .= "\n"; + $expout .= $this->writetext($subquestion->questiontext); + $expout .= $this->writefiles($files); + $expout .= ""; + $expout .= $this->writetext($subquestion->answertext); + $expout .= "\n"; $expout .= "\n"; } break; @@ -985,7 +1209,11 @@ class qformat_xml extends qformat_default { foreach ($question->options->answers as $answer) { $percent = 100 * $answer->fraction; $expout .= "\n"; - $expout .= " ".$this->writetext( $answer->feedback )."\n"; + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback); + $expout .= $this->writefiles($answer->feedbackfiles); + $expout .= " \n"; // fraction tag is deprecated // $expout .= " {$answer->fraction}\n"; $expout .= "\n"; @@ -999,9 +1227,26 @@ class qformat_xml extends qformat_default { $expout .= " {$question->options->single}\n"; $expout .= " {$question->options->answernumbering}\n"; $expout .= " ".$this->writetext($question->options->shuffleanswers, 3)."\n"; - $expout .= " ".$this->writetext($question->options->correctfeedback, 3)."\n"; - $expout .= " ".$this->writetext($question->options->partiallycorrectfeedback, 3)."\n"; - $expout .= " ".$this->writetext($question->options->incorrectfeedback, 3)."\n"; + + $component = 'qtype_' . $question->qtype; + $files = $fs->get_area_files($contextid, $component, 'correctfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->correctfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $files = $fs->get_area_files($contextid, $component, 'partiallycorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->partiallycorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $files = $fs->get_area_files($contextid, $component, 'incorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->incorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + foreach ($question->options->answers as $answer) { $tolerance = $answer->tolerance; $tolerancetype = $answer->tolerancetype; @@ -1015,25 +1260,48 @@ class qformat_xml extends qformat_default { $expout .= " $tolerancetype\n"; $expout .= " $correctanswerformat\n"; $expout .= " $correctanswerlength\n"; - $expout .= " ".$this->writetext( $answer->feedback )."\n"; + $feedbackformat = $this->get_format($answer->feedbackformat); + $expout .= " \n"; + $expout .= $this->writetext($answer->feedback); + $expout .= $this->writefiles($answer->feedbackfiles); + $expout .= " \n"; $expout .= "\n"; } - $expout .= " {$question->options->unitgradingtype}\n"; - $expout .= " {$question->options->unitpenalty}\n"; - $expout .= " {$question->options->showunits}\n"; - $expout .= " {$question->options->unitsleft}\n"; - $expout .= " ".$this->writetext($question->options->instructions, 3)."\n"; - $units = $question->options->units; - if (count($units)) { - $expout .= "\n"; - foreach ($units as $unit) { - $expout .= " \n"; - $expout .= " {$unit->multiplier}\n"; - $expout .= " {$unit->unit}\n"; - $expout .= " \n"; + if (!empty($question->options->unitgradingtype)) { + $expout .= " {$question->options->unitgradingtype}\n"; + } + if (!empty($question->options->unitpenalty)) { + $expout .= " {$question->options->unitpenalty}\n"; + } + if (!empty($question->options->showunits)) { + $expout .= " {$question->options->showunits}\n"; + } + if (!empty($question->options->unitsleft)) { + $expout .= " {$question->options->unitsleft}\n"; + } + + if (!empty($question->options->instructionsformat)) { + $textformat = $this->get_format($question->options->instructionsformat); + $files = $fs->get_area_files($contextid, $component, 'instruction', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->instructions, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + } + + if (isset($question->options->units)) { + $units = $question->options->units; + if (count($units)) { + $expout .= "\n"; + foreach ($units as $unit) { + $expout .= " \n"; + $expout .= " {$unit->multiplier}\n"; + $expout .= " {$unit->unit}\n"; + $expout .= " \n"; + } + $expout .= "\n"; } - $expout .= "\n"; - } + } //The tag $question->export_process has been set so we get all the data items in the database // from the function $QTYPES['calculated']->get_question_options(&$question); // calculatedsimple defaults to calculated @@ -1080,8 +1348,11 @@ class qformat_xml extends qformat_default { // close the question tag $expout .= "\n"; + // XXX: debuging + //echo ''; + return $expout; } } - -?> diff --git a/question/import.php b/question/import.php index 7affec34e0d..48be0d45252 100644 --- a/question/import.php +++ b/question/import.php @@ -28,10 +28,12 @@ if (!$category = $DB->get_record("question_categories", array("id" => $catid))) $PAGE->set_pagelayout('standard'); +$categorycontext = get_context_instance_by_id($category->contextid); +$category->context = $categorycontext; //this page can be called without courseid or cmid in which case //we get the context from the category object. if ($contexts === null) { // need to get the course from the chosen category - $contexts = new question_edit_contexts(get_context_instance_by_id($category->contextid)); + $contexts = new question_edit_contexts($categorycontext); $thiscontext = $contexts->lowest(); if ($thiscontext->contextlevel == CONTEXT_COURSE){ require_login($thiscontext->instanceid, false); @@ -65,22 +67,14 @@ if ($form = $import_form->get_data()) { // work out if this is an uploaded file // or one from the filesarea. - if (!empty($form->choosefile)) { - $importfile = "{$CFG->dataroot}/{$COURSE->id}/{$form->choosefile}"; - $realfilename = $form->choosefile; - if (file_exists($importfile)) { - $fileisgood = true; - } else { - print_error('uploadproblem', 'moodle', $form->choosefile); - } - } else { - $realfilename = $import_form->get_new_filename('newfile'); - $importfile = "{$CFG->dataroot}/{$COURSE->id}/{$realfilename}"; - if (!$result = $import_form->save_file('newfile', $importfile, true)) { - print_error('uploadproblem', 'moodle'); - }else { - $fileisgood = true; - } + $realfilename = $import_form->get_new_filename('newfile'); + + $importfile = "{$CFG->dataroot}/temp/questionimport/{$realfilename}"; + make_upload_directory('temp/questionimport'); + if (!$result = $import_form->save_file('newfile', $importfile, true)) { + print_error('uploadproblem', 'moodle'); + }else { + $fileisgood = true; } // process if we are happy file is ok @@ -114,7 +108,7 @@ if ($form = $import_form->get_data()) { } // Process the uploaded file - if (! $qformat->importprocess()) { + if (! $qformat->importprocess($category)) { //TODO: need more detailed error info print_error('cannotimport', '', $thispageurl->out()); } diff --git a/question/type/calculated/questiontype.php b/question/type/calculated/questiontype.php index b0faf5e344f..9e646a47aa9 100644 --- a/question/type/calculated/questiontype.php +++ b/question/type/calculated/questiontype.php @@ -64,7 +64,7 @@ class question_calculated_qtype extends default_questiontype { "WHERE a.question = ? " . "AND a.id = c.answer ". "ORDER BY a.id ASC", array($question->id))) { - echo $OUTPUT->notification('Error: Missing question answer for calculated question ' . $question->id . '!'); + // echo $OUTPUT->notification('Error: Missing question answer for calculated question ' . $question->id . '!'); return false; } @@ -151,7 +151,13 @@ class question_calculated_qtype extends default_questiontype { $options->$feedbackname = trim($feedback['text']); $feedbackformat = $feedbackname . 'format'; $options->$feedbackformat = trim($feedback['format']); - $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculated', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + if (isset($feedback['files'])) { + foreach ($feedback['files'] as $file) { + $this->import_file($question->context, 'qtype_calculated', $feedbackname, $question->id, $file); + } + } else { + $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculated', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + } } if ($update) { @@ -182,21 +188,33 @@ class question_calculated_qtype extends default_questiontype { $question->answers=$question->answer; } foreach ($question->answers as $key => $dataanswer) { + if (is_array($dataanswer)) { + $dataanswer = $dataanswer['text']; + } if ( trim($dataanswer) != '' ) { $answer = new stdClass; $answer->question = $question->id; $answer->answer = trim($dataanswer); $answer->fraction = $question->fraction[$key]; $answer->feedbackformat = $question->feedback[$key]['format']; + if (isset($question->feedback[$key]['files'])) { + $files = $question->feedback[$key]['files']; + } if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it $answer->id = $oldanswer->id; $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); $DB->update_record("question_answers", $answer); } else { // This is a completely new answer - $answer->feedback = ''; + $answer->feedback = trim($question->feedback[$key]['text']); $answer->id = $DB->insert_record("question_answers", $answer); - $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); + if (isset($files)) { + foreach ($files as $file) { + $this->import_file($context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); + } $DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id)); } diff --git a/question/type/calculatedmulti/questiontype.php b/question/type/calculatedmulti/questiontype.php index 3c05862b1bc..d7a4ec059d7 100644 --- a/question/type/calculatedmulti/questiontype.php +++ b/question/type/calculatedmulti/questiontype.php @@ -65,7 +65,15 @@ class question_calculatedmulti_qtype extends question_calculated_qtype { $feedbackformat = $feedbackname . 'format'; $feedback = $question->$feedbackname; $options->$feedbackformat = $feedback['format']; - $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculatedmulti', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + if (isset($feedback['files'])) { + $options->$feedbackname = trim($feedback['text']); + $files = $feedback['files']; + foreach ($files as $file) { + $this->import_file($question->context, 'qtype_calculatedmulti', $feedbackname, $question->id, $file); + } + } else { + $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculatedmulti', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + } } if ($update) { @@ -97,6 +105,9 @@ class question_calculatedmulti_qtype extends question_calculated_qtype { $question->answers = $question->answer; } foreach ($question->answers as $key => $dataanswer) { + if (is_array($dataanswer)) { + $dataanswer = $dataanswer['text']; + } if ( trim($dataanswer) != '' ) { $answer = new stdClass; $answer->question = $question->id; @@ -104,6 +115,9 @@ class question_calculatedmulti_qtype extends question_calculated_qtype { $answer->fraction = $question->fraction[$key]; $answer->feedback = trim($question->feedback[$key]['text']); $answer->feedbackformat = $question->feedback[$key]['format']; + if (isset($question->feedback[$key]['files'])) { + $files = $question->feedback[$key]['files']; + } if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it $answer->id = $oldanswer->id; @@ -111,7 +125,14 @@ class question_calculatedmulti_qtype extends question_calculated_qtype { $DB->update_record("question_answers", $answer); } else { // This is a completely new answer $answer->id = $DB->insert_record("question_answers", $answer); - $feedbacktext = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback); + if (isset($files)) { + $feedbacktext = $answer->feedback; + foreach ($files as $file) { + $this->import_file($context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $feedbacktext = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback); + } $DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id)); } diff --git a/question/type/calculatedsimple/questiontype.php b/question/type/calculatedsimple/questiontype.php index 3cbc40173e5..ea9d6cb7d7a 100644 --- a/question/type/calculatedsimple/questiontype.php +++ b/question/type/calculatedsimple/questiontype.php @@ -64,21 +64,33 @@ class question_calculatedsimple_qtype extends question_calculated_qtype { $question->answers=$question->answer; } foreach ($question->answers as $key => $dataanswer) { + if (is_array($dataanswer)) { + $dataanswer = $dataanswer['text']; + } if ( trim($dataanswer) != '' ) { $answer = new stdClass; $answer->question = $question->id; $answer->answer = trim($dataanswer); $answer->fraction = $question->fraction[$key]; $answer->feedbackformat = $question->feedback[$key]['format']; + if (isset($question->feedback[$key]['files'])) { + $files = $question->feedback[$key]['files']; + } if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it $answer->id = $oldanswer->id; $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); $DB->update_record("question_answers", $answer); } else { // This is a completely new answer - $answer->feedback = ''; + $answer->feedback = trim($question->feedback[$key]['text']); $answer->id = $DB->insert_record("question_answers", $answer); - $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); + if (isset($files)) { + foreach ($files as $file) { + $this->import_file($context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text'])); + } $DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id)); } diff --git a/question/type/essay/questiontype.php b/question/type/essay/questiontype.php index 02680ccea9f..7c8395159aa 100644 --- a/question/type/essay/questiontype.php +++ b/question/type/essay/questiontype.php @@ -56,7 +56,14 @@ class question_essay_qtype extends default_questiontype { $answer->feedback = $question->feedback['text']; $answer->answer = $answer->feedback; $answer->id = $DB->insert_record('question_answers', $answer); - $answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text'])); + if (isset($question->feedback['files'])) { + // import + foreach ($question->feedback['files'] as $file) { + $this->import_file($context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text'])); + } $answer->answer = $answer->feedback; $DB->update_record('question_answers', $answer); } diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php index 76b97a8a086..c185bb0efba 100644 --- a/question/type/match/questiontype.php +++ b/question/type/match/questiontype.php @@ -51,7 +51,12 @@ class question_match_qtype extends default_questiontype { // Insert all the new question+answer pairs foreach ($question->subquestions as $key => $questiontext) { - $itemid = $questiontext['itemid']; + if (!empty($questiontext['itemid'])) { + $itemid = $questiontext['itemid']; + } + if (!empty($questiontext['files'])) { + $files = $questiontext['files']; + } $format = $questiontext['format']; $questiontext = trim($questiontext['text']); $answertext = trim($question->subanswers[$key]); @@ -73,7 +78,13 @@ class question_match_qtype extends default_questiontype { $subquestion->questiontextformat = $format; $subquestion->answertext = $answertext; $subquestion->id = $DB->insert_record("question_match_sub", $subquestion); - $questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext); + if (!isset($itemid)) { + foreach ($files as $file) { + $this->import_file($context, 'qtype_match', 'subquestion', $subquestion->id, $file); + } + } else { + $questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext); + } $DB->set_field('question_match_sub', 'questiontext', $questiontext, array('id'=>$subquestion->id)); } $subquestions[] = $subquestion->id; diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index d82366f7cf3..10a2da3a5de 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -74,14 +74,27 @@ class question_multichoice_qtype extends default_questiontype { $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']); $DB->update_record("question_answers", $answer); } else { + // import goes here too unset($answer); - $answer->answer = $dataanswer; + if (is_array($dataanswer)) { + $answer->answer = $dataanswer['text']; + $answer->answerformat = $dataanswer['format']; + } else { + $answer->answer = $dataanswer; + } $answer->question = $question->id; $answer->fraction = $question->fraction[$key]; - $answer->feedback = ''; + $answer->feedback = $question->feedback[$key]['text']; $answer->feedbackformat = $feedbackformat; $answer->id = $DB->insert_record("question_answers", $answer); - $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']); + if (isset($question->feedback[$key]['files'])) { + foreach ($question->feedback[$key]['files'] as $file) { + $this->import_file($context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']); + } + $DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id)); } $answers[] = $answer->id; @@ -114,9 +127,18 @@ class question_multichoice_qtype extends default_questiontype { foreach (array('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) { $feedbackname = $feedbacktype . 'feedback'; $feedbackformat = $feedbackname . 'format'; + $feedbackfiles = $feedbackname . 'files'; $feedback = $question->$feedbackname; $options->$feedbackformat = trim($feedback['format']); - $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_multichoice', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + $options->$feedbackname = trim($feedback['text']); + if (isset($feedback['files'])) { + // import + foreach ($feedback['files'] as $file) { + $this->import_file($context, 'qtype_multichoice', $feedbackname, $question->id, $file); + } + } else { + $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_multichoice', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text'])); + } } if ($update) { diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index a16f6c399b4..7a3d79c496e 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -167,6 +167,9 @@ class question_numerical_qtype extends question_shortanswer_qtype { // Insert all the new answers foreach ($question->answer as $key => $dataanswer) { + if (is_array($dataanswer)) { + $dataanswer = $dataanswer['text']; + } // Check for, and ingore, completely blank answer from the form. if (trim($dataanswer) == '' && $question->fraction[$key] == 0 && html_is_blank($question->feedback[$key]['text'])) { @@ -186,10 +189,13 @@ class question_numerical_qtype extends question_shortanswer_qtype { $answer->fraction = $question->fraction[$key]; $feedbacktext = trim($question->feedback[$key]['text']); - $draftid = $question->feedback[$key]['itemid']; - - $answer->feedbackformat = $question->feedback[$key]['format']; + if (!empty($question->feedback[$key]['itemid'])) { + $draftid = $question->feedback[$key]['itemid']; + } + if ($question->feedback[$key]['files']) { + $feedbackfiles = $question->feedback[$key]['files']; + } if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it $feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, $feedbacktext); @@ -199,7 +205,13 @@ class question_numerical_qtype extends question_shortanswer_qtype { } else { // This is a completely new answer $answer->feedback = $feedbacktext; $answer->id = $DB->insert_record("question_answers", $answer); - $feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $feedbacktext); + if (!isset($draftid) && isset($feedbackfiles)) { + foreach ($feedbackfiles as $file) { + $this->import_file($question->context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { + $feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $feedbacktext); + } $DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id)); } @@ -319,14 +331,21 @@ class question_numerical_qtype extends question_shortanswer_qtype { $options->instructions = '' ; } $component = 'qtype_' . $question->qtype; - $options->instructions = file_save_draft_area_files($question->instructions['itemid'], - $question->context->id, // context - $component, // component - 'instruction', // filearea - $question->id, // itemid - self::$fileoptions, // options - $question->instructions['text'] // text - ); + if (isset($question->instructionsfiles) && is_array($question->instructionsfiles)) { + // import + foreach ($question->instructionsfiles as $file) { + $this->import_file($question->context, $component, 'instruction', $question->id, $file); + } + } else { + $options->instructions = file_save_draft_area_files($question->instructions['itemid'], + $question->context->id, // context + $component, // component + 'instruction', // filearea + $question->id, // itemid + self::$fileoptions, // options + $question->instructions['text'] // text + ); + } if ($update) { $DB->update_record("question_numerical_options", $options); } else { diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 9281a7fa710..7e10310113b 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -1782,4 +1782,28 @@ class default_questiontype { } } } + + function import_file($context, $component, $filearea, $itemid, $file) { + $fs = get_file_storage(); + $record = new stdclass; + if (is_object($context)) { + $record->contextid = $context->id; + } else { + $record->contextid = $context; + } + $record->component = $component; + $record->filearea = $filearea; + $record->itemid = $itemid; + $record->filename = $file->name; + $record->filepath = '/'; + return $fs->create_file_from_string($record, $this->decode_file($file)); + } + + function decode_file($file) { + switch ($file->encoding) { + case 'base64': + default: + return base64_decode($file->content); + } + } } diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index 19ee8d277fd..412842e9ace 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -93,6 +93,9 @@ class question_shortanswer_qtype extends default_questiontype { // Insert all the new answers foreach ($question->answer as $key => $dataanswer) { + if (is_array($dataanswer)) { + $dataanswer = $dataanswer['text']; + } // Check for, and ingore, completely blank answer from the form. if (trim($dataanswer) == '' && $question->fraction[$key] == 0 && html_is_blank($question->feedback[$key]['text'])) { @@ -100,6 +103,9 @@ class question_shortanswer_qtype extends default_questiontype { } $feedbackformat = $question->feedback[$key]['format']; + if (isset($question->feedback[$key]['files'])) { + $feedbackfiles = $question->feedback[$key]['files']; + } if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it $answer = $oldanswer; @@ -117,12 +123,19 @@ class question_shortanswer_qtype extends default_questiontype { $answer->question = $question->id; $answer->fraction = $question->fraction[$key]; // feedback content needs to be rewriten - $answer->feedback = ''; + $answer->feedback = $question->feedback[$key]['text']; $answer->feedbackformat = $feedbackformat; $answer->id = $DB->insert_record("question_answers", $answer); + if (isset($feedbackfiles)) { + // import + foreach ($feedbackfiles as $file) { + $this->import_file($question->context, 'question', 'answerfeedback', $answer->id, $file); + } + } else { // save draft file and rewrite text in feedback - $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']); + $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']); + } // update feedback content $DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id)); } diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index a0935cd5de0..1f06e1ae2ad 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -35,6 +35,7 @@ class question_truefalse_qtype extends default_questiontype { function save_question_options($question) { global $DB; $result = new stdClass; + $fs = get_file_storage(); // fetch old answer ids so that we can reuse them if (!$oldanswers = $DB->get_records("question_answers", array("question" => $question->id), "id ASC")) { @@ -44,6 +45,9 @@ class question_truefalse_qtype extends default_questiontype { $feedbacktext = $question->feedbacktrue['text']; $feedbackitemid = $question->feedbacktrue['itemid']; $feedbackformat = $question->feedbacktrue['format']; + if (isset($question->feedbacktruefiles)) { + $files = $question->feedbacktruefiles; + } // Save answer 'True' if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it $true->answer = get_string("true", "quiz"); @@ -61,7 +65,15 @@ class question_truefalse_qtype extends default_questiontype { $true->feedback = ''; $true->feedbackformat = $feedbackformat; $true->id = $DB->insert_record("question_answers", $true); - $feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext); + // import + if (isset($files)) { + foreach ($files as $file) { + $this->import_file($question->context, 'question', 'answerfeedback', $true->id, $file); + } + } else { + // moodle form + $feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext); + } $DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$true->id)); } @@ -85,7 +97,13 @@ class question_truefalse_qtype extends default_questiontype { $false->feedback = ''; $false->feedbackformat = $feedbackformat; $false->id = $DB->insert_record("question_answers", $false); - $feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext); + if ($feedbackitemid == null && !empty($files)) { + foreach ($files as $file) { + $this->import_file($question->context, 'question', 'answerfeedback', $false->id, $file); + } + } else { + $feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext); + } $DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$false->id)); }