PageRenderTime 72ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/hotpot/restorelib.php

https://bitbucket.org/ceu/moodle_demo
PHP | 535 lines | 380 code | 19 blank | 136 comment | 92 complexity | 01272f10cac7f1ecd5a17bb58066d28d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1
  1. <?php
  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) |git
  23. // | |
  24. // +-------+-------+
  25. // |
  26. // hotpot_strings
  27. // (UL, pk->id)
  28. //
  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
  35. //
  36. //-----------------------------------------------------------
  37. require_once ("$CFG->dirroot/mod/hotpot/lib.php");
  38. function hotpot_restore_mods($mod, $restore) {
  39. //This function restores a single hotpot activity
  40. // This function is called by "restore_create_modules" (in "backup/restorelib.php")
  41. // which is called by "backup/restore_execute.html" (included by "backup/restore.php")
  42. // $mod is an object
  43. // id : id field in 'modtype' table
  44. // modtype : 'hotpot'
  45. // $restore is an object
  46. // backup_unique_code : xxxxxxxxxx
  47. // file : '/full/path/to/backupfile.zip'
  48. // mods : an array of $modinfo's (see below)
  49. // restoreto : See RESTORETO_XXX constants in backup/lib.php
  50. // users : 0=all, 1=course, 2=none
  51. // logs : 0=no, 1=yes
  52. // user_files : 0=no, 1=yes
  53. // course_files : 0=no, 1=yes
  54. // course_id : id of course into which data is to be restored
  55. // deleting : true if 'restoreto'==RESTORETO_NEW_COURSE, otherwise false
  56. // original_wwwroot : 'http://your.server.com/moodle'
  57. // $modinfo is an array
  58. // 'modname' : array( 'restore'=> 0=no 1=yes, 'userinfo' => 0=no 1=yes)
  59. global $CFG;
  60. $status = true;
  61. // get course module data this hotpot activity
  62. $data = backup_getid($restore->backup_unique_code, 'hotpot', $mod->id);
  63. if ($data) {
  64. // $data is an object
  65. // backup_code => xxxxxxxxxx,
  66. // table_name => 'hotpot',
  67. // old_id => xxx,
  68. // new_id => NULL,
  69. // info => xml tree array of info backed up for this hotpot activity
  70. $xml = &$data->info['MOD']['#'];
  71. $table = 'hotpot';
  72. $foreign_keys = array('course' => $restore->course_id);
  73. $more_restore = '';
  74. // print a message after each hotpot is backed up
  75. if (!defined('RESTORE_SILENTLY')) {
  76. $more_restore .= 'print "<li>".get_string("modulename", "hotpot")." &quot;".format_string(stripslashes($record->name),true)."&quot;</li>";';
  77. }
  78. $more_restore .= 'backup_flush(300);';
  79. if (function_exists('restore_userdata_selected')) {
  80. // Moodle >= 1.6
  81. $restore_userdata_selected = restore_userdata_selected($restore, 'hotpot', $mod->id);
  82. } else {
  83. // Moodle <= 1.5
  84. $restore_userdata_selected = $restore->mods['hotpot']->userinfo;
  85. }
  86. if ($restore_userdata_selected) {
  87. $has_details = false;
  88. if (isset($xml["ATTEMPT_DATA"]["0"]["#"]["ATTEMPT"]["0"]["#"]["DETAILS"]["0"]["#"])) {
  89. $details = trim($xml["ATTEMPT_DATA"]["0"]["#"]["ATTEMPT"]["0"]["#"]["DETAILS"]["0"]["#"]);
  90. if ($details<>'' && $details<>'<?xml version="1.0"?><hpjsresult><fields></fields></hpjsresult>') {
  91. $has_details = true;
  92. }
  93. }
  94. if ($has_details && empty($xml["STRING_DATA"]) && empty($xml["QUESTION_DATA"])) {
  95. // HotPot v2.0.x (regenerate questions, responses and strings from attempt details)
  96. $more_restore .= '$status = hotpot_restore_attempts($restore, $status, $xml, $record, true);';
  97. } else {
  98. // HotPot v2.1+
  99. $more_restore .= '$status = hotpot_restore_strings($restore, $status, $xml, $record);';
  100. $more_restore .= '$status = hotpot_restore_questions($restore, $status, $xml, $record);';
  101. $more_restore .= '$status = hotpot_restore_attempts($restore, $status, $xml, $record);';
  102. }
  103. }
  104. // if necessary, adjust HotPot date/time fields and write to restorelog
  105. if ($restore->course_startdateoffset) {
  106. restore_log_date_changes('Hotpot', $restore, $xml, array('TIMEOPEN', 'TIMECLOSE', 'TIMECREATED', 'TIMEMODIFIED'));
  107. }
  108. $status = hotpot_restore_records(
  109. $restore, $status, $xml, $table, $foreign_keys, $more_restore
  110. );
  111. }
  112. return $status;
  113. }
  114. function hotpot_restore_strings(&$restore, $status, &$xml, &$record) {
  115. // $xml is an XML tree for a hotpot record
  116. // $record is the newly added hotpot record
  117. return hotpot_restore_records(
  118. $restore, $status, $xml, 'hotpot_strings', array(), '', 'STRING_DATA', 'STRING', 'md5key'
  119. );
  120. }
  121. function hotpot_restore_questions(&$restore, $status, &$xml, &$record) {
  122. // $xml is an XML tree for a hotpot record
  123. // $record is the newly added hotpot record
  124. $foreignkeys = array(
  125. 'hotpot'=>$record->id,
  126. 'text'=>'hotpot_strings'
  127. );
  128. return hotpot_restore_records(
  129. $restore, $status, $xml, 'hotpot_questions', $foreignkeys, '', 'QUESTION_DATA', 'QUESTION'
  130. );
  131. }
  132. function hotpot_restore_attempts(&$restore, $status, &$xml, &$record, $hotpot_v20=false) {
  133. // $xml is an XML tree for a hotpot record
  134. // $record is the newly added hotpot record
  135. $foreignkeys = array(
  136. 'userid'=>'user',
  137. 'hotpot'=>$record->id,
  138. );
  139. $more_restore = '';
  140. $more_restore .= 'hotpot_restore_details($restore, $status, $xml, $record);';
  141. if ($hotpot_v20) {
  142. // HotPot v2.0.x (regenerate questions and responses from details)
  143. $more_restore .= '$record->details=stripslashes($record->details);';
  144. $more_restore .= 'hotpot_add_attempt_details($record);'; // see "hotpot/lib.php"
  145. } else {
  146. // HotPot v2.1+
  147. $more_restore .= '$status = hotpot_restore_responses($restore, $status, $xml, $record);';
  148. // save clickreportid (to be updated it later)
  149. $more_restore .= 'if (!empty($record->clickreportid)) {';
  150. $more_restore .= '$GLOBALS["hotpot_backup_clickreportids"][$record->id]=$record->clickreportid;';
  151. $more_restore .= '}';
  152. // initialize global array to store clickreportids
  153. $GLOBALS["hotpot_backup_clickreportids"] = array();
  154. }
  155. $status = hotpot_restore_records(
  156. $restore, $status, $xml, 'hotpot_attempts', $foreignkeys, $more_restore, 'ATTEMPT_DATA', 'ATTEMPT'
  157. );
  158. if ($hotpot_v20) {
  159. if ($status) {
  160. global $CFG;
  161. // based on code in "mod/hotpot/db/update_to_v2.php"
  162. execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=1 WHERE hotpot=$record->id AND timefinish=0 AND score IS NULL", false);
  163. execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=3 WHERE hotpot=$record->id AND timefinish>0 AND score IS NULL", false);
  164. execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=4 WHERE hotpot=$record->id AND timefinish>0 AND score IS NOT NULL", false);
  165. execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET clickreportid=id WHERE hotpot=$record->id AND clickreportid IS NULL", false);
  166. }
  167. } else {
  168. $status = hotpot_restore_clickreportids($restore, $status);
  169. unset($GLOBALS["hotpot_backup_clickreportids"]); // tidy up
  170. }
  171. return $status;
  172. }
  173. function hotpot_restore_clickreportids(&$restore, $status) {
  174. // update clickreport ids, if any
  175. global $CFG;
  176. foreach ($GLOBALS["hotpot_backup_clickreportids"] as $id=>$clickreportid) {
  177. if ($status) {
  178. $attempt_record = backup_getid($restore->backup_unique_code, 'hotpot_attempts', $clickreportid);
  179. if ($attempt_record) {
  180. $new_clickreportid = $attempt_record->new_id;
  181. $status = execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET clickreportid=$new_clickreportid WHERE id=$id", false);
  182. } else {
  183. // New clickreport id could not be found
  184. if (!defined('RESTORE_SILENTLY')) {
  185. print "<ul><li>New clickreportid could not be found: attempt id=$id, clickreportid=$clickreportid</li></ul>";
  186. }
  187. $status = false;
  188. }
  189. }
  190. }
  191. return $status;
  192. }
  193. function hotpot_restore_responses(&$restore, $status, &$xml, &$record) {
  194. // $xml is an XML tree for an attempt record
  195. // $record is the newly added attempt record
  196. $foreignkeys = array(
  197. 'attempt'=>$record->id,
  198. 'question'=>'hotpot_questions',
  199. 'correct'=>'hotpot_strings',
  200. 'wrong'=>'hotpot_strings',
  201. 'ignored'=>'hotpot_strings'
  202. );
  203. return hotpot_restore_records(
  204. $restore, $status, $xml, 'hotpot_responses', $foreignkeys, '', 'RESPONSE_DATA', 'RESPONSE'
  205. );
  206. }
  207. function hotpot_restore_details(&$restore, $status, &$xml, &$record) {
  208. // $xml is an XML tree for an attempt record
  209. // $record is the newly added attempt record
  210. if (empty($record->details)) {
  211. $status = true;
  212. } else {
  213. $details = new stdClass();
  214. $details->attempt = $record->id;
  215. $details->details = $record->details;
  216. if (insert_record('hotpot_details', $details)) {
  217. $status = true;
  218. } else {
  219. if (!defined('RESTORE_SILENTLY')) {
  220. print "<ul><li>Details record could not be updated: attempt=$record->attempt</li></ul>";
  221. }
  222. $status = false;
  223. }
  224. }
  225. return $status;
  226. }
  227. function hotpot_restore_records(&$restore, $status, &$xml, $table, $foreign_keys, $more_restore='', $records_TAG='', $record_TAG='', $secondary_key='') {
  228. // general purpose function to restore a group of records
  229. // $restore : (see "hotpot_restore_mods" above)
  230. // $xml : an XML tree (or sub-tree)
  231. // $records_TAG : (optional) the name of an XML tag which starts a block of records
  232. // If no $records_TAG is specified, $xml is assumed to be a block of records
  233. // $record_TAG : (optional) the name of an XML tag which starts a single record
  234. // If no $record_TAG is specified, the block of records is assumed to be a single record
  235. // other parameters are explained in "hotpot_restore_record" below
  236. $i = 0; // index for $records_TAG
  237. do {
  238. unset($xml_records);
  239. if ($records_TAG) {
  240. if (isset($xml[$records_TAG][$i]['#'])) {
  241. $xml_records = &$xml[$records_TAG][$i]['#'];
  242. }
  243. } else {
  244. if ($i==0) {
  245. $xml_records = &$xml;
  246. }
  247. }
  248. if (isset($xml_records)) {
  249. $ii = 0; // index for $record_TAG
  250. do {
  251. unset($xml_record);
  252. if ($record_TAG) {
  253. if (isset($xml_records[$record_TAG][$ii]['#'])) {
  254. $xml_record = &$xml_records[$record_TAG][$ii]['#'];
  255. }
  256. } else {
  257. if ($ii==0) {
  258. $xml_record = &$xml_records;
  259. }
  260. }
  261. if (isset($xml_record)) {
  262. $status = hotpot_restore_record(
  263. $restore, $status, $xml_record, $table, $foreign_keys, $more_restore, $secondary_key
  264. );
  265. }
  266. $ii++;
  267. } while ($status && isset($xml_record));
  268. }
  269. $i++;
  270. } while ($status && isset($xml_records));
  271. return $status;
  272. }
  273. function hotpot_restore_record(&$restore, $status, &$xml, $table, $foreign_keys, $more_restore, $secondary_key) {
  274. // general purpose function to restore a single record
  275. // $restore : (see "hotpot_restore_mods" above)
  276. // $status : current status of backup (true or false)
  277. // $xml : XML tree of current record
  278. // $table : name of Moodle database table to restore to
  279. // $foreign_keys : array of foreign keys, if any, specifed as $key=>$value
  280. // $key : the name of a field in the current $record
  281. // $value : if $value is numeric, then $record->$key is set to $value.
  282. // Otherwise $value is assumed to be a table name and $record->$key
  283. // is treated as a comma separated list of ids in that table
  284. // $more_restore : optional PHP code to be eval(uated) for each record
  285. // $secondary_key :
  286. // the name of the secondary key field, if any, in the current $record.
  287. // If this field is specified, then the current record will only be added
  288. // if the $record->$secondarykey value does not already exist in $table
  289. // maintain a cache of info on table columns
  290. static $table_columns = array();
  291. if (empty($table_columns[$table])) {
  292. global $CFG, $db;
  293. $table_columns[$table] = $db->MetaColumns("$CFG->prefix$table");
  294. }
  295. // get values for fields in this record
  296. $record = new stdClass();
  297. $TAGS = array_keys($xml);
  298. foreach ($TAGS as $TAG) {
  299. $value = $xml[$TAG][0]['#'];
  300. if (is_string($value)) {
  301. $tag = strtolower($TAG);
  302. $record->$tag = backup_todb($value);
  303. }
  304. }
  305. // update foreign keys, if any
  306. $ok = true;
  307. foreach ($foreign_keys as $key=>$value) {
  308. if (is_numeric($value)) {
  309. $record->$key = $value;
  310. } else {
  311. $key_table = $value;
  312. $new_ids = array();
  313. if (isset($record->$key)) {
  314. $old_ids = explode(',', $record->$key);
  315. foreach ($old_ids as $old_id) {
  316. if (empty($old_id)) {
  317. // do nothing
  318. } else {
  319. $key_record = backup_getid($restore->backup_unique_code, $key_table, $old_id);
  320. if ($key_record) {
  321. $new_ids[] = $key_record->new_id;
  322. } else {
  323. // foreign key could not be updated
  324. if (!defined('RESTORE_SILENTLY')) {
  325. print "<ul><li><b>Warning:</b><br/>Foreign key could not be updated:<br/>";
  326. print "'$key_table' record (old id=$old_id) is missing from backup data<br/>";
  327. print "'$table' record ";
  328. if (isset($record->id)) {
  329. print "(old id=$record->id) ";
  330. }
  331. print "was not restored</li></ul>";
  332. }
  333. $ok = false;
  334. }
  335. }
  336. }
  337. }
  338. $record->$key = implode(',', $new_ids);
  339. }
  340. }
  341. // set md5 keys if necessary (restoring from Moodle<1.6)
  342. if ($table=='hotpot_questions' && empty($record->md5key)) {
  343. $record->md5key = md5($record->name);
  344. }
  345. if ($table=='hotpot_strings' && empty($record->md5key)) {
  346. $record->md5key = md5($record->string);
  347. }
  348. // check all "not null" fields have been set
  349. if (isset($table_columns[$table]) && is_array($table_columns[$table])) {
  350. foreach ($table_columns[$table] as $column) {
  351. if ($column->not_null) {
  352. $name = $column->name;
  353. if ($name=='id' || (isset($record->$name) && ! is_null($record->$name))) {
  354. // do nothing
  355. } else if (isset($column->default_value)) {
  356. $record->$name = $column->default_value;
  357. } else if (preg_match('/int|decimal|double|float|time|year/i', $column->type)) {
  358. $record->$name = 0;
  359. } else {
  360. $record->$name = '';
  361. }
  362. }
  363. }
  364. }
  365. // check everything is OK so far
  366. if ($ok) {
  367. // store old record id, if necessary
  368. if (isset($record->id)) {
  369. $record->old_id = $record->id;
  370. unset($record->id);
  371. }
  372. // if there is a secondary key field ...
  373. if ($secondary_key) {
  374. // check to see if a record with the same value already exists
  375. $key_records = get_records($table, $secondary_key, $record->$secondary_key);
  376. if ($key_records) {
  377. // set new record id from already existing record
  378. $key_record = reset($key_records);
  379. $record->id = $key_record->id;
  380. }
  381. }
  382. if (empty($record->id)) {
  383. // add the $record (and get new id)
  384. $record->id = insert_record($table, $record);
  385. }
  386. // check $record was added (or found)
  387. if (is_numeric($record->id)) {
  388. // if there was an old id, save a mapping to the new id
  389. if (isset($record->old_id)) {
  390. backup_putid($restore->backup_unique_code, $table, $record->old_id, $record->id);
  391. }
  392. } else {
  393. // failed to add (or find) $record
  394. if (!defined('RESTORE_SILENTLY')) {
  395. print "<ul><li>Record could not be added: table=$table</li></ul>";
  396. }
  397. $status = false;
  398. }
  399. // restore related records, if required
  400. if ($more_restore) {
  401. eval($more_restore);
  402. }
  403. }
  404. return $status;
  405. }
  406. //This function returns a log record with all the necessay transformations
  407. //done. It's used by restore_log_module() to restore modules log.
  408. function hotpot_restore_logs($restore, $log) {
  409. // assume the worst
  410. $status = false;
  411. switch ($log->action) {
  412. case "add":
  413. case "update":
  414. case "view":
  415. if ($log->cmid) {
  416. //Get the new_id of the module (to recode the info field)
  417. $mod = backup_getid($restore->backup_unique_code, $log->module, $log->info);
  418. if ($mod) {
  419. $log->url = "view.php?id=".$log->cmid;
  420. $log->info = $mod->new_id;
  421. $status = true;
  422. }
  423. }
  424. break;
  425. case "view all":
  426. $log->url = "index.php?id=".$log->course;
  427. $status = true;
  428. break;
  429. case "report":
  430. if ($log->cmid) {
  431. //Get the new_id of the module (to recode the info field)
  432. $mod = backup_getid($restore->backup_unique_code,$log->module,$log->info);
  433. if ($mod) {
  434. $log->url = "report.php?id=".$log->cmid;
  435. $log->info = $mod->new_id;
  436. $status = true;
  437. }
  438. }
  439. break;
  440. case "attempt":
  441. case "submit":
  442. case "review":
  443. if ($log->cmid) {
  444. //Get the new_id of the module (to recode the info field)
  445. $mod = backup_getid($restore->backup_unique_code,$log->module,$log->info);
  446. if ($mod) {
  447. //Extract the attempt id from the url field
  448. $attemptid = substr(strrchr($log->url,"="),1);
  449. //Get the new_id of the attempt (to recode the url field)
  450. $attempt = backup_getid($restore->backup_unique_code,"hotpot_attempts",$attemptid);
  451. if ($attempt) {
  452. $log->url = "review.php?id=".$log->cmid."&attempt=".$attempt->new_id;
  453. $log->info = $mod->new_id;
  454. $status = true;
  455. }
  456. }
  457. }
  458. break;
  459. default:
  460. // Oops, unknown $log->action
  461. if (!defined('RESTORE_SILENTLY')) {
  462. print "<p>action (".$log->module."-".$log->action.") unknown. Not restored</p>";
  463. }
  464. break;
  465. } // end switch
  466. return $status ? $log : false;
  467. }
  468. function hotpot_decode_content_links($content, $restore) {
  469. $search = '/\$@(HOTPOT)\*([a-z]+)\*([a-z]+)\*([0-9]+)@\$/is';
  470. if (preg_match_all($search, $content, $matches, PREG_OFFSET_CAPTURE)) {
  471. $i_max = count($matches[0]) - 1;
  472. for ($i=$i_max; $i>=0; $i--) {
  473. $start = $matches[0][$i][1];
  474. $length = strlen($matches[0][$i][0]);
  475. $replace = hotpot_decode_content_link(
  476. // $scriptname, $paramname, $paramvalue, $restore
  477. $matches[2][$i][0], $matches[3][$i][0], $matches[4][$i][0], $restore
  478. );
  479. $content = substr_replace($content, $replace, $start, $length);
  480. }
  481. }
  482. return $content;
  483. }
  484. function hotpot_decode_content_link($scriptname, $paramname, $paramvalue, &$restore) {
  485. global $CFG;
  486. $table = '';
  487. switch ($paramname) {
  488. case 'id':
  489. switch ($scriptname) {
  490. case 'index':
  491. $table = 'course';
  492. break;
  493. case 'report':
  494. case 'review':
  495. case 'view':
  496. $table = 'course_modules';
  497. break;
  498. case 'attempt':
  499. $table = 'hotpot_attempts';
  500. break;
  501. }
  502. break;
  503. case 'hp':
  504. case 'hotpotid':
  505. $table = 'hotpot';
  506. break;
  507. }
  508. $new_id = 0;
  509. if ($table) {
  510. if ($rec = backup_getid($restore->backup_unique_code, $table, $paramvalue)) {
  511. $new_id = $rec->new_id;
  512. }
  513. }
  514. return "$CFG->wwwroot/mod/hotpot/$scriptname.php?$paramname=$new_id";
  515. }
  516. ?>