category = $category; // Important $this->course = $course; return true; } /** * * @param $matchgrades string 'error' or 'nearest', mismatched grades handling */ function importprocess($filename, $matchgrades='error') { /// Processes a given file. There's probably little need to change this if (! $lines = $this->readdata($filename)) { notify( get_string('cannotread','quiz') ); return false; } if (! $questions = $this->readquestions($lines)) { // Extract all the questions notify( get_string('noquestionsinfile','quiz') ); return false; } notify( get_string('importingquestions','quiz',count($questions)) ); // get list of valid answer grades $grades = get_grade_options(); $gradeoptionsfull = $grades->gradeoptionsfull; $count = 0; foreach ($questions as $question) { // Process and store each question $count++; echo "
$count. ".stripslashes($question->questiontext)."
"; // check for answer grades validity (must match fixed list of grades) if (!empty($question->fraction)) { $fractions = $question->fraction; $answersvalid = true; // in case they are! foreach ($fractions as $key => $fraction) { $newfraction = match_grade_options($gradeoptionsfull, $fraction, $matchgrades); if ($newfraction===false) { $answersvalid = false; } else { $fractions[$key] = $newfraction; } } if (!$answersvalid) { notify( get_string('matcherror','quiz') ); continue; } else { $question->fraction = $fractions; } } $question->category = $this->category->id; $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) if (!$question->id = insert_record("question", $question)) { error( get_string('cannotinsert','quiz') ); } $this->questionids[] = $question->id; // Now to save all the answers and type-specific options global $QTYPES; $result = $QTYPES[$question->qtype] ->save_question_options($question); if (!empty($result->error)) { notify($result->error); return false; } if (!empty($result->notice)) { notify($result->notice); return true; } // Give the question a unique version stamp determined by question_hash() set_field('question', 'version', question_hash($question), 'id', $question->id); } return true; } function readdata($filename) { /// Returns complete file with an array, one item per line if (is_readable($filename)) { $filearray = file($filename); /// Check for Macintosh OS line returns (ie file on one line), and fix if (ereg("\r", $filearray[0]) AND !ereg("\n", $filearray[0])) { return explode("\r", $filearray[0]); } else { return $filearray; } } return false; } function readquestions($lines) { /// Parses an array of lines into an array of questions, /// where each item is a question object as defined by /// readquestion(). Questions are defined as anything /// between blank lines. $questions = array(); $currentquestion = array(); foreach ($lines as $line) { $line = trim($line); if (empty($line)) { if (!empty($currentquestion)) { if ($question = $this->readquestion($currentquestion)) { $questions[] = $question; } $currentquestion = array(); } } else { $currentquestion[] = $line; } } if (!empty($currentquestion)) { // There may be a final question if ($question = $this->readquestion($currentquestion)) { $questions[] = $question; } } return $questions; } function defaultquestion() { // returns an "empty" question // Somewhere to specify question parameters that are not handled // by import but are required db fields. // This should not be overridden. $question = new stdClass(); $question->shuffleanswers = 0; $question->defaultgrade = 1; $question->image = ""; $question->usecase = 0; $question->multiplier = array(); return $question; } function readquestion($lines) { /// Given an array of lines known to define a question in /// this format, this function converts it into a question /// object suitable for processing and insertion into Moodle. $formatnotimplemented = get_string( 'formatnotimplemented','quiz' ); echo "$formatnotimplemented
"; return NULL; } function importpostprocess() { /// Does any post-processing that may be desired /// Argument is a simple array of question ids that /// have just been added. return true; } function importimagefile( $path, $base64 ) { /// imports an image file encoded in base64 format /// This should not be overridden. global $CFG; // 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'] ); // 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 false; } $newfullpath = "$destination/$newfile"; if (!$fh = fopen( $newfullpath, 'w' )) { return false; } if (!fwrite( $fh, $content )) { return false; } fclose( $fh ); // return the (possibly) new filename return $newfile; } //================= // Export functions //================= function export_file_extension() { /// return the files extension appropriate for this type /// override if you don't want .txt return ".txt"; } function exportpreprocess($category, $course) { /// Does any pre-processing that may be desired $this->category = $category; // Important $this->course = $course; // As is this! return true; } function presave_process( $content ) { /// enables any processing to be done on the content /// just prior to the file being saved /// default is to do nothing return $content; } function exportprocess($filename) { /// Exports a given category. There's probably little need to change this global $CFG; // create a directory for the exports (if not already existing) if (! $export_dir = make_upload_directory($this->question_get_export_dir())) { error( get_string('cannotcreatepath','quiz',$export_dir) ); } $path = $CFG->dataroot.'/'.$this->question_get_export_dir(); // get the questions (from database) in this category // only get q's with no parents (no cloze subquestions specifically) $questions = get_questions_category( $this->category, true ); notify( get_string('exportingquestions','quiz') ); if (!count($questions)) { notify( get_string('noquestions','quiz') ); return false; } $count = 0; // results are first written into string (and then to a file) // so create/initialize the string here $expout = ""; // iterate through questions foreach($questions as $question) { // do not export hidden questions if (!empty($question->hidden)) { continue; } // do not export random questions if ($question->qtype==RANDOM) { continue; } // export the question displaying message $count++; echo "$count. ".stripslashes($question->questiontext)."
"; $expout .= $this->writequestion( $question ) . "\n"; } // final pre-process on exported data $expout = $this->presave_process( $expout ); // write file $filepath = $path."/".$filename . $this->export_file_extension(); if (!$fh=fopen($filepath,"w")) { error( get_string('cannotopen','quiz',$filepath) ); } if (!fwrite($fh, $expout)) { error( get_string('cannotwrite','quiz',$filepath) ); } fclose($fh); return true; } function exportpostprocess() { /// Does any post-processing that may be desired return true; } function writequestion($question) { /// Turns a question object into textual output in the given format /// must be overidden // if not overidden, then this is an error. $formatnotimplemented = get_string( 'formatnotimplemented','quiz' ); echo "$formatnotimplemented
"; return NULL; } function question_get_export_dir() { $dirname = get_string("exportfilename","quiz"); $path = $this->course->id.'/backupdata/'.$dirname; // backupdata is protected directory return $path; } } ?>