diff --git a/admin/settings/server.php b/admin/settings/server.php index 4b52291..ab8fb66 100644 --- a/admin/settings/server.php +++ b/admin/settings/server.php @@ -72,16 +72,6 @@ $temp->add(new admin_setting_configtext('supportpage', get_string('supportpage', $ADMIN->add('server', $temp); -// Jabber settingpage -$temp = new admin_settingpage('jabber', get_string('jabber', 'admin')); -$temp->add(new admin_setting_configtext('jabberhost', get_string('jabberhost', 'admin'), get_string('configjabberhost', 'admin'), '', PARAM_RAW)); -$temp->add(new admin_setting_configtext('jabberserver', get_string('jabberserver', 'admin'), get_string('configjabberserver', 'admin'), '', PARAM_RAW)); -$temp->add(new admin_setting_configtext('jabberusername', get_string('jabberusername', 'admin'), get_string('configjabberusername', 'admin'), '', PARAM_RAW)); -$temp->add(new admin_setting_configpasswordunmask('jabberpassword', get_string('jabberpassword', 'admin'), get_string('configjabberpassword', 'admin'), '')); -$temp->add(new admin_setting_configtext('jabberport', get_string('jabberport', 'admin'), get_string('configjabberport', 'admin'), 5222, PARAM_INT)); -$ADMIN->add('server', $temp); - - // "sessionhandling" settingpage $temp = new admin_settingpage('sessionhandling', get_string('sessionhandling', 'admin')); diff --git a/course/report/participation/index.php b/course/report/participation/index.php index c0d9fe8..f2600d6 100644 --- a/course/report/participation/index.php +++ b/course/report/participation/index.php @@ -189,32 +189,35 @@ } list($actionsql, $params) = $DB->get_in_or_equal($actions, SQL_PARAMS_NAMED, 'action0'); - $actionsql = "action $actionsql"; + $actionsql = "l.action $actionsql"; $relatedcontexts = get_related_contexts_string($context); - $sql = "SELECT ra.userid, u.firstname, u.lastname, u.idnumber, l.actioncount AS count - FROM (SELECT * FROM {role_assignments} WHERE contextid $relatedcontexts AND roleid = :roleid ) ra - JOIN {user} u ON u.id = ra.userid - LEFT JOIN ( - SELECT userid, COUNT(action) actioncount FROM {log} WHERE cmid = :instanceid AND time > :timefrom AND $actionsql GROUP BY userid - ) l ON (l.userid = ra.userid)"; + $sql = "SELECT ra.userid, u.firstname, u.lastname, u.idnumber, COUNT(l.action) AS count + FROM {role_assignments} ra + JOIN {user} u ON u.id = ra.userid + LEFT JOIN {log} l ON (l.userid = ra.userid AND l.cmid = :instanceid AND l.time > :timefrom AND $actionsql) + WHERE ra.contextid $relatedcontexts AND ra.roleid = :roleid"; $params['roleid'] = $roleid; $params['instanceid'] = $instanceid; $params['timefrom'] = $timefrom; list($twhere, $tparams) = $table->get_sql_where(); if ($twhere) { - $sql .= ' WHERE '.$twhere; //initial bar + $sql .= ' AND '.$twhere; //initial bar $params = array_merge($params, $tparams); } + $sql .= " GROUP BY ra.userid, u.firstname, u.lastname, u.idnumber"; + if ($table->get_sql_sort()) { $sql .= ' ORDER BY '.$table->get_sql_sort(); } $countsql = "SELECT COUNT(DISTINCT(ra.userid)) FROM {role_assignments} ra + JOIN {user} u ON u.id = ra.userid + LEFT OUTER JOIN {log} l ON (l.userid = ra.userid AND l.cmid = :instanceid AND l.time > :timefrom AND $actionsql) WHERE ra.contextid $relatedcontexts AND ra.roleid = :roleid"; $totalcount = $DB->count_records_sql($countsql, $params); diff --git a/lang/en/admin.php b/lang/en/admin.php index a5fa5b3..1486e01 100755 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -234,11 +234,6 @@ $string['configintroadmin'] = 'On this page you should configure your main admin $string['configintrosite'] = 'This page allows you to configure the front page and name of this new site. You can come back here later to change these settings any time using the Administration menus.'; $string['configintrotimezones'] = 'This page will search for new information about world timezones (including daylight savings time rules) and update your local database with this information. These locations will be checked, in order: {$a} This procedure is generally very safe and can not break normal installations. Do you wish to update your timezones now?'; $string['configiplookup'] = 'When you click on an IP address (such as 34.12.222.93), such as in the logs, you are shown a map with a best guess of where that IP is located. There are different plugins for this that you can choose from, each has benefits and disadvantages.'; -$string['configjabberhost'] = 'The server to connect to to send jabber message notifications'; -$string['configjabberserver'] = 'XMPP host ID (can be left empty if the same as Jabber host)'; -$string['configjabberusername'] = 'The user name to use when connecting to the Jabber server'; -$string['configjabberpassword'] = 'The password to use when connecting to the Jabber server'; -$string['configjabberport'] = 'The port to use when connecting to the Jabber server'; $string['configkeeptagnamecase'] = 'Check this if you want tag names to keep the original casing as entered by users who created them'; $string['configlang'] = 'Choose a default language for the whole site. Users can override this setting using the language menu or the setting in their personal profile.'; $string['configlangcache'] = 'Cache the language menu. Saves a lot of memory and processing power. If you enable this, the menu takes a few minutes to update after you have added or removed languages.'; @@ -602,12 +597,6 @@ It is recommended to install local copy of free GeoLite City database from MaxMi IP address location is displayed on simple map or using Google Maps. Please note that you need to have a Google account and apply for free Google Maps API key to enable interactive maps.'; $string['iplookupmaxmindnote'] = 'This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/.'; $string['iplookupnetgeonote'] = 'The NetGeo server is currently being used to look up geographical information. For more accurate results we recommend installing a local copy of the MaxMind GeoLite database.'; -$string['jabber'] = 'Jabber'; -$string['jabberhost'] = 'Jabber host'; -$string['jabberserver'] = 'Jabber server'; -$string['jabberusername'] = 'Jabber user name'; -$string['jabberpassword'] = 'Jabber password'; -$string['jabberport'] = 'Jabber port'; $string['keeptagnamecase'] = 'Keep tag name casing'; $string['lang'] = 'Default language'; $string['langcache'] = 'Cache language menu'; diff --git a/lang/en/message.php b/lang/en/message.php index 1b49d4c..379586b 100644 --- a/lang/en/message.php +++ b/lang/en/message.php @@ -46,7 +46,7 @@ $string['disabled'] = 'Messaging is disabled on this site'; $string['discussion'] = 'Discussion'; $string['editmymessage'] = 'Messaging'; $string['emailmessages'] = 'Email messages when I am offline'; -$string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.'; +$string['emailtagline'] = 'This email is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.'; $string['emptysearchstring'] = 'You must search for something'; $string['errorcallingprocessor'] = 'Error calling defined processor'; $string['formorethan'] = 'For more than'; diff --git a/lib/questionlib.php b/lib/questionlib.php index dc04838..db8a75d 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -3270,10 +3270,69 @@ function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filea function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) { global $DB, $CFG; + require_login($course, false); + + if ($filearea === '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, 'contextid' => $context->id))) { + send_file_not_found(); + } + + $qformat->setCategory($category); + $contexts = new question_edit_contexts($context); + $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 diff --git a/message/output/jabber/message_output_jabber.php b/message/output/jabber/message_output_jabber.php index 9481161..75fb6c5 100644 --- a/message/output/jabber/message_output_jabber.php +++ b/message/output/jabber/message_output_jabber.php @@ -31,6 +31,14 @@ * @package */ + +define("JABBER_SERVER","jabber80.com"); +define("JABBER_USERNAME",""); +define("JABBER_PASSWORD",""); +define("JABBER_PORT",5222); + +define("RUN_TIME",15); // set a maximum run time of 15 seconds + require_once($CFG->dirroot.'/message/output/lib.php'); require_once($CFG->libdir.'/jabber/XMPP/XMPP.php'); @@ -42,7 +50,7 @@ class message_output_jabber extends message_output { * @return true if ok, false if error */ function send_message($message){ - global $DB, $CFG; + global $DB; if (!$userfrom = $DB->get_record('user', array('id' => $message->useridfrom))) { return false; @@ -53,12 +61,11 @@ class message_output_jabber extends message_output { if (!$jabberaddress = get_user_preferences('message_processor_jabber_jabberid', $userto->email, $userto->id)) { $jabberaddress = $userto->email; } - $jabbermessage = fullname($userfrom).': '.$message->smallmessage; + $jabbermessage = fullname($userfrom).': '.$message->fullmessage; - $conn = new XMPPHP_XMPP($CFG->jabberhost,$CFG->jabberport,$CFG->jabberusername,$CFG->jabberpassword,'moodle',$CFG->jabberserver); + $conn = new XMPPHP_XMPP(JABBER_SERVER, JABBER_PORT, JABBER_USERNAME, JABBER_PASSWORD, 'moodle', JABBER_SERVER); try { - //$conn->useEncryption(false); $conn->connect(); $conn->processUntil('session_start'); $conn->presence(); diff --git a/question/export.php b/question/export.php index fd965af..775065e 100644 --- a/question/export.php +++ b/question/export.php @@ -37,68 +37,27 @@ $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)); - - - if ($from_form = $export_form->get_data()) { /// Filename + $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()) { 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'; } + $export_url = moodle_url::make_pluginfile_url($category->contextid, 'question', 'export', null, "/{$category->id}/{$from_form->format}/{$withcategories}/{$withcontexts}/", "export.xml"); + echo $OUTPUT->box_start(); + echo get_string('yourfileshoulddownload', 'question', $export_url->out()); + echo $OUTPUT->box_end(); - if (! $qformat->exportprocess()) { // Process the export data - print_error('exporterror', 'question', $thispageurl->out()); - } - - if (! $qformat->exportpostprocess()) { // In case anything needs to be done after - print_error('exporterror', 'question', $thispageurl->out()); - } - echo "
"; - - // 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 +70,3 @@ $export_form->display(); echo $OUTPUT->footer(); - diff --git a/question/export_form.php b/question/export_form.php index 3528ed2..f76a991 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 d8c52be..3748307 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 b5a3b91..0876601 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'] ); - } - else { - $answertext = trim($answer['#'][0]); - } - if ($answertext == '') { - $qo->answers[] = '*'; - } else { - $qo->answers[] = $answertext; + if (empty($ans->answer['text'])) { + $ans->answer['text'] = '*'; } - $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,24 @@ 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']); + $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; + } + } + $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 +717,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 +752,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 +936,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 @@ -791,34 +985,16 @@ class qformat_xml extends qformat_default { } /** - * 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 +1017,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 +1079,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 +1090,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 +1147,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 +1168,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 +1207,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 +1225,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 +1258,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 +1346,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 7affec3..c1b9a88 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,12 @@ 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}/{$COURSE->id}/{$realfilename}"; + 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 +106,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 8552ebc..75021ce 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 c5b019e..9181940 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 3cbc401..ea9d6cb 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 02680cc..7c83951 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 b4ea788..a97b2d3 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 772b37a..1e2b5dc 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 2dc1413..75e4aa0 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)); } @@ -303,14 +315,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 c067542..9162d99 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -1823,4 +1823,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 4404bd8..2d5dda1 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 c2fb469..26179e6 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)); }