PageRenderTime 38ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/quiz/db/upgrade.php

https://bitbucket.org/ngmares/moodle
PHP | 332 lines | 176 code | 68 blank | 88 comment | 30 complexity | 49d3f0e60faa906793fe18a6244f0a59 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-3.0, Apache-2.0, BSD-3-Clause
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Upgrade script for the quiz module.
  18. *
  19. * @package mod
  20. * @subpackage quiz
  21. * @copyright 2006 Eloy Lafuente (stronk7)
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. /**
  26. * Quiz module upgrade function.
  27. * @param string $oldversion the version we are upgrading from.
  28. */
  29. function xmldb_quiz_upgrade($oldversion) {
  30. global $CFG, $DB;
  31. $dbman = $DB->get_manager();
  32. // Moodle v2.2.0 release upgrade line.
  33. // Put any upgrade step following this.
  34. if ($oldversion < 2011120700) {
  35. // Define field lastcron to be dropped from quiz_reports.
  36. $table = new xmldb_table('quiz_reports');
  37. $field = new xmldb_field('lastcron');
  38. // Conditionally launch drop field lastcron.
  39. if ($dbman->field_exists($table, $field)) {
  40. $dbman->drop_field($table, $field);
  41. }
  42. // Quiz savepoint reached.
  43. upgrade_mod_savepoint(true, 2011120700, 'quiz');
  44. }
  45. if ($oldversion < 2011120701) {
  46. // Define field cron to be dropped from quiz_reports.
  47. $table = new xmldb_table('quiz_reports');
  48. $field = new xmldb_field('cron');
  49. // Conditionally launch drop field cron.
  50. if ($dbman->field_exists($table, $field)) {
  51. $dbman->drop_field($table, $field);
  52. }
  53. // Quiz savepoint reached.
  54. upgrade_mod_savepoint(true, 2011120701, 'quiz');
  55. }
  56. if ($oldversion < 2011120703) {
  57. // Track page of quiz attempts.
  58. $table = new xmldb_table('quiz_attempts');
  59. $field = new xmldb_field('currentpage', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0);
  60. if (!$dbman->field_exists($table, $field)) {
  61. $dbman->add_field($table, $field);
  62. }
  63. upgrade_mod_savepoint(true, 2011120703, 'quiz');
  64. }
  65. if ($oldversion < 2012030901) {
  66. // Configuration option for navigation method.
  67. $table = new xmldb_table('quiz');
  68. $field = new xmldb_field('navmethod', XMLDB_TYPE_CHAR, '16', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'free');
  69. if (!$dbman->field_exists($table, $field)) {
  70. $dbman->add_field($table, $field);
  71. }
  72. upgrade_mod_savepoint(true, 2012030901, 'quiz');
  73. }
  74. if ($oldversion < 2012040198) {
  75. // This step was added later. In MDL-32727, it was found that adding the
  76. // unique index on quiz-userid-attempt sometimes failed because of
  77. // duplicate entries {quizid}-{userid}-{attempt}. We do two things to
  78. // prevent these problems. First, here, we delete all preview attempts.
  79. // This code is an approximate copy-and-paste from
  80. // question_engine_data_mapper::delete_questions_usage_by_activities
  81. // Note that, for simplicity, the MySQL performance hack has been removed.
  82. // Since this code is for upgrade only, performance in not so critical,
  83. // where as simplicity of testing the code is.
  84. // Note that there is a limit to how far I am prepared to go in eliminating
  85. // all calls to library functions in this upgrade code. The only library
  86. // function still being used in question_engine::get_all_response_file_areas();
  87. // I think it is pretty safe not to inline it here.
  88. // Get a list of response variables that have files.
  89. require_once($CFG->dirroot . '/question/type/questiontypebase.php');
  90. $variables = array();
  91. foreach (get_plugin_list('qtype') as $qtypename => $path) {
  92. $file = $path . '/questiontype.php';
  93. if (!is_readable($file)) {
  94. continue;
  95. }
  96. include_once($file);
  97. $class = 'qtype_' . $qtypename;
  98. if (!class_exists($class)) {
  99. continue;
  100. }
  101. $qtype = new $class();
  102. if (!method_exists($qtype, 'response_file_areas')) {
  103. continue;
  104. }
  105. $variables += $qtype->response_file_areas();
  106. }
  107. // Conver that to a list of actual file area names.
  108. $fileareas = array();
  109. foreach (array_unique($variables) as $variable) {
  110. $fileareas[] = 'response_' . $variable;
  111. }
  112. // No point checking if this is empty as an optimisation, because essay
  113. // has response file areas, so the array will never be empty.
  114. // Get all the contexts where there are previews.
  115. $contextids = $DB->get_records_sql_menu("
  116. SELECT DISTINCT qu.contextid, 1
  117. FROM {question_usages} qu
  118. JOIN {quiz_attempts} quiza ON quiza.uniqueid = qu.id
  119. WHERE quiza.preview = 1");
  120. // Loop over contexts and files areas, deleting all files.
  121. $fs = get_file_storage();
  122. foreach ($contextids as $contextid => $notused) {
  123. foreach ($fileareas as $filearea) {
  124. upgrade_set_timeout(300);
  125. $fs->delete_area_files_select($contextid, 'question', $filearea,
  126. "IN (SELECT qas.id
  127. FROM {question_attempt_steps} qas
  128. JOIN {question_attempts} qa ON qa.id = qas.questionattemptid
  129. JOIN {quiz_attempts} quiza ON quiza.uniqueid = qa.questionusageid
  130. WHERE quiza.preview = 1)");
  131. }
  132. }
  133. // Now delete the question data associated with the previews.
  134. $DB->delete_records_select('question_attempt_step_data', "attemptstepid IN (
  135. SELECT qas.id
  136. FROM {question_attempt_steps} qas
  137. JOIN {question_attempts} qa ON qa.id = qas.questionattemptid
  138. JOIN {quiz_attempts} quiza ON quiza.uniqueid = qa.questionusageid
  139. WHERE quiza.preview = 1)");
  140. $DB->delete_records_select('question_attempt_steps', "questionattemptid IN (
  141. SELECT qa.id
  142. FROM {question_attempts} qa
  143. JOIN {quiz_attempts} quiza ON quiza.uniqueid = qa.questionusageid
  144. WHERE quiza.preview = 1)");
  145. $DB->delete_records_select('question_attempts', "{question_attempts}.questionusageid IN (
  146. SELECT uniqueid FROM {quiz_attempts} WHERE preview = 1)");
  147. $DB->delete_records_select('question_usages', "{question_usages}.id IN (
  148. SELECT uniqueid FROM {quiz_attempts} WHERE preview = 1)");
  149. // Finally delete the previews.
  150. $DB->delete_records('quiz_attempts', array('preview' => 1));
  151. // Quiz savepoint reached.
  152. upgrade_mod_savepoint(true, 2012040198, 'quiz');
  153. }
  154. if ($oldversion < 2012040199) {
  155. // This step was added later. In MDL-32727, it was found that adding the
  156. // unique index on quiz-userid-attempt sometimes failed because of
  157. // duplicate entries {quizid}-{userid}-{attempt}.
  158. // Here, if there are still duplicate entires, we renumber the values in
  159. // the attempt column.
  160. // Load all the problem quiz attempts.
  161. $problems = $DB->get_recordset_sql('
  162. SELECT qa.id, qa.quiz, qa.userid, qa.attempt
  163. FROM {quiz_attempts} qa
  164. JOIN (
  165. SELECT DISTINCT quiz, userid
  166. FROM {quiz_attempts}
  167. GROUP BY quiz, userid, attempt
  168. HAVING COUNT(1) > 1
  169. ) problems_view ON problems_view.quiz = qa.quiz AND
  170. problems_view.userid = qa.userid
  171. ORDER BY qa.quiz, qa.userid, qa.attempt, qa.id');
  172. // Renumber them.
  173. $currentquiz = null;
  174. $currentuserid = null;
  175. $attempt = 1;
  176. foreach ($problems as $problem) {
  177. if ($problem->quiz !== $currentquiz || $problem->userid !== $currentuserid) {
  178. $currentquiz = $problem->quiz;
  179. $currentuserid = $problem->userid;
  180. $attempt = 1;
  181. }
  182. if ($attempt != $problem->attempt) {
  183. $DB->set_field('quiz_attempts', 'attempt', $attempt, array('id' => $problem->id));
  184. }
  185. $attempt += 1;
  186. }
  187. $problems->close();
  188. // Quiz savepoint reached.
  189. upgrade_mod_savepoint(true, 2012040199, 'quiz');
  190. }
  191. if ($oldversion < 2012040200) {
  192. // Define index userid to be dropped form quiz_attempts
  193. $table = new xmldb_table('quiz_attempts');
  194. $index = new xmldb_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
  195. // Conditionally launch drop index quiz-userid-attempt.
  196. if ($dbman->index_exists($table, $index)) {
  197. $dbman->drop_index($table, $index);
  198. }
  199. // Quiz savepoint reached.
  200. upgrade_mod_savepoint(true, 2012040200, 'quiz');
  201. }
  202. if ($oldversion < 2012040201) {
  203. // Define key userid (foreign) to be added to quiz_attempts.
  204. $table = new xmldb_table('quiz_attempts');
  205. $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
  206. // Launch add key userid.
  207. $dbman->add_key($table, $key);
  208. // Quiz savepoint reached.
  209. upgrade_mod_savepoint(true, 2012040201, 'quiz');
  210. }
  211. if ($oldversion < 2012040202) {
  212. // Define index quiz-userid-attempt (unique) to be added to quiz_attempts.
  213. $table = new xmldb_table('quiz_attempts');
  214. $index = new xmldb_index('quiz-userid-attempt', XMLDB_INDEX_UNIQUE, array('quiz', 'userid', 'attempt'));
  215. // Conditionally launch add index quiz-userid-attempt.
  216. if (!$dbman->index_exists($table, $index)) {
  217. $dbman->add_index($table, $index);
  218. }
  219. // Quiz savepoint reached.
  220. upgrade_mod_savepoint(true, 2012040202, 'quiz');
  221. }
  222. if ($oldversion < 2012040203) {
  223. // Define field state to be added to quiz_attempts.
  224. $table = new xmldb_table('quiz_attempts');
  225. $field = new xmldb_field('state', XMLDB_TYPE_CHAR, '16', null, XMLDB_NOTNULL, null, 'inprogress', 'preview');
  226. // Conditionally launch add field state.
  227. if (!$dbman->field_exists($table, $field)) {
  228. $dbman->add_field($table, $field);
  229. }
  230. // Quiz savepoint reached.
  231. upgrade_mod_savepoint(true, 2012040203, 'quiz');
  232. }
  233. if ($oldversion < 2012040204) {
  234. // Update quiz_attempts.state for finished attempts.
  235. $DB->set_field_select('quiz_attempts', 'state', 'finished', 'timefinish > 0');
  236. // Other, more complex transitions (basically abandoned attempts), will
  237. // be handled by cron later.
  238. // Quiz savepoint reached.
  239. upgrade_mod_savepoint(true, 2012040204, 'quiz');
  240. }
  241. if ($oldversion < 2012040205) {
  242. // Define field overduehandling to be added to quiz.
  243. $table = new xmldb_table('quiz');
  244. $field = new xmldb_field('overduehandling', XMLDB_TYPE_CHAR, '16', null, XMLDB_NOTNULL, null, 'autoabandon', 'timelimit');
  245. // Conditionally launch add field overduehandling.
  246. if (!$dbman->field_exists($table, $field)) {
  247. $dbman->add_field($table, $field);
  248. }
  249. // Quiz savepoint reached.
  250. upgrade_mod_savepoint(true, 2012040205, 'quiz');
  251. }
  252. if ($oldversion < 2012040206) {
  253. // Define field graceperiod to be added to quiz.
  254. $table = new xmldb_table('quiz');
  255. $field = new xmldb_field('graceperiod', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'overduehandling');
  256. // Conditionally launch add field graceperiod.
  257. if (!$dbman->field_exists($table, $field)) {
  258. $dbman->add_field($table, $field);
  259. }
  260. // Quiz savepoint reached.
  261. upgrade_mod_savepoint(true, 2012040206, 'quiz');
  262. }
  263. return true;
  264. }