ef34acd2cdbfb058b814dfb3fe5d0a15bf0a01a8
[moodle.git] / backup / restorelib.php
1 <?php
2     //Functions used in restore
4     require_once($CFG->libdir.'/gradelib.php');
6 /**
7  * Group backup/restore constants, 0.
8  */
9 define('RESTORE_GROUPS_NONE', 0);
11 /**
12  * Group backup/restore constants, 1.
13  */
14 define('RESTORE_GROUPS_ONLY', 1);
16 /**
17  * Group backup/restore constants, 2.
18  */
19 define('RESTORE_GROUPINGS_ONLY', 2);
21 /**
22  * Group backup/restore constants, course/all.
23  */
24 define('RESTORE_GROUPS_GROUPINGS', 3);
26     //This function unzips a zip file in the same directory that it is
27     //It automatically uses pclzip or command line unzip
28     function restore_unzip ($file) {
30         return unzip_file($file, '', false);
32     }
34     //This function checks if moodle.xml seems to be a valid xml file
35     //(exists, has an xml header and a course main tag
36     function restore_check_moodle_file ($file) {
38         $status = true;
40         //Check if it exists
41         if ($status = is_file($file)) {
42             //Open it and read the first 200 bytes (chars)
43             $handle = fopen ($file, "r");
44             $first_chars = fread($handle,200);
45             $status = fclose ($handle);
46             //Chek if it has the requires strings
47             if ($status) {
48                 $status = strpos($first_chars,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
49                 if ($status !== false) {
50                     $status = strpos($first_chars,"<MOODLE_BACKUP>");
51                 }
52             }
53         }
55         return $status;
56     }
58     //This function iterates over all modules in backup file, searching for a
59     //MODNAME_refresh_events() to execute. Perhaps it should ve moved to central Moodle...
60     function restore_refresh_events($restore) {
62         global $CFG;
63         $status = true;
65         //Take all modules in backup
66         $modules = $restore->mods;
67         //Iterate
68         foreach($modules as $name => $module) {
69             //Only if the module is being restored
70             if (isset($module->restore) && $module->restore == 1) {
71                 //Include module library
72                 include_once("$CFG->dirroot/mod/$name/lib.php");
73                 //If module_refresh_events exists
74                 $function_name = $name."_refresh_events";
75                 if (function_exists($function_name)) {
76                     $status = $function_name($restore->course_id);
77                 }
78             }
79         }
80         return $status;
81     }
83     //This function makes all the necessary calls to xxxx_decode_content_links_caller()
84     //function in each module/block/course format..., passing them the desired contents to be decoded
85     //from backup format to destination site/course in order to mantain inter-activities
86     //working in the backup/restore process
87     function restore_decode_content_links($restore) {
88         global $CFG, $DB;
90         $status = true;
92         if (!defined('RESTORE_SILENTLY')) {
93             echo "<ul>";
94         }
96         // Recode links in the course summary.
97         if (!defined('RESTORE_SILENTLY')) {
98             echo '<li>' . get_string('from') . ' ' . get_string('course');
99         }
100         $course = $DB->get_record('course', array('id'=>$restore->course_id), 'id,summary');
101         $coursesummary = backup_todb($course->summary); // Exception: Process FILEPHP (not available when restored) MDL-18222
102         $coursesummary = restore_decode_content_links_worker($coursesummary, $restore);
103         if ($coursesummary != $course->summary) {
104             $course->summary = $coursesummary;
105             $DB->update_record('course', $course);
106         }
107         if (!defined('RESTORE_SILENTLY')) {
108             echo '</li>';
109         }
111         // Recode links in section summaries.
112         $sections = $DB->get_records('course_sections', array('course'=>$restore->course_id), 'id', 'id,summary');
113         if ($sections) {
114             if (!defined('RESTORE_SILENTLY')) {
115                 echo '<li>' . get_string('from') . ' ' . get_string('sections');
116             }
117             foreach ($sections as $section) {
118                 $sectionsummary = restore_decode_content_links_worker($section->summary, $restore);
119                 if ($sectionsummary != $section->summary) {
120                     $section->summary = $sectionsummary;
121                     $DB->update_record('course_sections', $section);
122                 }
123             }
124             if (!defined('RESTORE_SILENTLY')) {
125                 echo '</li>';
126             }
127         }
129         // Restore links in modules.
130         foreach ($restore->mods as $name => $info) {
131             //If the module is being restored
132             if (isset($info->restore) && $info->restore == 1) {
133                 //Check if the xxxx_decode_content_links_caller exists
134                 include_once("$CFG->dirroot/mod/$name/restorelib.php");
135                 $function_name = $name."_decode_content_links_caller";
136                 if (function_exists($function_name)) {
137                     if (!defined('RESTORE_SILENTLY')) {
138                         echo "<li>".get_string ("from")." ".get_string("modulenameplural",$name);
139                     }
140                     $status = $function_name($restore) && $status;
141                     if (!defined('RESTORE_SILENTLY')) {
142                         echo '</li>';
143                     }
144                 }
145             }
146         }
148         // For the course format call its decode_content_links method (if it exists)
149         $format = $DB->get_field('course','format', array('id'=>$restore->course_id));
150         if (file_exists("$CFG->dirroot/course/format/$format/restorelib.php")) {
151             include_once("$CFG->dirroot/course/format/$format/restorelib.php");
152             $function_name = $format.'_decode_format_content_links_caller';
154             if (function_exists($function_name)) {
155                 if (!defined('RESTORE_SILENTLY')) {
156                     echo "<li>".get_string ("from")." ".get_string("format").' '.$format;
157                 }
158                 $status = $function_name($restore);
159                 if (!defined('RESTORE_SILENTLY')) {
160                     echo '</li>';
161                 }
162             }
163         }
165         // Process all html text also in blocks too
166         if (!defined('RESTORE_SILENTLY')) {
167             echo '<li>'.get_string ('from').' '.get_string('blocks');
168         }
170         if ($blocks = $DB->get_records('block', array('visible'=>1))) {
171             foreach ($blocks as $block) {
172                 if ($blockobject = block_instance($block->name)) {
173                     $blockobject->decode_content_links_caller($restore);
174                 }
175             }
176         }
178         if (!defined('RESTORE_SILENTLY')) {
179             echo '</li>';
180         }
182         // Restore links in questions.
183         require_once("$CFG->dirroot/question/restorelib.php");
184         if (!defined('RESTORE_SILENTLY')) {
185             echo '<li>' . get_string('from') . ' ' . get_string('questions', 'quiz');
186         }
187         $status = question_decode_content_links_caller($restore) && $status;
188         if (!defined('RESTORE_SILENTLY')) {
189             echo '</li>';
190         }
192         if (!defined('RESTORE_SILENTLY')) {
193             echo "</ul>";
194         }
196         return $status;
197     }
199     //This function is called from all xxxx_decode_content_links_caller(),
200     //its task is to ask all modules (maybe other linkable objects) to restore
201     //links to them.
202     function restore_decode_content_links_worker($content,$restore) {
203         global $CFG, $DB;
205         foreach($restore->mods as $name => $info) {
206             $function_name = $name."_decode_content_links";
207             if (function_exists($function_name)) {
208                 $content = $function_name($content,$restore);
209             }
210         }
212         // For the current format, call decode_format_content_links if it exists
213         static $format_function_name;
214         if (!isset($format_function_name)) {
215             $format_function_name = false;
216             if ($format = $DB->get_field('course','format', array('id'=>$restore->course_id))) {
217                 if (file_exists("$CFG->dirroot/course/format/$format/restorelib.php")) {
218                     include_once("$CFG->dirroot/course/format/$format/restorelib.php");
219                     $function_name = $format.'_decode_format_content_links';
220                     if (function_exists($function_name)) {
221                         $format_function_name = $function_name;
222                     }
223                 }
224             }
225         }
226         // If the above worked - then we have a function to call
227         if ($format_function_name) {
228             $content = $format_function_name($content, $restore);
229         }
231         // For each block, call its encode_content_links method
232         static $blockobjects = null;
233         if (!isset($blockobjects)) {
234             $blockobjects = array();
235             if ($blocks = $DB->get_records('block', array('visible'=>1))) {
236                 foreach ($blocks as $block) {
237                     if ($blockobject = block_instance($block->name)) {
238                         $blockobjects[] = $blockobject;
239                     }
240                 }
241             }
242         }
244         foreach ($blockobjects as $blockobject) {
245             $content = $blockobject->decode_content_links($content,$restore);
246         }
248         return $content;
249     }
251     //This function converts all the wiki texts in the restored course
252     //to the Markdown format. Used only for backup files prior 2005041100.
253     //It calls every module xxxx_convert_wiki2markdown function
254     function restore_convert_wiki2markdown($restore) {
256         $status = true;
258         if (!defined('RESTORE_SILENTLY')) {
259             echo "<ul>";
260         }
261         foreach ($restore->mods as $name => $info) {
262             //If the module is being restored
263             if ($info->restore == 1) {
264                 //Check if the xxxx_restore_wiki2markdown exists
265                 $function_name = $name."_restore_wiki2markdown";
266                 if (function_exists($function_name)) {
267                     $status = $function_name($restore);
268                     if (!defined('RESTORE_SILENTLY')) {
269                         echo "<li>".get_string("modulenameplural",$name);
270                         echo '</li>';
271                     }
272                 }
273             }
274         }
275         if (!defined('RESTORE_SILENTLY')) {
276             echo "</ul>";
277         }
278         return $status;
279     }
281     //This function receives a wiki text in the restore process and
282     //return it with every link to modules " modulename:moduleid"
283     //converted if possible. See the space before modulename!!
284     function restore_decode_wiki_content($content,$restore) {
285         global $CFG;
287         $result = $content;
289         $searchstring='/ ([a-zA-Z]+):([0-9]+)\(([^)]+)\)/';
290         //We look for it
291         preg_match_all($searchstring,$content,$foundset);
292         //If found, then we are going to look for its new id (in backup tables)
293         if ($foundset[0]) {
294             //print_object($foundset);                                     //Debug
295             //Iterate over foundset[2]. They are the old_ids
296             foreach($foundset[2] as $old_id) {
297                 //We get the needed variables here (course id)
298                 $rec = backup_getid($restore->backup_unique_code,"course_modules",$old_id);
299                 //Personalize the searchstring
300                 $searchstring='/ ([a-zA-Z]+):'.$old_id.'\(([^)]+)\)/';
301                 //If it is a link to this course, update the link to its new location
302                 if($rec->new_id) {
303                     //Now replace it
304                     $result= preg_replace($searchstring,' $1:'.$rec->new_id.'($2)',$result);
305                 } else {
306                     //It's a foreign link so redirect it to its original URL
307                     $result= preg_replace($searchstring,$restore->original_wwwroot.'/mod/$1/view.php?id='.$old_id.'($2)',$result);
308                 }
309             }
310         }
311         return $result;
312     }
315     //This function read the xml file and store it data from the info zone in an object
316     function restore_read_xml_info ($xml_file) {
318         //We call the main read_xml function, with $todo = INFO
319         $info = restore_read_xml ($xml_file,"INFO",false);
321         return $info;
322     }
324     //This function read the xml file and store it data from the course header zone in an object
325     function restore_read_xml_course_header ($xml_file) {
327         //We call the main read_xml function, with $todo = COURSE_HEADER
328         $info = restore_read_xml ($xml_file,"COURSE_HEADER",false);
330         return $info;
331     }
333     //This function read the xml file and store its data from the blocks in a object
334     function restore_read_xml_blocks ($restore, $xml_file) {
336         //We call the main read_xml function, with $todo = BLOCKS
337         $info = restore_read_xml ($xml_file,'BLOCKS',$restore);
339         return $info;
340     }
342     //This function read the xml file and store its data from the sections in a object
343     function restore_read_xml_sections ($xml_file) {
345         //We call the main read_xml function, with $todo = SECTIONS
346         $info = restore_read_xml ($xml_file,"SECTIONS",false);
348         return $info;
349     }
351     //This function read the xml file and store its data from the course format in an object
352     function restore_read_xml_formatdata ($xml_file) {
354         //We call the main read_xml function, with $todo = FORMATDATA
355         $info = restore_read_xml ($xml_file,'FORMATDATA',false);
357         return $info;
358     }
360     //This function read the xml file and store its data from the metacourse in a object
361     function restore_read_xml_metacourse ($xml_file) {
363         //We call the main read_xml function, with $todo = METACOURSE
364         $info = restore_read_xml ($xml_file,"METACOURSE",false);
366         return $info;
367     }
369     //This function read the xml file and store its data from the gradebook in a object
370     function restore_read_xml_gradebook ($restore, $xml_file) {
372         //We call the main read_xml function, with $todo = GRADEBOOK
373         $info = restore_read_xml ($xml_file,"GRADEBOOK",$restore);
375         return $info;
376     }
378     //This function read the xml file and store its data from the users in
379     //backup_ids->info db (and user's id in $info)
380     function restore_read_xml_users ($restore,$xml_file) {
382         //We call the main read_xml function, with $todo = USERS
383         $info = restore_read_xml ($xml_file,"USERS",$restore);
385         return $info;
386     }
388     //This function read the xml file and store its data from the messages in
389     //backup_ids->message backup_ids->message_read and backup_ids->contact and db (and their counters in info)
390     function restore_read_xml_messages ($restore,$xml_file) {
392         //We call the main read_xml function, with $todo = MESSAGES
393         $info = restore_read_xml ($xml_file,"MESSAGES",$restore);
395         return $info;
396     }
398     //This function read the xml file and store its data from the blogs in
399     //backup_ids->blog and backup_ids->blog_tag and db (and their counters in info)
400     function restore_read_xml_blogs ($restore,$xml_file) {
402         //We call the main read_xml function, with $todo = BLOGS
403         $info = restore_read_xml ($xml_file,"BLOGS",$restore);
405         return $info;
406     }
409     //This function read the xml file and store its data from the questions in
410     //backup_ids->info db (and category's id in $info)
411     function restore_read_xml_questions ($restore,$xml_file) {
413         //We call the main read_xml function, with $todo = QUESTIONS
414         $info = restore_read_xml ($xml_file,"QUESTIONS",$restore);
416         return $info;
417     }
419     //This function read the xml file and store its data from the scales in
420     //backup_ids->info db (and scale's id in $info)
421     function restore_read_xml_scales ($restore,$xml_file) {
423         //We call the main read_xml function, with $todo = SCALES
424         $info = restore_read_xml ($xml_file,"SCALES",$restore);
426         return $info;
427     }
429     //This function read the xml file and store its data from the groups in
430     //backup_ids->info db (and group's id in $info)
431     function restore_read_xml_groups ($restore,$xml_file) {
433         //We call the main read_xml function, with $todo = GROUPS
434         $info = restore_read_xml ($xml_file,"GROUPS",$restore);
436         return $info;
437     }
439     //This function read the xml file and store its data from the groupings in
440     //backup_ids->info db (and grouping's id in $info)
441     function restore_read_xml_groupings ($restore,$xml_file) {
443         //We call the main read_xml function, with $todo = GROUPINGS
444         $info = restore_read_xml ($xml_file,"GROUPINGS",$restore);
446         return $info;
447     }
449     //This function read the xml file and store its data from the groupings in
450     //backup_ids->info db (and grouping's id in $info)
451     function restore_read_xml_groupings_groups ($restore,$xml_file) {
453         //We call the main read_xml function, with $todo = GROUPINGS
454         $info = restore_read_xml ($xml_file,"GROUPINGSGROUPS",$restore);
456         return $info;
457     }
459     //This function read the xml file and store its data from the events (course) in
460     //backup_ids->info db (and event's id in $info)
461     function restore_read_xml_events ($restore,$xml_file) {
463         //We call the main read_xml function, with $todo = EVENTS
464         $info = restore_read_xml ($xml_file,"EVENTS",$restore);
466         return $info;
467     }
469     //This function read the xml file and store its data from the modules in
470     //backup_ids->info
471     function restore_read_xml_modules ($restore,$xml_file) {
473         //We call the main read_xml function, with $todo = MODULES
474         $info = restore_read_xml ($xml_file,"MODULES",$restore);
476         return $info;
477     }
479     //This function read the xml file and store its data from the logs in
480     //backup_ids->info
481     function restore_read_xml_logs ($restore,$xml_file) {
483         //We call the main read_xml function, with $todo = LOGS
484         $info = restore_read_xml ($xml_file,"LOGS",$restore);
486         return $info;
487     }
489     function restore_read_xml_roles ($xml_file) {
490         //We call the main read_xml function, with $todo = ROLES
491         $info = restore_read_xml ($xml_file,"ROLES",false);
493         return $info;
494     }
496     //This function prints the contents from the info parammeter passed
497     function restore_print_info ($info) {
499         global $CFG, $OUTPUT;
501         $status = true;
502         if ($info) {
503             $table = new html_table();
504             //This is tha align to every ingo table
505             $table->align = array ("right","left");
506             //This is the nowrap clause
507             $table->wrap = array ("","nowrap");
508             //The width
509             $table->width = "70%";
510             //Put interesting info in table
511             //The backup original name
512             $tab[0][0] = "<b>".get_string("backuporiginalname").":</b>";
513             $tab[0][1] = $info->backup_name;
514             //The moodle version
515             $tab[1][0] = "<b>".get_string("moodleversion").":</b>";
516             $tab[1][1] = $info->backup_moodle_release." (".$info->backup_moodle_version.")";
517             //The backup version
518             $tab[2][0] = "<b>".get_string("backupversion").":</b>";
519             $tab[2][1] = $info->backup_backup_release." (".$info->backup_backup_version.")";
520             //The backup date
521             $tab[3][0] = "<b>".get_string("backupdate").":</b>";
522             $tab[3][1] = userdate($info->backup_date);
523             //Is this the same Moodle install?
524             if (!empty($info->original_siteidentifier)) {
525                 $tab[4][0] = "<b>".get_string("backupfromthissite").":</b>";
526                 if (backup_is_same_site($info)) {
527                     $tab[4][1] = get_string('yes');
528                 } else {
529                     $tab[4][1] = get_string('no');
530                 }
531             }
532             //Print title
533             echo $OUTPUT->heading(get_string("backup").":");
534             $table->data = $tab;
535             //Print backup general info
536             echo html_writer::table($table);
538             if ($info->backup_backup_version <= 2005070500) {
539                  echo $OUTPUT->notification(get_string('backupnonisowarning'));  // Message informing that this backup may not work!
540             }
542             //Now backup contents in another table
543             $tab = array();
544             //First mods info
545             $mods = $info->mods;
546             $elem = 0;
547             foreach ($mods as $key => $mod) {
548                 $tab[$elem][0] = "<b>".get_string("modulenameplural",$key).":</b>";
549                 if ($mod->backup == "false") {
550                     $tab[$elem][1] = get_string("notincluded");
551                 } else {
552                     if ($mod->userinfo == "true") {
553                         $tab[$elem][1] = get_string("included")." ".get_string("withuserdata");
554                     } else {
555                         $tab[$elem][1] = get_string("included")." ".get_string("withoutuserdata");
556                     }
557                     if (isset($mod->instances) && is_array($mod->instances) && count($mod->instances)) {
558                         foreach ($mod->instances as $instance) {
559                             if ($instance->backup) {
560                                 $elem++;
561                                 $tab[$elem][0] = $instance->name;
562                                 if ($instance->userinfo == 'true') {
563                                     $tab[$elem][1] = get_string("included")." ".get_string("withuserdata");
564                                 } else {
565                                     $tab[$elem][1] = get_string("included")." ".get_string("withoutuserdata");
566                                 }
567                             }
568                         }
569                     }
570                 }
571                 $elem++;
572             }
573             //Metacourse info
574             $tab[$elem][0] = "<b>".get_string("metacourse").":</b>";
575             if ($info->backup_metacourse == "true") {
576                 $tab[$elem][1] = get_string("yes");
577             } else {
578                 $tab[$elem][1] = get_string("no");
579             }
580             $elem++;
581             //Users info
582             $tab[$elem][0] = "<b>".get_string("users").":</b>";
583             $tab[$elem][1] = get_string($info->backup_users);
584             $elem++;
585             //Logs info
586             $tab[$elem][0] = "<b>".get_string("logs").":</b>";
587             if ($info->backup_logs == "true") {
588                 $tab[$elem][1] = get_string("yes");
589             } else {
590                 $tab[$elem][1] = get_string("no");
591             }
592             $elem++;
593             //User Files info
594             $tab[$elem][0] = "<b>".get_string("userfiles").":</b>";
595             if ($info->backup_user_files == "true") {
596                 $tab[$elem][1] = get_string("yes");
597             } else {
598                 $tab[$elem][1] = get_string("no");
599             }
600             $elem++;
601             //Course Files info
602             $tab[$elem][0] = "<b>".get_string("coursefiles").":</b>";
603             if ($info->backup_course_files == "true") {
604                 $tab[$elem][1] = get_string("yes");
605             } else {
606                 $tab[$elem][1] = get_string("no");
607             }
608             $elem++;
609             //site Files info
610             $tab[$elem][0] = "<b>".get_string("sitefiles").":</b>";
611             if (isset($info->backup_site_files) && $info->backup_site_files == "true") {
612                 $tab[$elem][1] = get_string("yes");
613             } else {
614                 $tab[$elem][1] = get_string("no");
615             }
616             $elem++;
617             //gradebook history info
618             $tab[$elem][0] = "<b>".get_string('gradebookhistories', 'grades').":</b>";
619             if (isset($info->gradebook_histories) && $info->gradebook_histories == "true") {
620                 $tab[$elem][1] = get_string("yes");
621             } else {
622                 $tab[$elem][1] = get_string("no");
623             }
624             $elem++;
625             //Messages info (only showed if present)
626             if ($info->backup_messages == 'true') {
627                 $tab[$elem][0] = "<b>".get_string('messages','message').":</b>";
628                 $tab[$elem][1] = get_string('yes');
629                 $elem++;
630             } else {
631                 //Do nothing
632             }
633             $elem++;
634             //Blogs info (only showed if present)
635             if (isset($info->backup_blogs) && $info->backup_blogs == 'true') {
636                 $tab[$elem][0] = "<b>".get_string('blogs','blog').":</b>";
637                 $tab[$elem][1] = get_string('yes');
638                 $elem++;
639             } else {
640                 //Do nothing
641             }
642             $table->data = $tab;
643             //Print title
644             echo $OUTPUT->heading(get_string("backupdetails").":");
645             //Print backup general info
646             echo html_writer::table($table);
647         } else {
648             $status = false;
649         }
651         return $status;
652     }
654     //This function prints the contents from the course_header parammeter passed
655     function restore_print_course_header ($course_header) {
656         global $OUTPUT;
657         $status = true;
658         if ($course_header) {
659             $table = new html_table();
660             //This is tha align to every ingo table
661             $table->align = array ("right","left");
662             //The width
663             $table->width = "70%";
664             //Put interesting course header in table
665             //The course name
666             $tab[0][0] = "<b>".get_string("name").":</b>";
667             $tab[0][1] = $course_header->course_fullname." (".$course_header->course_shortname.")";
668             //The course summary
669             $tab[1][0] = "<b>".get_string("summary").":</b>";
670             $tab[1][1] = $course_header->course_summary;
671             $table->data = $tab;
672             //Print title
673             echo $OUTPUT->heading(get_string("course").":");
674             //Print backup course header info
675             echo html_writer::table($table);
676         } else {
677             $status = false;
678         }
679         return $status;
680     }
682    /**
683     * Given one user object (from backup file), perform all the neccesary
684     * checks is order to decide how that user will be handled on restore.
685     *
686     * Note the function requires $user->mnethostid to be already calculated
687     * so it's caller responsibility to set it
688     *
689     * This function is used both by @restore_precheck_users() and
690     * @restore_create_users() to get consistent results in both places
691     *
692     * It returns:
693     *   - one user object (from DB), if match has been found and user will be remapped
694     *   - boolean true if the user needs to be created
695     *   - boolean false if some conflict happened and the user cannot be handled
696     *
697     * Each test is responsible for returning its results and interrupt
698     * execution. At the end, boolean true (user needs to be created) will be
699     * returned if no test has interrupted that.
700     *
701     * Here it's the logic applied, keep it updated:
702     *
703     *  If restoring users from same site backup:
704     *      1A - Normal check: If match by id and username and mnethost  => ok, return target user
705     *      1B - Handle users deleted in DB and "alive" in backup file:
706     *           If match by id and mnethost and user is deleted in DB and
707     *           (match by username LIKE 'backup_email.%' or by non empty email = md5(username)) => ok, return target user
708     *      1C - Handle users deleted in backup file and "alive" in DB:
709     *           If match by id and mnethost and user is deleted in backup file
710     *           and match by email = email_without_time(backup_email) => ok, return target user
711     *      1D - Conflict: If match by username and mnethost and doesn't match by id => conflict, return false
712     *      1E - None of the above, return true => User needs to be created
713     *
714     *  if restoring from another site backup (cannot match by id here, replace it by email/firstaccess combination):
715     *      2A - Normal check: If match by username and mnethost and (email or non-zero firstaccess) => ok, return target user
716     *      2B - Handle users deleted in DB and "alive" in backup file:
717     *           2B1 - If match by mnethost and user is deleted in DB and not empty email = md5(username) and
718     *                 (username LIKE 'backup_email.%' or non-zero firstaccess) => ok, return target user
719     *           2B2 - If match by mnethost and user is deleted in DB and
720     *                 username LIKE 'backup_email.%' and non-zero firstaccess) => ok, return target user
721     *                 (to cover situations were md5(username) wasn't implemented on delete we requiere both)
722     *      2C - Handle users deleted in backup file and "alive" in DB:
723     *           If match mnethost and user is deleted in backup file
724     *           and by email = email_without_time(backup_email) and non-zero firstaccess=> ok, return target user
725     *      2D - Conflict: If match by username and mnethost and not by (email or non-zero firstaccess) => conflict, return false
726     *      2E - None of the above, return true => User needs to be created
727     *
728     * Note: for DB deleted users email is stored in username field, hence we
729     *       are looking there for emails. See delete_user()
730     * Note: for DB deleted users md5(username) is stored *sometimes* in the email field,
731     *       hence we are looking there for usernames if not empty. See delete_user()
732     */
733     function restore_check_user($restore, $user) {
734         global $CFG, $DB;
736         // Verify mnethostid is set, return error if not
737         // it's parent responsibility to define that before
738         // arriving here
739         if (empty($user->mnethostid)) {
740             debugging("restore_check_user() wrong use, mnethostid not set for user $user->username", DEBUG_DEVELOPER);
741             return false;
742         }
744         // Handle checks from same site backups
745         if (backup_is_same_site($restore) && empty($CFG->forcedifferentsitecheckingusersonrestore)) {
747             // 1A - If match by id and username and mnethost => ok, return target user
748             if ($rec = $DB->get_record('user', array('id'=>$user->id, 'username'=>$user->username, 'mnethostid'=>$user->mnethostid))) {
749                 return $rec; // Matching user found, return it
750             }
752             // 1B - Handle users deleted in DB and "alive" in backup file
753             // Note: for DB deleted users email is stored in username field, hence we
754             //       are looking there for emails. See delete_user()
755             // Note: for DB deleted users md5(username) is stored *sometimes* in the email field,
756             //       hence we are looking there for usernames if not empty. See delete_user()
757             // If match by id and mnethost and user is deleted in DB and
758             // match by username LIKE 'backup_email.%' or by non empty email = md5(username) => ok, return target user
759             if ($rec = $DB->get_record_sql("SELECT *
760                                               FROM {user} u
761                                              WHERE id = ?
762                                                AND mnethostid = ?
763                                                AND deleted = 1
764                                                AND (
765                                                        username LIKE ?
766                                                     OR (
767                                                            ".$DB->sql_isnotempty('user', 'email', false, false)."
768                                                        AND email = ?
769                                                        )
770                                                    )",
771                                            array($user->id, $user->mnethostid, $user->email.'.%', md5($user->username)))) {
772                 return $rec; // Matching user, deleted in DB found, return it
773             }
775             // 1C - Handle users deleted in backup file and "alive" in DB
776             // If match by id and mnethost and user is deleted in backup file
777             // and match by email = email_without_time(backup_email) => ok, return target user
778             if ($user->deleted) {
779                 // Note: for DB deleted users email is stored in username field, hence we
780                 //       are looking there for emails. See delete_user()
781                 // Trim time() from email
782                 $trimemail = preg_replace('/(.*?)\.[0-9]+.?$/', '\\1', $user->username);
783                 if ($rec = $DB->get_record_sql("SELECT *
784                                                   FROM {user} u
785                                                  WHERE id = ?
786                                                    AND mnethostid = ?
787                                                    AND email = ?",
788                                                array($user->id, $user->mnethostid, $trimemail))) {
789                     return $rec; // Matching user, deleted in backup file found, return it
790                 }
791             }
793             // 1D - If match by username and mnethost and doesn't match by id => conflict, return false
794             if ($rec = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$user->mnethostid))) {
795                 if ($user->id != $rec->id) {
796                     return false; // Conflict, username already exists and belongs to another id
797                 }
798             }
800         // Handle checks from different site backups
801         } else {
803             // 2A - If match by username and mnethost and
804             //     (email or non-zero firstaccess) => ok, return target user
805             if ($rec = $DB->get_record_sql("SELECT *
806                                               FROM {user} u
807                                              WHERE username = ?
808                                                AND mnethostid = ?
809                                                AND (
810                                                        email = ?
811                                                     OR (
812                                                            firstaccess != 0
813                                                        AND firstaccess = ?
814                                                        )
815                                                    )",
816                                            array($user->username, $user->mnethostid, $user->email, $user->firstaccess))) {
817                 return $rec; // Matching user found, return it
818             }
820             // 2B - Handle users deleted in DB and "alive" in backup file
821             // Note: for DB deleted users email is stored in username field, hence we
822             //       are looking there for emails. See delete_user()
823             // Note: for DB deleted users md5(username) is stored *sometimes* in the email field,
824             //       hence we are looking there for usernames if not empty. See delete_user()
825             // 2B1 - If match by mnethost and user is deleted in DB and not empty email = md5(username) and
826             //       (by username LIKE 'backup_email.%' or non-zero firstaccess) => ok, return target user
827             if ($rec = $DB->get_record_sql("SELECT *
828                                               FROM {user} u
829                                              WHERE mnethostid = ?
830                                                AND deleted = 1
831                                                AND ".$DB->sql_isnotempty('user', 'email', false, false)."
832                                                AND email = ?
833                                                AND (
834                                                        username LIKE ?
835                                                     OR (
836                                                            firstaccess != 0
837                                                        AND firstaccess = ?
838                                                        )
839                                                    )",
840                                            array($user->mnethostid, md5($user->username), $user->email.'.%', $user->firstaccess))) {
841                 return $rec; // Matching user found, return it
842             }
844             // 2B2 - If match by mnethost and user is deleted in DB and
845             //       username LIKE 'backup_email.%' and non-zero firstaccess) => ok, return target user
846             //       (this covers situations where md5(username) wasn't being stored so we require both
847             //        the email & non-zero firstaccess to match)
848             if ($rec = $DB->get_record_sql("SELECT *
849                                               FROM {user} u
850                                              WHERE mnethostid = ?
851                                                AND deleted = 1
852                                                AND username LIKE ?
853                                                AND firstaccess != 0
854                                                AND firstaccess = ?",
855                                            array($user->mnethostid, $user->email.'.%', $user->firstaccess))) {
856                 return $rec; // Matching user found, return it
857             }
859             // 2C - Handle users deleted in backup file and "alive" in DB
860             // If match mnethost and user is deleted in backup file
861             // and match by email = email_without_time(backup_email) and non-zero firstaccess=> ok, return target user
862             if ($user->deleted) {
863                 // Note: for DB deleted users email is stored in username field, hence we
864                 //       are looking there for emails. See delete_user()
865                 // Trim time() from email
866                 $trimemail = preg_replace('/(.*?)\.[0-9]+.?$/', '\\1', $user->username);
867                 if ($rec = $DB->get_record_sql("SELECT *
868                                                   FROM {user} u
869                                                  WHERE mnethostid = ?
870                                                    AND email = ?
871                                                    AND firstaccess != 0
872                                                    AND firstaccess = ?",
873                                                array($user->mnethostid, $trimemail, $user->firstaccess))) {
874                     return $rec; // Matching user, deleted in backup file found, return it
875                 }
876             }
878             // 2D - If match by username and mnethost and not by (email or non-zero firstaccess) => conflict, return false
879             if ($rec = $DB->get_record_sql("SELECT *
880                                               FROM {user} u
881                                              WHERE username = ?
882                                                AND mnethostid = ?
883                                            AND NOT (
884                                                        email = ?
885                                                     OR (
886                                                            firstaccess != 0
887                                                        AND firstaccess = ?
888                                                        )
889                                                    )",
890                                            array($user->username, $user->mnethostid, $user->email, $user->firstaccess))) {
891                 return false; // Conflict, username/mnethostid already exist and belong to another user (by email/firstaccess)
892             }
893         }
895         // Arrived here, return true as the user will need to be created and no
896         // conflicts have been found in the logic above. This covers:
897         // 1E - else => user needs to be created, return true
898         // 2E - else => user needs to be created, return true
899         return true;
900     }
902    /**
903     * For all the users being restored, check if they are going to cause problems
904     * before executing the restore process itself, detecting situations like:
905     *   - conflicts preventing restore to continue - provided by @restore_check_user()
906     *   - prevent creation of users if not allowed - check some global settings/caps
907     */
908     function restore_precheck_users($xml_file, $restore, &$problems) {
909         global $CFG, $DB;
911         $status = true; // Init $status
913         // We aren't restoring users, nothing to check, allow continue
914         if ($restore->users == 2) {
915             return true;
916         }
918         // Get array of users from xml file and load them in backup_ids table
919         if (!$info = restore_read_xml_users($restore,$xml_file)) {
920             return true; // No users, nothing to check, allow continue
921         }
923         // We are going to map mnethostid, so load all the available ones
924         $mnethosts = $DB->get_records('mnet_host', array(), 'wwwroot', 'wwwroot, id');
926         // Calculate the context we are going to use for capability checking
927         if (!empty($restore->course_id)) { // Know the target (existing) course, check capabilities there
928             $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
929         } else if (!empty($restore->restore_restorecatto)) { // Know the category, check capabilities there
930             $context = get_context_instance(CONTEXT_COURSECAT, $restore->restore_restorecatto);
931         } else { // Last resort, check capabilities at system level
932             $context = get_context_instance(CONTEXT_SYSTEM);
933         }
935         // Calculate if we have perms to create users, by checking:
936         // to 'moodle/restore:createuser' and 'moodle/restore:userinfo'
937         // and also observe $CFG->disableusercreationonrestore
938         $cancreateuser = false;
939         if (has_capability('moodle/restore:createuser', $context) and
940             has_capability('moodle/restore:userinfo', $context) and
941             empty($CFG->disableusercreationonrestore)) { // Can create users
943             $cancreateuser = true;
944         }
946         // Iterate over all users, checking if they are likely to cause problems on restore
947         $counter = 0;
948         foreach ($info->users as $userid) {
949             $rec = backup_getid($restore->backup_unique_code, 'user', $userid);
950             $user = $rec->info;
952             // Find the correct mnethostid for user before performing any further check
953             if (empty($user->mnethosturl) || $user->mnethosturl === $CFG->wwwroot) {
954                 $user->mnethostid = $CFG->mnet_localhost_id;
955             } else {
956                 // fast url-to-id lookups
957                 if (isset($mnethosts[$user->mnethosturl])) {
958                     $user->mnethostid = $mnethosts[$user->mnethosturl]->id;
959                 } else {
960                     $user->mnethostid = $CFG->mnet_localhost_id;
961                 }
962             }
964             // Calculate the best way to handle this user from backup file
965             $usercheck = restore_check_user($restore, $user);
967             if (is_object($usercheck)) { // No problem, we have found one user in DB to be mapped to
968                 // Annotate it, for later process by restore_create_users(). Set new_id to mapping user->id
969                 backup_putid($restore->backup_unique_code, 'user', $userid, $usercheck->id, $user);
971             } else if ($usercheck === false) { // Found conflict, report it as problem
972                 $problems[] = get_string('restoreuserconflict', '', $user->username);
973                 $status = false;
975             } else if ($usercheck === true) { // User needs to be created, check if we are able
976                 if ($cancreateuser) { // Can create user, annotate it, for later process by restore_create_users(). Set new_id to 0
977                     backup_putid($restore->backup_unique_code, 'user', $userid, 0, $user);
979                 } else { // Cannot create user, report it as problem
981                     $problems[] = get_string('restorecannotcreateuser', '', $user->username);
982                     $status = false;
983                 }
985             } else { // Shouldn't arrive here ever, something is for sure wrong in restore_check_user()
986                 if (!defined('RESTORE_SILENTLY')) {
987                     notify('Unexpected error pre-checking user ' . s($user->username) . ' from backup file');
988                     return false;
989                 }
990             }
992             // Do some output
993             $counter++;
994             if ($counter % 10 == 0) {
995                 if (!defined('RESTORE_SILENTLY')) {
996                     echo ".";
997                     if ($counter % 200 == 0) {
998                         echo "<br />";
999                     }
1000                 }
1001                 backup_flush(300);
1002             }
1003         }
1005         return $status;
1006     }
1008     //This function create a new course record.
1009     //When finished, course_header contains the id of the new course
1010     function restore_create_new_course($restore,&$course_header) {
1011         global $CFG, $DB;
1013         $status = true;
1015         $fullname = $course_header->course_fullname;
1016         $shortname = $course_header->course_shortname;
1017         $currentfullname = "";
1018         $currentshortname = "";
1019         $counter = 0;
1020         //Iteratere while the name exists
1021         do {
1022             if ($counter) {
1023                 $suffixfull = " ".get_string("copyasnoun")." ".$counter;
1024                 $suffixshort = "_".$counter;
1025             } else {
1026                 $suffixfull = "";
1027                 $suffixshort = "";
1028             }
1029             $currentfullname = $fullname.$suffixfull;
1030             // Limit the size of shortname - database column accepts <= 100 chars
1031             $currentshortname = substr($shortname, 0, 100 - strlen($suffixshort)).$suffixshort;
1032             $coursefull  = $DB->get_record("course", array("fullname"=>$currentfullname));
1033             $courseshort = $DB->get_record("course", array("shortname"=>$currentshortname));
1034             $counter++;
1035         } while ($coursefull || $courseshort);
1037         //New name = currentname
1038         $course_header->course_fullname = $currentfullname;
1039         $course_header->course_shortname = $currentshortname;
1041         // first try to get it from restore
1042         if ($restore->restore_restorecatto) {
1043             $category = $DB->get_record('course_categories', array('id'=>$restore->restore_restorecatto));
1044         }
1046         // else we try to get it from the xml file
1047         //Now calculate the category
1048         if (empty($category)) {
1049             $category = $DB->get_record("course_categories",array("id"=>$course_header->category->id,
1050                                    "name"=>$course_header->category->name));
1051         }
1053         //If no exists, try by name only
1054         if (!$category) {
1055             $category = $DB->get_record("course_categories", array("name"=>$course_header->category->name));
1056         }
1058         //If no exists, get default category
1059         if (!$category) {
1060             $category = get_course_category();
1061         }
1063         $course_header->category->id = $category->id;
1064         $course_header->category->name = $category->name;
1066         //Create the course_object
1067         if ($status) {
1068             $course = new object();
1069             $course->category = $course_header->category->id;
1070             $course->password = $course_header->course_password;
1071             $course->fullname = $course_header->course_fullname;
1072             $course->shortname = $course_header->course_shortname;
1073             $course->idnumber = $course_header->course_idnumber;
1074             $course->idnumber = ''; //$course_header->course_idnumber; // we don't want this at all.
1075             $course->summary = $course_header->course_summary;
1076             $course->format = $course_header->course_format;
1077             $course->showgrades = $course_header->course_showgrades;
1078             $course->newsitems = $course_header->course_newsitems;
1079             $course->guest = $course_header->course_guest;
1080             $course->startdate = $course_header->course_startdate;
1081             $course->startdate += $restore->course_startdateoffset;
1082             $course->numsections = $course_header->course_numsections;
1083             //$course->showrecent = $course_header->course_showrecent;   INFO: This is out in 1.3
1084             $course->maxbytes = $course_header->course_maxbytes;
1085             $course->showreports = $course_header->course_showreports;
1086             if (isset($course_header->course_groupmode)) {
1087                 $course->groupmode = $course_header->course_groupmode;
1088             }
1089             if (isset($course_header->course_groupmodeforce)) {
1090                 $course->groupmodeforce = $course_header->course_groupmodeforce;
1091             }
1092             if (isset($course_header->course_defaultgroupingid)) {
1093                 //keep the original now - convert after groupings restored
1094                 $course->defaultgroupingid = $course_header->course_defaultgroupingid;
1095             }
1096             $course->lang = $course_header->course_lang;
1097             $course->theme = $course_header->course_theme;
1098             $course->cost = $course_header->course_cost;
1099             $course->currency = isset($course_header->course_currency)?$course_header->course_currency:'';
1100             $course->marker = $course_header->course_marker;
1101             $course->visible = $course_header->course_visible;
1102             $course->hiddensections = $course_header->course_hiddensections;
1103             $course->timecreated = $course_header->course_timecreated;
1104             $course->timemodified = $course_header->course_timemodified;
1105             $course->metacourse = $course_header->course_metacourse;
1106             $course->expirynotify = isset($course_header->course_expirynotify) ? $course_header->course_expirynotify:0;
1107             $course->notifystudents = isset($course_header->course_notifystudents) ? $course_header->course_notifystudents : 0;
1108             $course->expirythreshold = isset($course_header->course_expirythreshold) ? $course_header->course_expirythreshold : 0;
1109             $course->enrollable = isset($course_header->course_enrollable) ? $course_header->course_enrollable : 1;
1110             $course->enrolstartdate = isset($course_header->course_enrolstartdate) ? $course_header->course_enrolstartdate : 0;
1111             if ($course->enrolstartdate)  { //Roll course dates
1112                 $course->enrolstartdate += $restore->course_startdateoffset;
1113             }
1114             $course->enrolenddate = isset($course_header->course_enrolenddate) ? $course_header->course_enrolenddate : 0;
1115             if ($course->enrolenddate) { //Roll course dates
1116                 $course->enrolenddate  += $restore->course_startdateoffset;
1117             }
1118             $course->enrolperiod = $course_header->course_enrolperiod;
1119             $course->enablecompletion = isset($course_header->course_enablecompletion) ? $course_header->course_enablecompletion : 0;
1120             //Put as last course in category
1121             $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
1123             //Now, recode some languages (Moodle 1.5)
1124             if ($course->lang == 'ma_nt') {
1125                 $course->lang = 'mi_nt';
1126             }
1128             //Disable course->metacourse if avoided in restore config
1129             if (!$restore->metacourse) {
1130                 $course->metacourse = 0;
1131             }
1133             //Check if the theme exists in destination server
1134             $themes = get_list_of_themes();
1135             if (!in_array($course->theme, $themes)) {
1136                 $course->theme = '';
1137             }
1139             //Now insert the record
1140             $newid = $DB->insert_record("course",$course);
1141             if ($newid) {
1142                 //save old and new course id
1143                 backup_putid ($restore->backup_unique_code,"course",$course_header->course_id,$newid);
1144                 //Replace old course_id in course_header
1145                 $course_header->course_id = $newid;
1146                 $SESSION->restore->course_id = $newid;
1147                 return $newid;
1148             } else {
1149                 $status = false;
1150             }
1151         }
1153         return $status;
1154     }
1158     //This function creates all the block stuff when restoring courses
1159     //It calls selectively to  restore_create_block_instances() for 1.5
1160     //and above backups. Upwards compatible with old blocks.
1161     function restore_create_blocks($restore, $backup_block_format, $blockinfo, $xml_file) {
1162         global $CFG, $DB;
1163         $status = true;
1165         blocks_delete_all_on_page(PAGE_COURSE_VIEW, $restore->course_id);
1166         if (empty($backup_block_format)) {     // This is a backup from Moodle < 1.5
1167             if (empty($blockinfo)) {
1168                 // Looks like it's from Moodle < 1.3. Let's give the course default blocks...
1169                 blocks_add_default_course_blocks($DB->get_record('course', array('id' => $restore->course_id)));
1170             } else {
1171                 // We just have a blockinfo field, this is a legacy 1.4 or 1.3 backup
1172                 $blockrecords = $DB->get_records('block', null, '', 'name, id');
1173                 $temp_blocks_l = array();
1174                 $temp_blocks_r = array();
1175                 @list($temp_blocks_l, $temp_blocks_r) = explode(':', $blockinfo);
1176                 $temp_blocks = array(BLOCK_POS_LEFT => explode(',', $temp_blocks_l), BLOCK_POS_RIGHT => explode(',', $temp_blocks_r));
1177                 foreach($temp_blocks as $blockposition => $blocks) {
1178                     $blockweight = 0;
1179                     foreach($blocks as $blockname) {
1180                         if(!isset($blockrecords[$blockname])) {
1181                             // We don't know anything about this block!
1182                             continue;
1183                         }
1184                         $blockinstance = new stdClass;
1185                         // Remove any - prefix before doing the name-to-id mapping
1186                         if(substr($blockname, 0, 1) == '-') {
1187                             $blockname = substr($blockname, 1);
1188                             $blockinstance->visible = 0;
1189                         } else {
1190                             $blockinstance->visible = 1;
1191                         }
1192                         $blockinstance->blockid  = $blockrecords[$blockname]->id;
1193                         $blockinstance->pageid   = $restore->course_id;
1194                         $blockinstance->pagetype = PAGE_COURSE_VIEW;
1195                         $blockinstance->position = $blockposition;
1196                         $blockinstance->weight   = $blockweight;
1197                         $DB->insert_record('block_instance', $blockinstance);
1198                         ++$blockweight;
1199                     }
1200                 }
1201             }
1202         } else if($backup_block_format == 'instances') {
1203             $status = restore_create_block_instances($restore,$xml_file);
1204         }
1206         return $status;
1208     }
1210     //This function creates all the block_instances from xml when restoring in a
1211     //new course
1212     function restore_create_block_instances($restore,$xml_file) {
1213         global $CFG, $DB;
1214         $status = true;
1216         //Check it exists
1217         if (!file_exists($xml_file)) {
1218             $status = false;
1219         }
1220         //Get info from xml
1221         if ($status) {
1222             $info = restore_read_xml_blocks($restore,$xml_file);
1223         }
1225         if(empty($info->instances)) {
1226             return $status;
1227         }
1229         // First of all, iterate over the blocks to see which distinct pages we have
1230         // in our hands and arrange the blocks accordingly.
1231         $pageinstances = array();
1232         foreach($info->instances as $instance) {
1234             //pagetype and pageid black magic, we have to handle the case of blocks for the
1235             //course, blocks from other pages in that course etc etc etc.
1237             if($instance->pagetype == PAGE_COURSE_VIEW) {
1238                 // This one's easy...
1239                 $instance->pageid  = $restore->course_id;
1241             } else {
1242                 // restore activity blocks
1243                 $parts = explode('-', $instance->pagetype);
1244                 if($parts[0] == 'mod') {
1245                     if(!$restore->mods[$parts[1]]->restore) {
1246                         continue;
1247                     }
1248                     $getid = backup_getid($restore->backup_unique_code, $parts[1], $instance->pageid);
1250                     if (empty($getid->new_id)) {
1251                         // Failed, perhaps the module was not included in the restore  MDL-13554
1252                         continue;
1253                     }
1254                     $instance->pageid = $getid->new_id;
1255                 }
1256                 else {
1257                     // Not invented here ;-)
1258                     continue;
1259                 }
1261             }
1263             if(!isset($pageinstances[$instance->pagetype])) {
1264                 $pageinstances[$instance->pagetype] = array();
1265             }
1266             if(!isset($pageinstances[$instance->pagetype][$instance->pageid])) {
1267                 $pageinstances[$instance->pagetype][$instance->pageid] = array();
1268             }
1270             $pageinstances[$instance->pagetype][$instance->pageid][] = $instance;
1271         }
1273         $blocks = $DB->get_records('block', array('visible'=>1), '', 'name, id, multiple');
1275         // For each type of page we have restored
1276         foreach($pageinstances as $thistypeinstances) {
1278             // For each page id of that type
1279             foreach($thistypeinstances as $thisidinstances) {
1281                 $addedblocks = array();
1282                 $maxweights  = array();
1284                 // For each block instance in that page
1285                 foreach($thisidinstances as $instance) {
1287                     if(!isset($blocks[$instance->name])) {
1288                         //We are trying to restore a block we don't have...
1289                         continue;
1290                     }
1292                     //If we have already added this block once and multiples aren't allowed, disregard it
1293                     if(!$blocks[$instance->name]->multiple && isset($addedblocks[$instance->name])) {
1294                         continue;
1295                     }
1297                     //If its the first block we add to a new position, start weight counter equal to 0.
1298                     if(empty($maxweights[$instance->position])) {
1299                         $maxweights[$instance->position] = 0;
1300                     }
1302                     //If the instance weight is greater than the weight counter (we skipped some earlier
1303                     //blocks most probably), bring it back in line.
1304                     if($instance->weight > $maxweights[$instance->position]) {
1305                         $instance->weight = $maxweights[$instance->position];
1306                     }
1308                     //Add this instance
1309                     $instance->blockid = $blocks[$instance->name]->id;
1311                     // This will only be set if we come from 1.7 and above backups
1312                     //  Also, must do this before insert ($DB->insert_record unsets id)
1313                     if (!empty($instance->id)) {
1314                         $oldid = $instance->id;
1315                     } else {
1316                         $oldid = 0;
1317                     }
1319                     if ($instance->id = $DB->insert_record('block_instance', $instance)) {
1320                         // Create block instance
1321                         if (!$blockobj = block_instance($instance->name, $instance)) {
1322                             $status = false;
1323                             break;
1324                         }
1325                         // Run the block restore if needed
1326                         if ($blockobj->backuprestore_instancedata_used()) {
1327                             // Get restore information
1328                             $data = backup_getid($restore->backup_unique_code,'block_instance',$oldid);
1329                             $data->new_id = $instance->id;  // For completeness
1330                             if (!$blockobj->instance_restore($restore, $data)) {
1331                                 $status = false;
1332                                 break;
1333                             }
1334                         }
1335                         // Save oldid after block restore process because info will be over-written with blank string
1336                         if ($oldid) {
1337                             backup_putid ($restore->backup_unique_code,"block_instance",$oldid,$instance->id);
1338                         }
1340                     } else {
1341                         $status = false;
1342                         break;
1343                     }
1345                     //Get an object for the block and tell it it's been restored so it can update dates
1346                     //etc. if necessary
1347                     if ($blockobj = block_instance($instance->name,$instance)) {
1348                         $blockobj->after_restore($restore);
1349                     }
1351                     //Now we can increment the weight counter
1352                     ++$maxweights[$instance->position];
1354                     //Keep track of block types we have already added
1355                     $addedblocks[$instance->name] = true;
1357                 }
1358             }
1359         }
1361         return $status;
1362     }
1364     //This function creates all the course_sections and course_modules from xml
1365     //when restoring in a new course or simply checks sections and create records
1366     //in backup_ids when restoring in a existing course
1367     function restore_create_sections(&$restore, $xml_file) {
1368         global $CFG, $DB;
1370         $status = true;
1371         //Check it exists
1372         if (!file_exists($xml_file)) {
1373             $status = false;
1374         }
1375         //Get info from xml
1376         if ($status) {
1377             $info = restore_read_xml_sections($xml_file);
1378         }
1379         //Put the info in the DB, recoding ids and saving the in backup tables
1381         $sequence = "";
1383         if ($info) {
1384             //For each, section, save it to db
1385             foreach ($info->sections as $key => $sect) {
1386                 $sequence = "";
1387                 $section = new object();
1388                 $section->course = $restore->course_id;
1389                 $section->section = $sect->number;
1390                 $section->summary = backup_todb($sect->summary);
1391                 $section->visible = $sect->visible;
1392                 $section->sequence = "";
1393                 //Now calculate the section's newid
1394                 $newid = 0;
1395                 if ($restore->restoreto == RESTORETO_NEW_COURSE) {
1396                     //Save it to db (only if restoring to new course)
1397                     $newid = $DB->insert_record("course_sections",$section);
1398                 } else {
1399                     //Get section id when restoring in existing course
1400                     $rec = $DB->get_record("course_sections", array("course"=>$restore->course_id,
1401                                                         "section"=>$section->section));
1402                     //If section exists, has empty summary and backup has some summary, use it. MDL-8848
1403                     if ($rec && empty($rec->summary) && !empty($section->summary)) {
1404                         $rec->summary = $section->summary;
1405                         $DB->update_record("course_sections", $rec);
1406                     }
1407                     //If that section doesn't exist, get section 0 (every mod will be
1408                     //asigned there
1409                     if(!$rec) {
1410                         $rec = $DB->get_record("course_sections", array("course"=>$restore->course_id,
1411                                                             "section"=>"0"));
1412                     }
1413                     //New check. If section 0 doesn't exist, insert it here !!
1414                     //Teorically this never should happen but, in practice, some users
1415                     //have reported this issue.
1416                     if(!$rec) {
1417                         $zero_sec = new object();
1418                         $zero_sec->course = $restore->course_id;
1419                         $zero_sec->section = 0;
1420                         $zero_sec->summary = "";
1421                         $zero_sec->sequence = "";
1422                         $newid = $DB->insert_record("course_sections",$zero_sec);
1423                         $rec->id = $newid;
1424                         $rec->sequence = "";
1425                     }
1426                     $newid = $rec->id;
1427                     $sequence = $rec->sequence;
1428                 }
1429                 if ($newid) {
1430                     //save old and new section id
1431                     backup_putid ($restore->backup_unique_code,"course_sections",$key,$newid);
1432                 } else {
1433                     $status = false;
1434                 }
1435                 //If all is OK, go with associated mods
1436                 if ($status) {
1437                     //If we have mods in the section
1438                     if (!empty($sect->mods)) {
1439                         //For each mod inside section
1440                         foreach ($sect->mods as $keym => $mod) {
1441                             // Yu: This part is called repeatedly for every instance,
1442                             // so it is necessary to set the granular flag and check isset()
1443                             // when the first instance of this type of mod is processed.
1445                             //if (!isset($restore->mods[$mod->type]->granular) && isset($restore->mods[$mod->type]->instances) && is_array($restore->mods[$mod->type]->instances)) {
1447                             if (!isset($restore->mods[$mod->type]->granular)) {
1448                                 if (isset($restore->mods[$mod->type]->instances) && is_array($restore->mods[$mod->type]->instances)) {
1449                                     // This defines whether we want to restore specific
1450                                     // instances of the modules (granular restore), or
1451                                     // whether we don't care and just want to restore
1452                                     // all module instances (non-granular).
1453                                     $restore->mods[$mod->type]->granular = true;
1454                                 } else {
1455                                     $restore->mods[$mod->type]->granular = false;
1456                                 }
1457                             }
1459                             //Check if we've to restore this module (and instance)
1460                             if (!empty($restore->mods[$mod->type]->restore)) {
1461                                 if (empty($restore->mods[$mod->type]->granular)  // we don't care about per instance
1462                                     || (array_key_exists($mod->instance,$restore->mods[$mod->type]->instances)
1463                                         && !empty($restore->mods[$mod->type]->instances[$mod->instance]->restore))) {
1465                                     //Get the module id from modules
1466                                     $module = $DB->get_record("modules", array("name"=>$mod->type));
1467                                     if ($module) {
1468                                         $course_module = new object();
1469                                         $course_module->course = $restore->course_id;
1470                                         $course_module->module = $module->id;
1471                                         $course_module->section = $newid;
1472                                         $course_module->added = $mod->added;
1473                                         $course_module->score = $mod->score;
1474                                         $course_module->indent = $mod->indent;
1475                                         $course_module->visible = $mod->visible;
1476                                         $course_module->groupmode = $mod->groupmode;
1477                                         if ($mod->groupingid and $grouping = restore_grouping_getid($restore, $mod->groupingid)) {
1478                                             $course_module->groupingid = $grouping->new_id;
1479                                         } else {
1480                                             $course_module->groupingid = 0;
1481                                         }
1482                                         $course_module->groupmembersonly = $mod->groupmembersonly;
1483                                         $course_module->instance = 0;
1484                                         //NOTE: The instance (new) is calculated and updated in db in the
1485                                         //      final step of the restore. We don't know it yet.
1486                                         //print_object($course_module);                    //Debug
1487                                         //Save it to db
1488                                         if ($mod->idnumber) {
1489                                             $mod->idnumber=backup_todb($mod->idnumber);
1490                                             if (grade_verify_idnumber($mod->idnumber, $restore->course_id)) {
1491                                                 $course_module->idnumber = $mod->idnumber;
1492                                             }
1493                                         }
1495                                         $course_module->completion=$mod->completion;
1496                                         $course_module->completiongradeitemnumber=backup_todb($mod->completiongradeitemnumber);
1497                                         $course_module->completionview=$mod->completionview;
1498                                         $course_module->completionexpected=$mod->completionexpected;
1500                                         $course_module->availablefrom=$mod->availablefrom;
1501                                         if($mod->availablefrom!=0) {
1502                                             $course_module->availablefrom+=$restore->course_startdateoffset;
1503                                         }
1504                                         $course_module->availableuntil=$mod->availableuntil;
1505                                         if($mod->availableuntil!=0) {
1506                                             $course_module->availableuntil+=$restore->course_startdateoffset;
1507                                         }
1508                                         $course_module->showavailability=$mod->showavailability;
1510                                         $newidmod = $DB->insert_record("course_modules", $course_module);
1511                                         if ($newidmod) {
1512                                             //save old and new module id
1513                                             //In the info field, we save the original instance of the module
1514                                             //to use it later
1515                                             backup_putid ($restore->backup_unique_code,"course_modules",
1516                                                           $keym,$newidmod,$mod->instance);
1518                                             $restore->mods[$mod->type]->instances[$mod->instance]->restored_as_course_module = $newidmod;
1519                                         } else {
1520                                             $status = false;
1521                                         }
1522                                         //Now, calculate the sequence field
1523                                         if ($status) {
1524                                             if ($sequence) {
1525                                                 $sequence .= ",".$newidmod;
1526                                             } else {
1527                                                 $sequence = $newidmod;
1528                                             }
1529                                         }
1530                                     } else {
1531                                         $status = false;
1532                                     }
1533                                 }
1534                             }
1535                         }
1536                     }
1537                 }
1538                 //If all is OK, update sequence field in course_sections
1539                 if ($status) {
1540                     if (isset($sequence)) {
1541                         $update_rec = new object();
1542                         $update_rec->id = $newid;
1543                         $update_rec->sequence = $sequence;
1544                         $status = $DB->update_record("course_sections",$update_rec);
1545                     }
1546                 }
1547             }
1549             // Now that we have IDs for everything, store any completion data
1550             if($status && !empty($info->completiondata) && count($info->completiondata)>0) {
1551                 // Get list of users who can view course (& hence have
1552                 // completion data)
1553                 if(!isset($restore->userswhocanviewcourse)) {
1554                     // Because this is only used here, there is no point requesting
1555                     // anything except id
1556                     $restore->userswhocanviewcourse = get_enrolled_users(get_context_instance(CONTEXT_COURSE, $restore->course_id), '', 0, 'u.id');
1557                 }
1559                 foreach($info->completiondata as $data) {
1560                     // Convert cmid
1561                     $newcmid=backup_getid($restore->backup_unique_code, 'course_modules', $data->coursemoduleid);
1562                     if($newcmid) {
1563                         $data->coursemoduleid=$newcmid->new_id;
1564                     } else {
1565                         if (!defined('RESTORE_SILENTLY')) {
1566                             echo "<p>Can't find new ID for cm $data->coursemoduleid.</p>";
1567                         }
1568                         $status=false;
1569                         continue;
1570                     }
1572                     // Convert userid
1573                     $newuserid=backup_getid($restore->backup_unique_code, 'user', $data->userid);
1574                     if($newuserid) {
1575                         $data->userid=$newuserid->new_id;
1576                     } else {
1577                         // Skip missing users
1578                         debugging("Not restoring completion data for missing user {$data->userid}",DEBUG_DEVELOPER);
1579                         continue;
1580                     }
1582                     // Check user is still able to access new course (they
1583                     // might have had their role assignment excluded, and it
1584                     // creates arguably bogus database rows if we add completion
1585                     // data for them, and displays confusingly in the 'number
1586                     // of people who have already marked this complete' UI)
1587                     if(!array_key_exists($data->userid,$restore->userswhocanviewcourse)) {
1588                         continue;
1589                     }
1591                     // Add record
1592                     $DB->insert_record('course_modules_completion',$data);
1593                 }
1594             }
1596             // Store availability information
1597             if($status && !empty($info->availabilitydata) && count($info->availabilitydata)>0) {
1599                 foreach($info->availabilitydata as $data) {
1600                     // Convert cmid
1601                     $newcmid=backup_getid($restore->backup_unique_code, 'course_modules', $data->coursemoduleid);
1602                     if($newcmid) {
1603                         $data->coursemoduleid=$newcmid->new_id;
1604                     } else {
1605                         if (!defined('RESTORE_SILENTLY')) {
1606                             echo "<p>Can't find new ID for cm $data->coursemoduleid, ignoring availability condition.</p>";
1607                         }
1608                         continue;
1609                     }
1611                     // Convert source cmid
1612                     if($data->sourcecmid) {
1613                         $newcmid=backup_getid($restore->backup_unique_code, 'course_modules', $data->sourcecmid);
1614                         if($newcmid) {
1615                             $data->sourcecmid=$newcmid->new_id;
1616                         } else {
1617                             if (!defined('RESTORE_SILENTLY')) {
1618                                 echo "<p>Can't find new ID for source cm $data->sourcecmid, ignoring availability condition.</p>";
1619                             }
1620                             continue;
1621                         }
1622                     }
1624                     // Grade id is not converted (convert later)
1626                     // Add record
1627                     $DB->insert_record('course_modules_availability',$data);
1628                 }
1629             }
1630         } else {
1631             $status = false;
1632         }
1633         return $status;
1634     }
1636     //Called to set up any course-format specific data that may be in the file
1637     function restore_set_format_data($restore,$xml_file) {
1638         global $CFG, $DB;
1640         $status = true;
1641         //Check it exists
1642         if (!file_exists($xml_file)) {
1643             return false;
1644         }
1645         //Load data from XML to info
1646         if(!($info = restore_read_xml_formatdata($xml_file))) {
1647                 return false;
1648         }
1650         //Process format data if there is any
1651         if (isset($info->format_data)) {
1652                 if(!$format=$DB->get_field('course','format', array('id'=>$restore->course_id))) {
1653                     return false;
1654                 }
1655                 // If there was any data then it must have a restore method
1656                 $file=$CFG->dirroot."/course/format/$format/restorelib.php";
1657                 if(!file_exists($file)) {
1658                     return false;
1659                 }
1660                 require_once($file);
1661                 $function=$format.'_restore_format_data';
1662                 if(!function_exists($function)) {
1663                     return false;
1664                 }
1665                 return $function($restore,$info->format_data);
1666         }
1668         // If we got here then there's no data, but that's cool
1669         return true;
1670     }
1672     //This function creates all the metacourse data from xml, notifying
1673     //about each incidence
1674     function restore_create_metacourse($restore,$xml_file) {
1675         global $CFG, $DB;
1677         $status = true;
1678         //Check it exists
1679         if (!file_exists($xml_file)) {
1680             $status = false;
1681         }
1682         //Get info from xml
1683         if ($status) {
1684             //Load data from XML to info
1685             $info = restore_read_xml_metacourse($xml_file);
1686         }
1688         //Process info about metacourse
1689         if ($status and $info) {
1690             //Process child records
1691             if (!empty($info->childs)) {
1692                 foreach ($info->childs as $child) {
1693                     $dbcourse = false;
1694                     $dbmetacourse = false;
1695                     //Check if child course exists in destination server
1696                     //(by id in the same server or by idnumber and shortname in other server)
1697                     if (backup_is_same_site($restore)) {
1698                         //Same server, lets see by id
1699                         $dbcourse = $DB->get_record('course', array('id'=>$child->id));
1700                     } else {
1701                         //Different server, lets see by idnumber and shortname, and only ONE record
1702                         $dbcount = $DB->count_records('course', array('idnumber'=>$child->idnumber, 'shortname'=>$child->shortname));
1703                         if ($dbcount == 1) {
1704                             $dbcourse = $DB->get_record('course', array('idnumber'=>$child->idnumber, 'shortname'=>$child->shortname));
1705                         }
1706                     }
1707                     //If child course has been found, insert data
1708                     if ($dbcourse) {
1709                         $dbmetacourse->child_course = $dbcourse->id;
1710                         $dbmetacourse->parent_course = $restore->course_id;
1711                         $status = $DB->insert_record('course_meta',$dbmetacourse);
1712                     } else {
1713                         //Child course not found, notice!
1714                         if (!defined('RESTORE_SILENTLY')) {
1715                             echo '<ul><li>'.get_string ('childcoursenotfound').' ('.$child->id.'/'.$child->idnumber.'/'.$child->shortname.')</li></ul>';
1716                         }
1717                     }
1718                 }
1719                 //Now, recreate student enrolments...
1720                 sync_metacourse($restore->course_id);
1721             }
1722             //Process parent records
1723             if (!empty($info->parents)) {
1724                 foreach ($info->parents as $parent) {
1725                     $dbcourse = false;
1726                     $dbmetacourse = false;
1727                     //Check if parent course exists in destination server
1728                     //(by id in the same server or by idnumber and shortname in other server)
1729                     if (backup_is_same_site($restore)) {
1730                         //Same server, lets see by id
1731                         $dbcourse = $DB->get_record('course', array('id'=>$parent->id));
1732                     } else {
1733                         //Different server, lets see by idnumber and shortname, and only ONE record
1734                         $dbcount = $DB->count_records('course', array('idnumber'=>$parent->idnumber, 'shortname'=>$parent->shortname));
1735                         if ($dbcount == 1) {
1736                             $dbcourse = $DB->get_record('course', array('idnumber'=>$parent->idnumber, 'shortname'=>$parent->shortname));
1737                         }
1738                     }
1739                     //If parent course has been found, insert data if it is a metacourse
1740                     if ($dbcourse) {
1741                         if ($dbcourse->metacourse) {
1742                             $dbmetacourse->parent_course = $dbcourse->id;
1743                             $dbmetacourse->child_course = $restore->course_id;
1744                             $status = $DB->insert_record ('course_meta',$dbmetacourse);
1745                             //Now, recreate student enrolments in parent course
1746                             sync_metacourse($dbcourse->id);
1747                         } else {
1748                             //Parent course isn't metacourse, notice!
1749                             if (!defined('RESTORE_SILENTLY')) {
1750                                 echo '<ul><li>'.get_string ('parentcoursenotmetacourse').' ('.$parent->id.'/'.$parent->idnumber.'/'.$parent->shortname.')</li></ul>';
1751                             }
1752                         }
1753                     } else {
1754                         //Parent course not found, notice!
1755                         if (!defined('RESTORE_SILENTLY')) {
1756                             echo '<ul><li>'.get_string ('parentcoursenotfound').' ('.$parent->id.'/'.$parent->idnumber.'/'.$parent->shortname.')</li></ul>';
1757                         }
1758                     }
1759                 }
1760             }
1762         }
1763         return $status;
1764     }
1766     /**
1767      * This function migrades all the pre 1.9 gradebook data from xml
1768      */
1769     function restore_migrate_old_gradebook($restore,$xml_file) {
1770         global $CFG, $DB;
1772         $status = true;
1773         //Check it exists
1774         if (!file_exists($xml_file)) {
1775             return false;
1776         }
1778         // Get info from xml
1779         // info will contain the number of record to process
1780         $info = restore_read_xml_gradebook($restore, $xml_file);
1782         // If we have info, then process
1783         if (empty($info)) {
1784             return $status;
1785         }
1787         // make sure top course category exists
1788         $course_category = grade_category::fetch_course_category($restore->course_id);
1789         $course_category->load_grade_item();
1791         // we need to know if all grade items that were backed up are being restored
1792         // if that is not the case, we do not restore grade categories nor gradeitems of category type or course type
1793         // i.e. the aggregated grades of that category
1795         $restoreall = true;  // set to false if any grade_item is not selected/restored
1796         $importing  = !empty($SESSION->restore->importing); // there should not be a way to import old backups, but anyway ;-)
1798         if ($importing) {
1799             $restoreall = false;
1801         } else {
1802             $prev_grade_items = grade_item::fetch_all(array('courseid'=>$restore->course_id));
1803             $prev_grade_cats  = grade_category::fetch_all(array('courseid'=>$restore->course_id));
1805              // if any categories already present, skip restore of categories from backup
1806             if (count($prev_grade_items) > 1 or count($prev_grade_cats) > 1) {
1807                 $restoreall = false;
1808             }
1809             unset($prev_grade_items);
1810             unset($prev_grade_cats);
1811         }
1813         // force creation of all grade_items - the course_modules already exist
1814         grade_force_full_regrading($restore->course_id);
1815         grade_grab_course_grades($restore->course_id);
1817         // Start ul
1818         if (!defined('RESTORE_SILENTLY')) {
1819             echo '<ul>';
1820         }
1822     /// Process letters
1823         $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
1824         // respect current grade letters if defined
1825         if ($status and $restoreall and !$DB->record_exists('grade_letters', array('contextid'=>$context->id))) {
1826             if (!defined('RESTORE_SILENTLY')) {
1827                 echo '<li>'.get_string('gradeletters','grades').'</li>';
1828             }
1829             // Fetch recordset_size records in each iteration
1830             $recs = $DB->get_records("backup_ids", array("table_name"=>'grade_letter', "backup_code"=>$restore->backup_unique_code),
1831                                         "",
1832                                         "old_id");
1833             if ($recs) {
1834                 foreach ($recs as $rec) {
1835                     // Get the full record from backup_ids
1836                     $data = backup_getid($restore->backup_unique_code,'grade_letter',$rec->old_id);
1837                     if ($data) {
1838                         $info = $data->info;
1839                         $dbrec = new object();
1840                         $dbrec->contextid     = $context->id;
1841                         $dbrec->lowerboundary = backup_todb($info['GRADE_LETTER']['#']['GRADE_LOW']['0']['#']);
1842                         $dbrec->letter        = backup_todb($info['GRADE_LETTER']['#']['LETTER']['0']['#']);
1843                         $DB->insert_record('grade_letters', $dbrec);
1844                     }
1845                 }
1846             }
1847         }
1849         if (!defined('RESTORE_SILENTLY')) {
1850             echo '<li>'.get_string('categories','grades').'</li>';
1851         }
1852         //Fetch recordset_size records in each iteration
1853         $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_category', 'backup_code'=>$restore->backup_unique_code),
1854                                    "old_id",
1855                                    "old_id");
1856         $cat_count = count($recs);
1857         if ($recs) {
1858             foreach ($recs as $rec) {
1859                 //Get the full record from backup_ids
1860                 $data = backup_getid($restore->backup_unique_code,'grade_category',$rec->old_id);
1861                 if ($data) {
1862                     //Now get completed xmlized object
1863                     $info = $data->info;
1865                     if ($restoreall) {
1866                         if ($cat_count == 1) {
1867                             $course_category->fullname            = backup_todb($info['GRADE_CATEGORY']['#']['NAME']['0']['#'], false);
1868                             $course_category->droplow             = backup_todb($info['GRADE_CATEGORY']['#']['DROP_X_LOWEST']['0']['#'], false);
1869                             $course_category->aggregation         = GRADE_AGGREGATE_WEIGHTED_MEAN2;
1870                             $course_category->aggregateonlygraded = 0;
1871                             $course_category->update('restore');
1872                             $grade_category = $course_category;
1874                         } else {
1875                             $grade_category = new grade_category();
1876                             $grade_category->courseid            = $restore->course_id;
1877                             $grade_category->fullname            = backup_todb($info['GRADE_CATEGORY']['#']['NAME']['0']['#'], false);
1878                             $grade_category->droplow             = backup_todb($info['GRADE_CATEGORY']['#']['DROP_X_LOWEST']['0']['#'], false);
1879                             $grade_category->aggregation         = GRADE_AGGREGATE_WEIGHTED_MEAN2;
1880                             $grade_category->aggregateonlygraded = 0;
1881                             $grade_category->insert('restore');
1882                             $grade_category->load_grade_item(); // force cretion of grade_item
1883                         }
1885                     } else {
1886                         $grade_category = null;
1887                     }
1889                     /// now, restore grade_items
1890                     $items = array();
1891                     if (!empty($info['GRADE_CATEGORY']['#']['GRADE_ITEMS']['0']['#']['GRADE_ITEM'])) {
1892                         //Iterate over items
1893                         foreach ($info['GRADE_CATEGORY']['#']['GRADE_ITEMS']['0']['#']['GRADE_ITEM'] as $ite_info) {
1894                             $modname         = backup_todb($ite_info['#']['MODULE_NAME']['0']['#'], false);
1895                             $olditeminstance = backup_todb($ite_info['#']['CMINSTANCE']['0']['#'], false);
1896                             if (!$mod = backup_getid($restore->backup_unique_code,$modname, $olditeminstance)) {
1897                                 continue; // not restored
1898                             }
1899                             $iteminstance = $mod->new_id;
1900                             if (!$cm = get_coursemodule_from_instance($modname, $iteminstance, $restore->course_id)) {
1901                                 continue; // does not exist
1902                             }
1904                             if (!$grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$cm->modname, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course, 'itemnumber'=>0))) {
1905                                 continue; // no item yet??
1906                             }
1908                             if ($grade_category) {
1909                                 $grade_item->sortorder = backup_todb($ite_info['#']['SORT_ORDER']['0']['#'], false);
1910                                 $grade_item->set_parent($grade_category->id);
1911                             }
1913                             if ($importing
1914                               or ($grade_item->itemtype == 'mod' and !restore_userdata_selected($restore,  $grade_item->itemmodule, $olditeminstance))) {
1915                                 // module instance not selected when restored using granular
1916                                 // skip this item
1917                                 continue;
1918                             }
1920                             //Now process grade excludes
1921                             if (empty($ite_info['#']['GRADE_EXCEPTIONS'])) {
1922                                 continue;
1923                             }
1925                             foreach($ite_info['#']['GRADE_EXCEPTIONS']['0']['#']['GRADE_EXCEPTION'] as $exc_info) {
1926                                 if ($u = backup_getid($restore->backup_unique_code,"user",backup_todb($exc_info['#']['USERID']['0']['#']))) {
1927                                     $userid = $u->new_id;
1928                                     $grade_grade = new grade_grade(array('itemid'=>$grade_item->id, 'userid'=>$userid));
1929                                     $grade_grade->excluded = 1;
1930                                     if ($grade_grade->id) {
1931                                         $grade_grade->update('restore');
1932                                     } else {
1933                                         $grade_grade->insert('restore');
1934                                     }
1935                                 }
1936                             }
1937                         }
1938                     }
1939                 }
1940             }
1941         }
1943         if (!defined('RESTORE_SILENTLY')) {
1944         //End ul
1945             echo '</ul>';
1946         }
1948         return $status;
1949     }
1951     /**
1952      * This function creates all the gradebook data from xml
1953      */
1954     function restore_create_gradebook($restore,$xml_file) {
1955         global $CFG, $DB;
1957         $status = true;
1958         //Check it exists
1959         if (!file_exists($xml_file)) {
1960             return false;
1961         }
1963         // Get info from xml
1964         // info will contain the number of record to process
1965         $info = restore_read_xml_gradebook($restore, $xml_file);
1967         // If we have info, then process
1968         if (empty($info)) {
1969             return $status;
1970         }
1972         if (empty($CFG->disablegradehistory) and isset($info->gradebook_histories) and $info->gradebook_histories == "true") {
1973             $restore_histories = true;
1974         } else {
1975             $restore_histories = false;
1976         }
1978         // make sure top course category exists
1979         $course_category = grade_category::fetch_course_category($restore->course_id);
1980         $course_category->load_grade_item();
1982         // we need to know if all grade items that were backed up are being restored
1983         // if that is not the case, we do not restore grade categories nor gradeitems of category type or course type
1984         // i.e. the aggregated grades of that category
1986         $restoreall = true;  // set to false if any grade_item is not selected/restored or already exist
1987         $importing  = !empty($SESSION->restore->importing);
1989         if ($importing) {
1990             $restoreall = false;
1992         } else {
1993             $prev_grade_items = grade_item::fetch_all(array('courseid'=>$restore->course_id));
1994             $prev_grade_cats  = grade_category::fetch_all(array('courseid'=>$restore->course_id));
1996              // if any categories already present, skip restore of categories from backup - course item or category already exist
1997             if (count($prev_grade_items) > 1 or count($prev_grade_cats) > 1) {
1998                 $restoreall = false;
1999             }
2000             unset($prev_grade_items);
2001             unset($prev_grade_cats);
2003             if ($restoreall) {
2004                 if ($recs = $DB->get_records("backup_ids", array('table_name'=>'grade_items', 'backup_code'=>$restore->backup_unique_code), "", "old_id")) {
2005                     foreach ($recs as $rec) {
2006                         if ($data = backup_getid($restore->backup_unique_code,'grade_items',$rec->old_id)) {
2008                             $info = $data->info;
2009                             // do not restore if this grade_item is a mod, and
2010                             $itemtype = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#']);
2012                             if ($itemtype == 'mod') {
2013                                 $olditeminstance = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#']);
2014                                 $itemmodule      = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#']);
2016                                 if (empty($restore->mods[$itemmodule]->granular)) {
2017                                     continue;
2018                                 } else if (!empty($restore->mods[$itemmodule]->instances[$olditeminstance]->restore)) {
2019                                     continue;
2020                                 }
2021                                 // at least one activity should not be restored - do not restore categories and manual items at all
2022                                 $restoreall = false;
2023                                 break;
2024                             }
2025                         }
2026                     }
2027                 }
2028             }
2029         }
2031         // Start ul
2032         if (!defined('RESTORE_SILENTLY')) {
2033             echo '<ul>';
2034         }
2036         // array of restored categories - speedup ;-)
2037         $cached_categories = array();
2038         $outcomes          = array();
2040     /// Process letters
2041         $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
2042         // respect current grade letters if defined
2043         if ($status and $restoreall and !$DB->record_exists('grade_letters', array('contextid'=>$context->id))) {
2044             if (!defined('RESTORE_SILENTLY')) {
2045                 echo '<li>'.get_string('gradeletters','grades').'</li>';
2046             }
2047             // Fetch recordset_size records in each iteration
2048             $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_letters', 'backup_code'=>$restore->backup_unique_code),
2049                                         "",
2050                                         "old_id");
2051             if ($recs) {
2052                 foreach ($recs as $rec) {
2053                     // Get the full record from backup_ids
2054                     $data = backup_getid($restore->backup_unique_code,'grade_letters',$rec->old_id);
2055                     if ($data) {
2056                         $info = $data->info;
2057                         $dbrec = new object();
2058                         $dbrec->contextid     = $context->id;
2059                         $dbrec->lowerboundary = backup_todb($info['GRADE_LETTER']['#']['LOWERBOUNDARY']['0']['#']);
2060                         $dbrec->letter        = backup_todb($info['GRADE_LETTER']['#']['LETTER']['0']['#']);
2061                         $DB->insert_record('grade_letters', $dbrec);
2062                     }
2063                 }
2064             }
2065         }
2067     /// Preprocess outcomes - do not store them yet!
2068         if ($status and !$importing and $restoreall) {
2069             if (!defined('RESTORE_SILENTLY')) {
2070                 echo '<li>'.get_string('gradeoutcomes','grades').'</li>';
2071             }
2072             $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_outcomes', 'backup_code'=>$restore->backup_unique_code),
2073                                         "",
2074                                         "old_id");
2075             if ($recs) {
2076                 foreach ($recs as $rec) {
2077                     //Get the full record from backup_ids
2078                     $data = backup_getid($restore->backup_unique_code,'grade_outcomes',$rec->old_id);
2079                     if ($data) {
2080                         $info = $data->info;
2082                         //first find out if outcome already exists
2083                         $shortname = backup_todb($info['GRADE_OUTCOME']['#']['SHORTNAME']['0']['#']);
2085                         if ($candidates = $DB->get_records_sql("SELECT *
2086                                                                   FROM {grade_outcomes}
2087                                                                  WHERE (courseid IS NULL OR courseid = ?)
2088                                                                        AND shortname = ?
2089                                                               ORDER BY courseid ASC, id ASC", array($restore->course_id, $shortname))) {
2090                             $grade_outcome = reset($candidates);
2091                             $outcomes[$rec->old_id] = $grade_outcome;
2092                             continue;
2093                         }
2095                         $dbrec = new object();
2097                         if (has_capability('moodle/grade:manageoutcomes', get_context_instance(CONTEXT_SYSTEM))) {
2098                             $oldoutcome = backup_todb($info['GRADE_OUTCOME']['#']['COURSEID']['0']['#']);
2099                             if (empty($oldoutcome)) {
2100                                 //site wide
2101                                 $dbrec->courseid = null;
2102                             } else {
2103                                 //course only
2104                                 $dbrec->courseid = $restore->course_id;
2105                             }
2106                         } else {
2107                             // no permission to add site outcomes
2108                             $dbrec->courseid = $restore->course_id;
2109                         }
2111                         //Get the fields
2112                         $dbrec->shortname    = backup_todb($info['GRADE_OUTCOME']['#']['SHORTNAME']['0']['#'], false);
2113                         $dbrec->fullname     = backup_todb($info['GRADE_OUTCOME']['#']['FULLNAME']['0']['#'], false);
2114                         $dbrec->scaleid      = backup_todb($info['GRADE_OUTCOME']['#']['SCALEID']['0']['#'], false);
2115                         $dbrec->description  = backup_todb($info['GRADE_OUTCOME']['#']['DESCRIPTION']['0']['#'], false);
2116                         $dbrec->timecreated  = backup_todb($info['GRADE_OUTCOME']['#']['TIMECREATED']['0']['#'], false);
2117                         $dbrec->timemodified = backup_todb($info['GRADE_OUTCOME']['#']['TIMEMODIFIED']['0']['#'], false);
2118                         $dbrec->usermodified = backup_todb($info['GRADE_OUTCOME']['#']['USERMODIFIED']['0']['#'], false);
2120                         //Need to recode the scaleid
2121                         if ($scale = backup_getid($restore->backup_unique_code, 'scale', $dbrec->scaleid)) {
2122                             $dbrec->scaleid = $scale->new_id;
2123                         }
2125                         //Need to recode the usermodified
2126                         if ($modifier = backup_getid($restore->backup_unique_code, 'user', $dbrec->usermodified)) {
2127                             $dbrec->usermodified = $modifier->new_id;
2128                         }
2130                         $grade_outcome = new grade_outcome($dbrec, false);
2131                         $outcomes[$rec->old_id] = $grade_outcome;
2132                     }
2133                 }
2134             }
2135         }
2137     /// Process grade items and grades
2138         if ($status) {
2139             if (!defined('RESTORE_SILENTLY')) {
2140                 echo '<li>'.get_string('gradeitems','grades').'</li>';
2141             }
2142             $counter = 0;
2144             //Fetch recordset_size records in each iteration
2145             $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_items', 'backup_code'=>$restore->backup_unique_code),
2146                                         "id", // restore in the backup order
2147                                         "old_id");
2149             if ($recs) {
2150                 foreach ($recs as $rec) {
2151                     //Get the full record from backup_ids
2152                     $data = backup_getid($restore->backup_unique_code,'grade_items',$rec->old_id);
2153                     if ($data) {
2154                         $info = $data->info;
2156                         // first find out if category or normal item
2157                         $itemtype =  backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
2158                         if ($itemtype == 'course' or $itemtype == 'category') {
2159                             if (!$restoreall or $importing) {
2160                                 continue;
2161                             }
2163                             $oldcat = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#'], false);
2164                             if (!$cdata = backup_getid($restore->backup_unique_code,'grade_categories',$oldcat)) {
2165                                 continue;
2166                             }
2167                             $cinfo = $cdata->info;
2168                             unset($cdata);
2169                             if ($itemtype == 'course') {
2171                                 $course_category->fullname            = backup_todb($cinfo['GRADE_CATEGORY']['#']['FULLNAME']['0']['#'], false);
2172                                 $course_category->aggregation         = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATION']['0']['#'], false);
2173                                 $course_category->keephigh            = backup_todb($cinfo['GRADE_CATEGORY']['#']['KEEPHIGH']['0']['#'], false);
2174                                 $course_category->droplow             = backup_todb($cinfo['GRADE_CATEGORY']['#']['DROPLOW']['0']['#'], false);
2175                                 $course_category->aggregateonlygraded = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEONLYGRADED']['0']['#'], false);
2176                                 $course_category->aggregateoutcomes   = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEOUTCOMES']['0']['#'], false);
2177                                 $course_category->aggregatesubcats    = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATESUBCATS']['0']['#'], false);
2178                                 $course_category->timecreated         = backup_todb($cinfo['GRADE_CATEGORY']['#']['TIMECREATED']['0']['#'], false);
2179                                 $course_category->update('restore');
2181                                 $status = backup_putid($restore->backup_unique_code,'grade_categories',$oldcat,$course_category->id) && $status;
2182                                 $cached_categories[$oldcat] = $course_category;
2183                                 $grade_item = $course_category->get_grade_item();
2185                             } else {
2186                                 $oldparent = backup_todb($cinfo['GRADE_CATEGORY']['#']['PARENT']['0']['#'], false);
2187                                 if (empty($cached_categories[$oldparent])) {
2188                                     debugging('parent not found '.$oldparent);
2189                                     continue; // parent not found, sorry
2190                                 }
2191                                 $grade_category = new grade_category();
2192                                 $grade_category->courseid            = $restore->course_id;
2193                                 $grade_category->parent              = $cached_categories[$oldparent]->id;
2194                                 $grade_category->fullname            = backup_todb($cinfo['GRADE_CATEGORY']['#']['FULLNAME']['0']['#'], false);
2195                                 $grade_category->aggregation         = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATION']['0']['#'], false);
2196                                 $grade_category->keephigh            = backup_todb($cinfo['GRADE_CATEGORY']['#']['KEEPHIGH']['0']['#'], false);
2197                                 $grade_category->droplow             = backup_todb($cinfo['GRADE_CATEGORY']['#']['DROPLOW']['0']['#'], false);
2198                                 $grade_category->aggregateonlygraded = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEONLYGRADED']['0']['#'], false);
2199                                 $grade_category->aggregateoutcomes   = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEOUTCOMES']['0']['#'], false);
2200                                 $grade_category->aggregatesubcats    = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATESUBCATS']['0']['#'], false);
2201                                 $grade_category->timecreated         = backup_todb($cinfo['GRADE_CATEGORY']['#']['TIMECREATED']['0']['#'], false);
2202                                 $grade_category->insert('restore');
2204                                 $status = backup_putid($restore->backup_unique_code,'grade_categories',$oldcat,$grade_category->id) && $status;
2205                                 $cached_categories[$oldcat] = $grade_category;
2206                                 $grade_item = $grade_category->get_grade_item(); // creates grade_item too
2207                             }
2208                             unset($cinfo);
2210                             $idnumber = backup_todb($info['GRADE_ITEM']['#']['IDNUMBER']['0']['#'], false);
2211                             if (grade_verify_idnumber($idnumber, $restore->course_id)) {
2212                                 $grade_item->idnumber    = $idnumber;
2213                             }
2215                             $grade_item->itemname        = backup_todb($info['GRADE_ITEM']['#']['ITEMNAME']['0']['#'], false);
2216                             $grade_item->iteminfo        = backup_todb($info['GRADE_ITEM']['#']['ITEMINFO']['0']['#'], false);
2217                             $grade_item->gradetype       = backup_todb($info['GRADE_ITEM']['#']['GRADETYPE']['0']['#'], false);
2218                             $grade_item->calculation     = backup_todb($info['GRADE_ITEM']['#']['CALCULATION']['0']['#'], false);
2219                             $grade_item->grademax        = backup_todb($info['GRADE_ITEM']['#']['GRADEMAX']['0']['#'], false);
2220                             $grade_item->grademin        = backup_todb($info['GRADE_ITEM']['#']['GRADEMIN']['0']['#'], false);
2221                             $grade_item->gradepass       = backup_todb($info['GRADE_ITEM']['#']['GRADEPASS']['0']['#'], false);
2222                             $grade_item->multfactor      = backup_todb($info['GRADE_ITEM']['#']['MULTFACTOR']['0']['#'], false);
2223                             $grade_item->plusfactor      = backup_todb($info['GRADE_ITEM']['#']['PLUSFACTOR']['0']['#'], false);
2224                             $grade_item->aggregationcoef = backup_todb($info['GRADE_ITEM']['#']['AGGREGATIONCOEF']['0']['#'], false);
2225                             $grade_item->display         = backup_todb($info['GRADE_ITEM']['#']['DISPLAY']['0']['#'], false);
2226                             $grade_item->decimals        = backup_todb($info['GRADE_ITEM']['#']['DECIMALS']['0']['#'], false);
2227                             $grade_item->hidden          = backup_todb($info['GRADE_ITEM']['#']['HIDDEN']['0']['#'], false);
2228                             $grade_item->locked          = backup_todb($info['GRADE_ITEM']['#']['LOCKED']['0']['#'], false);
2229                             $grade_item->locktime        = backup_todb($info['GRADE_ITEM']['#']['LOCKTIME']['0']['#'], false);
2230                             $grade_item->timecreated     = backup_todb($info['GRADE_ITEM']['#']['TIMECREATED']['0']['#'], false);
2232                             if (backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false)) {
2233                                 $scale = backup_getid($restore->backup_unique_code,"scale",backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false));
2234                                 $grade_item->scaleid     = $scale->new_id;
2235                             }
2237                             if  (backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'], false)) {
2238                                 $outcome = backup_getid($restore->backup_unique_code,"grade_outcomes",backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'], false));
2239                                 $grade_item->outcomeid   = $outcome->new_id;
2240                             }
2242                             $grade_item->update('restore');
2243                             $status = backup_putid($restore->backup_unique_code,"grade_items", $rec->old_id, $grade_item->id) && $status;
2245                         } else {
2246                             if ($itemtype != 'mod' and (!$restoreall or $importing)) {
2247                                 // not extra gradebook stuff if restoring individual activities or something already there
2248                                 continue;
2249                             }
2251                             $dbrec = new object();
2253                             $dbrec->courseid      = $restore->course_id;
2254                             $dbrec->itemtype      = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
2255                             $dbrec->itemmodule    = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#'], false);
2257                             if ($itemtype == 'mod') {
2258                                 // iteminstance should point to new mod
2259                                 $olditeminstance = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#'], false);
2260                                 $mod = backup_getid($restore->backup_unique_code,$dbrec->itemmodule, $olditeminstance);
2261                                 $dbrec->iteminstance = $mod->new_id;
2262                                 if (!$cm = get_coursemodule_from_instance($dbrec->itemmodule, $mod->new_id)) {
2263                                     // item not restored - no item
2264                                     continue;
2265                                 }
2266                                 // keep in sync with activity idnumber
2267                                 $dbrec->idnumber = $cm->idnumber;
2269                             } else {
2270                                 $idnumber = backup_todb($info['GRADE_ITEM']['#']['IDNUMBER']['0']['#'], false);
2272                                 if (grade_verify_idnumber($idnumber, $restore->course_id)) {
2273                                     //make sure the new idnumber is unique
2274                                     $dbrec->idnumber  = $idnumber;
2275                                 }
2276                             }
2278                             $dbrec->itemname        = backup_todb($info['GRADE_ITEM']['#']['ITEMNAME']['0']['#'], false);
2279                             $dbrec->itemtype        = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
2280                             $dbrec->itemmodule      = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#'], false);
2281                             $dbrec->itemnumber      = backup_todb($info['GRADE_ITEM']['#']['ITEMNUMBER']['0']['#'], false);
2282                             $dbrec->iteminfo        = backup_todb($info['GRADE_ITEM']['#']['ITEMINFO']['0']['#'], false);
2283                             $dbrec->gradetype       = backup_todb($info['GRADE_ITEM']['#']['GRADETYPE']['0']['#'], false);
2284                             $dbrec->calculation     = backup_todb($info['GRADE_ITEM']['#']['CALCULATION']['0']['#'], false);
2285                             $dbrec->grademax        = backup_todb($info['GRADE_ITEM']['#']['GRADEMAX']['0']['#'], false);
2286                             $dbrec->grademin        = backup_todb($info['GRADE_ITEM']['#']['GRADEMIN']['0']['#'], false);
2287                             $dbrec->gradepass       = backup_todb($info['GRADE_ITEM']['#']['GRADEPASS']['0']['#'], false);
2288                             $dbrec->multfactor      = backup_todb($info['GRADE_ITEM']['#']['MULTFACTOR']['0']['#'], false);
2289                             $dbrec->plusfactor      = backup_todb($info['GRADE_ITEM']['#']['PLUSFACTOR']['0']['#'], false);
2290                             $dbrec->aggregationcoef = backup_todb($info['GRADE_ITEM']['#']['AGGREGATIONCOEF']['0']['#'], false);
2291                             $dbrec->display         = backup_todb($info['GRADE_ITEM']['#']['DISPLAY']['0']['#'], false);
2292                             $dbrec->decimals        = backup_todb($info['GRADE_ITEM']['#']['DECIMALS']['0']['#'], false);
2293                             $dbrec->hidden          = backup_todb($info['GRADE_ITEM']['#']['HIDDEN']['0']['#'], false);
2294                             $dbrec->locked          = backup_todb($info['GRADE_ITEM']['#']['LOCKED']['0']['#'], false);
2295                             $dbrec->locktime        = backup_todb($info['GRADE_ITEM']['#']['LOCKTIME']['0']['#'], false);
2296                             $dbrec->timecreated     = backup_todb($info['GRADE_ITEM']['#']['TIMECREATED']['0']['#'], false);
2298                             if (backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false)) {
2299                                 $scale = backup_getid($restore->backup_unique_code,"scale",backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false));
2300                                 $dbrec->scaleid = $scale->new_id;
2301                             }
2303                             if  (backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'])) {
2304                                 $oldoutcome = backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#']);
2305                                 if (empty($outcomes[$oldoutcome])) {
2306                                     continue; // error!
2307                                 }
2308                                 if (empty($outcomes[$oldoutcome]->id)) {
2309                                     $outcomes[$oldoutcome]->insert('restore');
2310                                     $outcomes[$oldoutcome]->use_in($restore->course_id);
2311                                     backup_putid($restore->backup_unique_code, "grade_outcomes", $oldoutcome, $outcomes[$oldoutcome]->id);
2312                                 }
2313                                 $dbrec->outcomeid = $outcomes[$oldoutcome]->id;
2314                             }
2316                             $grade_item = new grade_item($dbrec, false);
2317                             $grade_item->insert('restore');
2318                             if ($restoreall) {
2319                                 // set original parent if restored
2320                                 $oldcat = $info['GRADE_ITEM']['#']['CATEGORYID']['0']['#'];
2321                                 if (!empty($cached_categories[$oldcat])) {
2322                                     $grade_item->set_parent($cached_categories[$oldcat]->id);
2323                                 }
2324                             }
2325                             $status = backup_putid($restore->backup_unique_code,"grade_items", $rec->old_id, $grade_item->id) && $status;
2326                         }
2328                         // no need to restore grades if user data is not selected or importing activities
2329                         if ($importing
2330                           or ($grade_item->itemtype == 'mod' and !restore_userdata_selected($restore,  $grade_item->itemmodule, $olditeminstance))) {
2331                             // module instance not selected when restored using granular
2332                             // skip this item
2333                             continue;
2334                         }
2336                         /// now, restore grade_grades
2337                         if (!empty($info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']['GRADE'])) {
2338                             //Iterate over items
2339                             foreach ($info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']['GRADE'] as $g_info) {
2341                                 $grade = new grade_grade();
2342                                 $grade->itemid         = $grade_item->id;
2344                                 $olduser = backup_todb($g_info['#']['USERID']['0']['#'], false);
2345                                 $user = backup_getid($restore->backup_unique_code,"user",$olduser);
2346                                 $grade->userid         = $user->new_id;
2348                                 $grade->rawgrade       = backup_todb($g_info['#']['RAWGRADE']['0']['#'], false);
2349                                 $grade->rawgrademax    = backup_todb($g_info['#']['RAWGRADEMAX']['0']['#'], false);
2350                                 $grade->rawgrademin    = backup_todb($g_info['#']['RAWGRADEMIN']['0']['#'], false);
2351                                 // need to find scaleid
2352                                 if (backup_todb($g_info['#']['RAWSCALEID']['0']['#'])) {
2353                                     $scale = backup_getid($restore->backup_unique_code,"scale",backup_todb($g_info['#']['RAWSCALEID']['0']['#'], false));
2354                                     $grade->rawscaleid = $scale->new_id;
2355                                 }
2357                                 if (backup_todb($g_info['#']['USERMODIFIED']['0']['#'])) {
2358                                     if ($modifier = backup_getid($restore->backup_unique_code,"user", backup_todb($g_info['#']['USERMODIFIED']['0']['#'], false))) {
2359                                         $grade->usermodified = $modifier->new_id;
2360                                     }
2361                                 }
2363                                 $grade->finalgrade        = backup_todb($g_info['#']['FINALGRADE']['0']['#'], false);
2364                                 $grade->hidden            = backup_todb($g_info['#']['HIDDEN']['0']['#'], false);
2365                                 $grade->locked            = backup_todb($g_info['#']['LOCKED']['0']['#'], false);
2366                                 $grade->locktime          = backup_todb($g_info['#']['LOCKTIME']['0']['#'], false);
2367                                 $grade->exported          = backup_todb($g_info['#']['EXPORTED']['0']['#'], false);
2368                                 $grade->overridden        = backup_todb($g_info['#']['OVERRIDDEN']['0']['#'], false);
2369                                 $grade->excluded          = backup_todb($g_info['#']['EXCLUDED']['0']['#'], false);
2370                                 $grade->feedback          = backup_todb($g_info['#']['FEEDBACK']['0']['#'], false);
2371                                 $grade->feedbackformat    = backup_todb($g_info['#']['FEEDBACKFORMAT']['0']['#'], false);
2372                                 $grade->information       = backup_todb($g_info['#']['INFORMATION']['0']['#'], false);
2373                                 $grade->informationformat = backup_todb($g_info['#']['INFORMATIONFORMAT']['0']['#'], false);
2374                                 $grade->timecreated       = backup_todb($g_info['#']['TIMECREATED']['0']['#'], false);
2375                                 $grade->timemodified      = backup_todb($g_info['#']['TIMEMODIFIED']['0']['#'], false);
2377                                 $grade->insert('restore');
2378                                 backup_putid($restore->backup_unique_code,"grade_grades", backup_todb($g_info['#']['ID']['0']['#']), $grade->id);
2380                                 $counter++;
2381                                 if ($counter % 20 == 0) {
2382                                     if (!defined('RESTORE_SILENTLY')) {
2383                                         echo ".";
2384                                         if ($counter % 400 == 0) {
2385                                             echo "<br />";
2386                                         }
2387                                     }
2388                                     backup_flush(300);
2389                                 }
2390                             }
2391                         }
2392                     }
2393                 }
2394             }
2395         }
2397     /// add outcomes that are not used when doing full restore
2398         if ($status and $restoreall) {
2399             foreach ($outcomes as $oldoutcome=>$grade_outcome) {
2400                 if (empty($grade_outcome->id)) {
2401                     $grade_outcome->insert('restore');
2402                     $grade_outcome->use_in($restore->course_id);
2403                     backup_putid($restore->backup_unique_code, "grade_outcomes", $oldoutcome, $grade_outcome->id);
2404                 }
2405             }
2406         }
2409         if ($status and !$importing and $restore_histories) {
2410             /// following code is very inefficient
2412             $gchcount = $DB->count_records('backup_ids', array('backup_code'=>$restore->backup_unique_code, 'table_name'=>'grade_categories_history'));
2413             $gghcount = $DB->count_records('backup_ids', array('backup_code'=>$restore->backup_unique_code, 'table_name'=>'grade_grades_history'));
2414             $gihcount = $DB->count_records('backup_ids', array('backup_code'=>$restore->backup_unique_code, 'table_name'=>'grade_items_history'));
2415             $gohcount = $DB->count_records('backup_ids', array('backup_code'=>$restore->backup_unique_code, 'table_name'=>'grade_outcomes_history'));
2417             // Number of records to get in every chunk
2418             $recordset_size = 2;
2420             // process histories
2421             if ($gchcount && $status) {
2422                 if (!defined('RESTORE_SILENTLY')) {
2423                     echo '<li>'.get_string('gradecategoryhistory','grades').'</li>';
2424                 }
2425                 $counter = 0;
2426                 while ($counter < $gchcount) {
2427                     //Fetch recordset_size records in each iteration
2428                     $recs = $DB->get_records("backup_ids",array('table_name'=>'grade_categories_history', 'backup_code'=>$restore->backup_unique_code),
2429                                                 "old_id",
2430                                                 "old_id",
2431                                                 $counter,
2432                                                 $recordset_size);
2433                     if ($recs) {
2434                         foreach ($recs as $rec) {
2435                             //Get the full record from backup_ids
2436                             $data = backup_getid($restore->backup_unique_code,'grade_categories_history',$rec->old_id);
2437                             if ($data) {
2438                                 //Now get completed xmlized object
2439                                 $info = $data->info;
2440                                 //traverse_xmlize($info);                            //Debug
2441                                 //print_object ($GLOBALS['traverse_array']);         //Debug
2442                                 //$GLOBALS['traverse_array']="";                     //Debug
2444                                 $oldobj = backup_getid($restore->backup_unique_code,"grade_categories", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['OLDID']['0']['#']));
2445                                 if (empty($oldobj->new_id)) {
2446                                     // if the old object is not being restored, can't restoring its history
2447                                     $counter++;
2448                                     continue;
2449                                 }
2450                                 $dbrec->oldid = $oldobj->new_id;
2451                                 $dbrec->action = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['ACTION']['0']['#']);
2452                                 $dbrec->source = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['SOURCE']['0']['#']);
2453                                 $dbrec->timemodified = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
2455                                 // loggeduser might not be restored, e.g. admin
2456                                 if ($oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
2457                                     $dbrec->loggeduser = $oldobj->new_id;
2458                                 }
2460                                 // this item might not have a parent at all, do not skip it if no parent is specified
2461                                 if (backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PARENT']['0']['#'])) {
2462                                     $oldobj = backup_getid($restore->backup_unique_code,"grade_categories", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PARENT']['0']['#']));
2463                                     if (empty($oldobj->new_id)) {
2464                                         // if the parent category not restored
2465                                         $counter++;
2466                                         continue;
2467                                     }
2468                                 }
2469                                 $dbrec->parent = $oldobj->new_id;
2470                                 $dbrec->depth = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['DEPTH']['0']['#']);
2471                                 // path needs to be rebuilt
2472                                 if ($path = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PATH']['0']['#'])) {
2473                                 // to preserve the path and make it work, we need to replace the categories one by one
2474                                 // we first get the list of categories in current path
2475                                     if ($paths = explode("/", $path)) {
2476                                         $newpath = '';
2477                                         foreach ($paths as $catid) {
2478                                             if ($catid) {
2479                                                 // find the new corresponding path
2480                                                 $oldpath = backup_getid($restore->backup_unique_code,"grade_categories", $catid);
2481                                                 $newpath .= "/$oldpath->new_id";
2482                                             }
2483                                         }
2484                                         $dbrec->path = $newpath;
2485                                     }
2486                                 }
2487                                 $dbrec->fullname = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['FULLNAME']['0']['#']);
2488                                 $dbrec->aggregation = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGRETGATION']['0']['#']);
2489                                 $dbrec->keephigh = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['KEEPHIGH']['0']['#']);
2490                                 $dbrec->droplow = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['DROPLOW']['0']['#']);
2492                                 $dbrec->aggregateonlygraded = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATEONLYGRADED']['0']['#']);
2493                                 $dbrec->aggregateoutcomes = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATEOUTCOMES']['0']['#']);
2494                                 $dbrec->aggregatesubcats = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATESUBCATS']['0']['#']);
2496                                 $dbrec->courseid = $restore->course_id;
2497                                 $DB->insert_record('grade_categories_history', $dbrec);
2498                                 unset($dbrec);
2500                             }
2501                             //Increment counters
2502                             $counter++;
2503                             //Do some output
2504                             if ($counter % 1 == 0) {
2505                                 if (!defined('RESTORE_SILENTLY')) {
2506                                     echo ".";
2507                                     if ($counter % 20 == 0) {
2508                                         echo "<br />";
2509                                     }
2510                                 }
2511                                 backup_flush(300);
2512                             }
2513                         }
2514                     }
2515                 }
2516             }
2518             // process histories
2519             if ($gghcount && $status) {
2520                 if (!defined('RESTORE_SILENTLY')) {
2521                     echo '<li>'.get_string('gradegradeshistory','grades').'</li>';
2522                 }
2523                 $counter = 0;
2524                 while ($counter < $gghcount) {
2525                     //Fetch recordset_size records in each iteration
2526                     $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_grades_history', 'backup_code'=>$restore->backup_unique_code),
2527                                                 "old_id",
2528                                                 "old_id",
2529                                                 $counter,
2530                                                 $recordset_size);
2531                     if ($recs) {
2532                         foreach ($recs as $rec) {
2533                             //Get the full record from backup_ids
2534                             $data = backup_getid($restore->backup_unique_code,'grade_grades_history',$rec->old_id);
2535                             if ($data) {
2536                                 //Now get completed xmlized object
2537                                 $info = $data->info;
2538                                 //traverse_xmlize($info);                            //Debug
2539                                 //print_object ($GLOBALS['traverse_array']);         //Debug
2540                                 //$GLOBALS['traverse_array']="";                     //Debug
2542                                 $oldobj = backup_getid($restore->backup_unique_code,"grade_grades", backup_todb($info['GRADE_GRADES_HISTORY']['#']['OLDID']['0']['#']));
2543                                 if (empty($oldobj->new_id)) {
2544                                     // if the old object is not being restored, can't restoring its history
2545                                     $counter++;
2546                                     continue;
2547                                 }
2548                                 $dbrec->oldid = $oldobj->new_id;
2549                                 $dbrec->action = backup_todb($info['GRADE_GRADES_HISTORY']['#']['ACTION']['0']['#']);
2550                                 $dbrec->source = backup_todb($info['GRADE_GRADES_HISTORY']['#']['SOURCE']['0']['#']);
2551                                 $dbrec->timemodified = backup_todb($info['GRADE_GRADES_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
2552                                 if ($oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
2553                                     $dbrec->loggeduser = $oldobj->new_id;
2554                                 }
2556                                 $oldobj = backup_getid($restore->backup_unique_code,"grade_items", backup_todb($info['GRADE_GRADES_HISTORY']['#']['ITEMID']['0']['#']));
2557                                 $dbrec->itemid = $oldobj->new_id;
2558                                 if (empty($dbrec->itemid)) {
2559                                     $counter++;
2560                                     continue; // grade item not being restored
2561                                 }
2562                                 $oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['USERID']['0']['#']));
2563                                 $dbrec->userid = $oldobj->new_id;
2564                                 $dbrec->rawgrade = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADE']['0']['#']);
2565                                 $dbrec->rawgrademax = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADEMAX']['0']['#']);
2566                                 $dbrec->rawgrademin = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADEMIN']['0']['#']);
2567                                 if ($oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['USERMODIFIED']['0']['#']))) {
2568                                     $dbrec->usermodified = $oldobj->new_id;
2569                                 }
2571                                 if (backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWSCALEID']['0']['#'])) {
2572                                     $scale = backup_getid($restore->backup_unique_code,"scale",backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWSCALEID']['0']['#']));
2573                                     $dbrec->rawscaleid = $scale->new_id;
2574                                 }
2576                                 $dbrec->finalgrade = backup_todb($info['GRADE_GRADES_HISTORY']['#']['FINALGRADE']['0']['#']);
2577                                 $dbrec->hidden = backup_todb($info['GRADE_GRADES_HISTORY']['#']['HIDDEN']['0']['#']);
2578                                 $dbrec->locked = backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOCKED']['0']['#']);
2579                                 $dbrec->locktime = backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOCKTIME']['0']['#']);
2580                                 $dbrec->exported = backup_todb($info['GRADE_GRADES_HISTORY']['#']['EXPORTED']['0']['#']);
2581                                 $dbrec->overridden = backup_todb($info['GRADE_GRADES_HISTORY']['#']['OVERRIDDEN']['0']['#']);
2582                                 $dbrec->excluded = backup_todb($info['GRADE_GRADES_HISTORY']['#']['EXCLUDED']['0']['#']);
2583                                 $dbrec->feedback = backup_todb($info['GRADE_TEXT_HISTORY']['#']['FEEDBACK']['0']['#']);
2584                                 $dbrec->feedbackformat = backup_todb($info['GRADE_TEXT_HISTORY']['#']['FEEDBACKFORMAT']['0']['#']);
2585                                 $dbrec->information = backup_todb($info['GRADE_TEXT_HISTORY']['#']['INFORMATION']['0']['#']);
2586                                 $dbrec->informationformat = backup_todb($info['GRADE_TEXT_HISTORY']['#']['INFORMATIONFORMAT']['0']['#']);
2588                                 $DB->insert_record('grade_grades_history', $dbrec);
2589                                 unset($dbrec);
2591                             }
2592                             //Increment counters
2593                             $counter++;
2594                             //Do some output
2595                             if ($counter % 1 == 0) {
2596                                 if (!defined('RESTORE_SILENTLY')) {
2597                                     echo ".";
2598                                     if ($counter % 20 == 0) {
2599                                         echo "<br />";
2600                                     }
2601                                 }
2602                                 backup_flush(300);
2603                             }
2604                         }
2605                     }
2606                 }
2607             }
2609             // process histories
2611             if ($gihcount && $status) {
2612                 if (!defined('RESTORE_SILENTLY')) {
2613                     echo '<li>'.get_string('gradeitemshistory','grades').'</li>';
2614                 }
2615                 $counter = 0;
2616                 while ($counter < $gihcount) {
2617                     //Fetch recordset_size records in each iteration
2618                     $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_items_history', 'backup_code'=>$restore->backup_unique_code),
2619                                                 "old_id",
2620                                                 "old_id",
2621                                                 $counter,
2622                                                 $recordset_size);
2623                     if ($recs) {
2624                         foreach ($recs as $rec) {
2625                             //Get the full record from backup_ids
2626                             $data = backup_getid($restore->backup_unique_code,'grade_items_history',$rec->old_id);
2627                             if ($data) {
2628                                 //Now get completed xmlized object
2629                                 $info = $data->info;
2630                                 //traverse_xmlize($info);                            //Debug
2631                                 //print_object ($GLOBALS['traverse_array']);         //Debug
2632                                 //$GLOBALS['traverse_array']="";                     //Debug
2635                                 $oldobj = backup_getid($restore->backup_unique_code,"grade_items", backup_todb($info['GRADE_ITEM_HISTORY']['#']['OLDID']['0']['#']));
2636                                 if (empty($oldobj->new_id)) {
2637                                     // if the old object is not being restored, can't restoring its history
2638                                     $counter++;
2639                                     continue;
2640                                 }
2641                                 $dbrec->oldid = $oldobj->new_id;
2642                                 $dbrec->action = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ACTION']['0']['#']);
2643                                 $dbrec->source = backup_todb($info['GRADE_ITEM_HISTORY']['#']['SOURCE']['0']['#']);
2644                                 $dbrec->timemodified = backup_todb($info['GRADE_ITEM_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
2645                                 if ($oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
2646                                     $dbrec->loggeduser = $oldobj->new_id;
2647                                 }
2648                                 $dbrec->courseid = $restore->course_id;
2649                                 $oldobj = backup_getid($restore->backup_unique_code,'grade_categories',backup_todb($info['GRADE_ITEM_HISTORY']['#']['CATEGORYID']['0']['#']));
2650                                 $oldobj->categoryid = $category->new_id;
2651                                 if (empty($oldobj->categoryid)) {
2652                                     $counter++;
2653                                     continue; // category not restored
2654                                 }
2656                                 $dbrec->itemname= backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMNAME']['0']['#']);
2657                                 $dbrec->itemtype = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMTYPE']['0']['#']);
2658                                 $dbrec->itemmodule = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMMODULE']['0']['#']);
2660                                 // code from grade_items restore
2661                                 $iteminstance = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMINSTANCE']['0']['#']);
2662                                 // do not restore if this grade_item is a mod, and
2663                                 if ($dbrec->itemtype == 'mod') {
2665                                     if (!restore_userdata_selected($restore,  $dbrec->itemmodule, $iteminstance)) {
2666                                         // module instance not selected when restored using granular
2667                                         // skip this item
2668                                         $counter++;
2669                                         continue;
2670                                     }
2672                                     // iteminstance should point to new mod
2674                                     $mod = backup_getid($restore->backup_unique_code,$dbrec->itemmodule, $iteminstance);
2675                                     $dbrec->iteminstance = $mod->new_id;
2677                                 } else if ($dbrec->itemtype == 'category') {
2678                                     // the item instance should point to the new grade category
2680                                     // only proceed if we are restoring all grade items
2681                                     if ($restoreall) {
2682                                         $category = backup_getid($restore->backup_unique_code,'grade_categories', $iteminstance);
2683                                         $dbrec->iteminstance = $category->new_id;
2684                                     } else {
2685                                         // otherwise we can safely ignore this grade item and subsequent
2686                                         // grade_raws, grade_finals etc
2687                                         continue;
2688                                     }
2689                                 } elseif ($dbrec->itemtype == 'course') { // We don't restore course type to avoid duplicate course items
2690                                     if ($restoreall) {
2691                                         // TODO any special code needed here to restore course item without duplicating it?
2692                                         // find the course category with depth 1, and course id = current course id
2693                                         // this would have been already restored
2695                                         $cat = $DB->get_record('grade_categories', array('depth'=>1, 'courseid'=>$restore->course_id));
2696                                         $dbrec->iteminstance = $cat->id;
2698                                     } else {
2699                                         $counter++;
2700                                         continue;
2701                                     }
2702                                 }
2704                                 $dbrec->itemnumber = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMNUMBER']['0']['#']);
2705                                 $dbrec->iteminfo = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMINFO']['0']['#']);
2706                                 $dbrec->idnumber = backup_todb($info['GRADE_ITEM_HISTORY']['#']['IDNUMBER']['0']['#']);
2707                                 $dbrec->calculation = backup_todb($info['GRADE_ITEM_HISTORY']['#']['CALCULATION']['0']['#']);
2708                                 $dbrec->gradetype = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADETYPE']['0']['#']);
2709                                 $dbrec->grademax = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEMAX']['0']['#']);
2710                                 $dbrec->grademin = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEMIN']['0']['#']);
2711                                 if ($oldobj = backup_getid($restore->backup_unique_code,"scale", backup_todb($info['GRADE_ITEM_HISTORY']['#']['SCALEID']['0']['#']))) {
2712                                     // scaleid is optional
2713                                     $dbrec->scaleid = $oldobj->new_id;
2714                                 }
2715                                 if ($oldobj = backup_getid($restore->backup_unique_code,"grade_outcomes", backup_todb($info['GRADE_ITEM_HISTORY']['#']['OUTCOMEID']['0']['#']))) {
2716                                     // outcome is optional
2717                                     $dbrec->outcomeid = $oldobj->new_id;
2718                                 }
2719                                 $dbrec->gradepass = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEPASS']['0']['#']);
2720                                 $dbrec->multfactor = backup_todb($info['GRADE_ITEM_HISTORY']['#']['MULTFACTOR']['0']['#']);
2721                                 $dbrec->plusfactor = backup_todb($info['GRADE_ITEM_HISTORY']['#']['PLUSFACTOR']['0']['#']);
2722                                 $dbrec->aggregationcoef = backup_todb($info['GRADE_ITEM_HISTORY']['#']['AGGREGATIONCOEF']['0']['#']);
2723                                 $dbrec->sortorder = backup_todb($info['GRADE_ITEM_HISTORY']['#']['SORTORDER']['0']['#']);
2724                                 $dbrec->display = backup_todb($info['GRADE_ITEM_HISTORY']['#']['DISPLAY']['0']['#']);
2725                                 $dbrec->decimals = backup_todb($info['GRADE_ITEM_HISTORY']['#']['DECIMALS']['0']['#']);
2726                                 $dbrec->hidden = backup_todb($info['GRADE_ITEM_HISTORY']['#']['HIDDEN']['0']['#']);
2727                                 $dbrec->locked = backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOCKED']['0']['#']);
2728                                 $dbrec->locktime = backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOCKTIME']['0']['#']);
2729                                 $dbrec->needsupdate = backup_todb($info['GRADE_ITEM_HISTORY']['#']['NEEDSUPDATE']['0']['#']);
2731                                 $DB->insert_record('grade_items_history', $dbrec);
2732                                 unset($dbrec);
2734                             }
2735                             //Increment counters
2736                             $counter++;
2737                             //Do some output
2738                             if ($counter % 1 == 0) {
2739                                 if (!defined('RESTORE_SILENTLY')) {
2740                                     echo ".";
2741                                     if ($counter % 20 == 0) {
2742                                         echo "<br />";
2743                                     }
2744                                 }
2745                                 backup_flush(300);
2746                             }
2747                         }
2748                     }
2749                 }
2750             }
2752             // process histories
2753             if ($gohcount && $status) {
2754                 if (!defined('RESTORE_SILENTLY')) {
2755                     echo '<li>'.get_string('gradeoutcomeshistory','grades').'</li>';
2756                 }
2757                 $counter = 0;
2758                 while ($counter < $gohcount) {
2759                     //Fetch recordset_size records in each iteration
2760                     $recs = $DB->get_records("backup_ids", array('table_name'=>'grade_outcomes_history', 'backup_code'=>$restore->backup_unique_code),
2761                                                 "old_id",
2762                                                 "old_id",
2763                                                 $counter,
2764                                                 $recordset_size);
2765                     if ($recs) {
2766                         foreach ($recs as $rec) {
2767                             //Get the full record from backup_ids
2768                             $data = backup_getid($restore->backup_unique_code,'grade_outcomes_history',$rec->old_id);
2769                             if ($data) {
2770                                 //Now get completed xmlized object
2771                                 $info = $data->info;
2772                                 //traverse_xmlize($info);                            //Debug
2773                                 //print_object ($GLOBALS['traverse_array']);         //Debug
2774                                 //$GLOBALS['traverse_array']="";                     //Debug
2776                                 $oldobj = backup_getid($restore->backup_unique_code,"grade_outcomes", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['OLDID']['0']['#']));
2777                                 if (empty($oldobj->new_id)) {
2778                                     // if the old object is not being restored, can't restoring its history
2779                                     $counter++;
2780                                     continue;
2781                                 }
2782                                 $dbrec->oldid = $oldobj->new_id;
2783                                 $dbrec->action = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['ACTION']['0']['#']);
2784                                 $dbrec->source = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SOURCE']['0']['#']);
2785                                 $dbrec->timemodified = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
2786                                 if ($oldobj = backup_getid($restore->backup_unique_code,"user", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
2787                                     $dbrec->loggeduser = $oldobj->new_id;
2788                                 }
2789                                 $dbrec->courseid = $restore->course_id;
2790                                 $dbrec->shortname = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SHORTNAME']['0']['#']);
2791                                 $dbrec->fullname= backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['FULLNAME']['0']['#']);
2792                                 $oldobj = backup_getid($restore->backup_unique_code,"scale", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SCALEID']['0']['#']));
2793                                 $dbrec->scaleid = $oldobj->new_id;
2794                                 $dbrec->description = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['DESCRIPTION']['0']['#']);
2796                                 $DB->insert_record('grade_outcomes_history', $dbrec);
2797                                 unset($dbrec);
2799                             }
2800                             //Increment counters
2801                             $counter++;
2802                             //Do some output
2803                             if ($counter % 1 == 0) {
2804                                 if (!defined('RESTORE_SILENTLY')) {
2805                                     echo ".";
2806                                     if ($counter % 20 == 0) {
2807                                         echo "<br />";
2808                                     }
2809                                 }
2810                                 backup_flush(300);
2811                             }
2812                         }
2813                     }
2814                 }
2815             }
2816         }
2818         if (!defined('RESTORE_SILENTLY')) {
2819         //End ul
2820             echo '</ul>';
2821         }
2822         return $status;
2823     }
2825     //This function creates all the user, user_students, user_teachers
2826     //user_course_creators and user_admins from xml
2827     function restore_create_users($restore,$xml_file) {
2828         global $CFG, $DB;
2829         require_once ($CFG->dirroot.'/tag/lib.php');
2831         $authcache = array(); // Cache to get some bits from authentication plugins
2833         $status = true;
2835         // Users have already been checked by restore_precheck_users() so they are loaded
2836         // in backup_ids table. They don't need to be loaded (parsed) from XML again. Also, note
2837         // the same function has performed the needed modifications in the $user->mnethostid field
2838         // so we don't need to do it again here at all. Just some checks.
2840         // Get users ids from backup_ids table
2841         $userids = $DB->get_fieldset_select('backup_ids', 'old_id', 'backup_code = ? AND table_name = ?', array($restore->backup_unique_code, 'user'));
2843         // Have users to process, proceed with them
2844         if (!empty($userids)) {
2846         /// Get languages for quick search later
2847             $languages = get_string_manager()->get_list_of_translations();
2849         /// Iterate over all users loaded from xml
2850             $counter = 0;
2852         /// Init trailing messages
2853             $messages = array();
2854             foreach ($userids as $userid) {
2855                 // Defaults
2856                 $user_exists = false; // By default user does not exist
2857                 $newid = null;        // By default, there is not newid
2859                 // Get record from backup_ids
2860                 $useridsdbrec = backup_getid($restore->backup_unique_code, 'user', $userid);
2862                 // Based in restore_precheck_users() calculations, if the user exists
2863                 // new_id must contain the id of the matching user
2864                 if (!empty($useridsdbrec->new_id)) {
2865                     $user_exists = true;
2866                     $newid = $useridsdbrec->new_id;
2867                 }
2869                 $user = $useridsdbrec->info;
2870                 foreach (array_keys(get_object_vars($user)) as $field) {
2871                     if (!is_array($user->$field)) {
2872                         $user->$field = backup_todb($user->$field);
2873                         if (is_null($user->$field)) {
2874                             $user->$field = '';
2875                         }
2876                     }
2877                 }
2879                 //Now, recode some languages (Moodle 1.5)
2880                 if ($user->lang == 'ma_nt') {
2881                     $user->lang = 'mi_nt';
2882                 }
2884                 //Country list updates - MDL-13060
2885                 //Any user whose country code has been deleted or modified needs to be assigned a valid one.
2886                 $country_update_map = array(
2887                     'ZR' => 'CD',
2888                     'TP' => 'TL',
2889                     'FX' => 'FR',
2890                     'KO' => 'RS',
2891                     'CS' => 'RS',
2892                     'WA' => 'GB');
2893                 if (array_key_exists($user->country, $country_update_map)) {
2894                     $user->country = $country_update_map[$user->country];
2895                 }
2897                 //If language does not exist here - use site default
2898                 if (!array_key_exists($user->lang, $languages)) {
2899                     $user->lang = $CFG->lang;
2900                 }
2902                 //Check if it's admin and coursecreator
2903                 $is_admin =         !empty($user->roles['admin']);
2904                 $is_coursecreator = !empty($user->roles['coursecreator']);
2906                 //Check if it's teacher and student
2907                 $is_teacher = !empty($user->roles['teacher']);
2908                 $is_student = !empty($user->roles['student']);
2910                 //Check if it's needed
2911                 $is_needed = !empty($user->roles['needed']);
2913                 //Calculate if it is a course user
2914                 //Has role teacher or student or needed
2915                 $is_course_user = ($is_teacher or $is_student or $is_needed);
2917                 // Only try to perform mnethost/auth modifications if restoring to another server
2918                 // or if, while restoring to same server, the user doesn't exists yet (rebuilt site)
2919                 //
2920                 // So existing user data in same server *won't be modified by restore anymore*,
2921                 // under any circumpstance. If somehting is wrong with existing data, it's server fault.
2922                 if (!backup_is_same_site($restore) || (backup_is_same_site($restore) && !$user_exists)) {
2923                     //Arriving here, any user with mnet auth and using $CFG->mnet_localhost_id is wrong
2924                     //as own server cannot be accesed over mnet. Change auth to manual and inform about the switch
2925                     if ($user->auth == 'mnet' && $user->mnethostid == $CFG->mnet_localhost_id) {
2926                         // Respect registerauth
2927                         if ($CFG->registerauth == 'email') {
2928                             $user->auth = 'email';
2929                         } else {
2930                             $user->auth = 'manual';
2931                         }
2932                         // inform about the automatic switch of authentication/host
2933                         if(empty($user->mnethosturl)) {
2934                             $user->mnethosturl = '----';
2935                         }
2936                         $messages[] = get_string('mnetrestore_extusers_switchuserauth', 'admin', $user);
2937                     }
2938                 }
2939                 unset($user->mnethosturl);
2941                 //Flags to see what parts are we going to restore
2942                 $create_user = true;
2943                 $create_roles = true;
2944                 $create_custom_profile_fields = true;
2945                 $create_tags = true;
2946                 $create_preferences = true;
2948                 //If we are restoring course users and it isn't a course user
2949                 if ($restore->users == 1 and !$is_course_user) {
2950                     //If only restoring course_users and user isn't a course_user, inform to $backup_ids
2951                     $status = backup_putid($restore->backup_unique_code,"user",$userid,null,'notincourse');
2952                     $create_user = false;
2953                     $create_roles = false;
2954                     $create_custom_profile_fields = false;
2955                     $create_tags = false;
2956                     $create_preferences = false;
2957                 }
2959                 if ($user_exists and $create_user) {
2960                     //If user exists mark its newid in backup_ids (the same than old)
2961                     $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,'exists');
2962                     $create_user = false;
2963                     $create_custom_profile_fields = false;
2964                     $create_tags = false;
2965                     $create_preferences = false;
2966                 }
2968                 //Here, if create_user, do it
2969                 if ($create_user) {
2970                     //Unset the id because it's going to be inserted with a new one
2971                     unset ($user->id);
2973                 /// Disable pictures based on global setting or existing empty value (old backups can contain wrong empties)
2974                     if (!empty($CFG->disableuserimages) || empty($user->picture)) {
2975                         $user->picture = 0;
2976                     }
2978                     //We need to analyse the AUTH field to recode it:
2979                     //   - if the field isn't set, we are in a pre 1.4 backup and $CFG->registerauth will decide
2980                     //   - if the auth isn't enabled in target site, $CFG->registerauth will decide
2981                     //   - finally, if the auth resulting isn't enabled, default to 'manual'
2982                     if (empty($user->auth) || !is_enabled_auth($user->auth)) {
2983                         if ($CFG->registerauth == 'email') {
2984                             $user->auth = 'email';
2985                         } else {
2986                             $user->auth = 'manual';
2987                         }
2988                     }
2989                     if (!is_enabled_auth($user->auth)) { // Final auth check verify, default to manual if not enabled
2990                         $user->auth = 'manual';
2991                     }
2993                     // Now that we know the auth method, for users to be created without pass
2994                     // if password handling is internal and reset password is available
2995                     // we set the password to "restored" (plain text), so the login process
2996                     // will know how to handle that situation in order to allow the user to
2997                     // recover the password. MDL-20846
2998                     if (empty($user->password)) { // Only if restore comes without password
2999                         if (!array_key_exists($user->auth, $authcache)) { // Not in cache
3000                             $userauth = new stdClass();
3001                             $authplugin = get_auth_plugin($user->auth);
3002                             $userauth->preventpassindb = $authplugin->prevent_local_passwords();
3003                             $userauth->isinternal      = $authplugin->is_internal();
3004                             $userauth->canresetpwd     = $authplugin->can_reset_password();
3005                             $authcache[$user->auth] = $userauth;
3006                         } else {
3007                             $userauth = $authcache[$user->auth]; // Get from cache
3008                         }
3010                         // Most external plugins do not store passwords locally
3011                         if (!empty($userauth->preventpassindb)) {
3012                             $user->password = 'not cached';
3014                         // If Moodle is responsible for storing/validating pwd and reset functionality is available, mark
3015                         } else if ($userauth->isinternal and $userauth->canresetpwd) {
3016                             $user->password = 'restored';
3017                         }
3018                     }
3020                     //We need to process the POLICYAGREED field to recalculate it:
3021                     //    - if the destination site is different (by wwwroot) reset it.
3022                     //    - if the destination site is the same (by wwwroot), leave it unmodified
3024                     if (!backup_is_same_site($restore)) {
3025                         $user->policyagreed = 0;
3026                     } else {
3027                         //Nothing to do, we are in the same server
3028                     }
3030                     //Check if the theme exists in destination server
3031                     $themes = get_list_of_themes();
3032                     if (!in_array($user->theme, $themes)) {
3033                         $user->theme = '';
3034                     }
3036                     //set time created
3037                     if (empty($user->timecreated)) {
3038                         $user->timecreated = time();
3039                     }
3041                     //We are going to create the user
3042                     //The structure is exactly as we need
3044                     $newid = $DB->insert_record("user", $user);
3045                     //Put the new id
3046                     $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,"new");
3047                 }
3049                 ///TODO: This seccion is to support pre 1.7 course backups, using old roles
3050                 ///      teacher, coursecreator, student.... providing a basic mapping to new ones.
3051                 ///      Someday we'll drop support for them and this section will be safely deleted (2.0?)
3052                 //Here, if create_roles, do it as necessary
3053                 if ($create_roles) {
3054                     //Get the newid and current info from backup_ids
3055                     $data = backup_getid($restore->backup_unique_code,"user",$userid);
3056                     $newid = $data->new_id;
3057                     $currinfo = $data->info.",";
3059                     //Now, depending of the role, create records in user_studentes and user_teacher
3060                     //and/or mark it in backup_ids
3062                     if ($is_admin) {
3063                         //If the record (user_admins) doesn't exists
3064                         //Only put status in backup_ids
3065                         $currinfo = $currinfo."admin,";
3066                         $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3067                     }
3068                     if ($is_coursecreator) {
3069                         //If the record (user_coursecreators) doesn't exists
3070                         //Only put status in backup_ids
3071                         $currinfo = $currinfo."coursecreator,";
3072                         $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3073                     }
3074                     if ($is_needed) {
3075                         //Only put status in backup_ids
3076                         $currinfo = $currinfo."needed,";
3077                         $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3078                     }
3079                     if ($is_teacher) {
3080                         //If the record (teacher) doesn't exists
3081                         //Put status in backup_ids
3082                         $currinfo = $currinfo."teacher,";
3083                         $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3084                         //Set course and user
3085                         $user->roles['teacher']->course = $restore->course_id;
3086                         $user->roles['teacher']->userid = $newid;
3088                         //Need to analyse the enrol field
3089                         //    - if it isn't set, set it to $CFG->enrol
3090                         //    - if we are in a different server (by wwwroot), set it to $CFG->enrol
3091                         //    - if we are in the same server (by wwwroot), maintain it unmodified.
3092                         if (empty($user->roles['teacher']->enrol)) {
3093                             $user->roles['teacher']->enrol = $CFG->enrol;
3094                         } else if (!backup_is_same_site($restore)) {
3095                             $user->roles['teacher']->enrol = $CFG->enrol;
3096                         } else {
3097                             //Nothing to do. Leave it unmodified
3098                         }
3100                         $rolesmapping = $restore->rolesmapping;
3101                         $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
3102                         if ($user->roles['teacher']->editall) {
3103                             role_assign($rolesmapping['defaultteacheredit'],
3104                                         $newid,
3105                                         0,
3106                                         $context->id,
3107                                         $user->roles['teacher']->timestart,
3108                                         $user->roles['teacher']->timeend,
3109                                         0,
3110                                         $user->roles['teacher']->enrol);
3112                             // editting teacher
3113                         } else {
3114                             // non editting teacher
3115                             role_assign($rolesmapping['defaultteacher'],
3116                                         $newid,
3117                                         0,
3118                                         $context->id,
3119                                         $user->roles['teacher']->timestart,
3120                                         $user->roles['teacher']->timeend,
3121                                         0,
3122                                         $user->roles['teacher']->enrol);
3123                         }
3124                     }
3125                     if ($is_student) {
3127                         //Put status in backup_ids
3128                         $currinfo = $currinfo."student,";
3129                         $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3130                         //Set course and user
3131                         $user->roles['student']->course = $restore->course_id;
3132                         $user->roles['student']->userid = $newid;
3134                         //Need to analyse the enrol field
3135                         //    - if it isn't set, set it to $CFG->enrol
3136                         //    - if we are in a different server (by wwwroot), set it to $CFG->enrol
3137                         //    - if we are in the same server (by wwwroot), maintain it unmodified.
3138                         if (empty($user->roles['student']->enrol)) {
3139                             $user->roles['student']->enrol = $CFG->enrol;
3140                         } else if (!backup_is_same_site($restore)) {
3141                             $user->roles['student']->enrol = $CFG->enrol;
3142                         } else {
3143                             //Nothing to do. Leave it unmodified
3144                         }
3145                         $rolesmapping = $restore->rolesmapping;
3146                         $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
3148                         role_assign($rolesmapping['defaultstudent'],
3149                                     $newid,
3150                                     0,
3151                                     $context->id,
3152                                     $user->roles['student']->timestart,
3153                                     $user->roles['student']->timeend,
3154                                     0,
3155                                     $user->roles['student']->enrol);
3157                     }
3158                     if (!$is_course_user) {
3159                         //If the record (user) doesn't exists
3160                         if (!$DB->record_exists("user", array("id"=>$newid))) {
3161                             //Put status in backup_ids
3162                             $currinfo = $currinfo."user,";
3163                             $status = backup_putid($restore->backup_unique_code,"user",$userid,$newid,$currinfo);
3164                         }
3165                     }
3166                 }
3168             /// Here, if create_custom_profile_fields, do it as necessary
3169                 if ($create_custom_profile_fields) {
3170                     if (isset($user->user_custom_profile_fields)) {
3171                         foreach($user->user_custom_profile_fields as $udata) {
3172                         /// If the profile field has data and the profile shortname-datatype is defined in server
3173                             if ($udata->field_data) {
3174                                 if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
3175                                 /// Insert the user_custom_profile_field
3176                                     $rec = new object();
3177                                     $rec->userid = $newid;
3178                                     $rec->fieldid = $field->id;
3179                                     $rec->data    = $udata->field_data;
3180                                     $DB->insert_record('user_info_data', $rec);
3181                                 }
3182                             }
3183                         }
3184                     }
3185                 }
3187             /// Here, if create_tags, do it as necessary
3188                 if ($create_tags) {