MDL-63260 qtype_random: orphaned random questions should be deleted

This fix is based on some original code written by Bo Pierce.
This commit is contained in:
Tim Hunt 2018-12-06 19:27:29 +00:00
parent 44890bd738
commit 2fbd8e9ec0
6 changed files with 185 additions and 1 deletions

View File

@ -4518,6 +4518,12 @@ class restore_create_categories_and_questions extends restore_structure_step {
unset($data->idnumber);
}
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

View File

@ -0,0 +1,69 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A scheduled task to remove unneeded random questions.
*
* @package qtype_random
* @category task
* @copyright 2018 Bo Pierce <email.bO.pierce@gmail.com>
* @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 <email.bO.pierce@gmail.com>
* @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.');
}
}

View File

@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definition of question/type/random scheduled tasks.
*
* @package qtype_random
* @category task
* @copyright 2018 Bo Pierce <email.bO.pierce@gmail.com>
* @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' => '*'
)
);

View File

@ -45,3 +45,4 @@ $string['randomqplusnamesystemtags'] = 'Random (Any system-level category, tags:
$string['randomqplusnametags'] = 'Random ({$a->category} and subcategories, tags: {$a->tags})';
$string['selectedby'] = '{$a->questionname} selected by {$a->randomname}';
$string['selectmanualquestions'] = 'Random questions can use manually graded questions';
$string['taskunusedrandomscleanup'] = 'Remove unused random questions';

View File

@ -0,0 +1,70 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* 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]));
}
}

View File

@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'qtype_random';
$plugin->version = 2018120300;
$plugin->version = 2018120301;
$plugin->requires = 2018112800;