diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 529645f9bf8..7e4b7aac6ac 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -4446,6 +4446,12 @@ class restore_create_categories_and_questions extends restore_structure_step { // With newitemid = 0, let's create the question if (!$questionmapping->newitemid) { + if ($data->qtype === 'random') { + // Ensure that this newly created question is considered by + // \qtype_random\task\remove_unused_questions. + $data->hidden = 0; + } + $newitemid = $DB->insert_record('question', $data); $this->set_mapping('question', $oldid, $newitemid); // Also annotate them as question_created, we need diff --git a/question/type/random/classes/task/remove_unused_questions.php b/question/type/random/classes/task/remove_unused_questions.php new file mode 100644 index 00000000000..08844f8ffa0 --- /dev/null +++ b/question/type/random/classes/task/remove_unused_questions.php @@ -0,0 +1,69 @@ +. + +/** + * A scheduled task to remove unneeded random questions. + * + * @package qtype_random + * @category task + * @copyright 2018 Bo Pierce + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace qtype_random\task; + +defined('MOODLE_INTERNAL') || die(); + + +/** + * A scheduled task to remove unneeded random questions. + * + * @copyright 2018 Bo Pierce + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class remove_unused_questions extends \core\task\scheduled_task { + + public function get_name() { + return get_string('taskunusedrandomscleanup', 'qtype_random'); + } + + public function execute() { + global $DB, $CFG; + require_once($CFG->libdir . '/questionlib.php'); + + // Find potentially unused random questions (up to 10000). + // Note, because we call question_delete_question below, + // the question will not actually be deleted if something else + // is using them, but nothing else in Moodle core uses qtype_random, + // and not many third-party plugins do. + $unusedrandomids = $DB->get_records_sql(" + SELECT q.id, 1 + FROM {question} q + LEFT JOIN {quiz_slots} qslots ON q.id = qslots.questionid + WHERE qslots.questionid IS NULL + AND q.qtype = ? AND hidden = ?", ['random', 0], 0, 10000); + + $count = 0; + foreach ($unusedrandomids as $unusedrandomid => $notused) { + question_delete_question($unusedrandomid); + // In case the question was not actually deleted (because it was in use somehow + // mark it as hidden so the query above will not return it again. + $DB->set_field('question', 'hidden', 1, ['id' => $unusedrandomid]); + $count += 1; + } + mtrace('Cleaned up ' . $count . ' unused random questions.'); + } +} diff --git a/question/type/random/db/tasks.php b/question/type/random/db/tasks.php new file mode 100644 index 00000000000..e6298dcd781 --- /dev/null +++ b/question/type/random/db/tasks.php @@ -0,0 +1,38 @@ +. + +/** + * Definition of question/type/random scheduled tasks. + * + * @package qtype_random + * @category task + * @copyright 2018 Bo Pierce + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$tasks = array( + array( + 'classname' => 'qtype_random\task\remove_unused_questions', + 'blocking' => 0, + 'minute' => 'R', + 'hour' => '*', + 'day' => '*', + 'month' => '*', + 'dayofweek' => '*' + ) +); diff --git a/question/type/random/lang/en/qtype_random.php b/question/type/random/lang/en/qtype_random.php index d411e463100..f6b76048deb 100644 --- a/question/type/random/lang/en/qtype_random.php +++ b/question/type/random/lang/en/qtype_random.php @@ -33,3 +33,4 @@ $string['randomqname'] = 'Random ({$a})'; $string['randomqplusname'] = 'Random ({$a} and subcategories)'; $string['selectedby'] = '{$a->questionname} selected by {$a->randomname}'; $string['selectmanualquestions'] = 'Random questions can use manually graded questions'; +$string['taskunusedrandomscleanup'] = 'Remove unused random questions'; diff --git a/question/type/random/tests/cleanup_task_test.php b/question/type/random/tests/cleanup_task_test.php new file mode 100644 index 00000000000..a8f43b0b5e2 --- /dev/null +++ b/question/type/random/tests/cleanup_task_test.php @@ -0,0 +1,70 @@ +. + +/** + * Tests of the scheduled task for cleaning up random questions. + * + * @package qtype_random + * @copyright 2018 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + + +/** + * Tests of the scheduled task for cleaning up random questions. + * + * @copyright 2018 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_random_cleanup_task_testcase extends advanced_testcase { + + public function test_cleanup_task_removes_unused_question() { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $questiongenerator = $generator->get_plugin_generator('core_question'); + $quizgenerator = $generator->get_plugin_generator('mod_quiz'); + $cat = $questiongenerator->create_question_category(); + $quiz = $quizgenerator->create_instance(['course' => SITEID]); + + // Add two random questions. + quiz_add_random_questions($quiz, 0, $cat->id, 2, false); + $quizslots = $DB->get_records('quiz_slots', ['quizid' => $quiz->id], + 'slot', 'slot, id, questionid'); + + // Now remove the second from the quiz. (Do it manually, + // because the API cleans up the random question, but we are trying to + // create an orphaned random question.) + $DB->delete_records('quiz_slots', array('id' => $quizslots[2]->id)); + + // Run the scheduled task. + $task = new \qtype_random\task\remove_unused_questions(); + $this->expectOutputString("Cleaned up 1 unused random questions.\n"); + $task->execute(); + + // Verify. + $this->assertTrue($DB->record_exists('question', ['id' => $quizslots[1]->questionid])); + $this->assertFalse($DB->record_exists('question', ['id' => $quizslots[2]->questionid])); + } +} diff --git a/question/type/random/version.php b/question/type/random/version.php index 77ca68c8332..611212b1693 100644 --- a/question/type/random/version.php +++ b/question/type/random/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_random'; -$plugin->version = 2017111300; +$plugin->version = 2017111301; $plugin->requires = 2017110800;