Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / mod / quiz / backuplib.php
bloba821067250ff2ff1d04dd0c439e8f99afeb34b0f
1 <?php //$Id$
2 //This php script contains all the stuff to backup quizzes
4 //This is the "graphical" structure of the quiz mod:
5 //To see, put your terminal to 160cc
7 //
8 // quiz
9 // (CL,pk->id)
10 // |
11 // -------------------------------------------------------------------
12 // | | | | |
13 // | quiz_grades | quiz_question_versions |
14 // | (UL,pk->id,fk->quiz) | (CL,pk->id,fk->quiz) |
15 // | | |
16 // quiz_attempts quiz_question_instances quiz_feedback
17 // (UL,pk->id,fk->quiz) (CL,pk->id,fk->quiz,question) (CL,pk->id,fk->quiz)
19 // Meaning: pk->primary key field of the table
20 // fk->foreign key to link with parent
21 // nt->nested field (recursive data)
22 // SL->site level info
23 // CL->course level info
24 // UL->user level info
25 // files->table may have files
27 //-----------------------------------------------------------
29 // When we backup a quiz we also need to backup the questions and possibly
30 // the data about student interaction with the questions. The functions to do
31 // that are included with the following library
32 require_once("$CFG->libdir/questionlib.php");
33 require_once("$CFG->dirroot/question/backuplib.php");
35 //STEP 1. Backup categories/questions and associated structures
36 // (course independent)
38 //Insert necessary category ids to backup_ids table
39 function insert_category_ids($course, $backup_unique_code, $instances = null) {
40 global $CFG;
41 include_once("$CFG->dirroot/mod/quiz/lib.php");
43 // Create missing categories and reasign orphaned questions.
44 fix_orphaned_questions($course);
46 // First, all categories from this course.
47 $status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids
48 (backup_code, table_name, old_id, info)
49 SELECT '$backup_unique_code', 'question_categories', qc.id, ''
50 FROM {$CFG->prefix}question_categories qc
51 WHERE qc.course = $course", false);
53 // Then published categories from other courses used by the quizzes we are backing up.
54 $from = "{$CFG->prefix}quiz quiz,";
55 $where = "AND quiz.course = '$course'
56 AND qqi.quiz = quiz.id";
57 if (!empty($instances) && is_array($instances) && count($instances)) {
58 $from = '';
59 $where = 'AND qqi.quiz IN ('.implode(',',array_keys($instances)).')';
61 $categories = get_records_sql("
62 SELECT id, parent, 0 AS childrendone
63 FROM {$CFG->prefix}question_categories
64 WHERE course <> $course
65 AND id IN (
66 SELECT DISTINCT question.category
67 FROM {$CFG->prefix}question question,
68 $from
69 {$CFG->prefix}quiz_question_instances qqi
70 WHERE qqi.question = question.id
71 $where
72 )", false);
73 if (!$categories) {
74 $categories = array();
77 // Add the parent categories, of these categories up to the top of the category tree.
78 foreach ($categories as $category) {
79 while ($category->parent != 0) {
80 if (array_key_exists($category->parent, $categories)) {
81 // Parent category already on the list.
82 break;
84 $currentid = $category->id;
85 $category = get_record('question_categories', 'id', $category->parent, '', '', '', '', 'id, parent, 0 AS childrendone');
86 if ($category) {
87 $categories[$category->id] = $category;
88 } else {
89 // Parent not found: this indicates an error, but just fix it.
90 set_field('question_categories', 'parent', 0, 'id', $currentid);
91 break;
96 // Now we look for categories from other courses containing random questions
97 // in our quiz that select from the category and its subcategories. That implies
98 // those subcategories also need to be backed up. (The categories themselves
99 // and their parents will already have been included.)
100 $categorieswithrandom = get_records_sql("
101 SELECT DISTINCT question.category AS id
102 FROM {$CFG->prefix}quiz_question_instances qqi,
103 $from
104 {$CFG->prefix}question question
105 WHERE question.id = qqi.question
106 AND question.qtype = '" . RANDOM . "'
107 AND question.questiontext = '1'
108 $where
110 if ($categorieswithrandom) {
111 foreach ($categorieswithrandom as $category) {
112 $status = quiz_backup_add_sub_categories($categories, $category->id);
116 // Finally, add all these extra categories to the backup_ids table.
117 foreach ($categories as $category) {
118 $status = $status && backup_putid($backup_unique_code, 'question_categories', $category->id, 0);
121 return $status;
125 * Helper function adding the id of all the subcategories of a category to an array.
127 function quiz_backup_add_sub_categories(&$categories, $categoryid) {
128 $status = true;
129 if ($categories[$categoryid]->childrendone) {
130 return $status;
132 if ($subcategories = get_records('question_categories', 'parent', $categoryid, '', 'id, 0 AS childrendone')) {
133 foreach ($subcategories as $subcategory) {
134 if (!array_key_exists($subcategory->id, $categories)) {
135 $categories[$subcategory->id] = $subcategory;
137 $status = $status && quiz_backup_add_sub_categories($categories, $subcategory->id);
140 $categories[$categoryid]->childrendone = 1;
141 return $status;
145 //This function is used to detect orphaned questions (pointing to a
146 //non existing category) and to recreate such category. This function
147 //is used by the backup process, to ensure consistency and should be
148 //executed in the upgrade process and, perhaps in the health center.
149 function fix_orphaned_questions ($course) {
151 global $CFG;
153 $categories = get_records_sql("SELECT DISTINCT t.category, t.category
154 FROM {$CFG->prefix}question t,
155 {$CFG->prefix}quiz_question_instances g,
156 {$CFG->prefix}quiz q
157 WHERE q.course = '$course' AND
158 g.quiz = q.id AND
159 g.question = t.id",false);
160 if ($categories) {
161 foreach ($categories as $key => $category) {
162 $exist = get_record('question_categories','id', $key);
163 //If the category doesn't exist
164 if (!$exist) {
165 //Build a new category
166 $db_cat = new stdClass;
167 $db_cat->course = $course;
168 $db_cat->name = get_string('recreatedcategory','',$key);
169 $db_cat->info = get_string('recreatedcategory','',$key);
170 $db_cat->publish = 1;
171 $db_cat->stamp = make_unique_id_code();
172 //Insert the new category
173 $catid = insert_record('question_categories',$db_cat);
174 unset ($db_cat);
175 if ($catid) {
176 //Reasign orphaned questions to their new category
177 set_field ('question','category',$catid,'category',$key);
185 //STEP 2. Backup quizzes and associated structures
186 // (course dependent)
188 function quiz_backup_one_mod($bf,$preferences,$quiz) {
189 $status = true;
191 if (is_numeric($quiz)) {
192 $quiz = get_record('quiz','id',$quiz);
195 //Start mod
196 fwrite ($bf,start_tag("MOD",3,true));
197 //Print quiz data
198 fwrite ($bf,full_tag("ID",4,false,$quiz->id));
199 fwrite ($bf,full_tag("MODTYPE",4,false,"quiz"));
200 fwrite ($bf,full_tag("NAME",4,false,$quiz->name));
201 fwrite ($bf,full_tag("INTRO",4,false,$quiz->intro));
202 fwrite ($bf,full_tag("TIMEOPEN",4,false,$quiz->timeopen));
203 fwrite ($bf,full_tag("TIMECLOSE",4,false,$quiz->timeclose));
204 fwrite ($bf,full_tag("OPTIONFLAGS",4,false,$quiz->optionflags));
205 fwrite ($bf,full_tag("PENALTYSCHEME",4,false,$quiz->penaltyscheme));
206 fwrite ($bf,full_tag("ATTEMPTS_NUMBER",4,false,$quiz->attempts));
207 fwrite ($bf,full_tag("ATTEMPTONLAST",4,false,$quiz->attemptonlast));
208 fwrite ($bf,full_tag("GRADEMETHOD",4,false,$quiz->grademethod));
209 fwrite ($bf,full_tag("DECIMALPOINTS",4,false,$quiz->decimalpoints));
210 fwrite ($bf,full_tag("REVIEW",4,false,$quiz->review));
211 fwrite ($bf,full_tag("QUESTIONSPERPAGE",4,false,$quiz->questionsperpage));
212 fwrite ($bf,full_tag("SHUFFLEQUESTIONS",4,false,$quiz->shufflequestions));
213 fwrite ($bf,full_tag("SHUFFLEANSWERS",4,false,$quiz->shuffleanswers));
214 fwrite ($bf,full_tag("QUESTIONS",4,false,$quiz->questions));
215 fwrite ($bf,full_tag("SUMGRADES",4,false,$quiz->sumgrades));
216 fwrite ($bf,full_tag("GRADE",4,false,$quiz->grade));
217 fwrite ($bf,full_tag("TIMECREATED",4,false,$quiz->timecreated));
218 fwrite ($bf,full_tag("TIMEMODIFIED",4,false,$quiz->timemodified));
219 fwrite ($bf,full_tag("TIMELIMIT",4,false,$quiz->timelimit));
220 fwrite ($bf,full_tag("PASSWORD",4,false,$quiz->password));
221 fwrite ($bf,full_tag("SUBNET",4,false,$quiz->subnet));
222 fwrite ($bf,full_tag("POPUP",4,false,$quiz->popup));
223 fwrite ($bf,full_tag("DELAY1",4,false,$quiz->delay1));
224 fwrite ($bf,full_tag("DELAY2",4,false,$quiz->delay2));
225 //Now we print to xml question_instances (Course Level)
226 $status = backup_quiz_question_instances($bf,$preferences,$quiz->id);
227 //Now we print to xml quiz_feedback (Course Level)
228 $status = backup_quiz_feedback($bf,$preferences,$quiz->id);
229 //Now we print to xml question_versions (Course Level)
230 $status = backup_quiz_question_versions($bf,$preferences,$quiz->id);
231 //if we've selected to backup users info, then execute:
232 // - backup_quiz_grades
233 // - backup_quiz_attempts
234 if (backup_userdata_selected($preferences,'quiz',$quiz->id) && $status) {
235 $status = backup_quiz_grades($bf,$preferences,$quiz->id);
236 if ($status) {
237 $status = backup_quiz_attempts($bf,$preferences,$quiz->id);
240 //End mod
241 $status = fwrite ($bf,end_tag("MOD",3,true));
243 return $status;
247 function quiz_backup_mods($bf,$preferences) {
249 global $CFG;
251 $status = true;
253 //Iterate over quiz table
254 $quizzes = get_records ("quiz","course",$preferences->backup_course,"id");
255 if ($quizzes) {
256 foreach ($quizzes as $quiz) {
257 if (backup_mod_selected($preferences,'quiz',$quiz->id)) {
258 $status = quiz_backup_one_mod($bf,$preferences,$quiz);
262 return $status;
265 //Backup quiz_question_instances contents (executed from quiz_backup_mods)
266 function backup_quiz_question_instances ($bf,$preferences,$quiz) {
267 $status = true;
269 $quiz_question_instances = get_records("quiz_question_instances","quiz",$quiz,"id");
270 //If there are question_instances
271 if ($quiz_question_instances) {
272 //Write start tag
273 $status = fwrite ($bf,start_tag("QUESTION_INSTANCES",4,true));
274 //Iterate over each question_instance
275 foreach ($quiz_question_instances as $que_ins) {
276 //Start question instance
277 $status = fwrite ($bf,start_tag("QUESTION_INSTANCE",5,true));
278 //Print question_instance contents
279 fwrite ($bf,full_tag("ID",6,false,$que_ins->id));
280 fwrite ($bf,full_tag("QUESTION",6,false,$que_ins->question));
281 fwrite ($bf,full_tag("GRADE",6,false,$que_ins->grade));
282 //End question instance
283 $status = fwrite ($bf,end_tag("QUESTION_INSTANCE",5,true));
285 //Write end tag
286 $status = fwrite ($bf,end_tag("QUESTION_INSTANCES",4,true));
288 return $status;
291 //Backup quiz_question_instances contents (executed from quiz_backup_mods)
292 function backup_quiz_feedback ($bf,$preferences,$quiz) {
293 $status = true;
295 $quiz_feedback = get_records('quiz_feedback', 'quizid', $quiz, 'id');
296 // If there are question_instances ...
297 if ($quiz_feedback) {
298 // Write start tag.
299 $status = $status & fwrite($bf,start_tag('FEEDBACKS', 4, true));
301 // Iterate over each question_instance.
302 foreach ($quiz_feedback as $feedback) {
304 //Start feedback instance
305 $status = $status & fwrite($bf, start_tag('FEEDBACK',5,true));
307 //Print question_instance contents.
308 $status = $status & fwrite($bf, full_tag('ID', 6, false, $feedback->id));
309 $status = $status & fwrite($bf, full_tag('QUIZID', 6, false, $feedback->quizid));
310 $status = $status & fwrite($bf, full_tag('FEEDBACKTEXT', 6, false, $feedback->feedbacktext));
311 $status = $status & fwrite($bf, full_tag('MINGRADE', 6, false, $feedback->mingrade));
312 $status = $status & fwrite($bf, full_tag('MAXGRADE', 6, false, $feedback->maxgrade));
314 // End feedback instance.
315 $status = $status & fwrite($bf, end_tag('FEEDBACK', 5, true));
318 // Write end tag.
319 $status = $status & fwrite($bf, end_tag('FEEDBACKS', 4, true));
321 return $status;
324 //Backup quiz_question_versions contents (executed from quiz_backup_mods)
325 function backup_quiz_question_versions ($bf,$preferences,$quiz) {
326 $status = true;
328 $quiz_question_versions = get_records("quiz_question_versions","quiz",$quiz,"id");
329 //If there are question_versions
330 if ($quiz_question_versions) {
331 //Write start tag
332 $status = fwrite ($bf,start_tag("QUESTION_VERSIONS",4,true));
333 //Iterate over each question_version
334 foreach ($quiz_question_versions as $que_ver) {
335 //Start question version
336 $status = fwrite ($bf,start_tag("QUESTION_VERSION",5,true));
337 //Print question_version contents
338 fwrite ($bf,full_tag("ID",6,false,$que_ver->id));
339 fwrite ($bf,full_tag("OLDQUESTION",6,false,$que_ver->oldquestion));
340 fwrite ($bf,full_tag("NEWQUESTION",6,false,$que_ver->newquestion));
341 fwrite ($bf,full_tag("ORIGINALQUESTION",6,false,$que_ver->originalquestion));
342 fwrite ($bf,full_tag("USERID",6,false,$que_ver->userid));
343 fwrite ($bf,full_tag("TIMESTAMP",6,false,$que_ver->timestamp));
344 //End question version
345 $status = fwrite ($bf,end_tag("QUESTION_VERSION",5,true));
347 //Write end tag
348 $status = fwrite ($bf,end_tag("QUESTION_VERSIONS",4,true));
350 return $status;
354 //Backup quiz_grades contents (executed from quiz_backup_mods)
355 function backup_quiz_grades ($bf,$preferences,$quiz) {
356 $status = true;
358 $quiz_grades = get_records("quiz_grades","quiz",$quiz,"id");
359 //If there are grades
360 if ($quiz_grades) {
361 //Write start tag
362 $status = fwrite ($bf,start_tag("GRADES",4,true));
363 //Iterate over each grade
364 foreach ($quiz_grades as $gra) {
365 //Start grade
366 $status = fwrite ($bf,start_tag("GRADE",5,true));
367 //Print grade contents
368 fwrite ($bf,full_tag("ID",6,false,$gra->id));
369 fwrite ($bf,full_tag("USERID",6,false,$gra->userid));
370 fwrite ($bf,full_tag("GRADEVAL",6,false,$gra->grade));
371 fwrite ($bf,full_tag("TIMEMODIFIED",6,false,$gra->timemodified));
372 //End question grade
373 $status = fwrite ($bf,end_tag("GRADE",5,true));
375 //Write end tag
376 $status = fwrite ($bf,end_tag("GRADES",4,true));
378 return $status;
381 //Backup quiz_attempts contents (executed from quiz_backup_mods)
382 function backup_quiz_attempts ($bf,$preferences,$quiz) {
383 $status = true;
385 $quiz_attempts = get_records("quiz_attempts","quiz",$quiz,"id");
386 //If there are attempts
387 if ($quiz_attempts) {
388 //Write start tag
389 $status = fwrite ($bf,start_tag("ATTEMPTS",4,true));
390 //Iterate over each attempt
391 foreach ($quiz_attempts as $attempt) {
392 //Start attempt
393 $status = fwrite ($bf,start_tag("ATTEMPT",5,true));
394 //Print attempt contents
395 fwrite ($bf,full_tag("ID",6,false,$attempt->id));
396 fwrite ($bf,full_tag("UNIQUEID",6,false,$attempt->uniqueid));
397 fwrite ($bf,full_tag("USERID",6,false,$attempt->userid));
398 fwrite ($bf,full_tag("ATTEMPTNUM",6,false,$attempt->attempt));
399 fwrite ($bf,full_tag("SUMGRADES",6,false,$attempt->sumgrades));
400 fwrite ($bf,full_tag("TIMESTART",6,false,$attempt->timestart));
401 fwrite ($bf,full_tag("TIMEFINISH",6,false,$attempt->timefinish));
402 fwrite ($bf,full_tag("TIMEMODIFIED",6,false,$attempt->timemodified));
403 fwrite ($bf,full_tag("LAYOUT",6,false,$attempt->layout));
404 fwrite ($bf,full_tag("PREVIEW",6,false,$attempt->preview));
405 //Now write to xml the states (in this attempt)
406 $status = backup_question_states ($bf,$preferences,$attempt->uniqueid);
407 //Now write to xml the sessions (in this attempt)
408 $status = backup_question_sessions ($bf,$preferences,$attempt->uniqueid);
409 //End attempt
410 $status = fwrite ($bf,end_tag("ATTEMPT",5,true));
412 //Write end tag
413 $status = fwrite ($bf,end_tag("ATTEMPTS",4,true));
415 return $status;
418 function quiz_check_backup_mods_instances($instance,$backup_unique_code) {
419 // the keys in this array need to be unique as they get merged...
420 $info[$instance->id.'0'][0] = '<b>'.$instance->name.'</b>';
421 $info[$instance->id.'0'][1] = '';
423 //Categories
424 $info[$instance->id.'1'][0] = get_string("categories","quiz");
425 if ($ids = question_category_ids_by_backup ($backup_unique_code)) {
426 $info[$instance->id.'1'][1] = count($ids);
427 } else {
428 $info[$instance->id.'1'][1] = 0;
430 //Questions
431 $info[$instance->id.'2'][0] = get_string("questionsinclhidden","quiz");
432 if ($ids = question_ids_by_backup ($backup_unique_code)) {
433 $info[$instance->id.'2'][1] = count($ids);
434 } else {
435 $info[$instance->id.'2'][1] = 0;
438 //Now, if requested, the user_data
439 if (!empty($instance->userdata)) {
440 //Grades
441 $info[$instance->id.'3'][0] = get_string("grades");
442 if ($ids = quiz_grade_ids_by_instance ($instance->id)) {
443 $info[$instance->id.'3'][1] = count($ids);
444 } else {
445 $info[$instance->id.'3'][1] = 0;
448 return $info;
451 ////Return an array of info (name,value)
452 /// $instances is an array with key = instanceid, value = object (name,id,userdata)
453 function quiz_check_backup_mods($course,$user_data= false,$backup_unique_code,$instances=null) {
455 //Deletes data from mdl_backup_ids (categories section)
456 delete_category_ids ($backup_unique_code);
457 //Create date into mdl_backup_ids (categories section)
458 insert_category_ids ($course,$backup_unique_code,$instances);
459 if (!empty($instances) && is_array($instances) && count($instances)) {
460 $info = array();
461 foreach ($instances as $id => $instance) {
462 $info += quiz_check_backup_mods_instances($instance,$backup_unique_code);
464 return $info;
466 //First the course data
467 $info[0][0] = get_string("modulenameplural","quiz");
468 if ($ids = quiz_ids ($course)) {
469 $info[0][1] = count($ids);
470 } else {
471 $info[0][1] = 0;
473 //Categories
474 $info[1][0] = get_string("categories","quiz");
475 if ($ids = question_category_ids_by_backup ($backup_unique_code)) {
476 $info[1][1] = count($ids);
477 } else {
478 $info[1][1] = 0;
480 //Questions
481 $info[2][0] = get_string("questions","quiz");
482 if ($ids = question_ids_by_backup ($backup_unique_code)) {
483 $info[2][1] = count($ids);
484 } else {
485 $info[2][1] = 0;
488 //Now, if requested, the user_data
489 if ($user_data) {
490 //Grades
491 $info[3][0] = get_string("grades");
492 if ($ids = quiz_grade_ids_by_course ($course)) {
493 $info[3][1] = count($ids);
494 } else {
495 $info[3][1] = 0;
499 return $info;
502 //Return a content encoded to support interactivities linking. Every module
503 //should have its own. They are called automatically from the backup procedure.
504 function quiz_encode_content_links ($content,$preferences) {
506 global $CFG;
508 $base = preg_quote($CFG->wwwroot,"/");
510 //Link to the list of quizs
511 $buscar="/(".$base."\/mod\/quiz\/index.php\?id\=)([0-9]+)/";
512 $result= preg_replace($buscar,'$@QUIZINDEX*$2@$',$content);
514 //Link to quiz view by moduleid
515 $buscar="/(".$base."\/mod\/quiz\/view.php\?id\=)([0-9]+)/";
516 $result= preg_replace($buscar,'$@QUIZVIEWBYID*$2@$',$result);
518 return $result;
521 // INTERNAL FUNCTIONS. BASED IN THE MOD STRUCTURE
523 //Returns an array of quiz id
524 function quiz_ids ($course) {
526 global $CFG;
528 return get_records_sql ("SELECT a.id, a.course
529 FROM {$CFG->prefix}quiz a
530 WHERE a.course = '$course'");
533 function quiz_grade_ids_by_course ($course) {
535 global $CFG;
537 return get_records_sql ("SELECT g.id, g.quiz
538 FROM {$CFG->prefix}quiz a,
539 {$CFG->prefix}quiz_grades g
540 WHERE a.course = '$course' and
541 g.quiz = a.id");
544 function quiz_grade_ids_by_instance($instanceid) {
546 global $CFG;
548 return get_records_sql ("SELECT g.id, g.quiz
549 FROM {$CFG->prefix}quiz_grades g
550 WHERE g.quiz = $instanceid");