3 ////////////////////////////////////////////////////////////////////
4 /// format.php - Default format class for file imports/exports. //
6 /// Doesn't do everything on it's own -- it needs to be extended. //
7 ////////////////////////////////////////////////////////////////////
9 // Included by import.php and export.php
11 class qformat_default
{
13 var $displayerrors = true;
16 var $questionids = array();
18 // functions to indicate import/export functionality
19 // override to return true if implemented
21 function provide_import() {
25 function provide_export() {
29 /// Importing functions
31 function importpreprocess($category, $course=NULL ) {
32 /// Does any pre-processing that may be desired
34 $this->category
= $category; // Important
35 $this->course
= $course;
42 * @PARAM $matchgrades string 'error' or 'nearest', mismatched grades handling
44 function importprocess($filename, $matchgrades='error') {
45 /// Processes a given file. There's probably little need to change this
47 if (! $lines = $this->readdata($filename)) {
48 notify( get_string('cannotread','quiz') );
52 if (! $questions = $this->readquestions($lines)) { // Extract all the questions
53 notify( get_string('noquestionsinfile','quiz') );
57 notify( get_string('importingquestions','quiz',count($questions)) );
59 // get list of valid answer grades
60 $grades = get_grade_options();
61 $gradeoptionsfull = $grades->gradeoptionsfull
;
65 foreach ($questions as $question) { // Process and store each question
68 echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext
)."</p>";
70 // check for answer grades validity (must match fixed list of grades)
71 if (!empty($question->fraction
)) {
72 $fractions = $question->fraction
;
73 $answersvalid = true; // in case they are!
74 foreach ($fractions as $key => $fraction) {
75 $newfraction = match_grade_options($gradeoptionsfull, $fraction, $matchgrades);
76 if ($newfraction===false) {
77 $answersvalid = false;
80 $fractions[$key] = $newfraction;
84 notify( get_string('matcherror','quiz') );
88 $question->fraction
= $fractions;
92 $question->category
= $this->category
->id
;
93 $question->stamp
= make_unique_id_code(); // Set the unique code (not to be changed)
95 if (!$question->id
= insert_record("question", $question)) {
96 error( get_string('cannotinsert','quiz') );
99 $this->questionids
[] = $question->id
;
101 // Now to save all the answers and type-specific options
104 $result = $QTYPES[$question->qtype
]
105 ->save_question_options($question);
107 if (!empty($result->error
)) {
108 notify($result->error
);
112 if (!empty($result->notice
)) {
113 notify($result->notice
);
117 // Give the question a unique version stamp determined by question_hash()
118 set_field('question', 'version', question_hash($question), 'id', $question->id
);
124 function readdata($filename) {
125 /// Returns complete file with an array, one item per line
127 if (is_readable($filename)) {
128 $filearray = file($filename);
130 /// Check for Macintosh OS line returns (ie file on one line), and fix
131 if (ereg("\r", $filearray[0]) AND !ereg("\n", $filearray[0])) {
132 return explode("\r", $filearray[0]);
140 function readquestions($lines) {
141 /// Parses an array of lines into an array of questions,
142 /// where each item is a question object as defined by
143 /// readquestion(). Questions are defined as anything
144 /// between blank lines.
146 $questions = array();
147 $currentquestion = array();
149 foreach ($lines as $line) {
152 if (!empty($currentquestion)) {
153 if ($question = $this->readquestion($currentquestion)) {
154 $questions[] = $question;
156 $currentquestion = array();
159 $currentquestion[] = $line;
163 if (!empty($currentquestion)) { // There may be a final question
164 if ($question = $this->readquestion($currentquestion)) {
165 $questions[] = $question;
173 function defaultquestion() {
174 // returns an "empty" question
175 // Somewhere to specify question parameters that are not handled
176 // by import but are required db fields.
177 // This should not be overridden.
180 $question = new stdClass();
181 $question->shuffleanswers
= $CFG->quiz_shuffleanswers
;
182 $question->defaultgrade
= 1;
183 $question->image
= "";
184 $question->usecase
= 0;
185 $question->multiplier
= array();
187 // this option in case the questiontypes class wants
188 // to know where the data came from
189 $question->export_process
= true;
190 $question->import_process
= true;
195 function readquestion($lines) {
196 /// Given an array of lines known to define a question in
197 /// this format, this function converts it into a question
198 /// object suitable for processing and insertion into Moodle.
200 $formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
201 echo "<p>$formatnotimplemented</p>";
207 function importpostprocess() {
208 /// Does any post-processing that may be desired
209 /// Argument is a simple array of question ids that
210 /// have just been added.
215 function importimagefile( $path, $base64 ) {
216 /// imports an image file encoded in base64 format
217 /// This should not be overridden.
220 // all this to get the destination directory
222 $fullpath = "{$CFG->dataroot}/{$this->course->id}/$path";
223 $path_parts = pathinfo( $fullpath );
224 $destination = $path_parts['dirname'];
225 $file = clean_filename( $path_parts['basename'] );
227 // detect and fix any filename collision - get unique filename
228 $newfiles = resolve_filename_collisions( $destination, array($file) );
229 $newfile = $newfiles[0];
231 // convert and save file contents
232 if (!$content = base64_decode( $base64 )) {
235 $newfullpath = "$destination/$newfile";
236 if (!$fh = fopen( $newfullpath, 'w' )) {
239 if (!fwrite( $fh, $content )) {
244 // return the (possibly) new filename
252 function export_file_extension() {
253 /// return the files extension appropriate for this type
254 /// override if you don't want .txt
259 function exportpreprocess($category, $course) {
260 /// Does any pre-processing that may be desired
262 $this->category
= $category; // Important
263 $this->course
= $course; // As is this!
268 function presave_process( $content ) {
269 /// enables any processing to be done on the content
270 /// just prior to the file being saved
271 /// default is to do nothing
276 function exportprocess($filename) {
277 /// Exports a given category. There's probably little need to change this
281 // create a directory for the exports (if not already existing)
282 if (! $export_dir = make_upload_directory($this->question_get_export_dir())) {
283 error( get_string('cannotcreatepath','quiz',$export_dir) );
285 $path = $CFG->dataroot
.'/'.$this->question_get_export_dir();
287 // get the questions (from database) in this category
288 // only get q's with no parents (no cloze subquestions specifically)
289 $questions = get_questions_category( $this->category
, true );
291 notify( get_string('exportingquestions','quiz') );
292 if (!count($questions)) {
293 notify( get_string('noquestions','quiz') );
298 // results are first written into string (and then to a file)
299 // so create/initialize the string here
302 // iterate through questions
303 foreach($questions as $question) {
304 // do not export hidden questions
305 if (!empty($question->hidden
)) {
309 // do not export random questions
310 if ($question->qtype
==RANDOM
) {
314 // export the question displaying message
316 echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext
)."</p>";
317 $expout .= $this->writequestion( $question ) . "\n";
320 // final pre-process on exported data
321 $expout = $this->presave_process( $expout );
324 $filepath = $path."/".$filename . $this->export_file_extension();
325 if (!$fh=fopen($filepath,"w")) {
326 error( get_string('cannotopen','quiz',$filepath) );
328 if (!fwrite($fh, $expout)) {
329 error( get_string('cannotwrite','quiz',$filepath) );
336 function exportpostprocess() {
337 /// Does any post-processing that may be desired
342 function writequestion($question) {
343 /// Turns a question object into textual output in the given format
344 /// must be overidden
346 // if not overidden, then this is an error.
347 $formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
348 echo "<p>$formatnotimplemented</p>";
353 function question_get_export_dir() {
354 $dirname = get_string("exportfilename","quiz");
355 $path = $this->course
->id
.'/backupdata/'.$dirname; // backupdata is protected directory