| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php // $Id: backuplib.php,v 1.13.2.6 2008/06/18 14:29:40 tjhunt Exp $ 2 /** 3 * Question bank backup code. 4 * 5 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 6 * @package questionbank 7 *//** */ 8 9 //This is the "graphical" structure of the question database: 10 //To see, put your terminal to 160cc 11 12 // The following holds student-independent information about the questions 13 // 14 // question_categories 15 // (CL,pk->id) 16 // | 17 // | 18 // |....................................... 19 // | . 20 // | . 21 // | -------question_datasets------ . 22 // | | (CL,pk->id,fk->question, | . 23 // | | fk->dataset_definition) | . 24 // | | | . 25 // | | | . 26 // | | | . 27 // | | question_dataset_definitions 28 // | | (CL,pk->id,fk->category) 29 // question | 30 // (CL,pk->id,fk->category,files) | 31 // | question_dataset_items 32 // | (CL,pk->id,fk->definition) 33 // | 34 // | 35 // | 36 // -------------------------------------------------------------------------------------------------------------- 37 // | | | | | | | 38 // | | | | | | | 39 // | | | | question_calculated | | 40 // question_truefalse | question_multichoice | (CL,pl->id,fk->question) | | 41 // (CL,pk->id,fk->question) | (CL,pk->id,fk->question) | . | | question_randomsamatch 42 // . | . | . | |--(CL,pk->id,fk->question) 43 // . question_shortanswer . question_numerical . question_multianswer. | 44 // . (CL,pk->id,fk->question) . (CL,pk->id,fk->question) . (CL,pk->id,fk->question) | 45 // . . . . . . | question_match 46 // . . . . . . |--(CL,pk->id,fk->question) 47 // . . . . . . | . 48 // . . . . . . | . 49 // . . . . . . | . 50 // . . . . . . | question_match_sub 51 // ........................................................................................ |--(CL,pk->id,fk->question) 52 // . | 53 // . | 54 // . | question_numerical_units 55 // question_answers |--(CL,pk->id,fk->question) 56 // (CL,pk->id,fk->question)---------------------------------------------------------- 57 // 58 // 59 // The following holds the information about student interaction with the questions 60 // 61 // question_sessions 62 // (UL,pk->id,fk->attempt,question) 63 // . 64 // . 65 // question_states 66 // (UL,pk->id,fk->attempt,question) 67 // 68 // Meaning: pk->primary key field of the table 69 // fk->foreign key to link with parent 70 // nt->nested field (recursive data) 71 // SL->site level info 72 // CL->course level info 73 // UL->user level info 74 // files->table may have files 75 // 76 //----------------------------------------------------------- 77 78 require_once("$CFG->libdir/questionlib.php"); 79 80 function backup_question_category_context($bf, $contextid, $course) { 81 $status = true; 82 $context = get_context_instance_by_id($contextid); 83 $status = $status && fwrite($bf,start_tag("CONTEXT",4,true)); 84 switch ($context->contextlevel){ 85 case CONTEXT_MODULE: 86 $status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'module')); 87 $status = $status && fwrite($bf,full_tag("INSTANCE",5,false, $context->instanceid)); 88 break; 89 case CONTEXT_COURSE: 90 $status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'course')); 91 break; 92 case CONTEXT_COURSECAT: 93 $thiscourse = get_record('course', 'id', $course); 94 $cat = $thiscourse->category; 95 $catno = 1; 96 while($context->instanceid != $cat){ 97 $catno ++; 98 if ($cat ==0) { 99 return false; 100 } 101 $cat = get_field('course_categories', 'parent', 'id', $cat); 102 } 103 $status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'coursecategory')); 104 $status = $status && fwrite($bf,full_tag("COURSECATEGORYLEVEL",5,false, $catno)); 105 break; 106 case CONTEXT_SYSTEM: 107 $status = $status && fwrite($bf,full_tag("LEVEL",5,false, 'system')); 108 break; 109 default : 110 return false; 111 } 112 $status = $status && fwrite($bf,end_tag("CONTEXT",4,true)); 113 return $status; 114 } 115 116 function backup_question_categories($bf,$preferences) { 117 118 global $CFG; 119 120 $status = true; 121 122 //First, we get the used categories from backup_ids 123 $categories = question_category_ids_by_backup ($preferences->backup_unique_code); 124 125 //If we've categories 126 if ($categories) { 127 //Write start tag 128 $status = $status && fwrite($bf,start_tag("QUESTION_CATEGORIES",2,true)); 129 //Iterate over each category 130 foreach ($categories as $cat) { 131 //Start category 132 $status = $status && fwrite ($bf,start_tag("QUESTION_CATEGORY",3,true)); 133 //Get category data from question_categories 134 $category = get_record ("question_categories","id",$cat->old_id); 135 //Print category contents 136 $status = $status && fwrite($bf,full_tag("ID",4,false,$category->id)); 137 $status = $status && fwrite($bf,full_tag("NAME",4,false,$category->name)); 138 $status = $status && fwrite($bf,full_tag("INFO",4,false,$category->info)); 139 $status = $status && backup_question_category_context($bf, $category->contextid, $preferences->backup_course); 140 $status = $status && fwrite($bf,full_tag("STAMP",4,false,$category->stamp)); 141 $status = $status && fwrite($bf,full_tag("PARENT",4,false,$category->parent)); 142 $status = $status && fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder)); 143 //Now, backup their questions 144 $status = $status && backup_question($bf,$preferences,$category->id); 145 //End category 146 $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true)); 147 } 148 //Write end tag 149 $status = $status && fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true)); 150 } 151 152 return $status; 153 } 154 155 //This function backups all the questions in selected category and their 156 //asociated data 157 function backup_question($bf,$preferences,$category, $level = 4) { 158 159 global $CFG, $QTYPES; 160 161 $status = true; 162 163 // We'll fetch the questions sorted by parent so that questions with no parents 164 // (these are the ones which could be parents themselves) are backed up first. This 165 // is important for the recoding of the parent field during the restore process 166 // Only select questions with ids in backup_ids table 167 $questions = get_records_sql("SELECT q.* FROM {$CFG->prefix}backup_ids bk, {$CFG->prefix}question q ". 168 "WHERE q.category= $category AND ". 169 "bk.old_id=q.id AND ". 170 "bk.backup_code = {$preferences->backup_unique_code} ". 171 "ORDER BY parent ASC, id"); 172 //If there are questions 173 if ($questions) { 174 //Write start tag 175 $status = $status && fwrite ($bf,start_tag("QUESTIONS",$level,true)); 176 $counter = 0; 177 //Iterate over each question 178 foreach ($questions as $question) { 179 // Deal with missing question types - they need to be included becuase 180 // user data or quizzes may refer to them. 181 if (!array_key_exists($question->qtype, $QTYPES)) { 182 $question->qtype = 'missingtype'; 183 $question->questiontext = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext; 184 } 185 //Start question 186 $status = $status && fwrite ($bf,start_tag("QUESTION",$level + 1,true)); 187 //Print question contents 188 fwrite ($bf,full_tag("ID",$level + 2,false,$question->id)); 189 fwrite ($bf,full_tag("PARENT",$level + 2,false,$question->parent)); 190 fwrite ($bf,full_tag("NAME",$level + 2,false,$question->name)); 191 fwrite ($bf,full_tag("QUESTIONTEXT",$level + 2,false,$question->questiontext)); 192 fwrite ($bf,full_tag("QUESTIONTEXTFORMAT",$level + 2,false,$question->questiontextformat)); 193 fwrite ($bf,full_tag("IMAGE",$level + 2,false,$question->image)); 194 fwrite ($bf,full_tag("GENERALFEEDBACK",$level + 2,false,$question->generalfeedback)); 195 fwrite ($bf,full_tag("DEFAULTGRADE",$level + 2,false,$question->defaultgrade)); 196 fwrite ($bf,full_tag("PENALTY",$level + 2,false,$question->penalty)); 197 fwrite ($bf,full_tag("QTYPE",$level + 2,false,$question->qtype)); 198 fwrite ($bf,full_tag("LENGTH",$level + 2,false,$question->length)); 199 fwrite ($bf,full_tag("STAMP",$level + 2,false,$question->stamp)); 200 fwrite ($bf,full_tag("VERSION",$level + 2,false,$question->version)); 201 fwrite ($bf,full_tag("HIDDEN",$level + 2,false,$question->hidden)); 202 fwrite ($bf,full_tag("TIMECREATED",$level + 2,false,$question->timecreated)); 203 fwrite ($bf,full_tag("TIMEMODIFIED",$level + 2,false,$question->timemodified)); 204 fwrite ($bf,full_tag("CREATEDBY",$level + 2,false,$question->createdby)); 205 fwrite ($bf,full_tag("MODIFIEDBY",$level + 2,false,$question->modifiedby)); 206 // Backup question type specific data 207 $status = $status && $QTYPES[$question->qtype]->backup($bf,$preferences,$question->id, $level + 2); 208 //End question 209 $status = $status && fwrite ($bf,end_tag("QUESTION",$level + 1,true)); 210 //Do some output 211 $counter++; 212 if ($counter % 10 == 0) { 213 echo "."; 214 if ($counter % 200 == 0) { 215 echo "<br />"; 216 } 217 backup_flush(300); 218 } 219 } 220 //Write end tag 221 $status = $status && fwrite ($bf,end_tag("QUESTIONS",$level,true)); 222 } 223 return $status; 224 } 225 226 //This function backups the answers data in some question types 227 //(truefalse, shortanswer,multichoice,numerical,calculated) 228 function question_backup_answers($bf,$preferences,$question, $level = 6) { 229 230 global $CFG; 231 232 $status = true; 233 234 $answers = get_records("question_answers","question",$question,"id"); 235 //If there are answers 236 if ($answers) { 237 $status = $status && fwrite ($bf,start_tag("ANSWERS",$level,true)); 238 //Iterate over each answer 239 foreach ($answers as $answer) { 240 $status = $status && fwrite ($bf,start_tag("ANSWER",$level + 1,true)); 241 //Print answer contents 242 fwrite ($bf,full_tag("ID",$level + 2,false,$answer->id)); 243 fwrite ($bf,full_tag("ANSWER_TEXT",$level + 2,false,$answer->answer)); 244 fwrite ($bf,full_tag("FRACTION",$level + 2,false,$answer->fraction)); 245 fwrite ($bf,full_tag("FEEDBACK",$level + 2,false,$answer->feedback)); 246 $status = $status && fwrite ($bf,end_tag("ANSWER",$level + 1,true)); 247 } 248 $status = $status && fwrite ($bf,end_tag("ANSWERS",$level,true)); 249 } 250 return $status; 251 } 252 253 //This function backups question_numerical_units from different question types 254 function question_backup_numerical_units($bf,$preferences,$question,$level=7) { 255 256 global $CFG; 257 258 $status = true; 259 260 $numerical_units = get_records("question_numerical_units","question",$question,"id"); 261 //If there are numericals_units 262 if ($numerical_units) { 263 $status = $status && fwrite ($bf,start_tag("NUMERICAL_UNITS",$level,true)); 264 //Iterate over each numerical_unit 265 foreach ($numerical_units as $numerical_unit) { 266 $status = $status && fwrite ($bf,start_tag("NUMERICAL_UNIT",$level+1,true)); 267 //Print numerical_unit contents 268 fwrite ($bf,full_tag("MULTIPLIER",$level+2,false,$numerical_unit->multiplier)); 269 fwrite ($bf,full_tag("UNIT",$level+2,false,$numerical_unit->unit)); 270 //Now backup numerical_units 271 $status = $status && fwrite ($bf,end_tag("NUMERICAL_UNIT",$level+1,true)); 272 } 273 $status = $status && fwrite ($bf,end_tag("NUMERICAL_UNITS",$level,true)); 274 } 275 276 return $status; 277 278 } 279 280 //This function backups dataset_definitions (via question_datasets) from different question types 281 function question_backup_datasets($bf,$preferences,$question,$level=7) { 282 283 global $CFG; 284 285 $status = true; 286 287 //First, we get the used datasets for this question 288 $question_datasets = get_records("question_datasets","question",$question,"id"); 289 //If there are question_datasets 290 if ($question_datasets) { 291 $status = $status &&fwrite ($bf,start_tag("DATASET_DEFINITIONS",$level,true)); 292 //Iterate over each question_dataset 293 foreach ($question_datasets as $question_dataset) { 294 $def = NULL; 295 //Get dataset_definition 296 if ($def = get_record("question_dataset_definitions","id",$question_dataset->datasetdefinition)) {; 297 $status = $status &&fwrite ($bf,start_tag("DATASET_DEFINITION",$level+1,true)); 298 //Print question_dataset contents 299 fwrite ($bf,full_tag("CATEGORY",$level+2,false,$def->category)); 300 fwrite ($bf,full_tag("NAME",$level+2,false,$def->name)); 301 fwrite ($bf,full_tag("TYPE",$level+2,false,$def->type)); 302 fwrite ($bf,full_tag("OPTIONS",$level+2,false,$def->options)); 303 fwrite ($bf,full_tag("ITEMCOUNT",$level+2,false,$def->itemcount)); 304 //Now backup dataset_entries 305 $status = $status && question_backup_dataset_items($bf,$preferences,$def->id,$level+2); 306 //End dataset definition 307 $status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITION",$level+1,true)); 308 } 309 } 310 $status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITIONS",$level,true)); 311 } 312 313 return $status; 314 315 } 316 317 //This function backups datases_items from dataset_definitions 318 function question_backup_dataset_items($bf,$preferences,$datasetdefinition,$level=9) { 319 320 global $CFG; 321 322 $status = true; 323 324 //First, we get the datasets_items for this dataset_definition 325 $dataset_items = get_records("question_dataset_items","definition",$datasetdefinition,"id"); 326 //If there are dataset_items 327 if ($dataset_items) { 328 $status = $status &&fwrite ($bf,start_tag("DATASET_ITEMS",$level,true)); 329 //Iterate over each dataset_item 330 foreach ($dataset_items as $dataset_item) { 331 $status = $status &&fwrite ($bf,start_tag("DATASET_ITEM",$level+1,true)); 332 //Print question_dataset contents 333 fwrite ($bf,full_tag("NUMBER",$level+2,false,$dataset_item->itemnumber)); 334 fwrite ($bf,full_tag("VALUE",$level+2,false,$dataset_item->value)); 335 //End dataset definition 336 $status = $status &&fwrite ($bf,end_tag("DATASET_ITEM",$level+1,true)); 337 } 338 $status = $status &&fwrite ($bf,end_tag("DATASET_ITEMS",$level,true)); 339 } 340 341 return $status; 342 343 } 344 345 346 //Backup question_states contents (executed from backup_quiz_attempts) 347 function backup_question_states ($bf,$preferences,$attempt, $level = 6) { 348 349 global $CFG; 350 351 $status = true; 352 353 $question_states = get_records("question_states","attempt",$attempt,"id"); 354 //If there are states 355 if ($question_states) { 356 //Write start tag 357 $status = $status && fwrite ($bf,start_tag("STATES",$level,true)); 358 //Iterate over each state 359 foreach ($question_states as $state) { 360 //Start state 361 $status = $status && fwrite ($bf,start_tag("STATE",$level + 1,true)); 362 //Print state contents 363 fwrite ($bf,full_tag("ID",$level + 2,false,$state->id)); 364 fwrite ($bf,full_tag("QUESTION",$level + 2,false,$state->question)); 365 fwrite ($bf,full_tag("ORIGINALQUESTION",$level + 2,false,$state->originalquestion)); 366 fwrite ($bf,full_tag("SEQ_NUMBER",$level + 2,false,$state->seq_number)); 367 fwrite ($bf,full_tag("ANSWER",$level + 2,false,$state->answer)); 368 fwrite ($bf,full_tag("TIMESTAMP",$level + 2,false,$state->timestamp)); 369 fwrite ($bf,full_tag("EVENT",$level + 2,false,$state->event)); 370 fwrite ($bf,full_tag("GRADE",$level + 2,false,$state->grade)); 371 fwrite ($bf,full_tag("RAW_GRADE",$level + 2,false,$state->raw_grade)); 372 fwrite ($bf,full_tag("PENALTY",$level + 2,false,$state->penalty)); 373 //End state 374 $status = $status && fwrite ($bf,end_tag("STATE",$level + 1,true)); 375 } 376 //Write end tag 377 $status = $status && fwrite ($bf,end_tag("STATES",$level,true)); 378 } 379 } 380 381 //Backup question_sessions contents (executed from backup_quiz_attempts) 382 function backup_question_sessions ($bf,$preferences,$attempt, $level = 6) { 383 global $CFG; 384 385 $status = true; 386 387 $question_sessions = get_records("question_sessions","attemptid",$attempt,"id"); 388 //If there are sessions 389 if ($question_sessions) { 390 //Write start tag (the funny name 'newest states' has historical reasons) 391 $status = $status && fwrite ($bf,start_tag("NEWEST_STATES",$level,true)); 392 //Iterate over each newest_state 393 foreach ($question_sessions as $newest_state) { 394 //Start newest_state 395 $status = $status && fwrite ($bf,start_tag("NEWEST_STATE",$level + 1,true)); 396 //Print newest_state contents 397 fwrite ($bf,full_tag("ID",$level + 2,false,$newest_state->id)); 398 fwrite ($bf,full_tag("QUESTIONID",$level + 2,false,$newest_state->questionid)); 399 fwrite ($bf,full_tag("NEWEST",$level + 2,false,$newest_state->newest)); 400 fwrite ($bf,full_tag("NEWGRADED",$level + 2,false,$newest_state->newgraded)); 401 fwrite ($bf,full_tag("SUMPENALTY",$level + 2,false,$newest_state->sumpenalty)); 402 fwrite ($bf,full_tag("MANUALCOMMENT",$level + 2,false,$newest_state->manualcomment)); 403 //End newest_state 404 $status = $status && fwrite ($bf,end_tag("NEWEST_STATE",$level + 1,true)); 405 } 406 //Write end tag 407 $status = $status && fwrite ($bf,end_tag("NEWEST_STATES",$level,true)); 408 } 409 return $status; 410 } 411 412 //Returns an array of categories id 413 function question_category_ids_by_backup ($backup_unique_code) { 414 415 global $CFG; 416 417 return get_records_sql ("SELECT a.old_id, a.backup_code 418 FROM {$CFG->prefix}backup_ids a 419 WHERE a.backup_code = '$backup_unique_code' AND 420 a.table_name = 'question_categories'"); 421 } 422 423 function question_ids_by_backup ($backup_unique_code) { 424 425 global $CFG; 426 427 return get_records_sql ("SELECT old_id, backup_code 428 FROM {$CFG->prefix}backup_ids 429 WHERE backup_code = '$backup_unique_code' AND 430 table_name = 'question'"); 431 } 432 433 //Function for inserting question and category ids into db that are all called from 434 // quiz_check_backup_mods during execution of backup_check.html 435 436 437 function question_insert_c_and_q_ids_for_course($coursecontext, $backup_unique_code){ 438 global $CFG; 439 // First, all categories from this course's context. 440 $status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids 441 (backup_code, table_name, old_id, info) 442 SELECT '$backup_unique_code', 'question_categories', qc.id, 'course' 443 FROM {$CFG->prefix}question_categories qc 444 WHERE qc.contextid = {$coursecontext->id}", false); 445 $status = $status && question_insert_q_ids($backup_unique_code, 'course'); 446 return $status; 447 } 448 /* 449 * Insert all question ids for categories whose ids have already been inserted in the backup_ids table 450 * Insert code to identify categories to later insert all question ids later eg. course, quiz or other module name. 451 */ 452 function question_insert_q_ids($backup_unique_code, $info){ 453 global $CFG; 454 //put the ids of the questions from all these categories into the db. 455 $status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids 456 (backup_code, table_name, old_id, info) 457 SELECT '$backup_unique_code', 'question', q.id, '' 458 FROM {$CFG->prefix}question q, {$CFG->prefix}backup_ids bk 459 WHERE q.category = bk.old_id AND bk.table_name = 'question_categories' 460 AND bk.info = '$info' 461 AND bk.backup_code = '$backup_unique_code'", false); 462 return $status; 463 } 464 465 function question_insert_c_and_q_ids_for_module($backup_unique_code, $course, $modulename, $instances){ 466 global $CFG; 467 $status = true; 468 // using 'dummykeyname' in sql because otherwise get_records_sql_menu returns an error 469 // if two key names are the same. 470 $cmcontexts = array(); 471 if(!empty($instances)) { 472 $cmcontexts = get_records_sql_menu("SELECT c.id, c.id AS dummykeyname FROM {$CFG->prefix}modules m, 473 {$CFG->prefix}course_modules cm, 474 {$CFG->prefix}context c 475 WHERE m.name = '$modulename' AND m.id = cm.module AND cm.id = c.instanceid 476 AND c.contextlevel = ".CONTEXT_MODULE." AND cm.course = $course 477 AND cm.instance IN (".implode(',',array_keys($instances)).")"); 478 } 479 480 if ($cmcontexts){ 481 $status = $status && execute_sql("INSERT INTO {$CFG->prefix}backup_ids 482 (backup_code, table_name, old_id, info) 483 SELECT '$backup_unique_code', 'question_categories', qc.id, '$modulename' 484 FROM {$CFG->prefix}question_categories qc 485 WHERE qc.contextid IN (".join(array_keys($cmcontexts), ', ').")", false); 486 } 487 $status = $status && question_insert_q_ids($backup_unique_code, $modulename); 488 return $status; 489 } 490 491 function question_insert_site_file_names($course, $backup_unique_code){ 492 global $QTYPES, $CFG; 493 $status = true; 494 $questionids = question_ids_by_backup ($backup_unique_code); 495 $urls = array(); 496 if ($questionids){ 497 foreach ($questionids as $question_bk){ 498 $question = get_record('question', 'id', $question_bk->old_id); 499 $QTYPES[$question->qtype]->get_question_options($question); 500 $urls = array_merge_recursive($urls, $QTYPES[$question->qtype]->find_file_links($question, SITEID)); 501 } 502 } 503 ksort($urls); 504 foreach (array_keys($urls) as $url){ 505 if (file_exists($CFG->dataroot.'/'.SITEID.'/'.$url)){ 506 $inserturl = new object(); 507 $inserturl->backup_code = $backup_unique_code; 508 $inserturl->file_type = 'site'; 509 $url = clean_param($url, PARAM_PATH); 510 $inserturl->path = addslashes($url); 511 $status = $status && insert_record('backup_files', $inserturl); 512 } else { 513 notify(get_string('linkedfiledoesntexist', 'question', $url)); 514 } 515 } 516 return $status; 517 } 518 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |