From 2481a022d0521ea0df9d60ba1b7731ec34418e93 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Sun, 11 Mar 2018 17:08:24 +1100 Subject: [PATCH 1/7] MDL-61514 Quiz: Make sure tag id is int when being saved as json --- mod/quiz/locallib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 9956fad8c6c..1fd328548d6 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -2456,7 +2456,7 @@ function quiz_build_random_question_tag_json($tagrecords) { ]; } else if ($tag = core_tag_tag::get_by_name(0, $tagrecord->name, 'id, name')) { $tags[] = [ - 'id' => $tag->id, + 'id' => (int)$tag->id, 'name' => $tagrecord->name ]; } else { From 76d1f12d15df678bacff2786088f64de84f084be Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 12 Mar 2018 02:42:20 +1100 Subject: [PATCH 2/7] MDL-61514 Quiz: Add matchbyid arg to quiz_extract_random_question_tags Let the quiz_extract_random_question_tags and quiz_extract_random_question_tag_ids functions to decide whether they should use tag id to find tags or not. --- mod/quiz/locallib.php | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 1fd328548d6..9350ec95985 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -2449,7 +2449,7 @@ function quiz_is_overriden_calendar_event(\calendar_event $event) { function quiz_build_random_question_tag_json($tagrecords) { $tags = []; foreach ($tagrecords as $tagrecord) { - if ($tag = core_tag_tag::get($tagrecord->id, 'id, name')) { + if ($tagrecord->id && $tag = core_tag_tag::get($tagrecord->id, 'id, name')) { $tags[] = [ 'id' => (int)$tagrecord->id, 'name' => $tag->name @@ -2475,20 +2475,25 @@ function quiz_build_random_question_tag_json($tagrecords) { * @param string $tagsjson The JSON string representing an array of tags in the [{"id":tagid,"name":"tagname"}] format. * E.g. [{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}] * Usually equal to the value of the tags field retrieved from the quiz_slots table. + * @param bool $matchbyid If set to true, then the function tries to find tags by their id. + * If no tag is found by the tag id or if $matchbyid is set to false, then the function tries to find the tag by its name. * @return array An array of tags containing the id and name properties, indexed by tag ids. */ -function quiz_extract_random_question_tags($tagsjson) { +function quiz_extract_random_question_tags($tagsjson, $matchbyid = true) { $tagrecords = []; if (!empty($tagsjson)) { $tags = json_decode($tagsjson); - // Only work with tags that exist. + foreach ($tags as $tagdata) { - if (!array_key_exists($tagdata->id, $tagrecords)) { - if ($tag = core_tag_tag::get($tagdata->id, 'id, name')) { - $tagrecords[$tag->id] = $tag->to_object(); - } else if ($tag = core_tag_tag::get_by_name(0, $tagdata->name, 'id, name')) { - $tagrecords[$tag->id] = $tag->to_object(); - } + if ($matchbyid && $tag = core_tag_tag::get($tagdata->id, 'id, name')) { + $tagrecords[] = $tag->to_object(); + } else if ($tag = core_tag_tag::get_by_name(0, $tagdata->name, 'id, name')) { + $tagrecords[] = $tag->to_object(); + } else { + $tagrecords[] = (object)[ + 'id' => null, + 'name' => $tagdata->name + ]; } } } @@ -2502,9 +2507,13 @@ function quiz_extract_random_question_tags($tagsjson) { * @param string $tagsjson The JSON string representing an array of tags in the [{"id":tagid,"name":"tagname"}] format. * E.g. [{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}] * Usually equal to the value of the tags field retrieved from the {quiz_slots} table. + * @param bool $matchbyid If set to true, then this function relies on the tag ids that are stored in $tagsjson to find tags. + * If no tag is found by the tag id or if $matchbyid is set to false, then this function tries to find the tag by its name. * @return int[] List of tag ids. */ -function quiz_extract_random_question_tag_ids($tagsjson) { - $tags = quiz_extract_random_question_tags($tagsjson); - return array_keys($tags); +function quiz_extract_random_question_tag_ids($tagsjson, $matchbyid = true) { + $tags = quiz_extract_random_question_tags($tagsjson, $matchbyid); + + // Only work with tags that exist. + return array_filter(array_column($tags, 'id')); } \ No newline at end of file From fb5343a00c728cc7219470b761fe86892ba94969 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 12 Mar 2018 13:38:12 +1100 Subject: [PATCH 3/7] MDL-61514 Quiz: unit tests for new functions in mod/quiz/locallib.php unit testing - quiz_build_random_question_tag_json - quiz_extract_random_question_tags - quiz_extract_random_question_tag_ids --- mod/quiz/tests/locallib_test.php | 220 +++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/mod/quiz/tests/locallib_test.php b/mod/quiz/tests/locallib_test.php index 7da393e90e8..3ef0ad13d21 100644 --- a/mod/quiz/tests/locallib_test.php +++ b/mod/quiz/tests/locallib_test.php @@ -420,4 +420,224 @@ class mod_quiz_locallib_testcase extends advanced_testcase { $this->assertEquals($comparearray, quiz_get_user_timeclose($course->id)); } + + public function test_quiz_build_random_question_tag_json() { + $this->resetAfterTest(); + + // Setup test data. + $footagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'foo', + 'description' => 'foo desc' + ); + $footag = $this->getDataGenerator()->create_tag($footagrecord); + $bartagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'bar', + 'description' => 'bar desc' + ); + $bartag = $this->getDataGenerator()->create_tag($bartagrecord); + $baztagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'baz', + 'description' => 'baz desc' + ); + $baztag = $this->getDataGenerator()->create_tag($baztagrecord); + $quxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'qux', + 'description' => 'qux desc' + ); + $quxtag = $this->getDataGenerator()->create_tag($quxtagrecord); + $quuxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'quux', + 'description' => 'quux desc' + ); + $quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord); + + $tagrecords = array( + (object)[ + 'id' => $footag->id, + 'name' => 'foo' + ], + (object)[ + 'id' => 999, // An invalid tag id. + 'name' => 'bar' + ], + (object)[ + 'id' => null, + 'name' => 'baz' + ], + (object)[ + 'id' => $quxtag->id, + 'name' => 'invalidqux' // An invalid tag name. + ], + (object)[ + 'id' => 999, // An invalid tag id. + 'name' => 'invalidquux' // An invalid tag name. + ], + ); + + $expectedjson = json_encode(array( + ['id' => (int)$footag->id, 'name' => $footag->name], + ['id' => (int)$bartag->id, 'name' => $bartag->name], + ['id' => (int)$baztag->id, 'name' => $baztag->name], + ['id' => (int)$quxtag->id, 'name' => $quxtag->name], + ['id' => null, 'name' => 'invalidquux'], + )); + $this->assertEquals($expectedjson, quiz_build_random_question_tag_json($tagrecords)); + } + + public function test_quiz_extract_random_question_tags() { + $this->resetAfterTest(); + + // Setup test data. + $footagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'foo', + 'description' => 'foo desc' + ); + $footag = $this->getDataGenerator()->create_tag($footagrecord); + $bartagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'bar', + 'description' => 'bar desc' + ); + $bartag = $this->getDataGenerator()->create_tag($bartagrecord); + $baztagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'baz', + 'description' => 'baz desc' + ); + $baztag = $this->getDataGenerator()->create_tag($baztagrecord); + $quxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'qux', + 'description' => 'qux desc' + ); + $quxtag = $this->getDataGenerator()->create_tag($quxtagrecord); + $quuxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'quux', + 'description' => 'quux desc' + ); + $quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord); + + $tagjson = json_encode(array( + [ + 'id' => $footag->id, + 'name' => 'foo' + ], + [ + 'id' => 999, // An invalid tag id. + 'name' => 'bar' + ], + [ + 'id' => null, + 'name' => 'baz' + ], + [ + 'id' => $quxtag->id, + 'name' => 'invalidqux' // An invalid tag name. + ], + [ + 'id' => 999, // An invalid tag id. + 'name' => 'invalidquux' // An invalid tag name. + ], + )); + + $expectedrecords = array( + (object)['id' => $footag->id, 'name' => $footag->name], + (object)['id' => $bartag->id, 'name' => $bartag->name], + (object)['id' => $baztag->id, 'name' => $baztag->name], + (object)['id' => $quxtag->id, 'name' => $quxtag->name], + (object)['id' => null, 'name' => 'invalidquux'], + ); + + $this->assertEquals($expectedrecords, quiz_extract_random_question_tags($tagjson)); + } + + public function test_quiz_extract_random_question_tag_ids() { + $this->resetAfterTest(); + + // Setup test data. + $footagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'foo', + 'description' => 'foo desc' + ); + $footag = $this->getDataGenerator()->create_tag($footagrecord); + $bartagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'bar', + 'description' => 'bar desc' + ); + $bartag = $this->getDataGenerator()->create_tag($bartagrecord); + $baztagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'baz', + 'description' => 'baz desc' + ); + $baztag = $this->getDataGenerator()->create_tag($baztagrecord); + $quxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'qux', + 'description' => 'qux desc' + ); + $quxtag = $this->getDataGenerator()->create_tag($quxtagrecord); + $quuxtagrecord = array( + 'isstandard' => 1, + 'flag' => 0, + 'rawname' => 'quux', + 'description' => 'quux desc' + ); + $quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord); + + $tagjson = json_encode(array( + [ + 'id' => $footag->id, + 'name' => 'foo' + ], + [ + 'id' => 999, // An invalid tag id. + 'name' => 'bar' + ], + [ + 'id' => null, + 'name' => 'baz' + ], + [ + 'id' => $quxtag->id, + 'name' => 'invalidqux' // An invalid tag name. + ], + [ + 'id' => 999, // An invalid tag id. + 'name' => 'invalidquux' // An invalid tag name. + ], + )); + + $expectedrecords = array( + $footag->id, + $bartag->id, + $baztag->id, + $quxtag->id, + ); + + $this->assertEquals($expectedrecords, quiz_extract_random_question_tag_ids($tagjson)); + } } From c907987844f4e4207fa8a6bfc2d4d1e27b0b1735 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 12 Mar 2018 11:22:20 +1100 Subject: [PATCH 4/7] MDL-61514 Quiz: Display unavailable tags when editing random questions When editing a random question, the form will display the tags that the random question is configured to get questions from even if those tags do not exist anymore. --- mod/quiz/classes/form/randomquestion_form.php | 16 ++++++++++++++++ mod/quiz/lang/en/quiz.php | 1 + 2 files changed, 17 insertions(+) diff --git a/mod/quiz/classes/form/randomquestion_form.php b/mod/quiz/classes/form/randomquestion_form.php index ef138166a47..140f71810f4 100644 --- a/mod/quiz/classes/form/randomquestion_form.php +++ b/mod/quiz/classes/form/randomquestion_form.php @@ -81,4 +81,20 @@ class randomquestion_form extends \moodleform { $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); $mform->closeHeaderBefore('buttonar'); } + + public function set_data($defaultvalues) { + $mform = $this->_form; + + if ($defaultvalues->fromtags) { + $fromtagselement = $mform->getElement('fromtags'); + foreach ($defaultvalues->fromtags as $fromtag) { + if (!$fromtagselement->optionExists($fromtag)) { + $optionname = get_string('randomfromunavailabletag', 'mod_quiz', explode(',', $fromtag)[1]); + $fromtagselement->addOption($optionname, $fromtag); + } + } + } + + parent::set_data($defaultvalues); + } } diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 9171ba1a672..6f88c803e08 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -700,6 +700,7 @@ $string['randomcreate'] = 'Create random questions'; $string['randomediting'] = 'Editing a random question'; $string['randomfromcategory'] = 'Random question from category:'; $string['randomfromexistingcategory'] = 'Random question from an existing category'; +$string['randomfromunavailabletag'] = '{$a} (unavailable)'; $string['randomnumber'] = 'Number of random questions'; $string['randomnosubcat'] = 'Questions from this category only, not its subcategories.'; $string['randomquestion'] = 'Random question'; From 9859359fbcdba9a48fdb905616b594853178e5cc Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Thu, 1 Mar 2018 12:39:49 +1100 Subject: [PATCH 5/7] MDL-61514 Quiz: Include random question's new data in backup and restore --- mod/quiz/backup/moodle2/backup_quiz_stepslib.php | 2 +- mod/quiz/backup/moodle2/restore_quiz_stepslib.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index 59b3b67013a..97d867c1f20 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -58,7 +58,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru $qinstances = new backup_nested_element('question_instances'); $qinstance = new backup_nested_element('question_instance', array('id'), array( - 'slot', 'page', 'requireprevious', 'questionid', 'maxmark')); + 'slot', 'page', 'requireprevious', 'questionid', 'questioncategoryid', 'includingsubcategories', 'tags', 'maxmark')); $sections = new backup_nested_element('sections'); diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index d695ca15138..12036f51f26 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -292,6 +292,15 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $data->quizid = $this->get_new_parentid('quiz'); $data->questionid = $this->get_mappingid('question', $data->questionid); + if (isset($data->questioncategoryid)) { + $data->questioncategoryid = $this->get_mappingid('question_category', $data->questioncategoryid); + } + + if (isset($data->tags)) { + $tags = quiz_extract_random_question_tags($data->tags, $this->task->is_samesite()); + $data->tags = quiz_build_random_question_tag_json($tags); + } + $DB->insert_record('quiz_slots', $data); } From 33b7941d9416320522c84ebad5cfbb7452607913 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Thu, 1 Mar 2018 13:57:25 +1100 Subject: [PATCH 6/7] MDL-61514 Quiz: Backward compatibility for backups from 3.4 and earlier --- mod/quiz/backup/moodle2/restore_quiz_stepslib.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index 12036f51f26..5f76fa56fbd 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -290,10 +290,15 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st } $data->quizid = $this->get_new_parentid('quiz'); - $data->questionid = $this->get_mappingid('question', $data->questionid); + $questionmapping = $this->get_mapping('question', $data->questionid); + $data->questionid = $questionmapping ? $questionmapping->newitemid : false; if (isset($data->questioncategoryid)) { $data->questioncategoryid = $this->get_mappingid('question_category', $data->questioncategoryid); + } else if ($questionmapping && $questionmapping->info->qtype == 'random') { + // Backward compatibility for backups created using Moodle 3.4 or earlier. + $data->questioncategoryid = $this->get_mappingid('question_category', $questionmapping->parentitemid); + $data->includingsubcategories = $questionmapping->info->questiontext ? 1 : 0; } if (isset($data->tags)) { From ae88770b33b65b67fecf05967aee28c68a66d72b Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Sun, 11 Mar 2018 16:03:19 +1100 Subject: [PATCH 7/7] MDL-61514 Quiz: unit testing restore --- .../tests/fixtures/random_by_tag_quiz.mbz | Bin 0 -> 4894 bytes mod/quiz/tests/tags_test.php | 95 ++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 mod/quiz/tests/fixtures/random_by_tag_quiz.mbz create mode 100644 mod/quiz/tests/tags_test.php diff --git a/mod/quiz/tests/fixtures/random_by_tag_quiz.mbz b/mod/quiz/tests/fixtures/random_by_tag_quiz.mbz new file mode 100644 index 0000000000000000000000000000000000000000..643b0c1731958c7b3b4d2c35540456a0e5e80be6 GIT binary patch literal 4894 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 Q0RR63039i9fdH@o0QXO#r2qf` literal 0 HcmV?d00001 diff --git a/mod/quiz/tests/tags_test.php b/mod/quiz/tests/tags_test.php new file mode 100644 index 00000000000..c5fbbcd7b19 --- /dev/null +++ b/mod/quiz/tests/tags_test.php @@ -0,0 +1,95 @@ +. + +/** + * Unit tests for usage of tags in quizzes. + * + * @package mod_quiz + * @copyright 2018 Shamim Rezaie + * @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'); + +/** + * Class mod_quiz_tags_testcase + * Class for tests related to usage of question tags in quizzes. + * + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_quiz_tags_testcase extends advanced_testcase { + public function test_restore_random_question_by_tag() { + global $CFG, $USER, $DB; + + require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); + + $this->resetAfterTest(); + $this->setAdminUser(); + + $backupid = 'abc'; + $backuppath = $CFG->tempdir . '/backup/' . $backupid; + check_dir_exists($backuppath); + get_file_packer('application/vnd.moodle.backup')->extract_to_pathname( + __DIR__ . "/fixtures/random_by_tag_quiz.mbz", $backuppath); + + // Do the restore to new course with default settings. + $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); + $newcourseid = restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid); + $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_NEW_COURSE); + + $this->assertTrue($rc->execute_precheck()); + $rc->execute_plan(); + $rc->destroy(); + + // Get the information about the resulting course and check that it is set up correctly. + $modinfo = get_fast_modinfo($newcourseid); + $quiz = array_values($modinfo->get_instances_of('quiz'))[0]; + $quizobj = quiz::create($quiz->instance); + $structure = \mod_quiz\structure::create_for_quiz($quizobj); + + // Are the correct slots returned? + $slots = $structure->get_slots(); + $this->assertCount(1, $slots); + + $quizobj->preload_questions(); + $quizobj->load_questions(); + $questions = $quizobj->get_questions(); + + $this->assertCount(1, $questions); + + $question = array_values($questions)[0]; + + $tag1 = core_tag_tag::get_by_name(0, 't1', 'id, name'); + $this->assertNotFalse($tag1); + + $tag2 = core_tag_tag::get_by_name(0, 't2', 'id, name'); + $this->assertNotFalse($tag2); + + $tag3 = core_tag_tag::get_by_name(0, 't3', 'id, name'); + $this->assertNotFalse($tag3); + + $tagrecords = array($tag2->to_object()); + $this->assertEquals(quiz_build_random_question_tag_json($tagrecords), $question->randomfromtags); + + $defaultcategory = question_get_default_category(context_course::instance($newcourseid)->id); + $this->assertEquals($defaultcategory->id, $question->randomfromcategory); + $this->assertEquals(0, $question->randomincludingsubcategories); + } +}