Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / mod / hotpot / restorelib.php
blob9af917445feed8dd07d284f57887d5a98838edc9
1 <?PHP //$Id$
2 //This php script contains all the stuff to restore hotpot mods
3 //-----------------------------------------------------------
4 // This is the "graphical" structure of the hotpot mod:
5 //-----------------------------------------------------------
6 //
7 // hotpot
8 // (CL, pk->id,
9 // fk->course, files)
10 // |
11 // +--------------+---------------+
12 // | |
13 // hotpot_attempts hotpot_questions
14 // (UL, pk->id, (UL, pk->id,
15 // fk->hotpot) fk->hotpot, text)
16 // | | |
17 // +-------------------+----------+ |
18 // | | |
19 // hotpot_details hotpot_responses |
20 // (UL, pk->id, (UL, pk->id, |
21 // fk->attempt) fk->attempt, question, |
22 // correct, wrong, ignored) |
23 // | |
24 // +-------+-------+
25 // |
26 // hotpot_strings
27 // (UL, pk->id)
29 // Meaning: pk->primary key field of the table
30 // fk->foreign key to link with parent
31 // nt->nested field (recursive data)
32 // CL->course level info
33 // UL->user level info
34 // files->table may have files
36 //-----------------------------------------------------------
38 require_once ("$CFG->dirroot/mod/hotpot/lib.php");
40 function hotpot_restore_mods($mod, $restore) {
41 //This function restores a single hotpot activity
43 // This function is called by "restore_create_modules" (in "backup/restorelib.php")
44 // which is called by "backup/restore_execute.html" (included by "backup/restore.php")
45 // $mod is an object
46 // id : id field in 'modtype' table
47 // modtype : 'hotpot'
48 // $restore is an object
49 // backup_unique_code : xxxxxxxxxx
50 // file : '/full/path/to/backupfile.zip'
51 // mods : an array of $modinfo's (see below)
52 // restoreto : 0=existing course (replace), 1=existing course (append), 2=new course
53 // users : 0=all, 1=course, 2=none
54 // logs : 0=no, 1=yes
55 // user_files : 0=no, 1=yes
56 // course_files : 0=no, 1=yes
57 // course_id : id of course into which data is to be restored
58 // deleting : true if 'restoreto'==0, otherwise false
59 // original_wwwroot : 'http://your.server.com/moodle'
60 // $modinfo is an array
61 // 'modname' : array( 'restore'=> 0=no 1=yes, 'userinfo' => 0=no 1=yes)
63 global $CFG;
64 $status = true;
66 // get course module data this hotpot activity
67 $data = backup_getid($restore->backup_unique_code, 'hotpot', $mod->id);
68 if ($data) {
69 // $data is an object
70 // backup_code => xxxxxxxxxx,
71 // table_name => 'hotpot',
72 // old_id => xxx,
73 // new_id => NULL,
74 // info => xml tree array of info backed up for this hotpot activity
75 $xml = &$data->info['MOD']['#'];
76 $table = 'hotpot';
77 $foreign_keys = array('course' => $restore->course_id);
78 $more_restore = '';
79 // print a message after each hotpot is backed up
80 if (!defined('RESTORE_SILENTLY')) {
81 $more_restore .= 'print "<li>".get_string("modulename", "hotpot")." &quot;".format_string(stripslashes($record->name),true)."&quot;</li>";';
83 $more_restore .= 'backup_flush(300);';
84 if (function_exists('restore_userdata_selected')) {
85 // Moodle >= 1.6
86 $restore_userdata_selected = restore_userdata_selected($restore, 'hotpot', $mod->id);
87 } else {
88 // Moodle <= 1.5
89 $restore_userdata_selected = $restore->mods['hotpot']->userinfo;
91 if ($restore_userdata_selected) {
92 $has_details = false;
93 if (isset($xml["ATTEMPT_DATA"]["0"]["#"]["ATTEMPT"]["0"]["#"]["DETAILS"]["0"]["#"])) {
94 $details = trim($xml["ATTEMPT_DATA"]["0"]["#"]["ATTEMPT"]["0"]["#"]["DETAILS"]["0"]["#"]);
95 if ($details<>'' && $details<>'<?xml version="1.0"?><hpjsresult><fields></fields></hpjsresult>') {
96 $has_details = true;
99 if ($has_details && empty($xml["STRING_DATA"]) && empty($xml["QUESTION_DATA"])) {
100 // HotPot v2.0.x (regenerate questions, responses and strings from attempt details)
101 $more_restore .= '$status = hotpot_restore_attempts($restore, $status, $xml, $record, true);';
102 } else {
103 // HotPot v2.1+
104 $more_restore .= '$status = hotpot_restore_strings($restore, $status, $xml, $record);';
105 $more_restore .= '$status = hotpot_restore_questions($restore, $status, $xml, $record);';
106 $more_restore .= '$status = hotpot_restore_attempts($restore, $status, $xml, $record);';
110 // if necessary, adjust HotPot date/time fields and write to restorelog
111 if ($restore->course_startdateoffset) {
112 restore_log_date_changes('Hotpot', $restore, $xml, array('TIMEOPEN', 'TIMECLOSE', 'TIMECREATED', 'TIMEMODIFIED'));
115 $status = hotpot_restore_records(
116 $restore, $status, $xml, $table, $foreign_keys, $more_restore
119 return $status;
121 function hotpot_restore_strings(&$restore, $status, &$xml, &$record) {
122 // $xml is an XML tree for a hotpot record
123 // $record is the newly added hotpot record
124 return hotpot_restore_records(
125 $restore, $status, $xml, 'hotpot_strings', array(), '', 'STRING_DATA', 'STRING', 'string'
128 function hotpot_restore_questions(&$restore, $status, &$xml, &$record) {
129 // $xml is an XML tree for a hotpot record
130 // $record is the newly added hotpot record
131 $foreignkeys = array(
132 'hotpot'=>$record->id,
133 'text'=>'hotpot_strings'
135 return hotpot_restore_records(
136 $restore, $status, $xml, 'hotpot_questions', $foreignkeys, '', 'QUESTION_DATA', 'QUESTION'
139 function hotpot_restore_attempts(&$restore, $status, &$xml, &$record, $hotpot_v20=false) {
140 // $xml is an XML tree for a hotpot record
141 // $record is the newly added hotpot record
142 $foreignkeys = array(
143 'userid'=>'user',
144 'hotpot'=>$record->id,
146 $more_restore = '';
147 $more_restore .= 'hotpot_restore_details($restore, $status, $xml, $record);';
148 if ($hotpot_v20) {
149 // HotPot v2.0.x (regenerate questions and responses from details)
150 $more_restore .= '$record->details=stripslashes($record->details);';
151 $more_restore .= 'hotpot_add_attempt_details($record);'; // see "hotpot/lib.php"
152 } else {
153 // HotPot v2.1+
154 $more_restore .= '$status = hotpot_restore_responses($restore, $status, $xml, $record);';
155 // save clickreportid (to be updated it later)
156 $more_restore .= 'if (!empty($record->clickreportid)) {';
157 $more_restore .= '$GLOBALS["hotpot_backup_clickreportids"][$record->id]=$record->clickreportid;';
158 $more_restore .= '}';
159 // initialize global array to store clickreportids
160 $GLOBALS["hotpot_backup_clickreportids"] = array();
162 $status = hotpot_restore_records(
163 $restore, $status, $xml, 'hotpot_attempts', $foreignkeys, $more_restore, 'ATTEMPT_DATA', 'ATTEMPT'
165 if ($hotpot_v20) {
166 if ($status) {
167 global $CFG;
168 // based on code in "mod/hotpot/db/update_to_v2.php"
169 execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=1 WHERE hotpot=$record->id AND timefinish=0 AND score IS NULL", false);
170 execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=3 WHERE hotpot=$record->id AND timefinish>0 AND score IS NULL", false);
171 execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=4 WHERE hotpot=$record->id AND timefinish>0 AND score IS NOT NULL", false);
172 execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET clickreportid=id WHERE hotpot=$record->id AND clickreportid IS NULL", false);
174 } else {
175 $status = hotpot_restore_clickreportids($restore, $status);
176 unset($GLOBALS["hotpot_backup_clickreportids"]); // tidy up
178 return $status;
180 function hotpot_restore_clickreportids(&$restore, $status) {
181 // update clickreport ids, if any
182 global $CFG;
183 foreach ($GLOBALS["hotpot_backup_clickreportids"] as $id=>$clickreportid) {
184 if ($status) {
185 $attempt_record = backup_getid($restore->backup_unique_code, 'hotpot_attempts', $clickreportid);
186 if ($attempt_record) {
187 $new_clickreportid = $attempt_record->new_id;
188 $status = execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET clickreportid=$new_clickreportid WHERE id=$id", false);
189 } else {
190 // New clickreport id could not be found
191 if (!defined('RESTORE_SILENTLY')) {
192 print "<ul><li>New clickreportid could not be found: attempt id=$id, clickreportid=$clickreportid</li></ul>";
194 $status = false;
198 return $status;
200 function hotpot_restore_responses(&$restore, $status, &$xml, &$record) {
201 // $xml is an XML tree for an attempt record
202 // $record is the newly added attempt record
203 $foreignkeys = array(
204 'attempt'=>$record->id,
205 'question'=>'hotpot_questions',
206 'correct'=>'hotpot_strings',
207 'wrong'=>'hotpot_strings',
208 'ignored'=>'hotpot_strings'
210 return hotpot_restore_records(
211 $restore, $status, $xml, 'hotpot_responses', $foreignkeys, '', 'RESPONSE_DATA', 'RESPONSE'
214 function hotpot_restore_details(&$restore, $status, &$xml, &$record) {
215 // $xml is an XML tree for an attempt record
216 // $record is the newly added attempt record
217 if (empty($record->details)) {
218 $status = true;
219 } else {
220 $details = new stdClass();
221 $details->attempt = $record->id;
222 $details->details = $record->details;
223 if (insert_record('hotpot_details', $details)) {
224 $status = true;
225 } else {
226 if (!defined('RESTORE_SILENTLY')) {
227 print "<ul><li>Details record could not be updated: attempt=$record->attempt</li></ul>";
229 $status = false;
232 return $status;
234 function hotpot_restore_records(&$restore, $status, &$xml, $table, $foreign_keys, $more_restore='', $records_TAG='', $record_TAG='', $secondary_key='') {
235 // general purpose function to restore a group of records
236 // $restore : (see "hotpot_restore_mods" above)
237 // $xml : an XML tree (or sub-tree)
238 // $records_TAG : (optional) the name of an XML tag which starts a block of records
239 // If no $records_TAG is specified, $xml is assumed to be a block of records
240 // $record_TAG : (optional) the name of an XML tag which starts a single record
241 // If no $record_TAG is specified, the block of records is assumed to be a single record
242 // other parameters are explained in "hotpot_restore_record" below
244 $i = 0; // index for $records_TAG
245 do {
246 unset($xml_records);
247 if ($records_TAG) {
248 if (isset($xml[$records_TAG][$i]['#'])) {
249 $xml_records = &$xml[$records_TAG][$i]['#'];
251 } else {
252 if ($i==0) {
253 $xml_records = &$xml;
256 if (isset($xml_records)) {
257 $ii = 0; // index for $record_TAG
258 do {
259 unset($xml_record);
260 if ($record_TAG) {
261 if (isset($xml_records[$record_TAG][$ii]['#'])) {
262 $xml_record = &$xml_records[$record_TAG][$ii]['#'];
264 } else {
265 if ($ii==0) {
266 $xml_record = &$xml_records;
269 if (isset($xml_record)) {
270 $status = hotpot_restore_record(
271 $restore, $status, $xml_record, $table, $foreign_keys, $more_restore, $secondary_key
274 $ii++;
275 } while ($status && isset($xml_record));
277 $i++;
278 } while ($status && isset($xml_records));
279 return $status;
281 function hotpot_restore_record(&$restore, $status, &$xml, $table, $foreign_keys, $more_restore, $secondary_key) {
282 // general purpose function to restore a single record
283 // $restore : (see "hotpot_restore_mods" above)
284 // $status : current status of backup (true or false)
285 // $xml : XML tree of current record
286 // $table : name of Moodle database table to restore to
287 // $foreign_keys : array of foreign keys, if any, specifed as $key=>$value
288 // $key : the name of a field in the current $record
289 // $value : if $value is numeric, then $record->$key is set to $value.
290 // Otherwise $value is assumed to be a table name and $record->$key
291 // is treated as a comma separated list of ids in that table
292 // $more_restore : optional PHP code to be eval(uated) for each record
293 // $secondary_key :
294 // the name of the secondary key field, if any, in the current $record.
295 // If this field is specified, then the current record will only be added
296 // if the $record->$secondarykey value does not already exist in $table
298 // maintain a cache of info on table columns
299 static $table_columns = array();
300 if (empty($table_columns[$table])) {
301 global $CFG, $db;
302 $table_columns[$table] = $db->MetaColumns("$CFG->prefix$table");
305 // get values for fields in this record
306 $record = new stdClass();
307 $TAGS = array_keys($xml);
308 foreach ($TAGS as $TAG) {
309 $value = $xml[$TAG][0]['#'];
310 if (is_string($value)) {
311 $tag = strtolower($TAG);
312 $record->$tag = backup_todb($value);
316 // update foreign keys, if any
317 $ok = true;
318 foreach ($foreign_keys as $key=>$value) {
319 if (is_numeric($value)) {
320 $record->$key = $value;
321 } else {
322 $key_table = $value;
323 $new_ids = array();
324 if (isset($record->$key)) {
325 $old_ids = explode(',', $record->$key);
326 foreach ($old_ids as $old_id) {
327 if (empty($old_id)) {
328 // do nothing
329 } else {
330 $key_record = backup_getid($restore->backup_unique_code, $key_table, $old_id);
331 if ($key_record) {
332 $new_ids[] = $key_record->new_id;
333 } else {
334 // foreign key could not be updated
335 if (!defined('RESTORE_SILENTLY')) {
336 print "<ul><li><b>Warning:</b><br/>Foreign key could not be updated:<br/>";
337 print "'$key_table' record (old id=$old_id) is missing from backup data<br/>";
338 print "'$table' record ";
339 if (isset($record->id)) {
340 print "(old id=$record->id) ";
342 print "was not restored</li></ul>";
344 $ok = false;
349 $record->$key = implode(',', $new_ids);
353 // set md5 keys if necessary (restoring from Moodle<1.6)
354 if ($table=='hotpot_questions' && empty($record->md5key)) {
355 $record->md5key = md5($record->name);
357 if ($table=='hotpot_strings' && empty($record->md5key)) {
358 $record->md5key = md5($record->string);
361 // check all "not null" fields have been set
362 foreach ($table_columns[$table] as $column) {
363 if ($column->not_null) {
364 $name = $column->name;
365 if ($name<>'id' && empty($record->$name)) {
366 if (isset($column->default_value)) {
367 $default = $column->default_value;
368 } else {
369 if (preg_match('/int|decimal|double|float|time|year/i', $column->type)) {
370 $default = 0;
371 } else {
372 $default = '';
375 $record->$name = $default;
380 // check everything is OK so far
381 if ($ok) {
382 // store old record id, if necessary
383 if (isset($record->id)) {
384 $record->old_id = $record->id;
385 unset($record->id);
387 // if there is a secondary key field ...
388 if ($secondary_key) {
389 // check to see if a record with the same value already exists
390 $key_record = get_record($table, $secondary_key, $record->$secondary_key);
391 if ($key_record) {
392 // set new record id from already existing record
393 $record->id = $key_record->id;
396 if (empty($record->id)) {
397 // add the $record (and get new id)
398 $record->id = insert_record($table, $record);
400 // check $record was added (or found)
401 if (is_numeric($record->id)) {
402 // if there was an old id, save a mapping to the new id
403 if (isset($record->old_id)) {
404 backup_putid($restore->backup_unique_code, $table, $record->old_id, $record->id);
406 } else {
407 // failed to add (or find) $record
408 if (!defined('RESTORE_SILENTLY')) {
409 print "<ul><li>Record could not be added: table=$table</li></ul>";
411 $status = false;
413 // restore related records, if required
414 if ($more_restore) {
415 eval($more_restore);
418 return $status;
420 //This function returns a log record with all the necessay transformations
421 //done. It's used by restore_log_module() to restore modules log.
422 function hotpot_restore_logs($restore, $log) {
423 // assume the worst
424 $status = false;
425 switch ($log->action) {
426 case "add":
427 case "update":
428 case "view":
429 if ($log->cmid) {
430 //Get the new_id of the module (to recode the info field)
431 $mod = backup_getid($restore->backup_unique_code, $log->module, $log->info);
432 if ($mod) {
433 $log->url = "view.php?id=".$log->cmid;
434 $log->info = $mod->new_id;
435 $status = true;
438 break;
439 case "view all":
440 $log->url = "index.php?id=".$log->course;
441 $status = true;
442 break;
443 case "report":
444 if ($log->cmid) {
445 //Get the new_id of the module (to recode the info field)
446 $mod = backup_getid($restore->backup_unique_code,$log->module,$log->info);
447 if ($mod) {
448 $log->url = "report.php?id=".$log->cmid;
449 $log->info = $mod->new_id;
450 $status = true;
453 break;
454 case "attempt":
455 case "submit":
456 case "review":
457 if ($log->cmid) {
458 //Get the new_id of the module (to recode the info field)
459 $mod = backup_getid($restore->backup_unique_code,$log->module,$log->info);
460 if ($mod) {
461 //Extract the attempt id from the url field
462 $attemptid = substr(strrchr($log->url,"="),1);
463 //Get the new_id of the attempt (to recode the url field)
464 $attempt = backup_getid($restore->backup_unique_code,"hotpot_attempts",$attemptid);
465 if ($attempt) {
466 $log->url = "review.php?id=".$log->cmid."&attempt=".$attempt->new_id;
467 $log->info = $mod->new_id;
468 $status = true;
472 break;
473 default:
474 // Oops, unknown $log->action
475 if (!defined('RESTORE_SILENTLY')) {
476 print "<p>action (".$log->module."-".$log->action.") unknown. Not restored</p>";
478 break;
479 } // end switch
480 return $status ? $log : false;