From: gustav_delius Date: Tue, 21 Mar 2006 15:38:35 +0000 (+0000) Subject: Moving question backup from mod/quiz/ to question/. In a first step I am making a... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=924ff16a07096ac51d5ae9122f39526af70d1730;p=moodle.git Moving question backup from mod/quiz/ to question/. In a first step I am making a duplicate copy of the whole backuplib.php. Next I will go in and delete the question backupcode from mod/quiz/backuplib.php and the quiz backup code from question/backuplib.php --- diff --git a/question/backuplib.php b/question/backuplib.php new file mode 100644 index 0000000000..fc2093feca --- /dev/null +++ b/question/backuplib.php @@ -0,0 +1,1231 @@ +id) (CL,pk->id) + // | | + // ------------------------------------------------------------------- | + // | | | | |....................................... + // | quiz_grades | quiz_question_versions | . + // | (UL,pk->id,fk->quiz) | (CL,pk->id,fk->quiz) | . + // | | . | ----question_datasets---- . + // quiz_attempts quiz_question_instances . | | (CL,pk->id,fk->question, | . + // (UL,pk->id,fk->quiz) (CL,pk->id,fk->quiz,question) . | | fk->dataset_definition) | . + // | | . | | | . + // | question_sessions | . | | | . + // |---------(UL,pk->id,fk->attempt,question)-----| . | | | . + // | . | . | | question_dataset_definitions + // | . | . | | (CL,pk->id,fk->category) + // | question_states | question | + // ----------(UL,pk->id,fk->attempt,question)--------------------------(CL,pk->id,fk->category,files) | + // | | question_dataset_items + // | | (CL,pk->id,fk->definition) + // --------- | + // | | + // question_rqp_states | + // (UL,pk->id,fk->stateid) | question_rqp_type + // | (SL,pk->id) + // | | + // -------------------------------------------------------------------------------------------------------------- | + // | | | | | | | question_rqp + // | | | | | | |--(CL,pk->id,fk->question) + // | | | | question_calculated | | + // question_truefalse | question_multichoice | (CL,pl->id,fk->question) | | + // (CL,pk->id,fk->question) | (CL,pk->id,fk->question) | . | | question_randomsamatch + // . | . | . | |--(CL,pk->id,fk->question) + // . question_shortanswer . question_numerical . question_multianswer. | + // . (CL,pk->id,fk->question) . (CL,pk->id,fk->question) . (CL,pk->id,fk->question) | + // . . . . . . | question_match + // . . . . . . |--(CL,pk->id,fk->question) + // . . . . . . | . + // . . . . . . | . + // . . . . . . | . + // . . . . . . | question_match_sub + // ........................................................................................ |--(CL,pk->id,fk->question) + // . | + // . | + // . | question_numerical_units + // question_answers |--(CL,pk->id,fk->question) + // (CL,pk->id,fk->question)---------------------------------------------------------- + // + // Meaning: pk->primary key field of the table + // fk->foreign key to link with parent + // nt->nested field (recursive data) + // SL->site level info + // CL->course level info + // UL->user level info + // files->table may have files + // + //----------------------------------------------------------- + +// Comments: + //THIS MOD BACKUP NEEDS TO USE THE mdl_backup_ids TABLE + + //This module is special, because we make the backup in two steps: + // 1.-We backup every category and their questions (complete structure). It includes this tables: + // - question_categories + // - question + // - question_rqp + // - question_truefalse + // - question_shortanswer + // - question_multianswer + // - question_multichoice + // - question_numerical + // - question_randomsamatch + // - question_match + // - question_match_sub + // - question_calculated + // - question_answers + // - question_numerical_units + // - question_datasets + // - question_dataset_definitions + // - question_dataset_items + // All this backup info have its own section in moodle.xml (QUESTION_CATEGORIES) and it's generated + // before every module backup standard invocation. And only if to backup quizzes has been selected !! + // It's invoked with quiz_backup_question_categories. (course independent). + + // 2.-Standard module backup (Invoked via quiz_backup_mods). It includes this tables: + // - quiz + // - quiz_question_versions + // - quiz_question_instances + // - quiz_attempts + // - quiz_grades + // - question_states + // - question_sessions + // This step is the standard mod backup. (course dependent). + +//STEP 1. Backup categories/questions and associated structures + // (course independent) + + //Insert necessary category ids to backup_ids table + function insert_category_ids ($course,$backup_unique_code,$instances=null) { + global $CFG; + include_once("$CFG->dirroot/mod/quiz/lib.php"); + + //Create missing categories and reasign orphaned questions + fix_orphaned_questions($course); + + // defaults + $where = "q.course = '$course' AND g.quiz = q.id AND "; + $from = ", {$CFG->prefix}quiz q"; + // but if we have an array of quiz ids, use those instead. + if (!empty($instances) && is_array($instances) && count($instances)) { + $where = ' g.quiz IN ('.implode(',',array_keys($instances)).') AND '; + $from = ''; + } + + //Detect used categories (by category in questions) + $status = execute_sql("INSERT INTO {$CFG->prefix}backup_ids + (backup_code, table_name, old_id) + SELECT DISTINCT $backup_unique_code,'question_categories',t.category + FROM {$CFG->prefix}question t, + {$CFG->prefix}quiz_question_instances g + $from + WHERE $where g.question = t.id",false); + + //Now, foreach detected category, we look for their parents upto 0 (top category) + $categories = get_records_sql("SELECT old_id, old_id + FROM {$CFG->prefix}backup_ids + WHERE backup_code = $backup_unique_code AND + table_name = 'question_categories'"); + + if ($categories) { + foreach ($categories as $category) { + if ($dbcat = get_record('question_categories','id',$category->old_id)) { + //echo $dbcat->name; //Debug + //Go up to 0 + while ($dbcat->parent != 0) { + //echo '->'; //Debug + $current = $dbcat->id; + if ($dbcat = get_record('question_categories','id',$dbcat->parent)) { + //Found parent, add it to backup_ids (by using backup_putid + //we ensure no duplicates!) + $status = backup_putid($backup_unique_code,'question_categories',$dbcat->id,0); + //echo $dbcat->name; //Debug + } else { + //Parent not found, fix it (set its parent to 0) + set_field ('question_categories','parent',0,'id',$current); + //echo 'assigned to top!'; //Debug + } + } + //echo '
'; //Debug + } + } + } + + // Now we look for random questions that can use questions from subcategories + // because we will have to add these subcategories + $sql = "SELECT t.id, t.category + FROM {$CFG->prefix}quiz_question_instances AS g, + {$CFG->prefix}question AS t + $from + WHERE $where t.id = g.question + AND t.qtype = '".RANDOM."' + AND t.questiontext = '1'"; + if ($randoms = get_records_sql($sql)) { + foreach ($randoms as $random) { + $status = quiz_backup_add_category_tree($backup_unique_code, $random->category); + } + } + + return $status; + } + + /** + * Helper function adding the id of a category and all its descendents to the backup_ids + */ + function quiz_backup_add_category_tree($backup_unique_code, $categoryid) { + $status = backup_putid($backup_unique_code,'question_categories',$categoryid,0); + if ($subcategories = get_records('question_categories', 'parent', $categoryid, 'sortorder ASC', 'id, id')) { + foreach ($subcategories as $subcategory) { + $status = quiz_backup_add_category_tree($backup_unique_code, $subcategory->id); + } + } + return $status; + } + + + //This function is used to detect orphaned questions (pointing to a + //non existing category) and to recreate such category. This function + //is used by the backup process, to ensure consistency and should be + //executed in the upgrade process and, perhaps in the health center. + function fix_orphaned_questions ($course) { + + global $CFG; + + $categories = get_records_sql("SELECT DISTINCT t.category, t.category + FROM {$CFG->prefix}question t, + {$CFG->prefix}quiz_question_instances g, + {$CFG->prefix}quiz q + WHERE q.course = '$course' AND + g.quiz = q.id AND + g.question = t.id",false); + if ($categories) { + foreach ($categories as $key => $category) { + $exist = get_record('question_categories','id', $key); + //If the category doesn't exist + if (!$exist) { + //Build a new category + $db_cat->course = $course; + $db_cat->name = get_string('recreatedcategory','',$key); + $db_cat->info = get_string('recreatedcategory','',$key); + $db_cat->publish = 1; + $db_cat->stamp = make_unique_id_code(); + //Insert the new category + $catid = insert_record('question_categories',$db_cat); + unset ($db_cat); + if ($catid) { + //Reasign orphaned questions to their new category + set_field ('question','category',$catid,'category',$key); + } + } + } + } + } + + //Delete category ids from backup_ids table + function delete_category_ids ($backup_unique_code) { + global $CFG; + $status = true; + $status = execute_sql("DELETE FROM {$CFG->prefix}backup_ids + WHERE backup_code = '$backup_unique_code'",false); + return $status; + } + + function quiz_backup_question_categories($bf,$preferences) { + + global $CFG; + + $status = true; + + //First, we get the used categories from backup_ids + $categories = question_category_ids_by_backup ($preferences->backup_unique_code); + + //If we've categories + if ($categories) { + //Write start tag + $status = fwrite($bf,start_tag("QUESTION_CATEGORIES",2,true)); + //Iterate over each category + foreach ($categories as $cat) { + //Start category + $status = fwrite ($bf,start_tag("QUESTION_CATEGORY",3,true)); + //Get category data from question_categories + $category = get_record ("question_categories","id",$cat->old_id); + //Print category contents + fwrite($bf,full_tag("ID",4,false,$category->id)); + fwrite($bf,full_tag("NAME",4,false,$category->name)); + fwrite($bf,full_tag("INFO",4,false,$category->info)); + fwrite($bf,full_tag("PUBLISH",4,false,$category->publish)); + fwrite($bf,full_tag("STAMP",4,false,$category->stamp)); + fwrite($bf,full_tag("PARENT",4,false,$category->parent)); + fwrite($bf,full_tag("SORTORDER",4,false,$category->sortorder)); + //Now, backup their questions + $status = quiz_backup_question($bf,$preferences,$category->id); + //End category + $status = fwrite ($bf,end_tag("QUESTION_CATEGORY",3,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("QUESTION_CATEGORIES",2,true)); + } + + return $status; + } + + //This function backups all the questions in selected category and their + //asociated data + function quiz_backup_question($bf,$preferences,$category) { + + global $CFG; + + $status = true; + + // We'll fetch the questions sorted by parent so that questions with no parents + // (these are the ones which could be parents themselves) are backed up first. This + // is important for the recoding of the parent field during the restore process + $questions = get_records("question","category",$category,"parent ASC, id"); + //If there are questions + if ($questions) { + //Write start tag + $status = fwrite ($bf,start_tag("QUESTIONS",4,true)); + $counter = 0; + //Iterate over each question + foreach ($questions as $question) { + //Start question + $status = fwrite ($bf,start_tag("QUESTION",5,true)); + //Print question contents + fwrite ($bf,full_tag("ID",6,false,$question->id)); + fwrite ($bf,full_tag("PARENT",6,false,$question->parent)); + fwrite ($bf,full_tag("NAME",6,false,$question->name)); + fwrite ($bf,full_tag("QUESTIONTEXT",6,false,$question->questiontext)); + fwrite ($bf,full_tag("QUESTIONTEXTFORMAT",6,false,$question->questiontextformat)); + fwrite ($bf,full_tag("IMAGE",6,false,$question->image)); + fwrite ($bf,full_tag("DEFAULTGRADE",6,false,$question->defaultgrade)); + fwrite ($bf,full_tag("PENALTY",6,false,$question->penalty)); + fwrite ($bf,full_tag("QTYPE",6,false,$question->qtype)); + fwrite ($bf,full_tag("LENGTH",6,false,$question->length)); + fwrite ($bf,full_tag("STAMP",6,false,$question->stamp)); + fwrite ($bf,full_tag("VERSION",6,false,$question->version)); + fwrite ($bf,full_tag("HIDDEN",6,false,$question->hidden)); + //Now, depending of the qtype, call one function or other + if ($question->qtype == "1") { + $status = quiz_backup_shortanswer($bf,$preferences,$question->id); + } else if ($question->qtype == "2") { + $status = quiz_backup_truefalse($bf,$preferences,$question->id); + } else if ($question->qtype == "3") { + $status = quiz_backup_multichoice($bf,$preferences,$question->id); + } else if ($question->qtype == "4") { + //Random question. Nothing to write. + } else if ($question->qtype == "5") { + $status = quiz_backup_match($bf,$preferences,$question->id); + } else if ($question->qtype == "6") { + $status = quiz_backup_randomsamatch($bf,$preferences,$question->id); + } else if ($question->qtype == "7") { + //Description question. Nothing to write. + } else if ($question->qtype == "8") { + $status = quiz_backup_numerical($bf,$preferences,$question->id); + } else if ($question->qtype == "9") { + $status = quiz_backup_multianswer($bf,$preferences,$question->id); + } else if ($question->qtype == "10") { + $status = quiz_backup_calculated($bf,$preferences,$question->id); + } else if ($question->qtype == "11") { + $status = quiz_backup_rqp($bf,$preferences,$question->id); + } else if ($question->qtype == "12") { + $status = quiz_backup_essay($bf,$preferences,$question->id); + } + //End question + $status = fwrite ($bf,end_tag("QUESTION",5,true)); + //Do some output + $counter++; + if ($counter % 10 == 0) { + echo "."; + if ($counter % 200 == 0) { + echo "
"; + } + backup_flush(300); + } + } + //Write end tag + $status = fwrite ($bf,end_tag("QUESTIONS",4,true)); + } + return $status; + } + + //This function backups the data in a truefalse question (qtype=2) and its + //asociated data + function quiz_backup_truefalse($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $truefalses = get_records("question_truefalse","question",$question,"id"); + //If there are truefalses + if ($truefalses) { + //Iterate over each truefalse + foreach ($truefalses as $truefalse) { + $status = fwrite ($bf,start_tag("TRUEFALSE",6,true)); + //Print truefalse contents + fwrite ($bf,full_tag("TRUEANSWER",7,false,$truefalse->trueanswer)); + fwrite ($bf,full_tag("FALSEANSWER",7,false,$truefalse->falseanswer)); + $status = fwrite ($bf,end_tag("TRUEFALSE",6,true)); + } + //Now print question_answers + $status = quiz_backup_answers($bf,$preferences,$question); + } + return $status; + } + + //This function backups the data in a shortanswer question (qtype=1) and its + //asociated data + function quiz_backup_shortanswer($bf,$preferences,$question,$level=6,$include_answers=true) { + + global $CFG; + + $status = true; + + $shortanswers = get_records("question_shortanswer","question",$question,"id"); + //If there are shortanswers + if ($shortanswers) { + //Iterate over each shortanswer + foreach ($shortanswers as $shortanswer) { + $status = fwrite ($bf,start_tag("SHORTANSWER",$level,true)); + //Print shortanswer contents + fwrite ($bf,full_tag("ANSWERS",$level+1,false,$shortanswer->answers)); + fwrite ($bf,full_tag("USECASE",$level+1,false,$shortanswer->usecase)); + $status = fwrite ($bf,end_tag("SHORTANSWER",$level,true)); + } + //Now print question_answers + if ($include_answers) { + $status = quiz_backup_answers($bf,$preferences,$question); + } + } + return $status; + } + + //This function backups the data in a multichoice question (qtype=3) and its + //asociated data + function quiz_backup_multichoice($bf,$preferences,$question,$level=6,$include_answers=true) { + + global $CFG; + + $status = true; + + $multichoices = get_records("question_multichoice","question",$question,"id"); + //If there are multichoices + if ($multichoices) { + //Iterate over each multichoice + foreach ($multichoices as $multichoice) { + $status = fwrite ($bf,start_tag("MULTICHOICE",$level,true)); + //Print multichoice contents + fwrite ($bf,full_tag("LAYOUT",$level+1,false,$multichoice->layout)); + fwrite ($bf,full_tag("ANSWERS",$level+1,false,$multichoice->answers)); + fwrite ($bf,full_tag("SINGLE",$level+1,false,$multichoice->single)); + fwrite ($bf,full_tag("SHUFFLEANSWERS",$level+1,false,$randomsamatch->shuffleanswers)); + $status = fwrite ($bf,end_tag("MULTICHOICE",$level,true)); + } + //Now print question_answers + if ($include_answers) { + $status = quiz_backup_answers($bf,$preferences,$question); + } + } + return $status; + } + + //This function backups the data in a randomsamatch question (qtype=6) and its + //asociated data + function quiz_backup_randomsamatch($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $randomsamatchs = get_records("question_randomsamatch","question",$question,"id"); + //If there are randomsamatchs + if ($randomsamatchs) { + //Iterate over each randomsamatch + foreach ($randomsamatchs as $randomsamatch) { + $status = fwrite ($bf,start_tag("RANDOMSAMATCH",6,true)); + //Print randomsamatch contents + fwrite ($bf,full_tag("CHOOSE",7,false,$randomsamatch->choose)); + fwrite ($bf,full_tag("SHUFFLEANSWERS",7,false,$randomsamatch->shuffleanswers)); + $status = fwrite ($bf,end_tag("RANDOMSAMATCH",6,true)); + } + } + return $status; + } + + //This function backups the data in a match question (qtype=5) and its + //asociated data + function quiz_backup_match($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $matchs = get_records("question_match_sub","question",$question,"id"); + //If there are matchs + if ($matchs) { + $status = fwrite ($bf,start_tag("MATCHS",6,true)); + //Iterate over each match + foreach ($matchs as $match) { + $status = fwrite ($bf,start_tag("MATCH",7,true)); + //Print match contents + fwrite ($bf,full_tag("ID",8,false,$match->id)); + fwrite ($bf,full_tag("CODE",8,false,$match->code)); + fwrite ($bf,full_tag("QUESTIONTEXT",8,false,$match->questiontext)); + fwrite ($bf,full_tag("ANSWERTEXT",8,false,$match->answertext)); + $status = fwrite ($bf,end_tag("MATCH",7,true)); + } + $status = fwrite ($bf,end_tag("MATCHS",6,true)); + } + return $status; + } + + //This function backups the data in a numerical question (qtype=8) and its + //asociated data + function quiz_backup_numerical($bf,$preferences,$question,$level=6) { + + global $CFG; + + $status = true; + + $numericals = get_records("question_numerical","question",$question,"id"); + //If there are numericals + if ($numericals) { + //Iterate over each numerical + foreach ($numericals as $numerical) { + $status = fwrite ($bf,start_tag("NUMERICAL",$level,true)); + //Print numerical contents + fwrite ($bf,full_tag("ANSWER",$level+1,false,$numerical->answer)); + fwrite ($bf,full_tag("TOLERANCE",$level+1,false,$numerical->tolerance)); + //Now backup numerical_units + $status = quiz_backup_numerical_units($bf,$preferences,$question,7); + $status = fwrite ($bf,end_tag("NUMERICAL",$level,true)); + } + //Now print question_answers + $status = quiz_backup_answers($bf,$preferences,$question); + } + return $status; + } + + //This function backups the data in a multianswer question (qtype=9) and its + //asociated data + function quiz_backup_multianswer($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $multianswers = get_records("question_multianswer","question",$question,"id"); + //If there are multianswers + if ($multianswers) { + //Print multianswers header + $status = fwrite ($bf,start_tag("MULTIANSWERS",6,true)); + //Iterate over each multianswer + foreach ($multianswers as $multianswer) { + $status = fwrite ($bf,start_tag("MULTIANSWER",7,true)); + //Print multianswer contents + fwrite ($bf,full_tag("ID",8,false,$multianswer->id)); + fwrite ($bf,full_tag("QUESTION",8,false,$multianswer->question)); + fwrite ($bf,full_tag("SEQUENCE",8,false,$multianswer->sequence)); + $status = fwrite ($bf,end_tag("MULTIANSWER",7,true)); + } + //Print multianswers footer + $status = fwrite ($bf,end_tag("MULTIANSWERS",6,true)); + //Now print question_answers + $status = quiz_backup_answers($bf,$preferences,$question); + } + return $status; + } + + //This function backups the data in a calculated question (qtype=10) and its + //asociated data + function quiz_backup_calculated($bf,$preferences,$question,$level=6,$include_answers=true) { + + global $CFG; + + $status = true; + + $calculateds = get_records("question_calculated","question",$question,"id"); + //If there are calculated-s + if ($calculateds) { + //Iterate over each calculateds + foreach ($calculateds as $calculated) { + $status = $status &&fwrite ($bf,start_tag("CALCULATED",$level,true)); + //Print calculated contents + fwrite ($bf,full_tag("ANSWER",$level+1,false,$calculated->answer)); + fwrite ($bf,full_tag("TOLERANCE",$level+1,false,$calculated->tolerance)); + fwrite ($bf,full_tag("TOLERANCETYPE",$level+1,false,$calculated->tolerancetype)); + fwrite ($bf,full_tag("CORRECTANSWERLENGTH",$level+1,false,$calculated->correctanswerlength)); + fwrite ($bf,full_tag("CORRECTANSWERFORMAT",$level+1,false,$calculated->correctanswerformat)); + //Now backup numerical_units + $status = quiz_backup_numerical_units($bf,$preferences,$question,7); + //Now backup required dataset definitions and items... + $status = quiz_backup_datasets($bf,$preferences,$question,7); + //End calculated data + $status = $status &&fwrite ($bf,end_tag("CALCULATED",$level,true)); + } + //Now print question_answers + if ($include_answers) { + $status = quiz_backup_answers($bf,$preferences,$question); + } + } + return $status; + } + + //This function backups the data in an rqp question (qtype=11) and its + //asociated data + function quiz_backup_rqp($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $rqp = get_records("question_rqp","question",$question,"id"); + //If there are rqps + if ($rqps) { + //Iterate over each rqp + foreach ($rqps as $rqp) { + $status = fwrite ($bf,start_tag("RQP",6,true)); + //Print rqp contents + fwrite ($bf,full_tag("TYPE",7,false,$rqp->type)); + fwrite ($bf,full_tag("SOURCE",7,false,$rqp->source)); + fwrite ($bf,full_tag("FORMAT",7,false,$rqp->format)); + fwrite ($bf,full_tag("FLAGS",7,false,$rqp->flags)); + fwrite ($bf,full_tag("MAXSCORE",7,false,$rqp->maxscore)); + $status = fwrite ($bf,end_tag("RQP",6,true)); + } + } + return $status; + } + + //This function backups the data in an essay question (qtype=12) and its + //asociated data + function quiz_backup_essay($bf,$preferences,$question,$level=6) { + + global $CFG; + + $status = true; + + $essays = get_records('question_essay', 'question', $question, "id"); + //If there are essays + if ($essays) { + //Iterate over each essay + foreach ($essays as $essay) { + $status = fwrite ($bf,start_tag("ESSAY",$level,true)); + //Print essay contents + fwrite ($bf,full_tag("ANSWER",$level+1,false,$essay->answer)); + $status = fwrite ($bf,end_tag("ESSAY",$level,true)); + } + //Now print question_answers + $status = quiz_backup_answers($bf,$preferences,$question); + } + return $status; + } + + + //This function backups the answers data in some question types + //(truefalse, shortanswer,multichoice,numerical,calculated) + function quiz_backup_answers($bf,$preferences,$question) { + + global $CFG; + + $status = true; + + $answers = get_records("question_answers","question",$question,"id"); + //If there are answers + if ($answers) { + $status = fwrite ($bf,start_tag("ANSWERS",6,true)); + //Iterate over each answer + foreach ($answers as $answer) { + $status = fwrite ($bf,start_tag("ANSWER",7,true)); + //Print answer contents + fwrite ($bf,full_tag("ID",8,false,$answer->id)); + fwrite ($bf,full_tag("ANSWER_TEXT",8,false,$answer->answer)); + fwrite ($bf,full_tag("FRACTION",8,false,$answer->fraction)); + fwrite ($bf,full_tag("FEEDBACK",8,false,$answer->feedback)); + $status = fwrite ($bf,end_tag("ANSWER",7,true)); + } + $status = fwrite ($bf,end_tag("ANSWERS",6,true)); + } + return $status; + } + + //This function backups question_numerical_units from different question types + function quiz_backup_numerical_units($bf,$preferences,$question,$level=7) { + + global $CFG; + + $status = true; + + $numerical_units = get_records("question_numerical_units","question",$question,"id"); + //If there are numericals_units + if ($numerical_units) { + $status = fwrite ($bf,start_tag("NUMERICAL_UNITS",$level,true)); + //Iterate over each numerical_unit + foreach ($numerical_units as $numerical_unit) { + $status = fwrite ($bf,start_tag("NUMERICAL_UNIT",$level+1,true)); + //Print numerical_unit contents + fwrite ($bf,full_tag("MULTIPLIER",$level+2,false,$numerical_unit->multiplier)); + fwrite ($bf,full_tag("UNIT",$level+2,false,$numerical_unit->unit)); + //Now backup numerical_units + $status = fwrite ($bf,end_tag("NUMERICAL_UNIT",$level+1,true)); + } + $status = fwrite ($bf,end_tag("NUMERICAL_UNITS",$level,true)); + } + + return $status; + + } + + //This function backups dataset_definitions (via question_datasets) from different question types + function quiz_backup_datasets($bf,$preferences,$question,$level=7) { + + global $CFG; + + $status = true; + + //First, we get the used datasets for this question + $question_datasets = get_records("question_datasets","question",$question,"id"); + //If there are question_datasets + if ($question_datasets) { + $status = $status &&fwrite ($bf,start_tag("DATASET_DEFINITIONS",$level,true)); + //Iterate over each question_dataset + foreach ($question_datasets as $question_dataset) { + $def = NULL; + //Get dataset_definition + if ($def = get_record("question_dataset_definitions","id",$question_dataset->datasetdefinition)) {; + $status = $status &&fwrite ($bf,start_tag("DATASET_DEFINITION",$level+1,true)); + //Print question_dataset contents + fwrite ($bf,full_tag("CATEGORY",$level+2,false,$def->category)); + fwrite ($bf,full_tag("NAME",$level+2,false,$def->name)); + fwrite ($bf,full_tag("TYPE",$level+2,false,$def->type)); + fwrite ($bf,full_tag("OPTIONS",$level+2,false,$def->options)); + fwrite ($bf,full_tag("ITEMCOUNT",$level+2,false,$def->itemcount)); + //Now backup dataset_entries + $status = quiz_backup_dataset_items($bf,$preferences,$def->id,$level+2); + //End dataset definition + $status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITION",$level+1,true)); + } + } + $status = $status &&fwrite ($bf,end_tag("DATASET_DEFINITIONS",$level,true)); + } + + return $status; + + } + + //This function backups datases_items from dataset_definitions + function quiz_backup_dataset_items($bf,$preferences,$datasetdefinition,$level=9) { + + global $CFG; + + $status = true; + + //First, we get the datasets_items for this dataset_definition + $dataset_items = get_records("question_dataset_items","definition",$datasetdefinition,"id"); + //If there are dataset_items + if ($dataset_items) { + $status = $status &&fwrite ($bf,start_tag("DATASET_ITEMS",$level,true)); + //Iterate over each dataset_item + foreach ($dataset_items as $dataset_item) { + $status = $status &&fwrite ($bf,start_tag("DATASET_ITEM",$level+1,true)); + //Print question_dataset contents + fwrite ($bf,full_tag("NUMBER",$level+2,false,$dataset_item->number)); + fwrite ($bf,full_tag("VALUE",$level+2,false,$dataset_item->value)); + //End dataset definition + $status = $status &&fwrite ($bf,end_tag("DATASET_ITEM",$level+1,true)); + } + $status = $status &&fwrite ($bf,end_tag("DATASET_ITEMS",$level,true)); + } + + return $status; + + } + +//STEP 2. Backup quizzes and associated structures + // (course dependent) + + function quiz_backup_one_mod($bf,$preferences,$quiz) { + $status = true; + + if (is_numeric($quiz)) { + $quiz = get_record('quiz','id',$quiz); + } + + //Start mod + fwrite ($bf,start_tag("MOD",3,true)); + //Print quiz data + fwrite ($bf,full_tag("ID",4,false,$quiz->id)); + fwrite ($bf,full_tag("MODTYPE",4,false,"quiz")); + fwrite ($bf,full_tag("NAME",4,false,$quiz->name)); + fwrite ($bf,full_tag("INTRO",4,false,$quiz->intro)); + fwrite ($bf,full_tag("TIMEOPEN",4,false,$quiz->timeopen)); + fwrite ($bf,full_tag("TIMECLOSE",4,false,$quiz->timeclose)); + fwrite ($bf,full_tag("OPTIONFLAGS",4,false,$quiz->optionflags)); + fwrite ($bf,full_tag("PENALTYSCHEME",4,false,$quiz->penaltyscheme)); + fwrite ($bf,full_tag("ATTEMPTS_NUMBER",4,false,$quiz->attempts)); + fwrite ($bf,full_tag("ATTEMPTONLAST",4,false,$quiz->attemptonlast)); + fwrite ($bf,full_tag("GRADEMETHOD",4,false,$quiz->grademethod)); + fwrite ($bf,full_tag("DECIMALPOINTS",4,false,$quiz->decimalpoints)); + fwrite ($bf,full_tag("REVIEW",4,false,$quiz->review)); + fwrite ($bf,full_tag("QUESTIONSPERPAGE",4,false,$quiz->questionsperpage)); + fwrite ($bf,full_tag("SHUFFLEQUESTIONS",4,false,$quiz->shufflequestions)); + fwrite ($bf,full_tag("SHUFFLEANSWERS",4,false,$quiz->shuffleanswers)); + fwrite ($bf,full_tag("QUESTIONS",4,false,$quiz->questions)); + fwrite ($bf,full_tag("SUMGRADES",4,false,$quiz->sumgrades)); + fwrite ($bf,full_tag("GRADE",4,false,$quiz->grade)); + fwrite ($bf,full_tag("TIMECREATED",4,false,$quiz->timecreated)); + fwrite ($bf,full_tag("TIMEMODIFIED",4,false,$quiz->timemodified)); + fwrite ($bf,full_tag("TIMELIMIT",4,false,$quiz->timelimit)); + fwrite ($bf,full_tag("PASSWORD",4,false,$quiz->password)); + fwrite ($bf,full_tag("SUBNET",4,false,$quiz->subnet)); + fwrite ($bf,full_tag("POPUP",4,false,$quiz->popup)); + fwrite ($bf,full_tag("DELAY1",4,false,$quiz->delay1)); + fwrite ($bf,full_tag("DELAY2",4,false,$quiz->delay2)); + //Now we print to xml question_instances (Course Level) + $status = backup_quiz_question_instances($bf,$preferences,$quiz->id); + //Now we print to xml question_versions (Course Level) + $status = backup_quiz_question_versions($bf,$preferences,$quiz->id); + //if we've selected to backup users info, then execute: + // - backup_quiz_grades + // - backup_quiz_attempts + if (backup_userdata_selected($preferences,'quiz',$quiz->id) && $status) { + $status = backup_quiz_grades($bf,$preferences,$quiz->id); + if ($status) { + $status = backup_quiz_attempts($bf,$preferences,$quiz->id); + } + } + //End mod + $status = fwrite ($bf,end_tag("MOD",3,true)); + + return $status; + } + + + function quiz_backup_mods($bf,$preferences) { + + global $CFG; + + $status = true; + + //Iterate over quiz table + $quizzes = get_records ("quiz","course",$preferences->backup_course,"id"); + if ($quizzes) { + foreach ($quizzes as $quiz) { + if (backup_mod_selected($preferences,'quiz',$quiz->id)) { + $status = quiz_backup_one_mod($bf,$preferences,$quiz); + } + } + } + return $status; + } + + //Backup quiz_question_instances contents (executed from quiz_backup_mods) + function backup_quiz_question_instances ($bf,$preferences,$quiz) { + + global $CFG; + + $status = true; + + $quiz_question_instances = get_records("quiz_question_instances","quiz",$quiz,"id"); + //If there are question_instances + if ($quiz_question_instances) { + //Write start tag + $status = fwrite ($bf,start_tag("QUESTION_INSTANCES",4,true)); + //Iterate over each question_instance + foreach ($quiz_question_instances as $que_ins) { + //Start question instance + $status = fwrite ($bf,start_tag("QUESTION_INSTANCE",5,true)); + //Print question_instance contents + fwrite ($bf,full_tag("ID",6,false,$que_ins->id)); + fwrite ($bf,full_tag("QUESTION",6,false,$que_ins->question)); + fwrite ($bf,full_tag("GRADE",6,false,$que_ins->grade)); + //End question instance + $status = fwrite ($bf,end_tag("QUESTION_INSTANCE",5,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("QUESTION_INSTANCES",4,true)); + } + return $status; + } + + //Backup quiz_question_versions contents (executed from quiz_backup_mods) + function backup_quiz_question_versions ($bf,$preferences,$quiz) { + + global $CFG; + + $status = true; + + $quiz_question_versions = get_records("quiz_question_versions","quiz",$quiz,"id"); + //If there are question_versions + if ($quiz_question_versions) { + //Write start tag + $status = fwrite ($bf,start_tag("QUESTION_VERSIONS",4,true)); + //Iterate over each question_version + foreach ($quiz_question_versions as $que_ver) { + //Start question version + $status = fwrite ($bf,start_tag("QUESTION_VERSION",5,true)); + //Print question_version contents + fwrite ($bf,full_tag("ID",6,false,$que_ver->id)); + fwrite ($bf,full_tag("OLDQUESTION",6,false,$que_ver->oldquestion)); + fwrite ($bf,full_tag("NEWQUESTION",6,false,$que_ver->newquestion)); + fwrite ($bf,full_tag("ORIGINALQUESTION",6,false,$que_ver->originalquestion)); + fwrite ($bf,full_tag("USERID",6,false,$que_ver->userid)); + fwrite ($bf,full_tag("TIMESTAMP",6,false,$que_ver->timestamp)); + //End question version + $status = fwrite ($bf,end_tag("QUESTION_VERSION",5,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("QUESTION_VERSIONS",4,true)); + } + return $status; + } + + + //Backup quiz_grades contents (executed from quiz_backup_mods) + function backup_quiz_grades ($bf,$preferences,$quiz) { + + global $CFG; + + $status = true; + + $quiz_grades = get_records("quiz_grades","quiz",$quiz,"id"); + //If there are grades + if ($quiz_grades) { + //Write start tag + $status = fwrite ($bf,start_tag("GRADES",4,true)); + //Iterate over each grade + foreach ($quiz_grades as $gra) { + //Start grade + $status = fwrite ($bf,start_tag("GRADE",5,true)); + //Print grade contents + fwrite ($bf,full_tag("ID",6,false,$gra->id)); + fwrite ($bf,full_tag("USERID",6,false,$gra->userid)); + fwrite ($bf,full_tag("GRADEVAL",6,false,$gra->grade)); + fwrite ($bf,full_tag("TIMEMODIFIED",6,false,$gra->timemodified)); + //End question grade + $status = fwrite ($bf,end_tag("GRADE",5,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("GRADES",4,true)); + } + return $status; + } + + //Backup quiz_attempts contents (executed from quiz_backup_mods) + function backup_quiz_attempts ($bf,$preferences,$quiz) { + + global $CFG; + + $status = true; + + $quiz_attempts = get_records("quiz_attempts","quiz",$quiz,"id"); + //If there are attempts + if ($quiz_attempts) { + //Write start tag + $status = fwrite ($bf,start_tag("ATTEMPTS",4,true)); + //Iterate over each attempt + foreach ($quiz_attempts as $attempt) { + //Start attempt + $status = fwrite ($bf,start_tag("ATTEMPT",5,true)); + //Print attempt contents + fwrite ($bf,full_tag("ID",6,false,$attempt->id)); + fwrite ($bf,full_tag("UNIQUEID",6,false,$attempt->uniqueid)); + fwrite ($bf,full_tag("USERID",6,false,$attempt->userid)); + fwrite ($bf,full_tag("ATTEMPTNUM",6,false,$attempt->attempt)); + fwrite ($bf,full_tag("SUMGRADES",6,false,$attempt->sumgrades)); + fwrite ($bf,full_tag("TIMESTART",6,false,$attempt->timestart)); + fwrite ($bf,full_tag("TIMEFINISH",6,false,$attempt->timefinish)); + fwrite ($bf,full_tag("TIMEMODIFIED",6,false,$attempt->timemodified)); + fwrite ($bf,full_tag("LAYOUT",6,false,$attempt->layout)); + fwrite ($bf,full_tag("PREVIEW",6,false,$attempt->preview)); + //Now write to xml the states (in this attempt) + $status = backup_question_states ($bf,$preferences,$attempt->uniqueid); + //Now write to xml the sessions (in this attempt) + $status = backup_question_sessions ($bf,$preferences,$attempt->uniqueid); + //End attempt + $status = fwrite ($bf,end_tag("ATTEMPT",5,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("ATTEMPTS",4,true)); + } + return $status; + } + + //Backup question_states contents (executed from backup_quiz_attempts) + function backup_question_states ($bf,$preferences,$attempt) { + + global $CFG; + + $status = true; + + $question_states = get_records("question_states","attempt",$attempt,"id"); + //If there are states + if ($question_states) { + //Write start tag + $status = fwrite ($bf,start_tag("STATES",6,true)); + //Iterate over each state + foreach ($question_states as $state) { + //Start state + $status = fwrite ($bf,start_tag("STATE",7,true)); + //Print state contents + fwrite ($bf,full_tag("ID",8,false,$state->id)); + fwrite ($bf,full_tag("QUESTION",8,false,$state->question)); + fwrite ($bf,full_tag("ORIGINALQUESTION",8,false,$state->originalquestion)); + fwrite ($bf,full_tag("SEQ_NUMBER",8,false,$state->seq_number)); + fwrite ($bf,full_tag("ANSWER",8,false,$state->answer)); + fwrite ($bf,full_tag("TIMESTAMP",8,false,$state->timestamp)); + fwrite ($bf,full_tag("EVENT",8,false,$state->event)); + fwrite ($bf,full_tag("GRADE",8,false,$state->grade)); + fwrite ($bf,full_tag("RAW_GRADE",8,false,$state->raw_grade)); + fwrite ($bf,full_tag("PENALTY",8,false,$state->penalty)); + // now back up question type specific state information + $status = backup_question_rqp_state ($bf,$preferences,$state->id); + $status = backup_question_essay_state ($bf,$preferences,$state->id); + //End state + $status = fwrite ($bf,end_tag("STATE",7,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("STATES",6,true)); + } + } + + //Backup question_sessions contents (executed from backup_quiz_attempts) + function backup_question_sessions ($bf,$preferences,$attempt) { + global $CFG; + + $status = true; + + $question_sessions = get_records("question_sessions","attemptid",$attempt,"id"); + //If there are sessions + if ($question_sessions) { + //Write start tag (the funny name 'newest states' has historical reasons) + $status = fwrite ($bf,start_tag("NEWEST_STATES",6,true)); + //Iterate over each newest_state + foreach ($question_sessions as $newest_state) { + //Start newest_state + $status = fwrite ($bf,start_tag("NEWEST_STATE",7,true)); + //Print newest_state contents + fwrite ($bf,full_tag("ID",8,false,$newest_state->id)); + fwrite ($bf,full_tag("QUESTIONID",8,false,$newest_state->questionid)); + fwrite ($bf,full_tag("NEWEST",8,false,$newest_state->newest)); + fwrite ($bf,full_tag("NEWGRADED",8,false,$newest_state->newgraded)); + fwrite ($bf,full_tag("SUMPENALTY",8,false,$newest_state->sumpenalty)); + //End newest_state + $status = fwrite ($bf,end_tag("NEWEST_STATE",7,true)); + } + //Write end tag + $status = fwrite ($bf,end_tag("NEWEST_STATES",6,true)); + } + return $status; + } + + function quiz_check_backup_mods_instances($instance,$backup_unique_code) { + // the keys in this array need to be unique as they get merged... + $info[$instance->id.'0'][0] = ''.$instance->name.''; + $info[$instance->id.'0'][1] = ''; + + //Categories + $info[$instance->id.'1'][0] = get_string("categories","quiz"); + if ($ids = question_category_ids_by_backup ($backup_unique_code)) { + $info[$instance->id.'1'][1] = count($ids); + } else { + $info[$instance->id.'1'][1] = 0; + } + //Questions + $info[$instance->id.'2'][0] = get_string("questionsinclhidden","quiz"); + if ($ids = quiz_question_ids_by_backup ($backup_unique_code)) { + $info[$instance->id.'2'][1] = count($ids); + } else { + $info[$instance->id.'2'][1] = 0; + } + + //Now, if requested, the user_data + if (!empty($instance->userdata)) { + //Grades + $info[$instance->id.'3'][0] = get_string("grades"); + if ($ids = quiz_grade_ids_by_instance ($instance->id)) { + $info[$instance->id.'3'][1] = count($ids); + } else { + $info[$instance->id.'3'][1] = 0; + } + } + return $info; + } + + //Backup question_rqp_state contents (executed from backup_question_states) + function backup_question_rqp_state ($bf,$preferences,$state) { + + global $CFG; + + $status = true; + + $rqp_state = get_record("question_rqp_states","stateid",$state); + //If there is a state + if ($rqp_state) { + //Write start tag + $status = fwrite ($bf,start_tag("RQP_STATE",8,true)); + //Print state contents + fwrite ($bf,full_tag("RESPONSES",9,false,$rqp_state->responses)); + fwrite ($bf,full_tag("PERSISTENT_DATA",9,false,$rqp_state->persistent_data)); + fwrite ($bf,full_tag("TEMPLATE_VARS",9,false,$rqp_state->template_vars)); + //Write end tag + $status = fwrite ($bf,end_tag("RQP_STATE",8,true)); + } + return $status; + } + + //Backup question_essay_state contents (executed from backup_question_states) + function backup_question_essay_state ($bf,$preferences,$state) { + + global $CFG; + + $status = true; + + $essay_state = get_record("question_essay_states", "stateid", $state); + //If there is a state + if ($essay_state) { + //Write start tag + $status = fwrite ($bf,start_tag("ESSAY_STATE",8,true)); + //Print state contents + fwrite ($bf,full_tag("GRADED",9,false,$essay_state->graded)); + fwrite ($bf,full_tag("FRACTION",9,false,$essay_state->fraction)); + fwrite ($bf,full_tag("RESPONSE",9,false,$essay_state->response)); + //Write end tag + $status = fwrite ($bf,end_tag("ESSAY_STATE",8,true)); + } + return $status; + } + + ////Return an array of info (name,value) +/// $instances is an array with key = instanceid, value = object (name,id,userdata) + function quiz_check_backup_mods($course,$user_data= false,$backup_unique_code,$instances=null) { + + //Deletes data from mdl_backup_ids (categories section) + delete_category_ids ($backup_unique_code); + //Create date into mdl_backup_ids (categories section) + insert_category_ids ($course,$backup_unique_code,$instances); + if (!empty($instances) && is_array($instances) && count($instances)) { + $info = array(); + foreach ($instances as $id => $instance) { + $info += quiz_check_backup_mods_instances($instance,$backup_unique_code); + } + return $info; + } + //First the course data + $info[0][0] = get_string("modulenameplural","quiz"); + if ($ids = quiz_ids ($course)) { + $info[0][1] = count($ids); + } else { + $info[0][1] = 0; + } + //Categories + $info[1][0] = get_string("categories","quiz"); + if ($ids = question_category_ids_by_backup ($backup_unique_code)) { + $info[1][1] = count($ids); + } else { + $info[1][1] = 0; + } + //Questions + $info[2][0] = get_string("questions","quiz"); + if ($ids = quiz_question_ids_by_backup ($backup_unique_code)) { + $info[2][1] = count($ids); + } else { + $info[2][1] = 0; + } + + //Now, if requested, the user_data + if ($user_data) { + //Grades + $info[3][0] = get_string("grades"); + if ($ids = quiz_grade_ids_by_course ($course)) { + $info[3][1] = count($ids); + } else { + $info[3][1] = 0; + } + } + + return $info; + } + + //Return a content encoded to support interactivities linking. Every module + //should have its own. They are called automatically from the backup procedure. + function quiz_encode_content_links ($content,$preferences) { + + global $CFG; + + $base = preg_quote($CFG->wwwroot,"/"); + + //Link to the list of quizs + $buscar="/(".$base."\/mod\/quiz\/index.php\?id\=)([0-9]+)/"; + $result= preg_replace($buscar,'$@QUIZINDEX*$2@$',$content); + + //Link to quiz view by moduleid + $buscar="/(".$base."\/mod\/quiz\/view.php\?id\=)([0-9]+)/"; + $result= preg_replace($buscar,'$@QUIZVIEWBYID*$2@$',$result); + + return $result; + } + +// INTERNAL FUNCTIONS. BASED IN THE MOD STRUCTURE + + //Returns an array of quiz id + function quiz_ids ($course) { + + global $CFG; + + return get_records_sql ("SELECT a.id, a.course + FROM {$CFG->prefix}quiz a + WHERE a.course = '$course'"); + } + + //Returns an array of categories id + function question_category_ids_by_backup ($backup_unique_code) { + + global $CFG; + + return get_records_sql ("SELECT a.old_id, a.backup_code + FROM {$CFG->prefix}backup_ids a + WHERE a.backup_code = '$backup_unique_code' AND + a.table_name = 'question_categories'"); + } + + function quiz_question_ids_by_backup ($backup_unique_code) { + + global $CFG; + + return get_records_sql ("SELECT q.id, q.category + FROM {$CFG->prefix}backup_ids a, + {$CFG->prefix}question q + WHERE a.backup_code = '$backup_unique_code' AND + q.category = a.old_id AND + a.table_name = 'question_categories'"); + } + + function quiz_grade_ids_by_course ($course) { + + global $CFG; + + return get_records_sql ("SELECT g.id, g.quiz + FROM {$CFG->prefix}quiz a, + {$CFG->prefix}quiz_grades g + WHERE a.course = '$course' and + g.quiz = a.id"); + } + + function quiz_grade_ids_by_instance($instanceid) { + + global $CFG; + + return get_records_sql ("SELECT g.id, g.quiz + FROM {$CFG->prefix}quiz_grades g + WHERE g.quiz = $instanceid"); + } + +?>