mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
Merge branch 'MDL-47787-master' of https://github.com/tbannister/moodle
This commit is contained in:
commit
1b80849856
103
admin/cli/fix_orphaned_question_categories.php
Normal file
103
admin/cli/fix_orphaned_question_categories.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This script fixes orphaned question categories.
|
||||
*
|
||||
* Orphaned question categories have had their associated context deleted
|
||||
* but the category itself remains in the database with an invalid context.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2013 Tyler Bannister (tyler.bannister@remote-learner.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
require_once($CFG->libdir.'/questionlib.php');
|
||||
|
||||
$long = array('fix' => false, 'help' => false);
|
||||
$short = array('f' => 'fix', 'h' => 'help');
|
||||
|
||||
// Now get cli options.
|
||||
list($options, $unrecognized) = cli_get_params($long, $short);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
$help =
|
||||
"Fix orphaned question categories.
|
||||
|
||||
This scripts detects question categories that have had their
|
||||
context deleted, thus severing them from their original purpose.
|
||||
|
||||
This script will find the orphaned categories and delete the unused
|
||||
questions in each category found. Used questions will not be
|
||||
deleted, instead they will be moved to a rescue question category.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
-f, --fix Fix the orphaned question categories in the DB.
|
||||
If not specified only check and report problems to STDERR.
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php -f
|
||||
";
|
||||
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
cli_heading('Checking for orphaned categories');
|
||||
|
||||
|
||||
$sql = 'SELECT qc.id, qc.contextid, qc.name
|
||||
FROM {question_categories} qc
|
||||
LEFT JOIN {context} c ON qc.contextid = c.id
|
||||
WHERE c.id IS NULL';
|
||||
$categories = $DB->get_recordset_sql($sql);
|
||||
|
||||
$i = 0;
|
||||
foreach ($categories as $category) {
|
||||
$i += 1;
|
||||
echo "Found orphaned category: {$category->name}\n";
|
||||
if (!empty($options['fix'])) {
|
||||
echo "Cleaning...";
|
||||
// One transaction per category.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
question_category_delete_safe($category);
|
||||
$transaction->allow_commit();
|
||||
echo " Done!\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (($i > 0) && !empty($options['fix'])) {
|
||||
echo "Found and removed {$i} orphaned question categories\n";
|
||||
} else if ($i > 0) {
|
||||
echo "Found {$i} orphaned question categories. To fix, run:\n";
|
||||
echo "\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php --fix\n";
|
||||
} else {
|
||||
echo "No orphaned question categories found.\n";
|
||||
}
|
||||
|
||||
|
||||
$categories->close();
|
@ -1633,7 +1633,7 @@ function set_coursemodule_visible($id, $visible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will handles the whole deletion process of a module. This includes calling
|
||||
* This function will handle the whole deletion process of a module. This includes calling
|
||||
* the modules delete_instance function, deleting files, events, grades, conditional data,
|
||||
* the data in the course_module and course_sections table and adding a module deletion
|
||||
* event to the DB.
|
||||
@ -1645,9 +1645,10 @@ function course_delete_module($cmid) {
|
||||
global $CFG, $DB;
|
||||
|
||||
require_once($CFG->libdir.'/gradelib.php');
|
||||
require_once($CFG->libdir.'/questionlib.php');
|
||||
require_once($CFG->dirroot.'/blog/lib.php');
|
||||
require_once($CFG->dirroot.'/calendar/lib.php');
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
|
||||
// Get the course module.
|
||||
if (!$cm = $DB->get_record('course_modules', array('id' => $cmid))) {
|
||||
@ -1679,6 +1680,9 @@ function course_delete_module($cmid) {
|
||||
"Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/$modulename/lib.php.");
|
||||
}
|
||||
|
||||
// Delete activity context questions and question categories.
|
||||
question_delete_activity($cm);
|
||||
|
||||
// Call the delete_instance function, if it returns false throw an exception.
|
||||
if (!$deleteinstancefunction($cm->instance)) {
|
||||
throw new moodle_exception('cannotdeletemoduleinstance', '', '', null,
|
||||
|
@ -539,7 +539,23 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
return $moduleinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for course_delete module
|
||||
*
|
||||
* @return array An array of arrays contain test data
|
||||
*/
|
||||
public function provider_course_delete_module() {
|
||||
$data = array();
|
||||
|
||||
$data['assign'] = array('assign', array('duedate' => time()));
|
||||
$data['quiz'] = array('quiz', array('duedate' => time()));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the create_course function
|
||||
*/
|
||||
public function test_create_course() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
@ -1476,51 +1492,92 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($pagecm->visible, 0);
|
||||
}
|
||||
|
||||
public function test_course_delete_module() {
|
||||
/**
|
||||
* Tests the function that deletes a course module
|
||||
*
|
||||
* @param string $type The type of module for the test
|
||||
* @param array $options The options for the module creation
|
||||
* @dataProvider provider_course_delete_module
|
||||
*/
|
||||
public function test_course_delete_module($type, $options) {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create course and modules.
|
||||
$course = $this->getDataGenerator()->create_course(array('numsections' => 5));
|
||||
$options['course'] = $course->id;
|
||||
|
||||
// Generate an assignment with due date (will generate a course event).
|
||||
$assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
|
||||
$module = $this->getDataGenerator()->create_module($type, $options);
|
||||
|
||||
// Get the module context.
|
||||
$modcontext = context_module::instance($assign->cmid);
|
||||
$modcontext = context_module::instance($module->cmid);
|
||||
|
||||
// Verify context exists.
|
||||
$this->assertInstanceOf('context_module', $modcontext);
|
||||
|
||||
// Add some tags to this assignment.
|
||||
tag_set('assign', $assign->id, array('Tag 1', 'Tag 2', 'Tag 3'), 'mod_assign', $modcontext->id);
|
||||
// Make module specific messes.
|
||||
switch ($type) {
|
||||
case 'assign':
|
||||
// Add some tags to this assignment.
|
||||
tag_set('assign', $module->id, array('Tag 1', 'Tag 2', 'Tag 3'), 'mod_assign', $modcontext->id);
|
||||
|
||||
// Confirm the tag instances were added.
|
||||
$this->assertEquals(3, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' =>
|
||||
$modcontext->id)));
|
||||
// Confirm the tag instances were added.
|
||||
$criteria = array('component' => 'mod_assign', 'contextid' => $modcontext->id);
|
||||
$this->assertEquals(3, $DB->count_records('tag_instance', $criteria));
|
||||
|
||||
// Verify event assignment event has been generated.
|
||||
$eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
|
||||
$this->assertEquals(1, $eventcount);
|
||||
// Verify event assignment event has been generated.
|
||||
$eventcount = $DB->count_records('event', array('instance' => $module->id, 'modulename' => $type));
|
||||
$this->assertEquals(1, $eventcount);
|
||||
|
||||
break;
|
||||
case 'quiz':
|
||||
$qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$qcat = $qgen->create_question_category(array('contextid' => $modcontext->id));
|
||||
$questions = array(
|
||||
$qgen->create_question('shortanswer', null, array('category' => $qcat->id)),
|
||||
$qgen->create_question('shortanswer', null, array('category' => $qcat->id)),
|
||||
);
|
||||
$this->expectOutputRegex('/'.get_string('unusedcategorydeleted', 'question').'/');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Run delete..
|
||||
course_delete_module($assign->cmid);
|
||||
course_delete_module($module->cmid);
|
||||
|
||||
// Verify the context has been removed.
|
||||
$this->assertFalse(context_module::instance($assign->cmid, IGNORE_MISSING));
|
||||
$this->assertFalse(context_module::instance($module->cmid, IGNORE_MISSING));
|
||||
|
||||
// Verify the course_module record has been deleted.
|
||||
$cmcount = $DB->count_records('course_modules', array('id' => $assign->cmid));
|
||||
$cmcount = $DB->count_records('course_modules', array('id' => $module->cmid));
|
||||
$this->assertEmpty($cmcount);
|
||||
|
||||
// Verify event assignment events have been removed.
|
||||
$eventcount = $DB->count_records('event', array('instance' => $assign->id, 'modulename' => 'assign'));
|
||||
$this->assertEmpty($eventcount);
|
||||
// Test clean up of module specific messes.
|
||||
switch ($type) {
|
||||
case 'assign':
|
||||
// Verify event assignment events have been removed.
|
||||
$eventcount = $DB->count_records('event', array('instance' => $module->id, 'modulename' => $type));
|
||||
$this->assertEmpty($eventcount);
|
||||
|
||||
// Verify the tag instances were deleted.
|
||||
$this->assertEquals(0, $DB->count_records('tag_instance', array('component' => 'mod_assign', 'contextid' =>
|
||||
$modcontext->id)));
|
||||
// Verify the tag instances were deleted.
|
||||
$criteria = array('component' => 'mod_assign', 'contextid' => $modcontext->id);
|
||||
$this->assertEquals(0, $DB->count_records('tag_instance', $criteria));
|
||||
break;
|
||||
case 'quiz':
|
||||
// Verify category deleted.
|
||||
$criteria = array('contextid' => $modcontext->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,6 +220,52 @@ function match_grade_options($gradeoptionsfull, $grade, $matchgrades = 'error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Category is about to be deleted,
|
||||
* 1/ All questions are deleted for this question category.
|
||||
* 2/ Any questions that can't be deleted are moved to a new category
|
||||
* NOTE: this function is called from lib/db/upgrade.php
|
||||
*
|
||||
* @param object|coursecat $category course category object
|
||||
*/
|
||||
function question_category_delete_safe($category) {
|
||||
global $DB;
|
||||
$criteria = array('category' => $category->id);
|
||||
$context = context::instance_by_id($category->contextid, IGNORE_MISSING);
|
||||
$rescue = null; // See the code around the call to question_save_from_deletion.
|
||||
|
||||
// Deal with any questions in the category.
|
||||
if ($questions = $DB->get_records('question', $criteria, '', 'id,qtype')) {
|
||||
|
||||
// Try to delete each question.
|
||||
foreach ($questions as $question) {
|
||||
question_delete_question($question->id);
|
||||
}
|
||||
|
||||
// Check to see if there were any questions that were kept because
|
||||
// they are still in use somehow, even though quizzes in courses
|
||||
// in this category will already have been deleted. This could
|
||||
// happen, for example, if questions are added to a course,
|
||||
// and then that course is moved to another category (MDL-14802).
|
||||
$questionids = $DB->get_records_menu('question', $criteria, '', 'id, 1');
|
||||
if (!empty($questionids)) {
|
||||
$parentcontextid = SYSCONTEXTID;
|
||||
$name = get_string('unknown', 'question');
|
||||
if ($context !== false) {
|
||||
$name = $context->get_context_name();
|
||||
$parentcontext = $context->get_parent_context();
|
||||
if ($parentcontext) {
|
||||
$parentcontextid = $parentcontext->id;
|
||||
}
|
||||
}
|
||||
question_save_from_deletion(array_keys($questionids), $parentcontextid, $name, $rescue);
|
||||
}
|
||||
}
|
||||
|
||||
// Now delete the category.
|
||||
$DB->delete_records('question_categories', array('id' => $category->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether any question in a category is used by any part of Moodle.
|
||||
*
|
||||
@ -305,6 +351,36 @@ function question_delete_question($questionid) {
|
||||
question_bank::notify_question_edited($questionid);
|
||||
}
|
||||
|
||||
/**
|
||||
* All question categories and their questions are deleted for this context id.
|
||||
*
|
||||
* @param object $contextid The contextid to delete question categories from
|
||||
* @return array Feedback from deletes (if any)
|
||||
*/
|
||||
function question_delete_context($contextid) {
|
||||
global $DB;
|
||||
|
||||
//To store feedback to be showed at the end of the process
|
||||
$feedbackdata = array();
|
||||
|
||||
//Cache some strings
|
||||
$strcatdeleted = get_string('unusedcategorydeleted', 'question');
|
||||
$fields = 'id, parent, name, contextid';
|
||||
if ($categories = $DB->get_records('question_categories', array('contextid' => $contextid), 'parent', $fields)) {
|
||||
//Sort categories following their tree (parent-child) relationships
|
||||
//this will make the feedback more readable
|
||||
$categories = sort_categories_by_tree($categories);
|
||||
|
||||
foreach ($categories as $category) {
|
||||
question_category_delete_safe($category);
|
||||
|
||||
//Fill feedback
|
||||
$feedbackdata[] = array($category->name, $strcatdeleted);
|
||||
}
|
||||
}
|
||||
return $feedbackdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* All question categories and their questions are deleted for this course.
|
||||
*
|
||||
@ -313,47 +389,15 @@ function question_delete_question($questionid) {
|
||||
* @return boolean
|
||||
*/
|
||||
function question_delete_course($course, $feedback=true) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
//To store feedback to be showed at the end of the process
|
||||
$feedbackdata = array();
|
||||
|
||||
//Cache some strings
|
||||
$strcatdeleted = get_string('unusedcategorydeleted', 'question');
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$categoriescourse = $DB->get_records('question_categories',
|
||||
array('contextid' => $coursecontext->id), 'parent', 'id, parent, name, contextid');
|
||||
$feedbackdata = question_delete_context($coursecontext->id, $feedback);
|
||||
|
||||
if ($categoriescourse) {
|
||||
|
||||
//Sort categories following their tree (parent-child) relationships
|
||||
//this will make the feedback more readable
|
||||
$categoriescourse = sort_categories_by_tree($categoriescourse);
|
||||
|
||||
foreach ($categoriescourse as $category) {
|
||||
|
||||
//Delete it completely (questions and category itself)
|
||||
//deleting questions
|
||||
if ($questions = $DB->get_records('question',
|
||||
array('category' => $category->id), '', 'id,qtype')) {
|
||||
foreach ($questions as $question) {
|
||||
question_delete_question($question->id);
|
||||
}
|
||||
$DB->delete_records("question", array("category" => $category->id));
|
||||
}
|
||||
//delete the category
|
||||
$DB->delete_records('question_categories', array('id' => $category->id));
|
||||
|
||||
//Fill feedback
|
||||
$feedbackdata[] = array($category->name, $strcatdeleted);
|
||||
}
|
||||
//Inform about changes performed if feedback is enabled
|
||||
if ($feedback) {
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('category', 'question'), get_string('action'));
|
||||
$table->data = $feedbackdata;
|
||||
echo html_writer::table($table);
|
||||
}
|
||||
// Inform about changes performed if feedback is enabled.
|
||||
if ($feedback && $feedbackdata) {
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('category', 'question'), get_string('action'));
|
||||
$table->data = $feedbackdata;
|
||||
echo html_writer::table($table);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -374,58 +418,10 @@ function question_delete_course_category($category, $newcategory, $feedback=true
|
||||
|
||||
$context = context_coursecat::instance($category->id);
|
||||
if (empty($newcategory)) {
|
||||
$feedbackdata = array(); // To store feedback to be showed at the end of the process
|
||||
$rescueqcategory = null; // See the code around the call to question_save_from_deletion.
|
||||
$strcatdeleted = get_string('unusedcategorydeleted', 'question');
|
||||
|
||||
// Loop over question categories.
|
||||
if ($categories = $DB->get_records('question_categories',
|
||||
array('contextid'=>$context->id), 'parent', 'id, parent, name')) {
|
||||
foreach ($categories as $category) {
|
||||
|
||||
// Deal with any questions in the category.
|
||||
if ($questions = $DB->get_records('question',
|
||||
array('category' => $category->id), '', 'id,qtype')) {
|
||||
|
||||
// Try to delete each question.
|
||||
foreach ($questions as $question) {
|
||||
question_delete_question($question->id);
|
||||
}
|
||||
|
||||
// Check to see if there were any questions that were kept because
|
||||
// they are still in use somehow, even though quizzes in courses
|
||||
// in this category will already have been deleted. This could
|
||||
// happen, for example, if questions are added to a course,
|
||||
// and then that course is moved to another category (MDL-14802).
|
||||
$questionids = $DB->get_records_menu('question',
|
||||
array('category'=>$category->id), '', 'id, 1');
|
||||
if (!empty($questionids)) {
|
||||
$parentcontextid = false;
|
||||
$parentcontext = $context->get_parent_context();
|
||||
if ($parentcontext) {
|
||||
$parentcontextid = $parentcontext->id;
|
||||
}
|
||||
if (!$rescueqcategory = question_save_from_deletion(
|
||||
array_keys($questionids), $parentcontextid,
|
||||
$context->get_context_name(), $rescueqcategory)) {
|
||||
return false;
|
||||
}
|
||||
$feedbackdata[] = array($category->name,
|
||||
get_string('questionsmovedto', 'question', $rescueqcategory->name));
|
||||
}
|
||||
}
|
||||
|
||||
// Now delete the category.
|
||||
if (!$DB->delete_records('question_categories', array('id'=>$category->id))) {
|
||||
return false;
|
||||
}
|
||||
$feedbackdata[] = array($category->name, $strcatdeleted);
|
||||
|
||||
} // End loop over categories.
|
||||
}
|
||||
$feedbackdata = question_delete_context($context->id, $feedback);
|
||||
|
||||
// Output feedback if requested.
|
||||
if ($feedback and $feedbackdata) {
|
||||
if ($feedback && $feedbackdata) {
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('questioncategory', 'question'), get_string('action'));
|
||||
$table->data = $feedbackdata;
|
||||
@ -460,7 +456,7 @@ function question_delete_course_category($category, $newcategory, $feedback=true
|
||||
* Enter description here...
|
||||
*
|
||||
* @param array $questionids of question ids
|
||||
* @param object $newcontext the context to create the saved category in.
|
||||
* @param object $newcontextid the context to create the saved category in.
|
||||
* @param string $oldplace a textual description of the think being deleted,
|
||||
* e.g. from get_context_name
|
||||
* @param object $newcategory
|
||||
@ -497,44 +493,16 @@ function question_save_from_deletion($questionids, $newcontextid, $oldplace,
|
||||
* @return boolean
|
||||
*/
|
||||
function question_delete_activity($cm, $feedback=true) {
|
||||
global $DB, $OUTPUT;
|
||||
global $DB;
|
||||
|
||||
//To store feedback to be showed at the end of the process
|
||||
$feedbackdata = array();
|
||||
|
||||
//Cache some strings
|
||||
$strcatdeleted = get_string('unusedcategorydeleted', 'question');
|
||||
$modcontext = context_module::instance($cm->id);
|
||||
if ($categoriesmods = $DB->get_records('question_categories',
|
||||
array('contextid' => $modcontext->id), 'parent', 'id, parent, name, contextid')) {
|
||||
//Sort categories following their tree (parent-child) relationships
|
||||
//this will make the feedback more readable
|
||||
$categoriesmods = sort_categories_by_tree($categoriesmods);
|
||||
|
||||
foreach ($categoriesmods as $category) {
|
||||
|
||||
//Delete it completely (questions and category itself)
|
||||
//deleting questions
|
||||
if ($questions = $DB->get_records('question',
|
||||
array('category' => $category->id), '', 'id,qtype')) {
|
||||
foreach ($questions as $question) {
|
||||
question_delete_question($question->id);
|
||||
}
|
||||
$DB->delete_records("question", array("category"=>$category->id));
|
||||
}
|
||||
//delete the category
|
||||
$DB->delete_records('question_categories', array('id'=>$category->id));
|
||||
|
||||
//Fill feedback
|
||||
$feedbackdata[] = array($category->name, $strcatdeleted);
|
||||
}
|
||||
//Inform about changes performed if feedback is enabled
|
||||
if ($feedback) {
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('category', 'question'), get_string('action'));
|
||||
$table->data = $feedbackdata;
|
||||
echo html_writer::table($table);
|
||||
}
|
||||
$feedbackdata = question_delete_context($modcontext->id, $feedback);
|
||||
// Inform about changes performed if feedback is enabled.
|
||||
if ($feedback && $feedbackdata) {
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('category', 'question'), get_string('action'));
|
||||
$table->data = $feedbackdata;
|
||||
echo html_writer::table($table);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1343,7 +1311,7 @@ function question_has_capability_on($question, $cap, $cachecat = -1) {
|
||||
if (!isset($categories[$question->category])) {
|
||||
if (!$categories[$question->category] = $DB->get_record('question_categories',
|
||||
array('id'=>$question->category))) {
|
||||
print_error('invalidcategory', 'quiz');
|
||||
print_error('invalidcategory', 'question');
|
||||
}
|
||||
}
|
||||
$category = $categories[$question->category];
|
||||
|
@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
|
||||
// Get the necessary files to perform backup and restore.
|
||||
@ -58,6 +59,61 @@ class core_questionlib_testcase extends advanced_testcase {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true and false to test functions with feedback on and off.
|
||||
*
|
||||
* @return array Test data
|
||||
*/
|
||||
public function provider_feedback() {
|
||||
return array(
|
||||
'Feedback test' => array(true),
|
||||
'No feedback test' => array(false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a course, a quiz, a question category and a question for testing.
|
||||
*
|
||||
* @param string $type The type of question category to create.
|
||||
* @return array The created data objects
|
||||
*/
|
||||
public function setup_quiz_and_questions($type = 'module') {
|
||||
// Create course category.
|
||||
$category = $this->getDataGenerator()->create_category();
|
||||
|
||||
// Create course.
|
||||
$course = $this->getDataGenerator()->create_course(array('numsections' => 5));
|
||||
|
||||
$options = array(
|
||||
'course' => $course->id,
|
||||
'duedate' => time(),
|
||||
);
|
||||
|
||||
// Generate an assignment with due date (will generate a course event).
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', $options);
|
||||
|
||||
$qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
if ('course' == $type) {
|
||||
$context = context_course::instance($course->id);
|
||||
} else if ('category' == $type) {
|
||||
$context = context_coursecat::instance($category->id);
|
||||
} else {
|
||||
$context = context_module::instance($quiz->cmid);
|
||||
}
|
||||
|
||||
$qcat = $qgen->create_question_category(array('contextid' => $context->id));
|
||||
|
||||
$questions = array(
|
||||
$qgen->create_question('shortanswer', null, array('category' => $qcat->id)),
|
||||
$qgen->create_question('shortanswer', null, array('category' => $qcat->id)),
|
||||
);
|
||||
|
||||
quiz_add_quiz_question($questions[0]->id, $quiz);
|
||||
|
||||
return array($category, $course, $quiz, $qcat, $questions);
|
||||
}
|
||||
|
||||
public function test_question_reorder_qtypes() {
|
||||
$this->assertEquals(
|
||||
array(0 => 't2', 1 => 't1', 2 => 't3'),
|
||||
@ -205,4 +261,140 @@ class core_questionlib_testcase extends advanced_testcase {
|
||||
// Check that there are two questions in the restored to course's context.
|
||||
$this->assertEquals(2, $DB->count_records('question', array('category' => $restoredcategory->id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tests the question_category_delete_safe function.
|
||||
*/
|
||||
public function test_question_category_delete_safe() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions();
|
||||
|
||||
question_category_delete_safe($qcat);
|
||||
|
||||
// Verify category deleted.
|
||||
$criteria = array('id' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted or moved.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
|
||||
// Verify question not deleted.
|
||||
$criteria = array('id' => $questions[0]->id);
|
||||
$this->assertEquals(1, $DB->count_records('question', $criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tests the question_delete_activity function.
|
||||
*
|
||||
* @param bool $feedback Whether to return feedback
|
||||
* @dataProvider provider_feedback
|
||||
*/
|
||||
public function test_question_delete_activity($feedback) {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions();
|
||||
|
||||
$cm = get_coursemodule_from_instance('quiz', $quiz->id);
|
||||
// Test that the feedback works.
|
||||
if ($feedback) {
|
||||
$this->expectOutputRegex('|'.get_string('unusedcategorydeleted', 'question').'|');
|
||||
}
|
||||
question_delete_activity($cm, $feedback);
|
||||
|
||||
// Verify category deleted.
|
||||
$criteria = array('id' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted or moved.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tests the question_delete_context function.
|
||||
*/
|
||||
public function test_question_delete_context() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions();
|
||||
|
||||
// Get the module context id.
|
||||
$result = question_delete_context($qcat->contextid);
|
||||
|
||||
// Verify category deleted.
|
||||
$criteria = array('id' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted or moved.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
|
||||
// Test that the feedback works.
|
||||
$expected[] = array($qcat->name, get_string('unusedcategorydeleted', 'question'));
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tests the question_delete_course function.
|
||||
*
|
||||
* @param bool $feedback Whether to return feedback
|
||||
* @dataProvider provider_feedback
|
||||
*/
|
||||
public function test_question_delete_course($feedback) {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course');
|
||||
|
||||
// Test that the feedback works.
|
||||
if ($feedback) {
|
||||
$this->expectOutputRegex('|'.get_string('unusedcategorydeleted', 'question').'|');
|
||||
}
|
||||
question_delete_course($course, $feedback);
|
||||
|
||||
// Verify category deleted.
|
||||
$criteria = array('id' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted or moved.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tests the question_delete_course_category function.
|
||||
*
|
||||
* @param bool $feedback Whether to return feedback
|
||||
* @dataProvider provider_feedback
|
||||
*/
|
||||
public function test_question_delete_course_category($feedback) {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category');
|
||||
|
||||
// Test that the feedback works.
|
||||
if ($feedback) {
|
||||
$this->expectOutputRegex('|'.get_string('unusedcategorydeleted', 'question').'|');
|
||||
}
|
||||
question_delete_course_category($category, 0, $feedback);
|
||||
|
||||
// Verify category deleted.
|
||||
$criteria = array('id' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question_categories', $criteria));
|
||||
|
||||
// Verify questions deleted or moved.
|
||||
$criteria = array('category' => $qcat->id);
|
||||
$this->assertEquals(0, $DB->count_records('question', $criteria));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user