PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/hotpot/db/update_to_v2.php

https://bitbucket.org/ceu/moodle_demo
PHP | 1392 lines | 1153 code | 62 blank | 177 comment | 362 complexity | 30d4a50b8990119fc4c4be6ef4d3638d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1
  1. <?PHP
  2. if (file_exists("$CFG->dirroot/lib/ddllib.php")) {
  3. // Moodle 1.8+
  4. include_once "$CFG->dirroot/lib/ddllib.php";
  5. }
  6. function hotpot_update_to_v2_2() {
  7. global $CFG;
  8. $ok = true;
  9. // remove the index on hotpot_questions.name
  10. $table = 'hotpot_questions';
  11. $field = 'name';
  12. if (strtolower($CFG->dbfamily)=='postgres') {
  13. $index = "{$CFG->prefix}{$table}_{$field}_idx";
  14. } else {
  15. $index = "{$table}_{$field}_idx";
  16. }
  17. hotpot_db_delete_index("{$CFG->prefix}$table", $index);
  18. // add new hotpot_questions.md5key field (and index)
  19. $table = 'hotpot_questions';
  20. $field = 'md5key';
  21. $ok = $ok && hotpot_db_update_field_type($table, '', $field, 'VARCHAR', 32, '', 'NOT NULL', '');
  22. $ok = $ok && hotpot_db_add_index($table, $field);
  23. // add new values hotpot_questions.md5key
  24. $table = 'hotpot_questions';
  25. if ($records = get_records($table)) {
  26. foreach ($records as $record) {
  27. $ok = $ok && set_field($table, 'md5key', md5($record->name), 'id', $record->id);
  28. }
  29. }
  30. // remove the index on hotpot_strings.string
  31. $table = 'hotpot_strings';
  32. $field = 'string';
  33. if (strtolower($CFG->dbfamily)=='postgres') {
  34. $index = "{$CFG->prefix}{$table}_{$field}_idx";
  35. } else {
  36. $index = "{$table}_{$field}_idx";
  37. }
  38. hotpot_db_delete_index("{$CFG->prefix}$table", $index);
  39. // add new hotpot_strings.md5key field (and index)
  40. $table = 'hotpot_strings';
  41. $field = 'md5key';
  42. $ok = $ok && hotpot_db_update_field_type($table, '', $field, 'VARCHAR', 32, '', 'NOT NULL', '');
  43. $ok = $ok && hotpot_db_add_index($table, $field);
  44. // add new values hotpot_strings.md5key
  45. $table = 'hotpot_strings';
  46. if ($records = get_records($table)) {
  47. foreach ($records as $record) {
  48. $ok = $ok && set_field($table, 'md5key', md5($record->string), 'id', $record->id);
  49. }
  50. }
  51. return $ok;
  52. }
  53. function hotpot_update_to_v2_1_21() {
  54. global $CFG;
  55. $ok = true;
  56. if (strtolower($CFG->dbfamily)=='postgres') {
  57. // ensure setting of default values on certain fields
  58. // this was originally done in postgres7.php, but was found to be incompatible with PG7 :-(
  59. $table="hotpot";
  60. execute_sql("UPDATE {$CFG->prefix}$table SET studentfeedbackurl = '' WHERE studentfeedbackurl IS NULL");
  61. $ok = $ok && hotpot_db_update_field_type($table, '', 'studentfeedbackurl', 'VARCHAR', 255, '', 'NOT NULL', '');
  62. $ok = $ok && hotpot_db_update_field_type($table, '', 'studentfeedback', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  63. $ok = $ok && hotpot_db_update_field_type($table, '', 'clickreporting', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  64. $table="hotpot_attempts";
  65. $ok = $ok && hotpot_db_update_field_type($table, '', 'score', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  66. $ok = $ok && hotpot_db_update_field_type($table, '', 'penalties', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  67. $ok = $ok && hotpot_db_update_field_type($table, '', 'status', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 1);
  68. $table="hotpot_questions";
  69. $ok = $ok && hotpot_db_update_field_type($table, '', 'type', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  70. $table="hotpot_responses";
  71. $ok = $ok && hotpot_db_update_field_type($table, '', 'score', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  72. $ok = $ok && hotpot_db_update_field_type($table, '', 'weighting', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  73. $ok = $ok && hotpot_db_update_field_type($table, '', 'correct', 'VARCHAR', 255, '', 'NOT NULL', '');
  74. execute_sql("UPDATE {$CFG->prefix}$table SET wrong = '' WHERE wrong IS NULL");
  75. $ok = $ok && hotpot_db_update_field_type($table, '', 'wrong', 'VARCHAR', 255, '', 'NOT NULL', '');
  76. execute_sql("UPDATE {$CFG->prefix}$table SET ignored = '' WHERE ignored IS NULL");
  77. $ok = $ok && hotpot_db_update_field_type($table, '', 'ignored', 'VARCHAR', 255, '', 'NOT NULL', '');
  78. $ok = $ok && hotpot_db_update_field_type($table, '', 'hints', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  79. $ok = $ok && hotpot_db_update_field_type($table, '', 'clues', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  80. $ok = $ok && hotpot_db_update_field_type($table, '', 'checks', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  81. $table="hotpot_strings";
  82. $ok = $ok && hotpot_db_update_field_type($table, '', 'string', 'TEXT', '', '', 'NOT NULL', '');
  83. }
  84. return $ok;
  85. }
  86. function hotpot_update_to_v2_1_18() {
  87. $ok = true;
  88. // remove all orphan records (there shouldn't be any, but if there are they can mess up the utfdbmigrate)
  89. $ok = $ok && hotpot_remove_orphans('hotpot_attempts', 'hotpot', 'hotpot');
  90. $ok = $ok && hotpot_remove_orphans('hotpot_questions', 'hotpot', 'hotpot');
  91. $ok = $ok && hotpot_remove_orphans('hotpot_responses', 'attempt', 'hotpot_attempts');
  92. $ok = $ok && hotpot_remove_orphans('hotpot_responses', 'question', 'hotpot_questions');
  93. $ok = $ok && hotpot_remove_orphans('hotpot_details', 'attempt', 'hotpot_attempts');
  94. // allow negative weighting and scores
  95. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'weighting', '6', false);
  96. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'score', '6', false);
  97. return $ok;
  98. }
  99. function hotpot_remove_orphans($secondarytable, $secondarykeyfield, $primarytable, $primarykeyfield='id') {
  100. global $CFG,$db;
  101. $ok = true;
  102. // save and switch off SQL message echo
  103. $debug = $db->debug;
  104. $db->debug = false;
  105. $records = get_records_sql("
  106. SELECT
  107. t2.$secondarykeyfield, t2.$secondarykeyfield
  108. FROM
  109. {$CFG->prefix}$secondarytable t2 LEFT JOIN {$CFG->prefix}$primarytable t1
  110. ON (t2.$secondarykeyfield = t1.id)
  111. WHERE
  112. t1.$primarykeyfield IS NULL
  113. ");
  114. // restore SQL message echo setting
  115. $db->debug = $debug;
  116. if ($records) {
  117. $keys = implode(',', array_keys($records));
  118. print "removing orphan record(s) from {$CFG->prefix}$secondarytable<br/>";
  119. $ok = $ok && execute_sql("DELETE FROM {$CFG->prefix}$secondarytable WHERE $secondarykeyfield IN ($keys)");
  120. }
  121. return $ok;
  122. }
  123. function hotpot_update_to_v2_1_17() {
  124. global $CFG;
  125. $ok = true;
  126. // convert and disable null values on certain numeric fields
  127. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'starttime', '10');
  128. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'endtime', '10');
  129. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'score', '6');
  130. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'penalties', '6');
  131. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'timestart', '10');
  132. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'timefinish', '10');
  133. $ok = $ok && hotpot_denull_int_field('hotpot_attempts', 'clickreportid', '10');
  134. $ok = $ok && hotpot_denull_int_field('hotpot_questions', 'type', '4');
  135. $ok = $ok && hotpot_denull_int_field('hotpot_questions', 'text', '10');
  136. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'weighting', '6', false);
  137. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'score', '6', false);
  138. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'hints', '6');
  139. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'clues', '6');
  140. $ok = $ok && hotpot_denull_int_field('hotpot_responses', 'checks', '6');
  141. return $ok;
  142. }
  143. function hotpot_denull_int_field($table, $field, $size, $unsigned=true) {
  144. global $CFG;
  145. $ok = true;
  146. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}$table SET $field=0 WHERE $field IS NULL", false);
  147. if ($unsigned) {
  148. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}$table SET $field=0 WHERE $field<0", false);
  149. }
  150. $ok = $ok && hotpot_db_update_field_type($table, $field, $field, 'INTEGER', $size, $unsigned, 'NOT NULL', 0);
  151. return $ok;
  152. }
  153. function hotpot_update_to_v2_1_16() {
  154. global $CFG;
  155. $ok = true;
  156. // remove the questions name index
  157. hotpot_db_delete_index("{$CFG->prefix}hotpot_questions", "hotpot_questions_name_idx");
  158. hotpot_db_delete_index("{$CFG->prefix}hotpot_questions", "{$CFG->prefix}hotpot_questions_name_idx");
  159. // make sure type of 'name' is a text field (not varchar 255)
  160. $ok = $ok && hotpot_db_update_field_type('hotpot_questions', 'name', 'name', 'TEXT', '', '', 'NOT NULL', '');
  161. if (strtolower($CFG->dbfamily)=='mysql') {
  162. // set default values on certain VARCHAR(255) fields
  163. $fields = array(
  164. 'hotpot' => 'studentfeedbackurl',
  165. 'hotpot_responses' => 'correct',
  166. 'hotpot_responses' => 'wrong',
  167. 'hotpot_responses' => 'ignored'
  168. );
  169. foreach ($fields as $table=>$field) {
  170. execute_sql("UPDATE {$CFG->prefix}$table SET $field='' WHERE $field IS NULL");
  171. $ok = $ok && hotpot_db_update_field_type($table, $field, $field, 'VARCHAR', 255, '', 'NOT NULL', '');
  172. }
  173. // remove $CFG->prefix from all index names
  174. $ok = $ok && hotpot_index_remove_prefix('hotpot_attempts', 'hotpot');
  175. $ok = $ok && hotpot_index_remove_prefix('hotpot_attempts', 'userid');
  176. $ok = $ok && hotpot_index_remove_prefix('hotpot_details', 'attempt');
  177. $ok = $ok && hotpot_index_remove_prefix('hotpot_questions', 'hotpot');
  178. $ok = $ok && hotpot_index_remove_prefix('hotpot_responses', 'attempt');
  179. $ok = $ok && hotpot_index_remove_prefix('hotpot_responses', 'question');
  180. }
  181. return $ok;
  182. }
  183. function hotpot_index_remove_prefix($table, $field) {
  184. global $CFG;
  185. hotpot_db_delete_index("{$CFG->prefix}$table", "{$CFG->prefix}{$table}_{$field}_idx");
  186. hotpot_db_delete_index("{$CFG->prefix}$table", "{$table}_{$field}_idx");
  187. return hotpot_db_add_index($table, $field);
  188. }
  189. function hotpot_update_to_v2_1_8() {
  190. global $CFG;
  191. $ok = true;
  192. if (strtolower($CFG->dbfamily)=='postgres') {
  193. // add, delete and rename certain fields and indexes
  194. // that were not correctly setup by postgres7.sql
  195. // hotpot
  196. $table = 'hotpot';
  197. if (hotpot_db_field_exists($table, 'microreporting')) {
  198. $ok = $ok && hotpot_db_update_field_type($table, 'microreporting', 'clickreporting', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', '0');
  199. }
  200. }
  201. return $ok;
  202. }
  203. function hotpot_update_to_v2_1_6() {
  204. global $CFG;
  205. $ok = true;
  206. if (strtolower($CFG->dbfamily)=='postgres') {
  207. // add, delete and rename certain fields and indexes
  208. // that were not correctly setup by postgres7.sql
  209. // hotpot
  210. $table = 'hotpot';
  211. if (hotpot_db_field_exists($table, 'studentfeedback') && !hotpot_db_field_exists($table, 'studentfeedbackurl')) {
  212. $ok = $ok && hotpot_db_update_field_type($table, 'studentfeedback', 'studentfeedbackurl', 'VARCHAR', 255, '', 'NULL');
  213. $ok = $ok && hotpot_db_update_field_type($table, '', 'studentfeedback', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', '0');
  214. }
  215. // hotpot_attempts
  216. $table = 'hotpot_attempts';
  217. $ok = $ok && hotpot_db_remove_field($table, 'groupid');
  218. if (hotpot_db_field_exists($table, 'microreportid') && !hotpot_db_field_exists($table, 'clickreportid')) {
  219. $ok = $ok && hotpot_db_update_field_type($table, 'microreportid', 'clickreportid', 'INTEGER', 10, 'UNSIGNED', 'NULL');
  220. }
  221. }
  222. return $ok;
  223. }
  224. function hotpot_update_to_v2_1_2() {
  225. global $CFG, $db;
  226. $ok = true;
  227. // save and switch off SQL message echo
  228. $debug = $db->debug;
  229. $db->debug = false;
  230. // extract info about attempts by each user on each hotpot (cases where
  231. // the user has only one attempt, or no "in progess" attempt are ignored)
  232. $rs = $db->Execute("
  233. SELECT userid, hotpot, COUNT(*), MIN(status)
  234. FROM {$CFG->prefix}hotpot_attempts
  235. GROUP BY userid, hotpot
  236. HAVING COUNT(*)>1 AND MIN(status)=1
  237. ");
  238. if ($rs && $rs->RecordCount()) {
  239. $records = $rs->GetArray();
  240. // start message to browser
  241. print "adjusting status of ".count($records)." &quot;in progress&quot; attempts ... ";
  242. // loop through records
  243. foreach ($records as $record) {
  244. // get all attempts by this user at this hotpot
  245. $attempts = get_records_sql("
  246. SELECT id, userid, hotpot, score, timestart, timefinish, status
  247. FROM {$CFG->prefix}hotpot_attempts
  248. WHERE userid = ".$record['userid']." AND hotpot=".$record['hotpot']."
  249. ORDER BY timestart DESC, id DESC
  250. ");
  251. unset($previous_timestart);
  252. foreach ($attempts as $attempt) {
  253. // if this attempt has a status of "in progress" and is not
  254. // the most recent one in the group, set the status to "abandoned"
  255. if ($attempt->status==1 && isset($previous_timestart)) {
  256. $values = 'status=3';
  257. if (empty($attempt->score)) {
  258. $values .= ',score=0';
  259. }
  260. if (empty($attempt->timefinish)) {
  261. $values .= ",timefinish=$previous_timestart";
  262. }
  263. execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET $values WHERE id=$attempt->id", false);
  264. print ".";
  265. hotpot_flush(300);
  266. }
  267. $previous_timestart = $attempt->timestart;
  268. } // end foreach $attempts
  269. } // end foreach $records
  270. // finish message to browser
  271. print $ok ? get_string('success') : 'failed';
  272. print "<br />\n";
  273. }
  274. // restore SQL message echo setting
  275. $db->debug = $debug;
  276. return $ok;
  277. }
  278. function hotpot_update_to_v2_1() {
  279. global $CFG, $db;
  280. $ok = true;
  281. // hotpot_questions: reduce size of "type" field to "4"
  282. $ok = $ok && hotpot_db_update_field_type('hotpot_questions', 'type', 'type', 'INTEGER', 4, 'UNSIGNED', 'NULL');
  283. // hotpot_questions: change type of "name" field to "text"
  284. $ok = $ok && hotpot_db_update_field_type('hotpot_questions', 'name', 'name', 'TEXT', '', '', 'NOT NULL', '');
  285. // hotpot_questions: nullify empty and non-numeric (shouldn't be any) values in "text" field
  286. switch (strtolower($CFG->dbfamily)) {
  287. case 'mysql' :
  288. $NOT_REGEXP = 'NOT REGEXP';
  289. break;
  290. case 'postgres' :
  291. $NOT_REGEXP = '!~';
  292. break;
  293. default:
  294. $NOT_REGEXP = '';
  295. break;
  296. }
  297. if ($NOT_REGEXP) {
  298. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}hotpot_questions SET text=NULL WHERE text $NOT_REGEXP '^[0-9]+$'");
  299. }
  300. // hotpot_questions: change type of "text" field to "INT(10)"
  301. $ok = $ok && hotpot_db_update_field_type('hotpot_questions', 'text', 'text', 'INTEGER', 10, 'UNSIGNED', 'NULL');
  302. // hotpot_attempts
  303. // hotpot_attempts: move "details" to separate table
  304. $table = 'hotpot_details';
  305. if (hotpot_db_table_exists($table)) {
  306. // do nothing
  307. } else {
  308. $ok = $ok && hotpot_create_table($table);
  309. switch (strtolower($CFG->dbfamily)) {
  310. case 'mysql' :
  311. case 'postgres' :
  312. $sql = "
  313. INSERT INTO {$CFG->prefix}$table (attempt, details)
  314. SELECT a.id AS attempt, a.details AS details
  315. FROM {$CFG->prefix}hotpot_attempts a
  316. WHERE
  317. a.details IS NOT NULL AND a.details <> ''
  318. AND a.details LIKE '<?xml%' AND a.details LIKE '%</hpjsresult>'
  319. ";
  320. break;
  321. default:
  322. $sql = '';
  323. break;
  324. }
  325. if ($sql) {
  326. $ok = $ok && execute_sql($sql);
  327. }
  328. }
  329. // hotpot_attempts: remove the "details" field
  330. $ok = $ok && hotpot_db_remove_field('hotpot_attempts', 'details');
  331. // hotpot_attempts: create and set status field (1=in-progress, 2=timed-out, 3=abandoned, 4=completed)
  332. $ok = $ok && hotpot_db_update_field_type('hotpot_attempts', '', 'status', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 1);
  333. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=1 WHERE timefinish=0 AND SCORE IS NULL");
  334. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=3 WHERE timefinish>0 AND SCORE IS NULL");
  335. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET status=4 WHERE timefinish>0 AND SCORE IS NOT NULL");
  336. // hotpot_attempts: create and set clickreport fields
  337. $ok = $ok && hotpot_db_update_field_type('hotpot', '', 'clickreporting', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  338. $ok = $ok && hotpot_db_update_field_type('hotpot_attempts', '', 'clickreportid', 'INTEGER', 10, 'UNSIGNED', 'NULL');
  339. $ok = $ok && execute_sql("UPDATE {$CFG->prefix}hotpot_attempts SET clickreportid=id WHERE clickreportid IS NULL");
  340. // hotpot_attempts: create and set studentfeedback field (0=none, 1=formmail, 2=moodleforum, 3=moodlemessaging)
  341. $ok = $ok && hotpot_db_update_field_type('hotpot', '', 'studentfeedback', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', '0');
  342. $ok = $ok && hotpot_db_update_field_type('hotpot', '', 'studentfeedbackurl', 'VARCHAR', 255, '', 'NULL');
  343. // add indexes
  344. $ok = $ok && hotpot_db_add_index('hotpot_attempts', 'hotpot');
  345. $ok = $ok && hotpot_db_add_index('hotpot_attempts', 'userid');
  346. $ok = $ok && hotpot_db_add_index('hotpot_details', 'attempt');
  347. $ok = $ok && hotpot_db_add_index('hotpot_questions', 'hotpot');
  348. $ok = $ok && hotpot_db_add_index('hotpot_responses', 'attempt');
  349. $ok = $ok && hotpot_db_add_index('hotpot_responses', 'question');
  350. // hotpot_string: correct double-encoded HTML entities
  351. $ok = $ok && execute_sql("
  352. UPDATE {$CFG->prefix}hotpot_strings
  353. SET string = REPLACE(string, '&amp;','&')
  354. WHERE string LIKE '%&amp;#%'
  355. AND (string LIKE '<' OR string LIKE '>')
  356. ");
  357. // hotpot_question: remove questions which refer to deleted hotpots
  358. if ($ok) {
  359. // try and get all hotpot records
  360. if ($records = get_records('hotpot')) {
  361. $ids = implode(',', array_keys($records));
  362. $sql = "DELETE FROM {$CFG->prefix}hotpot_questions WHERE hotpot NOT IN ($ids)";
  363. } else {
  364. // remove all question records (because there are no valid hotpot ids)
  365. $sql = "TRUNCATE {$CFG->prefix}hotpot_questions";
  366. }
  367. print "Removing unused question records ...";
  368. execute_sql($sql);
  369. }
  370. if ($ok) {
  371. // remove old 'v6' templates folder (replaced by 'template' folder)
  372. $ds = DIRECTORY_SEPARATOR;
  373. $dir = "mod{$ds}hotpot{$ds}v6";
  374. print "removing old templates ($dir) ... ";
  375. if (hotpot_rm("$CFG->dirroot{$ds}$dir", false)) {
  376. print get_string('success');
  377. } else {
  378. print "failed<br/>Please remove '$CFG->dirroot{$ds}$dir' manually";
  379. }
  380. print "<br />\n";
  381. }
  382. return $ok;
  383. }
  384. function hotpot_update_to_v2_from_v1() {
  385. global $CFG;
  386. $ok = true;
  387. // remove, alter and add fields in database
  388. $table = 'hotpot';
  389. if (hotpot_db_table_exists($table)) {
  390. $ok = $ok && hotpot_update_fields($table);
  391. } else {
  392. $ok = $ok && hotpot_create_table($table);
  393. }
  394. $table = 'hotpot_attempts';
  395. $oldtable = 'hotpot_events';
  396. if (hotpot_db_table_exists($oldtable)) {
  397. $ok = $ok && hotpot_update_fields($oldtable);
  398. $ok = $ok && hotpot_db_append_table($oldtable, $table);
  399. } else {
  400. $ok = $ok && hotpot_create_table($table);
  401. }
  402. // create new tables (from mysql.sql)
  403. $ok = $ok && hotpot_create_table('hotpot_questions');
  404. $ok = $ok && hotpot_create_table('hotpot_responses');
  405. $ok = $ok && hotpot_create_table('hotpot_strings');
  406. // remove redundant scripts
  407. $files = array('coursefiles.php', 'details.php', 'dummy.html', 'hotpot.php', 'hotpot2db.php');
  408. foreach ($files as $file) {
  409. $filepath = "$CFG->dirroot/mod/hotpot/$file";
  410. if (file_exists($filepath)) {
  411. @unlink($filepath); // don't worry about errors
  412. }
  413. }
  414. return $ok;
  415. }
  416. function hotpot_update_to_v2_from_hotpotatoes() {
  417. global $CFG;
  418. $ok = true; // hope for the best!
  419. // check we have the minimum required hotpot module
  420. $minimum = 2005031400;
  421. $module = get_record("modules", "name", "hotpot");
  422. if (empty($module) || $module->version<$minimum) {
  423. if ($module) {
  424. print ("<p>The update to the HotPotatoes module requires at least version $minimum of the HotPot module.</p>");
  425. print ("<p>The current version of the HotPot module on this site is $module->version.</p>");
  426. }
  427. print ("<p>Please install the latest version of the HotPot module and then try the update again.</p>");
  428. $ok = false;
  429. } else {
  430. // arrays to map foreign keys
  431. $new = array();
  432. $new['hotpot'] = array();
  433. $new['attempt'] = array();
  434. $new['question'] = array();
  435. $new['string'] = array();
  436. // save and switch off SQL message echo
  437. global $db;
  438. $debug = $db->debug;
  439. $db->debug = false;
  440. // import hotpotatoes (and save old ids)
  441. $ok = $ok && hotpot_update_fields('hotpotatoes');
  442. $ok = $ok && hotpot_transfer_records('hotpotatoes', 'hotpot', array(), 'hotpot', $new);
  443. // update course modules and logs
  444. $ok = $ok && hotpot_update_course_modules('hotpotatoes', 'hotpot', $new);
  445. // import hotpotatoes_strings (and save old ids)
  446. $ok = $ok && hotpot_transfer_records('hotpotatoes_strings', 'hotpot_strings', array(), 'string', $new);
  447. // import hotpotatoes_attempts (and save old ids)
  448. $ok = $ok && hotpot_transfer_records('hotpotatoes_attempts', 'hotpot_attempts', array('hotpotatoes'=>'hotpot'), 'attempt', $new);
  449. // import hotpotatoes_questions (and save old ids)
  450. $ok = $ok && hotpot_transfer_records('hotpotatoes_questions', 'hotpot_questions', array('hotpotatoes'=>'hotpot'), 'question', $new);
  451. // import hotpotatoes_responses
  452. $ok = $ok && hotpot_transfer_records('hotpotatoes_responses', 'hotpot_responses', array('attempt'=>'attempt', 'question'=>'question'), 'response', $new);
  453. // restore SQL message echo setting
  454. $db->debug = $debug;
  455. // remove the hotpotatoes tables, if the update went ok
  456. if ($ok) {
  457. // hotpot_db_remove_table('hotpotatoes');
  458. // hotpot_db_remove_table('hotpotatoes_attempts');
  459. // hotpot_db_remove_table('hotpotatoes_questions');
  460. // hotpot_db_remove_table('hotpotatoes_responses');
  461. // hotpot_db_remove_table('hotpotatoes_strings');
  462. }
  463. // hide the hotpotatoes module (see admin/modules.php))
  464. if ($ok && ($module = get_record("modules", "name", "hotpotatoes"))) {
  465. set_field("modules", "visible", "0", "id", $module->id);
  466. print '<p>All HotPotatoes activities have been imported to the HotPot module.<br />'."\n";
  467. print 'The HotPotatoes module has been hidden and can safely be deleted from this Moodle site.<br />'."\n";
  468. print ' &nbsp; &nbsp; <a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/modules.php">Configuration -> Modules</A>, then click &quot;Delete&quot; for &quot;Hot Potatoes XML Quiz&quot;</p>'."\n";
  469. }
  470. }
  471. if ($ok) {
  472. print '<p align="center">Thank you for using the HotPotatoes module.<br />';
  473. print 'The HotPotatoes module has been replaced by<br />version 2 of the HotPot module. Enjoy!</p>';
  474. }
  475. return $ok;
  476. }
  477. function hotpot_create_table($table) {
  478. global $CFG;
  479. static $sql;
  480. static $xmldb_file;
  481. // check table does not already exist
  482. if (hotpot_db_table_exists($table)) {
  483. return true;
  484. }
  485. if (! isset($xmldb_file)) { // first time only
  486. if (class_exists('XMLDBFile')) {
  487. $xmldb_file = new XMLDBFile("$CFG->dirroot/mod/hotpot/db/install.xml");
  488. if (! $xmldb_file->fileExists() || !$xmldb_file->loadXMLStructure() || !$xmldb_file->isLoaded()) {
  489. unset($xmldb_file);
  490. }
  491. }
  492. if (empty($xmldb_file)) {
  493. $xmldb_file = false;
  494. }
  495. }
  496. if ($xmldb_file) {
  497. // Moodle 1.8 (and later)
  498. $ok = false;
  499. foreach ($xmldb_file->xmldb_structure->tables as $xmldb_table) {
  500. if ($xmldb_table->name==$table) {
  501. $ok = create_table($xmldb_table);
  502. break;
  503. }
  504. }
  505. return $ok;
  506. }
  507. // Moodle 1.7 (and earlier)
  508. if (! isset($sql)) { // first time only
  509. $sqlfilepath = "$CFG->dirroot/mod/hotpot/db/$CFG->dbtype.sql";
  510. if (file_exists($sqlfilepath)) {
  511. if (function_exists('file_get_contents')) {
  512. $sql = file_get_contents($sqlfilepath);
  513. } else { // PHP < 4.3
  514. $sql = file($sqlfilepath);
  515. if (is_array($sql)) {
  516. $sql = implode('', $sql);
  517. }
  518. }
  519. }
  520. if (empty($sql)) {
  521. $sql = '';
  522. }
  523. }
  524. // extract and execute all CREATE statements relating to this table
  525. if (preg_match_all("/CREATE (TABLE|INDEX)(\s[^;]*)? prefix_{$table}(\s[^;]*)?;/s", $sql, $strings)) {
  526. $ok = true;
  527. foreach ($strings[0] as $string) {
  528. $ok = $ok && modify_database('', $string);
  529. }
  530. return $ok;
  531. }
  532. // table could not be created
  533. return false;
  534. }
  535. function hotpot_transfer_records($oldtable, $table, $foreignkeys, $primarykey, &$new) {
  536. global $db;
  537. $ok = true;
  538. // get the records, if any
  539. if (hotpot_db_table_exists($oldtable) && ($records = get_records($oldtable))) {
  540. // start progress report
  541. $i = 0;
  542. $count = count($records);
  543. hotpot_update_print("Transferring $count records from &quot;$oldtable&quot; to &quot;$table&quot; ... ");
  544. // transfer all $records
  545. foreach ($records as $record) {
  546. switch ($table) {
  547. case 'hotpot' :
  548. $record->summary = addslashes($record->summary);
  549. break;
  550. case 'hotpot_attempts' :
  551. $record->details = addslashes($record->details);
  552. break;
  553. case 'hotpot_questions' :
  554. $record->name = addslashes($record->name);
  555. hotpot_update_string_id_list($table, $record, 'TEXT', $new);
  556. break;
  557. case 'hotpot_responses' :
  558. hotpot_update_string_id_list($table, $record, 'correct', $new);
  559. hotpot_update_string_id_list($table, $record, 'ignored', $new);
  560. hotpot_update_string_id_list($table, $record, 'wrong', $new);
  561. break;
  562. case 'hotpot_strings' :
  563. $record->string = addslashes($record->string);
  564. break;
  565. }
  566. // update foreign keys, if any
  567. foreach ($foreignkeys as $oldkey=>$key) {
  568. // transfer (and update) key
  569. $value = $record->$oldkey;
  570. if (isset($new[$key][$value])) {
  571. $record->$key = $new[$key][$value];
  572. } else {
  573. // foreign key could not be updated
  574. $ok = hotpot_update_print_warning($key, $value, $oldtable, $record->id) && $ok;
  575. unset($record->id);
  576. }
  577. }
  578. if ($ok && isset($record->id)) {
  579. // store and remove old primary key
  580. $id = $record->id;
  581. unset($record->id);
  582. // add the updated record and store the new id
  583. $new[$primarykey][$id] = insert_record($table, $record, true);
  584. // check id is numeric
  585. if (!is_numeric($new[$primarykey][$id])) {
  586. hotpot_update_print("<li>Record could not added to $table table ($oldtable id=$id)</li>\n");
  587. //$ok = false;
  588. }
  589. }
  590. $i++;
  591. hotpot_update_print_progress($i);
  592. }
  593. // finish progress report
  594. hotpot_update_print_ok($ok);
  595. }
  596. return $ok;
  597. }
  598. function hotpot_update_course_modules($oldmodulename, $modulename, &$new) {
  599. $ok = true;
  600. $oldmoduleid = get_field('modules', 'id', 'name', $oldmodulename);
  601. $moduleid = get_field('modules', 'id', 'name', $modulename);
  602. if (is_numeric($oldmoduleid) && is_numeric($moduleid)) {
  603. // get module records
  604. if ($records = get_records('course_modules', 'module', $oldmoduleid)) {
  605. // start progress report
  606. $count = count($records);
  607. hotpot_update_print("Updating $count course modules from &quot;$oldmodulename&quot; to &quot;$modulename&quot; ... ");
  608. // update foreign keys in all $records
  609. foreach ($records as $record) {
  610. // update instance
  611. $instance = $record->instance;
  612. if (isset($new[$modulename][$instance])) {
  613. $record->instance = $new[$modulename][$instance];
  614. } else if ($record->deleted) {
  615. unset($record->id);
  616. } else {
  617. // could not find new id of course module
  618. $ok = hotpot_update_print_warning("$modulename instance", $instance, 'course_modules', $record->id) && $ok;
  619. unset($record->id);
  620. }
  621. // update module id
  622. if ($ok && isset($record->id)) {
  623. $record->module = $moduleid;
  624. $ok = update_record('course_modules', $record);
  625. }
  626. }
  627. // finish progress report
  628. hotpot_update_print_ok($ok);
  629. }
  630. // update logs
  631. $ok = $ok && hotpot_update_logs($oldmodulename, $modulename, $moduleid, $new);
  632. }
  633. return $ok;
  634. }
  635. function hotpot_update_logs($oldmodulename, $modulename, $moduleid, &$new) {
  636. $table = 'log';
  637. $ok = true;
  638. // get log records for the oldmodule
  639. if ($records = get_records($table, 'module', $oldmodulename)) {
  640. // start progress report
  641. $i = 0;
  642. $count = count($records);
  643. hotpot_update_print("Updating $count log records ... ");
  644. // update foreign keys in all $records
  645. foreach ($records as $record) {
  646. // update course module name
  647. $record->module = $modulename;
  648. // check if module id was given (usually it is)
  649. if ($record->cmid) {
  650. // update course module id, if necessary
  651. if (isset($new[$modulename][$record->cmid])) {
  652. $record->cmid = $new[$modulename][$record->cmid];
  653. } else {
  654. // could not update course module id
  655. $ok = hotpot_update_print_warning('cmid', $record->cmid, 'log', $record->id) && $ok;
  656. unset($record->id);
  657. }
  658. // update url and info
  659. switch ($record->action) {
  660. case "add":
  661. case "update":
  662. case "view":
  663. $record->url = "view.php?id=".$record->cmid;
  664. $record->info = $moduleid;
  665. break;
  666. case "view all":
  667. // do nothing
  668. break;
  669. case "report":
  670. $record->url = "report.php?id=".$record->cmid;
  671. $record->info = $moduleid;
  672. break;
  673. case "attempt":
  674. case "submit":
  675. case "review":
  676. $id = substr(strrchr($record->url,"="),1);
  677. if (isset($new->attempt[$id])) {
  678. $id = $new->attempt[$id];
  679. }
  680. $record->url = "review.php?id=".$record->cmid."&attempt=$id";
  681. $record->info = $moduleid;
  682. break;
  683. default:
  684. // unknown log action
  685. $ok = hotpot_update_print_warning('action', $record->action, 'log', $record->id) && $ok;
  686. unset($record->id);
  687. } // end switch
  688. }
  689. if (isset($record->id)) {
  690. $ok = $ok && update_record($table, $record);
  691. }
  692. $i++;
  693. hotpot_update_print_progress($i);
  694. } // end foreach
  695. // finish progress report
  696. hotpot_update_print_ok($ok);
  697. }
  698. return $ok;
  699. }
  700. function hotpot_update_fields($table, $feedback=false) {
  701. global $CFG, $db;
  702. $ok = true;
  703. // check the table exists
  704. if (hotpot_db_table_exists($table)) {
  705. switch ($table) {
  706. case 'hotpot' :
  707. // == ADD ==
  708. hotpot_db_update_field_type($table, '', 'location', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  709. hotpot_db_update_field_type($table, '', 'navigation', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 1);
  710. hotpot_db_update_field_type($table, '', 'outputformat', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 1);
  711. hotpot_db_update_field_type($table, '', 'shownextquiz', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  712. hotpot_db_update_field_type($table, '', 'forceplugins', 'INTEGER', 4, 'UNSIGNED', 'NOT NULL', 0);
  713. hotpot_db_update_field_type($table, '', 'password', 'VARCHAR', 255, '', 'NOT NULL', '');
  714. hotpot_db_update_field_type($table, '', 'subnet', 'VARCHAR', 255, '', 'NOT NULL', '');
  715. // == ALTER ==
  716. hotpot_db_update_field_type($table, 'summary', 'summary', 'TEXT', '', '', 'NOT NULL', '');
  717. hotpot_db_update_field_type($table, 'reference', 'reference', 'VARCHAR', 255, '', 'NOT NULL', '');
  718. // == REMOVE ==
  719. hotpot_db_remove_field($table, 'intro');
  720. hotpot_db_remove_field($table, 'attemptonlast');
  721. hotpot_db_remove_field($table, 'sumgrades');
  722. hotpot_db_set_table_comment($table, 'details about Hot Potatoes quizzes');
  723. break;
  724. case 'hotpot_events' :
  725. // == ADD ==
  726. hotpot_db_update_field_type($table, '', 'hotpot', 'INTEGER', 10, 'UNSIGNED', 'NOT NULL');
  727. hotpot_db_update_field_type($table, '', 'attempt', 'INTEGER', 6, 'UNSIGNED', 'NOT NULL');
  728. hotpot_db_update_field_type($table, '', 'details', 'TEXT', '', '', '', '');
  729. hotpot_db_update_field_type($table, '', 'timestart', 'INTEGER', 10, 'UNSIGNED', 'NOT NULL', 0);
  730. hotpot_db_update_field_type($table, '', 'timefinish', 'INTEGER', 10, 'UNSIGNED', 'NOT NULL', 0);
  731. // == ALTER ==
  732. hotpot_db_update_field_type($table, 'score', 'score', 'INTEGER', 6, 'UNSIGNED', 'NULL');
  733. hotpot_db_update_field_type($table, 'wrong', 'penalties', 'INTEGER', 6, 'UNSIGNED', 'NULL');
  734. hotpot_db_update_field_type($table, 'starttime', 'starttime', 'INTEGER', 10, 'UNSIGNED', 'NULL');
  735. hotpot_db_update_field_type($table, 'endtime', 'endtime', 'INTEGER', 10, 'UNSIGNED', 'NULL');
  736. // save and switch off SQL message echo
  737. $debug = $db->debug;
  738. $db->debug = $feedback;
  739. // get array mapping course module ids to hotpot ids
  740. $hotpotmoduleid = get_field('modules', 'id', 'name', 'hotpot');
  741. $coursemodules = get_records('course_modules', 'module', $hotpotmoduleid, 'id', 'id, instance');
  742. // get all event records
  743. if (hotpot_db_field_exists($table, 'hotpotid')) {
  744. $records = get_records($table, '', '', 'userid,hotpotid,time');
  745. } else {
  746. $records = false; // table has already been updated
  747. }
  748. if ($records) {
  749. $count = count($records);
  750. hotpot_update_print("Updating $count records in $table ... ");
  751. $ids = array_keys($records);
  752. foreach ($ids as $i=>$id) {
  753. // reference to current record
  754. $record = &$records[$id];
  755. // set timestart and timefinish (the times recorded by Moodle)
  756. if (empty($record->timestart) && $record->time) {
  757. $record->timestart = $record->time;
  758. }
  759. if (empty($record->timefinish) && $record->timestart) {
  760. if ($record->starttime && $record->endtime) {
  761. $duration = ($record->endtime - $record->starttime);
  762. } else {
  763. if (($i+1)>=$count) {
  764. $nextrecord = NULL;
  765. } else {
  766. $nextrecord = &$records[$ids[$i+1]];
  767. }
  768. if (isset($nextrecord) && $nextrecord->userid==$record->userid && $nextrecord->hotpotid==$record->hotpotid) {
  769. $duration = $nextrecord->time - $record->time;
  770. } else {
  771. $duration = NULL;
  772. }
  773. }
  774. if (isset($duration)) {
  775. $record->timefinish = $record->timestart + $duration;
  776. }
  777. }
  778. // unset score and penalties, if quiz was abandoned
  779. if (empty($record->endtime) || (empty($record->penalties) && empty($record->score))) {
  780. unset($record->score);
  781. unset($record->penalties);
  782. }
  783. // get last (=previous) record
  784. if ($i==0) {
  785. $lastrecord = NULL;
  786. } else {
  787. $lastrecord = &$records[$ids[$i-1]];
  788. }
  789. // increment or reset $attempt number
  790. if (isset($lastrecord) && $lastrecord->userid==$record->userid && $lastrecord->hotpotid==$record->hotpotid) {
  791. $attempt++;
  792. } else {
  793. $attempt = 1;
  794. }
  795. // set $record->$attempt, if necessary
  796. if (empty($record->attempt) || $record->attempt<$attempt) {
  797. $record->attempt = $attempt;
  798. } else {
  799. $attempt = $record->attempt;
  800. }
  801. // set hotpot id and update record
  802. if (isset($record->hotpotid) && isset($record->id)) {
  803. if (isset($coursemodules[$record->hotpotid])) {
  804. $record->hotpot = $coursemodules[$record->hotpotid]->instance;
  805. hotpot_db_update_record($table, $record, true);
  806. } else {
  807. // hotpotid is invalid (shouldn't happen)
  808. $ok = hotpot_update_print_warning('hotpotid', $record->hotpotid, $table, $record->id) && $ok;
  809. delete_records($table, 'id', $record->id);
  810. }
  811. } else {
  812. // empty record (shouldn't happen)
  813. }
  814. hotpot_update_print_progress($i);
  815. }
  816. // finish progress report
  817. hotpot_update_print_ok($ok);
  818. }
  819. // restore SQL message echo setting
  820. $db->debug = $debug;
  821. // == REMOVE ==
  822. hotpot_db_remove_field($table, 'hotpotid');
  823. hotpot_db_remove_field($table, 'course');
  824. hotpot_db_remove_field($table, 'time');
  825. hotpot_db_remove_field($table, 'event');
  826. hotpot_db_set_table_comment($table, 'details about Hot Potatoes quiz attempts');
  827. break;
  828. case 'hotpotatoes' :
  829. // == ALTER ==
  830. hotpot_db_update_field_type($table, 'intro', 'summary', 'TEXT', '', '', '', 'NULL');
  831. break;
  832. }
  833. }
  834. return $ok;
  835. }
  836. function hotpot_update_string_id_list($table, &$record, $field, &$new) {
  837. $ok = true;
  838. if (isset($record->$field)) {
  839. $oldids = explode(',', $record->$field);
  840. $newids = array();
  841. foreach ($oldids as $id) {
  842. if (isset($new['string'][$id])) {
  843. $newids[] = $new['string'][$id];
  844. } else if (is_numeric($id)) {
  845. // string id could not be updated
  846. $ok = hotpot_update_print_warning("string id in $field", $id, $table, $record->id) && $ok;
  847. } else {
  848. // ignore non-numeric ids (e.g. blanks)
  849. }
  850. }
  851. if ($ok) {
  852. $record->$field = implode(',', $newids);
  853. }
  854. }
  855. return $ok;
  856. }
  857. ///////////////////////////
  858. // print functions
  859. ///////////////////////////
  860. function hotpot_update_print($msg=false, $n=300) {
  861. // this function prints $msg and flush output buffer
  862. if ($msg) {
  863. if (is_string($msg)) {
  864. print $msg;
  865. } else {
  866. print strftime("%X", time());
  867. }
  868. }
  869. // fill output buffer
  870. if ($n) {
  871. print str_repeat(" ", $n);
  872. }
  873. // some browser's require newline to flush
  874. print "\n";
  875. // flush PHP's output buffer
  876. flush();
  877. }
  878. function hotpot_update_print_progress($i) {
  879. if ($i%10==0) {
  880. $msg = '.';
  881. hotpot_update_print($msg);
  882. }
  883. }
  884. function hotpot_update_print_ok($ok) {
  885. if ($ok) {
  886. hotpot_update_print('<font color="green">'.get_string('success')."</font><br />\n");
  887. } else {
  888. hotpot_update_print('<font color="red">'.get_string('error')."</font><br />\n");
  889. }
  890. }
  891. function hotpot_update_print_warning($field, $value, $table, $id) {
  892. hotpot_update_print("<li><b>Warning:</b> invalid $field field (value=$value) in $table (id=$id)</li>\n");
  893. return true;
  894. }
  895. ///////////////////////////
  896. // database functions
  897. ///////////////////////////
  898. function hotpot_db_index_exists($table, $index, $feedback=false) {
  899. global $CFG, $db;
  900. $exists = false;
  901. // save and switch off SQL message echo
  902. $debug = $db->debug;
  903. $db->debug = $feedback;
  904. switch (strtolower($CFG->dbfamily)) {
  905. case 'mysql' :
  906. $rs = $db->Execute("SHOW INDEX FROM `$table`");
  907. if ($rs && $rs->RecordCount()>0) {
  908. $records = $rs->GetArray();
  909. foreach ($records as $record) {
  910. if (isset($record['Key_name']) && $record['Key_name']==$index) {
  911. $exists = true;
  912. break;
  913. }
  914. }
  915. }
  916. break;
  917. case 'postgres' :
  918. $rs = $db->Execute("SELECT relname FROM pg_class WHERE relname = '$index' AND relkind='i'");
  919. if ($rs && $rs->RecordCount()>0) {
  920. $exists = true;
  921. }
  922. break;
  923. }
  924. // restore SQL message echo
  925. $db->debug = $debug;
  926. return $exists;
  927. }
  928. function hotpot_db_delete_index($table, $index, $feedback=false) {
  929. global $CFG, $db;
  930. $ok = true;
  931. // check index exists
  932. if (hotpot_db_index_exists($table, $index)) {
  933. switch (strtolower($CFG->dbfamily)) {
  934. case 'mysql' :
  935. $sql = "ALTER TABLE `$table` DROP INDEX `$index`";
  936. break;
  937. case 'postgres' :
  938. $sql = "DROP INDEX $index";
  939. break;
  940. default: // unknown database type
  941. $sql = '';
  942. break;
  943. }
  944. if ($sql) {
  945. // save and switch off SQL message echo
  946. $debug = $db->debug;
  947. $db->debug = $feedback;
  948. $ok = $db->Execute($sql) ? true : false;
  949. // restore SQL message echo
  950. $db->debug = $debug;
  951. } else { // unknown database type
  952. $ok = false;
  953. }
  954. }
  955. return $ok;
  956. }
  957. function hotpot_db_add_index($table, $field, $length='') {
  958. global $CFG, $db;
  959. if (strtolower($CFG->dbfamily)=='postgres') {
  960. $index = "{$CFG->prefix}{$table}_{$field}_idx";
  961. } else {
  962. // mysql (and others)
  963. $index = "{$table}_{$field}_idx";
  964. }
  965. $table = "{$CFG->prefix}$table";
  966. // delete $index if it already exists
  967. $ok = hotpot_db_delete_index($table, $index);
  968. switch (strtolower($CFG->dbfamily)) {
  969. case 'mysql' :
  970. $ok = $ok && $db->Execute("ALTER TABLE `$table` ADD INDEX `$index` (`$field`)");
  971. break;
  972. case 'postgres' :
  973. $ok = $ok && $db->Execute("CREATE INDEX $index ON $table (\"$field\")");
  974. break;
  975. default: // unknown database type
  976. $ok = false;
  977. break;
  978. }
  979. return $ok;
  980. }
  981. function hotpot_db_table_exists($table, $feedback=false) {
  982. return hotpot_db_object_exists($table, '', $feedback);
  983. }
  984. function hotpot_db_field_exists($table, $field, $feedback=false) {
  985. return
  986. hotpot_db_object_exists($table, '', $feedback) &&
  987. hotpot_db_object_exists($table, $field, $feedback)
  988. ;
  989. }
  990. function hotpot_db_object_exists($table, $field='', $feedback=false) {
  991. global $CFG,$db;
  992. // expand table name
  993. $table = "{$CFG->prefix}$table";
  994. // set $sql
  995. switch (strtolower($CFG->dbfamily)) {
  996. case 'mysql' :
  997. if (empty($field)) {
  998. $sql = "SHOW TABLES LIKE '$table'";
  999. } else {
  1000. $sql = "SHOW COLUMNS FROM `$table` LIKE '$field'";
  1001. }
  1002. break;
  1003. case 'postgres' :
  1004. if (empty($field)) {
  1005. $sql = "SELECT relname FROM pg_class WHERE relname = '$table' AND relkind='r'";
  1006. } else {
  1007. $sql = "
  1008. SELECT attname FROM pg_attribute WHERE attname = '$field'
  1009. AND attrelid = (SELECT oid FROM pg_class WHERE relname = '$table')
  1010. ";
  1011. }
  1012. break;
  1013. }
  1014. // save and switch off SQL message echo
  1015. $debug = $db->debug;
  1016. $db->debug = $feedback;
  1017. // execute sql
  1018. $rs = $db->Execute($sql);
  1019. // restore SQL message echo setting
  1020. $db->debug = $debug;
  1021. // report error if required
  1022. if (empty($rs) && debugging()) {
  1023. notify($db->ErrorMsg()."<br /><br />$sql");
  1024. }
  1025. return ($rs && $rs->RecordCount()>0);
  1026. }
  1027. function hotpot_db_remove_table($table, $feedback=true) {
  1028. global $CFG;
  1029. if (hotpot_db_table_exists($table)) {
  1030. $ok = execute_sql("DROP TABLE {$CFG->prefix}$table", $feedback);
  1031. } else {
  1032. $ok = true;
  1033. }
  1034. return $ok;
  1035. }
  1036. function hotpot_db_rename_table($oldtable, $table, $feedback=true) {
  1037. global $CFG;
  1038. if (hotpot_db_table_exists($oldtable)) {
  1039. $ok = execute_sql("ALTER TABLE {$CFG->prefix}$oldtable RENAME TO {$CFG->prefix}$table", $feedback);
  1040. } else {
  1041. $ok = true;
  1042. }
  1043. return $ok;
  1044. }
  1045. function hotpot_db_append_table($oldtable, $table, $feedback=true) {
  1046. global $CFG, $db;
  1047. if (hotpot_db_table_exists($oldtable)) {
  1048. if (hotpot_db_table_exists($table)) {
  1049. // expand table names
  1050. $table = "{$CFG->prefix}$table";
  1051. $oldtable = "{$CFG->prefix}$oldtable";
  1052. // get field info
  1053. $fields = $db->MetaColumns($table);
  1054. $oldfields = $db->MetaColumns($oldtable);
  1055. $fieldnames = array();
  1056. if (!empty($fields) || !empty($oldfields)) {
  1057. foreach ($fields as $field) {
  1058. if ($field->name!='id' && isset($oldfields[strtoupper($field->name)])) {
  1059. $fieldnames[] = $field->name;
  1060. }
  1061. }
  1062. }
  1063. $fieldnames = implode(',', $fieldnames);
  1064. if (empty($fieldnames)) {
  1065. $ok = false;
  1066. } else {
  1067. switch (strtolower($CFG->dbfamily)) {
  1068. case 'mysql':
  1069. $ok = execute_sql("INSERT INTO `$table` ($fieldnames) SELECT $fieldnames FROM `$oldtable` WHERE 1");
  1070. break;
  1071. case 'postgres':
  1072. $ok = execute_sql("INSERT INTO $table ($fieldnames) SELECT $fieldnames FROM $oldtable");
  1073. break;
  1074. default:
  1075. $ok = false;
  1076. break;
  1077. }
  1078. }
  1079. } else { // $table does not exist
  1080. $ok = hotpot_db_rename_table($oldtable, $table, $feedback);
  1081. }
  1082. } else { // $oldtable does not exist
  1083. $ok = hotpot_db_table_exists($table, $feedback);
  1084. }
  1085. return $ok;
  1086. }
  1087. function hotpot_db_set_table_comment($table, $comment, $feedback=true) {
  1088. global $CFG;
  1089. $ok = true;
  1090. switch (strtolower($CFG->dbfamily)) {
  1091. case 'mysql' :
  1092. $ok = execute_sql("ALTER TABLE {$CFG->prefix}$table COMMENT='$comment'");
  1093. break;
  1094. case 'postgres' :
  1095. $ok = execute_sql("COMMENT ON TABLE {$CFG->prefix}$table IS '$comment'");
  1096. break;
  1097. }
  1098. return $ok;
  1099. }
  1100. function hotpot_db_remove_field($table, $field, $feedback=true) {
  1101. global $CFG;
  1102. if (hotpot_db_field_exists($table, $field)) {
  1103. $ok = execute_sql("ALTER TABLE {$CFG->prefix}$table DROP COLUMN $field", $feedback);
  1104. } else {
  1105. $ok = true;
  1106. }
  1107. return $ok;
  1108. }
  1109. function hotpot_db_update_field_type($table, $oldfield, $field, $type, $size, $unsigned, $notnull, $default=NULL, $after=NULL) {
  1110. $ok = true;
  1111. global $CFG,$db;
  1112. // check validity of arguments, and adjust if necessary
  1113. if ($oldfield && !hotpot_db_field_exists($table, $oldfield)) {
  1114. $oldfield = '';
  1115. }
  1116. if (empty($oldfield) && hotpot_db_field_exists($table, $field)) {
  1117. $oldfield = $field;
  1118. }
  1119. if (is_string($unsigned)) {
  1120. $unsigned = (strtoupper($unsigned)=='UNSIGNED');
  1121. }
  1122. if (is_string($notnull)) {
  1123. $notnull = (strtoupper($notnull)=='NOT NULL');
  1124. }
  1125. if (isset($default)) {
  1126. if (!is_numeric($default) && strtoupper($default)!='NULL' && !preg_match("|^'.*'$|", $default)) {
  1127. $default = "'$default'";
  1128. }
  1129. }
  1130. // set full table name
  1131. $table = "{$CFG->prefix}$table";
  1132. // update the field in the database
  1133. switch (strtolower($CFG->dbfamily)) {
  1134. case 'mysql':
  1135. // optimize integer types
  1136. switch (strtoupper($type)) {
  1137. case 'TEXT':
  1138. $size = '';
  1139. $unsigned = false;
  1140. break;
  1141. case 'INTEGER' :
  1142. if (!is_numeric($size)) {
  1143. $size = '';
  1144. } else if ($size <= 4) {
  1145. $type = "TINYINT"; // 1 byte
  1146. } else if ($size <= 6) {
  1147. $type = "SMALLINT"; // 2 bytes
  1148. } else if ($size <= 8) {
  1149. $type = "MEDIUMINT"; // 3 bytes
  1150. } else if ($size <= 10) {
  1151. $type = "INTEGER"; // 4 bytes (=INT)
  1152. } else if ($size > 10) {
  1153. $type = "BIGINT"; // 8 bytes
  1154. }
  1155. break;
  1156. case 'VARCHAR':
  1157. $unsigned = false;
  1158. break;
  1159. }
  1160. // set action
  1161. if (empty($oldfield)) {
  1162. $action = "ADD";
  1163. } else {
  1164. $action = "CHANGE `$oldfield`";
  1165. }
  1166. // set fieldtype
  1167. $fieldtype = $type;
  1168. if ($size) {
  1169. $fieldtype .= "($size)";
  1170. }
  1171. if ($unsigned) {
  1172. $fieldtype .= ' UNSIGNED';
  1173. }
  1174. if ($notnull) {
  1175. $fieldtype .= ' NOT NULL';
  1176. }
  1177. if (isset($default)) {
  1178. $fieldtype .= " DEFAULT $default";
  1179. }
  1180. if (!empty($after)) {
  1181. $fieldtype .= " AFTER `$after`";
  1182. }
  1183. $ok = $ok && execute_sql("ALTER TABLE `$table` $action `$field` $fieldtype");
  1184. break;
  1185. case 'postgres':
  1186. // get db version
  1187. // N.B. $db->ServerInfo() usually returns blank
  1188. // (except lib/adodb/drivers/adodb-postgre64-inc.php)
  1189. $dbversion = '';
  1190. $rs = $db->Execute("SELECT version()");
  1191. if ($rs && $rs->RecordCount()>0) {
  1192. $records = $rs->GetArray();
  1193. if (preg_match('/\d+\.\d+/', $records[0][0], $matches)) {
  1194. $dbversion = $matches[0];
  1195. }
  1196. }
  1197. $tmpfield = 'temporary_'.$field.'_'.time();
  1198. switch (strtoupper($type)) {
  1199. case "INTEGER":
  1200. if (!is_numeric($size)) {
  1201. $fieldtype = "INTEGER";
  1202. } else if ($size <= 4) {
  1203. $fieldtype = "INT2"; // 2 bytes
  1204. } else if ($size <= 10) {
  1205. $fieldtype = "INT4"; // 4 bytes (=INTEGER)
  1206. } else if ($size > 10) {
  1207. $fieldtype = "INT8"; // 8 bytes
  1208. }
  1209. break;
  1210. case "VARCHAR":
  1211. $fieldtype = "VARCHAR($size)";
  1212. break;
  1213. default:
  1214. $fieldtype = $type;
  1215. }
  1216. // start transaction
  1217. execute_sql('BEGIN');
  1218. // create temporary field
  1219. execute_sql('ALTER TABLE '.$table.' ADD COLUMN "'.$tmpfield.'" '.$fieldtype);
  1220. // set default
  1221. if (isset($default)) {
  1222. execute_sql('UPDATE '.$table.' SET "'.$tmpfield.'" = '.$default);
  1223. execute_sql('ALTER TABLE '.$table.' ALTER COLUMN "'.$tmpfield.'" SET DEFAULT '.$default);
  1224. } else {
  1225. execute_sql('ALTER TABLE '.$table.' ALTER COLUMN "'.$tmpfield.'" DROP DEFAULT');
  1226. }
  1227. // set not null
  1228. if ($dbversion=='' || $dbversion >= "7.3") {
  1229. $notnull = ($notnull ? 'SET NOT NULL' : 'DROP NOT NULL');
  1230. execute_sql('ALTER TABLE '.$table.' ALTER COLUMN "'.$tmpfield.'" '.$notnull);
  1231. } else {
  1232. execute_sql("
  1233. UPDATE pg_attribute SET attnotnull=".($notnull ? 'TRUE' : 'FALSE')."
  1234. WHERE attname = '$tmpfield'
  1235. AND attrelid = (SELECT oid FROM pg_class WHERE relname = '$table')
  1236. ");
  1237. }
  1238. // transfer $oldfield values, if necessary
  1239. if ( $oldfield != '' ) {
  1240. execute_sql('UPDATE '.$table.' SET "'.$tmpfield.'" = CAST ("'.$oldfield.'" AS '.$fieldtype.')');
  1241. execute_sql('ALTER TABLE '.$table.' DROP COLUMN "'.$oldfield.'"');
  1242. }
  1243. // rename $tmpfield to $field
  1244. execute_sql('ALTER TABLE '.$table.' RENAME COLUMN "'.$tmpfield.'" TO "'.$field.'"');
  1245. // do the transaction
  1246. execute_sql('COMMIT');
  1247. // reclaim disk space (must be done outside transaction)
  1248. if ($oldfield != '' && $dbversion >= "7.3") {
  1249. execute_sql('UPDATE '.$table.' SET "'.$field.'" = "'.$field.'"');
  1250. execute_sql('VACUUM FULL '.$table);
  1251. }
  1252. break;
  1253. } // end switch $CGF->dbfamily
  1254. return $ok;
  1255. }
  1256. function hotpot_db_update_record($table, $record, $forcenull=false) {
  1257. global $CFG, $db;
  1258. $ok = true;
  1259. // set full table name
  1260. $table = "{$CFG->prefix}$table";
  1261. // get field names
  1262. $fields = $db->MetaColumns($table);
  1263. if (empty($fields)) {
  1264. $ok = false;
  1265. } else {
  1266. // get values
  1267. $values = array();
  1268. foreach ($fields as $field) {
  1269. $fieldname = $field->name;
  1270. if ($fieldname!='id' && ($forcenull || isset($record->$fieldname))) {
  1271. $value = isset($record->$fieldname) ? "'".$record->$fieldname."'" : 'NULL';
  1272. $values[] = "$fieldname = $value";
  1273. }
  1274. }
  1275. $values = implode(',', $values);
  1276. // update values (if there are any)
  1277. if ($values) {
  1278. $sql = "UPDATE $table SET $values WHERE id='$record->id'";
  1279. $rs = $db->Execute($sql);
  1280. if (empty($rs)) {
  1281. $ok = false;
  1282. debugging($db->ErrorMsg()."<br /><br />$sql");
  1283. }
  1284. }
  1285. }
  1286. return $ok;
  1287. }
  1288. function hotpot_rm($target, $output=true) {
  1289. $ok = true;
  1290. if (!empty($target)) {
  1291. if (is_file($target)) {
  1292. if ($output) {
  1293. print "removing file: $target ... ";
  1294. }
  1295. $ok = @unlink($target);
  1296. } else if (is_dir($target)) {
  1297. $dir = dir($target);
  1298. while(false !== ($entry = $dir->read())) {
  1299. if ($entry!='.' && $entry!='..') {
  1300. $ok = $ok && hotpot_rm($target.DIRECTORY_SEPARATOR.$entry, $output);
  1301. }
  1302. }
  1303. $dir->close();
  1304. if ($output) {
  1305. print "removing folder: $target ... ";
  1306. }
  1307. $ok = $ok && @rmdir($target);
  1308. } else { // not a file or directory (probably doesn't exist)
  1309. $output = false;
  1310. }
  1311. if ($output) {
  1312. if ($ok) {
  1313. print '<font color="green">OK</font><br />';
  1314. } else {
  1315. print '<font color="red">Failed</font><br />';
  1316. }
  1317. }
  1318. }
  1319. return $ok;
  1320. }
  1321. function hotpot_flush($n=0, $time=false) {
  1322. if ($time) {
  1323. $t = strftime("%X",time());
  1324. } else {
  1325. $t = "";
  1326. }
  1327. echo str_repeat(" ", $n) . $t . "\n";
  1328. flush();
  1329. }
  1330. ?>