From a0cf3d2da53afe3c93ca99de071e0a7356271a9c Mon Sep 17 00:00:00 2001 From: Simon Adams Date: Fri, 16 Aug 2024 09:37:40 +0100 Subject: [PATCH] MDL-71378 core_question & mod_quiz: Refactor deprecated contexts --- .upgradenotes/MDL-71378-2024070813542859.yml | 17 + .upgradenotes/MDL-71378-2024070813572581.yml | 42 + .../local/areas/core_question/answerbase.php | 13 +- .../local/areas/core_question/base.php | 29 +- admin/tool/brickfield/tests/area_test.php | 44 +- .../core_question/questionanswers_test.php | 132 +-- .../areas/core_question/questiontext_test.php | 149 +-- backup/moodle2/restore_stepslib.php | 12 +- course/classes/category.php | 9 - lang/en/error.php | 4 +- lang/en/question.php | 4 +- lib/classes/event/question_base.php | 16 +- lib/classes/event/question_category_base.php | 10 +- lib/classes/event/question_moved.php | 13 +- lib/classes/event/questions_exported.php | 10 +- lib/classes/event/questions_imported.php | 10 +- lib/moodlelib.php | 6 - lib/questionlib.php | 225 +--- .../behat/menu_navigation_topics.feature | 2 +- lib/tests/navigation/views/secondary_test.php | 2 +- lib/tests/questionlib_test.php | 1047 ++--------------- mod/quiz/classes/structure.php | 28 +- mod/quiz/edit.php | 2 +- mod/quiz/editgrading.php | 2 +- mod/quiz/lang/en/deprecated.txt | 3 + mod/quiz/lang/en/quiz.php | 9 +- .../overview/tests/behat/regrading.feature | 6 +- mod/quiz/tests/behat/attempt_basic.feature | 17 +- mod/quiz/tests/behat/attempt_begin.feature | 7 +- .../behat/attempt_multiple_grades.feature | 10 +- .../behat/attempt_require_previous.feature | 7 +- .../behat/attempt_review_options.feature | 7 +- .../tests/behat/attempt_sequential.feature | 10 +- mod/quiz/tests/behat/behat_mod_quiz.php | 5 +- ...completion_condition_attempts_used.feature | 12 +- .../tests/behat/completion_manual.feature | 12 +- mod/quiz/tests/behat/editing_add.feature | 29 +- .../tests/behat/editing_move_by_click.feature | 10 +- .../behat/editing_multiple_grades.feature | 10 +- .../behat/editing_question_numbering.feature | 13 +- .../editing_remove_multiple_questions.feature | 10 +- .../behat/editing_remove_question.feature | 10 +- .../behat/editing_require_previous.feature | 7 +- .../behat/editing_section_headings.feature | 7 +- .../editing_set_marks_no_attempts.feature | 8 +- .../tests/behat/editing_switch_bank.feature | 7 - .../behat/feedback_on_override_mark.feature | 10 +- mod/quiz/tests/behat/flag_questions.feature | 17 +- mod/quiz/tests/behat/group_mode.feature | 5 +- mod/quiz/tests/behat/info_page.feature | 7 +- .../behat/manually_mark_question.feature | 11 +- mod/quiz/tests/behat/preview.feature | 10 +- .../behat/quiz_activity_availability.feature | 7 +- .../behat/quiz_activity_certainty.feature | 10 +- .../behat/quiz_activity_completion.feature | 12 +- .../quiz_activity_completion_locked.feature | 12 +- .../quiz_activity_completion_unlocked.feature | 14 +- mod/quiz/tests/behat/quiz_feedback.feature | 7 +- .../quiz_interactive_multiple_tries.feature | 10 +- .../behat/quiz_question_versions.feature | 10 +- mod/quiz/tests/behat/quiz_reset.feature | 12 +- mod/quiz/tests/behat/random_question.feature | 35 +- mod/quiz/tests/behat/reattempt_quiz.feature | 10 +- .../settings_form_fields_disableif.feature | 9 +- mod/quiz/tests/quiz_question_restore_test.php | 11 +- .../classes/column_manager.php | 2 +- .../tests/behat/question_bank.feature | 2 +- .../tests/column_manager_test.php | 4 +- question/bank/comment/tests/backup_test.php | 3 +- .../tests/behat/question_comment.feature | 4 +- .../behat/question_comment_column.feature | 4 +- question/bank/comment/tests/lib_test.php | 6 +- .../customfields/tests/customfield_test.php | 3 +- .../tests/question_handler_test.php | 5 +- .../deletequestion/classes/delete_action.php | 8 +- question/bank/deletequestion/delete.php | 17 +- .../behat/delete_question_column.feature | 5 +- question/bank/editquestion/addquestion.php | 29 +- .../bank/editquestion/classes/copy_action.php | 6 +- .../bank/editquestion/classes/edit_action.php | 6 +- question/bank/editquestion/question.php | 34 +- .../tests/behat/qbank_editquestion.feature | 4 +- .../tests/behat/question_status.feature | 4 +- question/bank/exporttoxml/classes/helper.php | 18 +- question/bank/exporttoxml/exportone.php | 20 +- .../tests/behat/export_to_xml_action.feature | 4 +- .../tests/behat/preview_question.feature | 7 +- question/bank/history/classes/helper.php | 27 + .../bank/history/classes/history_action.php | 4 +- .../history/classes/question_history_view.php | 4 + question/bank/history/history.php | 2 +- .../behat/question_history_action.feature | 8 +- .../behat/question_version_column.feature | 4 +- question/bank/history/tests/helper_test.php | 12 +- .../tests/question_history_view_test.php | 9 +- question/bank/importquestions/import.php | 10 +- question/bank/managecategories/category.php | 18 +- .../classes/category_condition.php | 7 +- .../classes/question_categories.php | 4 + .../classes/question_category_list_item.php | 6 +- .../behat/move_question_categories.feature | 51 +- .../tests/behat/question_categories.feature | 45 +- .../question_categories_idnumber.feature | 21 +- .../view_manage_categories_plugin.feature | 4 +- .../tests/external/move_category_test.php | 142 +-- .../managecategories/tests/helper_test.php | 2 +- .../tests/manage_category_test_base.php | 46 +- .../tests/question_categories_test.php | 16 +- .../bank/previewquestion/classes/helper.php | 24 +- question/bank/previewquestion/preview.php | 7 +- .../tests/behat/preview_question.feature | 4 +- .../behat/preview_question_action.feature | 4 +- .../tests/qbank_preview_helper_test.php | 12 +- .../tests/behat/statistics_action.feature | 4 +- .../tests/behat/statistics_values.feature | 23 +- .../classes/external/submit_tags.php | 25 +- .../tagquestion/classes/form/tags_form.php | 19 - question/bank/tagquestion/lib.php | 10 +- .../tests/behat/tag_question_action.feature | 4 +- .../tests/external/submit_tags_test.php | 176 --- .../behat/question_last_used_column.feature | 4 +- .../tests/behat/question_usage_column.feature | 5 +- .../behat/view_view_creator_column.feature | 4 +- .../behat/question_in_place_editing.feature | 4 +- .../behat/view_question_name_column.feature | 4 +- .../behat/view_question_text_column.feature | 4 +- .../behat/view_question_type_column.feature | 4 +- question/classes/local/bank/view.php | 15 +- .../output/question_bank_filter_ui.php | 14 +- question/edit.php | 5 + question/editlib.php | 44 +- question/format.php | 54 +- .../aiken/tests/behat/aiken_export.feature | 9 +- .../aiken/tests/behat/aiken_import.feature | 5 +- .../aiken/tests/qformat_aiken_export_test.php | 6 +- question/format/gift/format.php | 2 +- .../gift/tests/behat/import_export.feature | 7 +- question/format/xml/format.php | 2 +- .../xml/tests/behat/import_export.feature | 13 +- .../fixtures/categories_reverse_order.xml | 4 +- .../fixtures/category_with_description.xml | 2 +- .../tests/fixtures/deprecated_category.xml | 40 + .../xml/tests/fixtures/export_category.xml | 2 +- .../fixtures/html_chars_in_idnumbers.xml | 2 +- .../format/xml/tests/fixtures/multianswer.xml | 2 +- .../format/xml/tests/fixtures/multichoice.xml | 2 +- .../xml/tests/fixtures/nested_categories.xml | 6 +- .../nested_categories_with_questions.xml | 8 +- .../xml/tests/fixtures/old_format_file.xml | 4 +- .../sample_questions_with_old_image_tag.xml | 2 +- .../format/xml/tests/fixtures/truefalse.xml | 2 +- .../tests/qformat_xml_import_export_test.php | 61 +- question/templates/qbank_action_menu.mustache | 8 +- question/tests/backup_test.php | 13 +- .../behat/bank_add_default_shared.feature | 1 - question/tests/behat/bank_view.feature | 13 +- question/tests/behat/behat_core_question.php | 64 +- question/tests/behat/delete_questions.feature | 13 +- .../tests/behat/duplicate_questions.feature | 9 +- .../tests/behat/edit_question_tags.feature | 9 +- .../behat/edit_question_versioning.feature | 10 +- question/tests/behat/edit_questions.feature | 13 +- .../edit_questions_standard_tags.feature | 5 +- .../behat/filter_questions_by_tag.feature | 10 +- ...lter_questions_combined_conditions.feature | 12 +- .../tests/behat/question_categories.feature | 39 - .../question_categories_idnumber.feature | 39 +- .../behat/question_filter_pagination.feature | 11 +- .../tests/behat/question_highlight.feature | 7 +- question/tests/behat/select_questions.feature | 9 +- question/tests/behat/sort_questions.feature | 10 +- question/tests/category_manager_test.php | 37 +- question/tests/generator/lib.php | 19 + question/tests/privacy/provider_test.php | 26 +- question/tests/question_bank_column_test.php | 10 +- .../calculated/tests/question_type_test.php | 2 +- question/type/essay/tests/restore_test.php | 2 +- .../gapselect/tests/behat/import_test.feature | 1 + .../multianswer/tests/question_type_test.php | 2 +- question/type/questiontypebase.php | 2 + .../truefalse/tests/question_type_test.php | 2 +- .../tests/behat/question_reports.feature | 24 +- ...ehat_theme_classic_behat_core_question.php | 11 +- 183 files changed, 1266 insertions(+), 2721 deletions(-) create mode 100644 .upgradenotes/MDL-71378-2024070813542859.yml create mode 100644 .upgradenotes/MDL-71378-2024070813572581.yml create mode 100644 question/format/xml/tests/fixtures/deprecated_category.xml delete mode 100644 question/tests/behat/question_categories.feature diff --git a/.upgradenotes/MDL-71378-2024070813542859.yml b/.upgradenotes/MDL-71378-2024070813542859.yml new file mode 100644 index 00000000000..6f8a98b0390 --- /dev/null +++ b/.upgradenotes/MDL-71378-2024070813542859.yml @@ -0,0 +1,17 @@ +issueNumber: MDL-71378 +notes: + tool_brickfield: + - message: > + tool_brickfield\local\areas\core_question\answerbase::find_system_areas + + + No replacement. System context no longer a valid context to assign a + question category + type: deprecated + - message: > + tool_brickfield\local\areas\core_question\base::find_system_areas + + + No replacement. System context no longer a valid context to assign a + question category + type: deprecated diff --git a/.upgradenotes/MDL-71378-2024070813572581.yml b/.upgradenotes/MDL-71378-2024070813572581.yml new file mode 100644 index 00000000000..5acaf7a941b --- /dev/null +++ b/.upgradenotes/MDL-71378-2024070813572581.yml @@ -0,0 +1,42 @@ +issueNumber: MDL-71378 +notes: + core: + - message: > + question_make_default_categories() + + + No longer creates a default category in either CONTEXT_SYSTEM, + CONTEXT_COURSE, or CONTEXT_COURSECAT. + + Superceded by question_get_default_category which can optionally create one if it does not exist. + type: deprecated + - message: | + question_delete_course() + + No replacement. Course contexts no longer hold question categories. + type: deprecated + - message: | + question_delete_course_category() + + Course category contexts no longer hold question categories. + type: deprecated + qbank_bulkmove: + - message: > + qbank_bulkmove/helper::get_displaydata + + + Superceded by a modal and webservice, see + qbank_bulkmove/modal_question_bank_bulkmove and + core_question_external\move_questions + type: deprecated + - message: | + qbank_bulkmove\output\renderer::render_bulk_move_form + + Superceded by qbank_bulkmove\output\bulk_move + type: deprecated + core_question: + - message: | + question_type::generate_test + + No replacement, not used anywhere in core. + type: deprecated diff --git a/admin/tool/brickfield/classes/local/areas/core_question/answerbase.php b/admin/tool/brickfield/classes/local/areas/core_question/answerbase.php index 66c96fbee78..fa824dc7923 100644 --- a/admin/tool/brickfield/classes/local/areas/core_question/answerbase.php +++ b/admin/tool/brickfield/classes/local/areas/core_question/answerbase.php @@ -82,8 +82,12 @@ abstract class answerbase extends base { * question categories at the system context, or course category context. * * @return mixed + * @deprecated since Moodle 5.0. + * @todo MDL-82413 Final deprecation in Moodle 6.0. */ + #[\core\attribute\deprecated(null, since: '5.0', reason: 'This method should not be used', mdl: 'MDL-71378')] public function find_system_areas(): ?\moodle_recordset { + \core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]); global $DB; $params = [ 'syscontext' => CONTEXT_SYSTEM, @@ -132,8 +136,6 @@ abstract class answerbase extends base { $coursecontext = \context_course::instance($courseid); $param = [ - 'ctxcourse' => CONTEXT_COURSE, - 'courseid' => $courseid, 'module' => CONTEXT_MODULE, 'coursecontextpath' => $DB->sql_like_escape($coursecontext->path) . '/%', ]; @@ -157,11 +159,8 @@ abstract class answerbase extends base { ON qc.id = qbe.questioncategoryid INNER JOIN {context} ctx ON ctx.id = qc.contextid - WHERE (ctx.contextlevel = :ctxcourse - AND ctx.id = qc.contextid - AND ctx.instanceid = :courseid) - OR (ctx.contextlevel = :module - AND {$DB->sql_like('ctx.path', ':coursecontextpath')}) + WHERE ctx.contextlevel = :module + AND {$DB->sql_like('ctx.path', ':coursecontextpath')} ORDER BY a.id ASC"; return $DB->get_recordset_sql($sql, $param); diff --git a/admin/tool/brickfield/classes/local/areas/core_question/base.php b/admin/tool/brickfield/classes/local/areas/core_question/base.php index 195b3da8857..be04ff035b6 100644 --- a/admin/tool/brickfield/classes/local/areas/core_question/base.php +++ b/admin/tool/brickfield/classes/local/areas/core_question/base.php @@ -74,8 +74,6 @@ abstract class base extends area_base { global $DB; $coursecontext = \context_course::instance($courseid); $param = [ - 'ctxcourse' => CONTEXT_COURSE, - 'courseid' => $courseid, 'module' => CONTEXT_MODULE, 'coursecontextpath' => $DB->sql_like_escape($coursecontext->path) . '/%', ]; @@ -96,11 +94,8 @@ abstract class base extends area_base { ON qc.id = qbe.questioncategoryid INNER JOIN {context} ctx ON ctx.id = qc.contextid - WHERE (ctx.contextlevel = :ctxcourse - AND ctx.id = qc.contextid - AND ctx.instanceid = :courseid) - OR (ctx.contextlevel = :module - AND {$DB->sql_like('ctx.path', ':coursecontextpath')}) + WHERE ctx.contextlevel = :module + AND {$DB->sql_like('ctx.path', ':coursecontextpath')} ORDER BY q.id ASC"; return $DB->get_recordset_sql($sql, $param); @@ -111,8 +106,12 @@ abstract class base extends area_base { * question categories at the system context only. * * @return \moodle_recordset + * @deprecated since Moodle 5.0. + * @todo MDL-82413 Final deprecation in Moodle 6.0. */ + #[\core\attribute\deprecated(null, since: '5.0', reason: 'This method should not be used', mdl: 'MDL-71378')] public function find_system_areas(): ?\moodle_recordset { + \core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]); global $DB; $params = [ 'syscontext' => CONTEXT_SYSTEM, @@ -175,15 +174,7 @@ abstract class base extends area_base { $catid = 'null'; if ($record = self::get_course_and_category(CONTEXT_MODULE, $event->objectid)) { - if ($record->contextlevel == CONTEXT_MODULE) { - $courseid = $record->courseid; - } else if ($record->contextlevel == CONTEXT_COURSE) { - $courseid = $record->instanceid; - } else if ($record->contextlevel == CONTEXT_COURSECAT) { - $catid = $record->instanceid; - } else if ($record->contextlevel == CONTEXT_SYSTEM) { - $courseid = 1; - } + $courseid = $record->courseid; } return " @@ -202,6 +193,10 @@ abstract class base extends area_base { public static function get_course_and_category($coursemodule, $refid) { global $DB; + if ($coursemodule !== CONTEXT_MODULE) { + debugging("Invalid contextlevel: ($coursemodule}", DEBUG_DEVELOPER); + } + $sql = 'SELECT ctx.instanceid, cm.course as courseid, ctx.contextlevel @@ -214,7 +209,7 @@ abstract class base extends area_base { ON qc.id = qbe.questioncategoryid INNER JOIN {context} ctx ON ctx.id = qc.contextid - LEFT JOIN {course_modules} cm + INNER JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :coursemodule WHERE q.id = :refid'; diff --git a/admin/tool/brickfield/tests/area_test.php b/admin/tool/brickfield/tests/area_test.php index f3b2d4b8084..9432fc28e7e 100644 --- a/admin/tool/brickfield/tests/area_test.php +++ b/admin/tool/brickfield/tests/area_test.php @@ -93,51 +93,30 @@ class area_test extends area_test_base { $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $component = 'core_question'; - list($category1, $course1, $qcat1, $questions1) = $generator->setup_course_and_questions('course'); - list($category2, $course2, $qcat2, $questions2) = $generator->setup_course_and_questions('category'); - list($category3, $course3, $qcat3, $questions3) = $generator->setup_course_and_questions('system'); + [$category, $course, $qcat, $questions, $qbank] = $generator->setup_course_and_questions(); $c = new \tool_brickfield\local\areas\core_question\questiontext(); // Set up results arrays from the recordset for easier testing. - $course1areas = $this->array_from_recordset($c->find_course_areas($course1->id)); - $course2areas = $c->find_course_areas($course2->id); - $course3areas = $c->find_course_areas($course3->id); - $sysareas = $this->array_from_recordset($c->find_system_areas()); + $course1areas = $this->array_from_recordset($c->find_course_areas($course->id)); - // Assert the core_question area exists for the individual question's context, courseid and categoryid. + // Assert the core_question area exists for the individual question's context, courseid and categoryid. $this->assert_area_in_array( $course1areas, $component, - \context_course::instance($course1->id)->id, - $questions1[0]->id, - $course1->id, - null - ); - $this->assert_area_in_array( - $sysareas, - $component, - \context_coursecat::instance($category2->id)->id, - $questions2[0]->id, - SITEID, - $category2->id - ); - $this->assert_area_in_array( - $sysareas, - $component, - \context_system::instance()->id, - $questions3[0]->id, - SITEID, + \context_module::instance($qbank->cmid)->id, + $questions[0]->id, + $course->id, null ); // Emulate the question_created event. - $event = \core\event\question_created::create_from_question_instance($questions1[1], - \context_course::instance($course1->id)); + $event = \core\event\question_created::create_from_question_instance($questions[1], + \context_module::instance($qbank->cmid)); $relevantresults = $this->array_from_recordset($c->find_relevant_areas($event)); $this->assert_area_in_array( - $course1areas, + $relevantresults, $component, - \context_course::instance($relevantresults[0]->courseid)->id, + $relevantresults[0]->contextid, $relevantresults[0]->itemid, $relevantresults[0]->courseid, $relevantresults[0]->categoryid @@ -154,7 +133,8 @@ class area_test extends area_test_base { /** @var \core_question_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $course = $this->getDataGenerator()->create_course(); - $cat = $generator->create_question_category(['contextid' => \context_course::instance($course->id)->id]); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $cat = $generator->create_question_category(['contextid' => \context_module::instance($qbank->cmid)->id]); $question1 = $generator->create_question('multichoice', null, ['name' => 'Example multichoice question', 'category' => $cat->id]); $question2 = $generator->create_question('numerical', null, diff --git a/admin/tool/brickfield/tests/local/areas/core_question/questionanswers_test.php b/admin/tool/brickfield/tests/local/areas/core_question/questionanswers_test.php index 6e9ecd2b02b..dae564cb2c0 100644 --- a/admin/tool/brickfield/tests/local/areas/core_question/questionanswers_test.php +++ b/admin/tool/brickfield/tests/local/areas/core_question/questionanswers_test.php @@ -34,92 +34,6 @@ class questionanswers_test extends \advanced_testcase { parent::setUpBeforeClass(); } - /** - * Test find course areas. - */ - public function test_find_course_areas(): void { - $this->resetAfterTest(); - $this->setAdminUser(); - - $category = $this->getDataGenerator()->create_category(); - $course = $this->getDataGenerator()->create_course(['category' => $category->id]); - $coursecontext = \context_course::instance($course->id); - $catcontext = \context_coursecat::instance($category->id); - $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat1 = $generator->create_question_category(['contextid' => $coursecontext->id]); - $question1 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); - $question2 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); - $questionanswers = new questionanswers(); - $rs = $questionanswers->find_course_areas($course->id); - $this->assertNotNull($rs); - - // Each multichoice question generated has four answers. So there should be eight records. - $count = 0; - foreach ($rs as $rec) { - $count++; - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - if ($count <= 4) { - $this->assertEquals($question1->id, $rec->refid); - } else { - $this->assertEquals($question2->id, $rec->refid); - } - } - $rs->close(); - $this->assertEquals(8, $count); - - // Add a question to a quiz in the course. - $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz1']); - $quizmodule = get_coursemodule_from_instance('quiz', $quiz->id, $course->id); - $quizcontext = \context_module::instance($quizmodule->id); - - // Add a question to the quiz context. - $cat2 = $generator->create_question_category(['contextid' => $quizcontext->id]); - $question3 = $generator->create_question('multichoice', null, ['category' => $cat2->id]); - $rs2 = $questionanswers->find_course_areas($course->id); - $this->assertNotNull($rs2); - - // Each multichoice question generated has four answers. So there should be twelve records now. - $count = 0; - foreach ($rs2 as $rec) { - $count++; - if ($count <= 4) { - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question1->id, $rec->refid); - } else if ($count <= 8) { - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question2->id, $rec->refid); - } else { - $this->assertEquals($quizcontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question3->id, $rec->refid); - } - } - $rs2->close(); - $this->assertEquals(12, $count); - - // Add a question to the category context. - $cat3 = $generator->create_question_category(['contextid' => $catcontext->id]); - $question4 = $generator->create_question('multichoice', null, ['category' => $cat3->id]); - $rs3 = $questionanswers->find_course_areas($course->id); - $this->assertNotNull($rs3); - - // The category level questions should not be found. - $count = 0; - foreach ($rs3 as $rec) { - $count++; - if ($count > 8) { - $this->assertEquals($quizcontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question3->id, $rec->refid); - } - } - $rs2->close(); - $this->assertEquals(12, $count); - } - /** * Test find relevant areas. */ @@ -128,9 +42,10 @@ class questionanswers_test extends \advanced_testcase { $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); - $coursecontext = \context_course::instance($course->id); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbankcontext = \context_module::instance($qbank->cmid); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat1 = $generator->create_question_category(['contextid' => $coursecontext->id]); + $cat1 = $generator->create_question_category(['contextid' => $qbankcontext->id]); $question1 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); $question2 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); $questionanswers = new questionanswers(); @@ -143,50 +58,11 @@ class questionanswers_test extends \advanced_testcase { $count = 0; foreach ($rs as $rec) { $count++; - $this->assertEquals($coursecontext->id, $rec->contextid); + $this->assertEquals($qbankcontext->id, $rec->contextid); $this->assertEquals($course->id, $rec->courseid); $this->assertEquals($question1->id, $rec->refid); } $rs->close(); $this->assertEquals(4, $count); } - - /** - * Test find system areas. - */ - public function test_find_system_areas(): void { - $this->resetAfterTest(); - $this->setAdminUser(); - - $category = $this->getDataGenerator()->create_category(); - $catcontext = \context_coursecat::instance($category->id); - $systemcontext = \context_system::instance(); - $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat = $generator->create_question_category(['contextid' => $catcontext->id]); - $cat2 = $generator->create_question_category(['contextid' => $systemcontext->id]); - $question = $generator->create_question('multichoice', null, ['category' => $cat2->id]); - $question2 = $generator->create_question('multichoice', null, ['category' => $cat->id]); - $questionanswers = new questionanswers(); - $rs = $questionanswers->find_system_areas(); - $this->assertNotNull($rs); - - // Each multichoice question generated has four answers. - $count = 0; - foreach ($rs as $rec) { - $count++; - if ($count <= 4) { - $this->assertEquals($systemcontext->id, $rec->contextid); - $this->assertEquals(1, $rec->courseid); - $this->assertEquals(0, $rec->categoryid); - $this->assertEquals($question->id, $rec->refid); - } else { - $this->assertEquals($catcontext->id, $rec->contextid); - $this->assertEquals(1, $rec->courseid); - $this->assertEquals($category->id, $rec->categoryid); - $this->assertEquals($question2->id, $rec->refid); - } - } - $rs->close(); - $this->assertEquals(8, $count); - } } diff --git a/admin/tool/brickfield/tests/local/areas/core_question/questiontext_test.php b/admin/tool/brickfield/tests/local/areas/core_question/questiontext_test.php index 522e056d9fa..36622a6a9fd 100644 --- a/admin/tool/brickfield/tests/local/areas/core_question/questiontext_test.php +++ b/admin/tool/brickfield/tests/local/areas/core_question/questiontext_test.php @@ -40,90 +40,6 @@ class questiontext_test extends area_test_base { parent::setUpBeforeClass(); } - /** - * Test find course areas. - */ - public function test_find_course_areas(): void { - $this->resetAfterTest(); - $this->setAdminUser(); - - $category = $this->getDataGenerator()->create_category(); - $course = $this->getDataGenerator()->create_course(['category' => $category->id]); - $coursecontext = \context_course::instance($course->id); - $catcontext = \context_coursecat::instance($category->id); - $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat1 = $generator->create_question_category(['contextid' => $coursecontext->id]); - $question1 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); - $question2 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); - $questiontext = new questiontext(); - $rs = $questiontext->find_course_areas($course->id); - $this->assertNotNull($rs); - - $count = 0; - foreach ($rs as $rec) { - $count++; - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - if ($count <= 1) { - $this->assertEquals($question1->id, $rec->itemid); - } else { - $this->assertEquals($question2->id, $rec->itemid); - } - } - $rs->close(); - $this->assertEquals(2, $count); - - // Add a question to a quiz in the course. - $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz1']); - $quizmodule = get_coursemodule_from_instance('quiz', $quiz->id, $course->id); - $quizcontext = \context_module::instance($quizmodule->id); - - // Add a question to the quiz context. - $cat2 = $generator->create_question_category(['contextid' => $quizcontext->id]); - $question3 = $generator->create_question('multichoice', null, ['category' => $cat2->id]); - $rs2 = $questiontext->find_course_areas($course->id); - $this->assertNotNull($rs2); - - $count = 0; - foreach ($rs2 as $rec) { - $count++; - if ($count <= 1) { - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question1->id, $rec->itemid); - } else if ($count <= 2) { - $this->assertEquals($coursecontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question2->id, $rec->itemid); - } else { - $this->assertEquals($quizcontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question3->id, $rec->itemid); - } - } - $rs2->close(); - $this->assertEquals(3, $count); - - // Add a question to the category context. - $cat3 = $generator->create_question_category(['contextid' => $catcontext->id]); - $question4 = $generator->create_question('multichoice', null, ['category' => $cat3->id]); - $rs3 = $questiontext->find_course_areas($course->id); - $this->assertNotNull($rs3); - - // The category level questions should not be found. - $count = 0; - foreach ($rs3 as $rec) { - $count++; - if ($count > 2) { - $this->assertEquals($quizcontext->id, $rec->contextid); - $this->assertEquals($course->id, $rec->courseid); - $this->assertEquals($question3->id, $rec->itemid); - } - } - $rs2->close(); - $this->assertEquals(3, $count); - } - /** * Test find relevant areas. */ @@ -132,9 +48,10 @@ class questiontext_test extends area_test_base { $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); - $coursecontext = \context_course::instance($course->id); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbankcontext = \context_module::instance($qbank->cmid); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat1 = $generator->create_question_category(['contextid' => $coursecontext->id]); + $cat1 = $generator->create_question_category(['contextid' => $qbankcontext->id]); $question1 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); $question2 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); $questiontext = new questiontext(); @@ -146,7 +63,7 @@ class questiontext_test extends area_test_base { $count = 0; foreach ($rs as $rec) { $count++; - $this->assertEquals($coursecontext->id, $rec->contextid); + $this->assertEquals($qbankcontext->id, $rec->contextid); $this->assertEquals($course->id, $rec->courseid); $this->assertEquals($question1->id, $rec->itemid); } @@ -154,45 +71,6 @@ class questiontext_test extends area_test_base { $this->assertEquals(1, $count); } - /** - * Test find system areas. - */ - public function test_find_system_areas(): void { - $this->resetAfterTest(); - $this->setAdminUser(); - - $category = $this->getDataGenerator()->create_category(); - $catcontext = \context_coursecat::instance($category->id); - $systemcontext = \context_system::instance(); - $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $component = 'core_question'; - - $cat = $generator->create_question_category(['contextid' => $catcontext->id]); - $cat2 = $generator->create_question_category(['contextid' => $systemcontext->id]); - $question = $generator->create_question('multichoice', null, ['category' => $cat2->id]); - $question2 = $generator->create_question('multichoice', null, ['category' => $cat->id]); - $questiontext = new questiontext(); - $areas = $this->array_from_recordset($questiontext->find_system_areas()); - - // Assert the core_question area exists for the individual question's context, courseid and categoryid. - $this->assert_area_in_array( - $areas, - $component, - $systemcontext->id, - $question->id, - SITEID, - null - ); - $this->assert_area_in_array( - $areas, - $component, - $catcontext->id, - $question2->id, - SITEID, - $category->id - ); - } - /** * Test get course and category. * @@ -203,21 +81,18 @@ class questiontext_test extends area_test_base { $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); - $coursecontext = \context_course::instance($course->id); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbankcontext = \context_module::instance($qbank->cmid); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $cat1 = $generator->create_question_category(['contextid' => $coursecontext->id]); + $cat1 = $generator->create_question_category(['contextid' => $qbankcontext->id]); $question1 = $generator->create_question('multichoice', null, ['category' => $cat1->id]); - $event = \core\event\question_updated::create_from_question_instance($question1, - \context_course::instance($course->id)); - $rs = base::get_course_and_category(CONTEXT_COURSE, $event->objectid); + $event = \core\event\question_updated::create_from_question_instance($question1, $qbankcontext); + $rs = base::get_course_and_category(CONTEXT_MODULE, $event->objectid); $this->assertNotNull($rs); - $this->assertEquals(CONTEXT_COURSE, $rs->contextlevel); - $this->assertNotEquals(CONTEXT_MODULE, $rs->contextlevel); - // Invalid objectid. + $this->assertEquals(CONTEXT_MODULE, $rs->contextlevel); + // Invalid objectid and contextlevel. $rs = base::get_course_and_category(CONTEXT_COURSE, 0); $this->assertFalse($rs); - // Incorrect objectid. - $rs = base::get_course_and_category(CONTEXT_COURSE, 100); - $this->assertFalse($rs); + $this->assertDebuggingCalled(); } } diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 41ad4e4ed72..3f72c09ef8e 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -5119,7 +5119,14 @@ class restore_create_categories_and_questions extends restore_structure_step { // As we can't create a 'Top' category in CONTEXT_COURSE we'll make a default // qbank module and map it to that until they are created later. if (empty($mapping->info->parent) && $before35) { - $top = question_get_top_category($data->contextid, true); + if ($context->contextlevel === CONTEXT_COURSE) { + $course = get_course($context->instanceid); + $defaultbank = \core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true); + $bankcontextid = $defaultbank->context->id; + } else { + $bankcontextid = $data->contextid; + } + $top = question_get_top_category($bankcontextid, true); $data->parent = $top->id; } @@ -5426,8 +5433,9 @@ class restore_create_categories_and_questions extends restore_structure_step { $newparent = 0; // No ctx match for both cats, no parent relationship } } + $context = \core\context::instance_by_id($dbcat->contextid); // Here with $newparent empty, problem with contexts or remapping, set it to top cat - if (!$newparent && $dbcat->parent) { + if (!$newparent && $dbcat->parent && $context->contextlevel === CONTEXT_MODULE) { $topcat = question_get_top_category($dbcat->contextid, true); if ($dbcat->parent != $topcat->id) { $DB->set_field('question_categories', 'parent', $topcat->id, array('id' => $dbcat->id)); diff --git a/course/classes/category.php b/course/classes/category.php index 86b6abb17ad..07716ccc216 100644 --- a/course/classes/category.php +++ b/course/classes/category.php @@ -2056,9 +2056,6 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr if (!$cb->delete_contents($this->get_context())) { throw new moodle_exception('errordeletingcontentfromcategory', 'contentbank', '', $this->get_formatted_name()); } - if (!question_delete_course_category($this, null)) { - throw new moodle_exception('cannotdeletecategoryquestions', '', '', $this->get_formatted_name()); - } // Delete all events in the category. $DB->delete_records('event', array('categoryid' => $this->id)); @@ -2245,12 +2242,6 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr ); } } - if (!question_delete_course_category($this, $newparentcat)) { - if ($showfeedback) { - echo $OUTPUT->notification(get_string('errordeletingquestionsfromcategory', 'question', $catname), 'notifysuccess'); - } - return false; - } // Finally delete the category and it's context. $categoryrecord = $this->get_db_record(); diff --git a/lang/en/error.php b/lang/en/error.php index 68c808b5ccf..64a6385ede1 100644 --- a/lang/en/error.php +++ b/lang/en/error.php @@ -65,7 +65,6 @@ $string['cannotcreateuploaddir'] = 'Cannot create upload folder. The site admini $string['cannotcustomisefiltersblockuser'] = 'You cannot customise filters settings in user or block contexts.'; $string['cannotdeletebackupids'] = 'Couldn\'t delete previous backup ids'; $string['cannotdeletecategorycourse'] = 'Course \'{$a}\' failed to be deleted.'; -$string['cannotdeletecategoryquestions'] = 'Could not delete questions from category \'{$a}\''; $string['cannotdeletecourse'] = 'You do not have the permission to delete this course'; $string['cannotdeletecustomfield'] = 'Error deleting custom field data'; $string['cannotdeletedir'] = 'Cannot delete ({$a})'; @@ -664,3 +663,6 @@ $string['unknownhelp'] = 'Unknown help section {$a}'; // Deprecated since Moodle 4.5. $string['cannotsetpassword'] = 'Could not set user password!'; $string['myisamproblem'] = 'Database tables are using MyISAM database engine, it is recommended to use ACID compliant engine with full transaction support such as InnoDB.'; + +// Deprecated since Moodle 5.0. +$string['cannotdeletecategoryquestions'] = 'Could not delete questions from category \'{$a}\''; diff --git a/lang/en/question.php b/lang/en/question.php index 2dd7cbd77de..961095d64bf 100644 --- a/lang/en/question.php +++ b/lang/en/question.php @@ -131,7 +131,6 @@ $string['editthiscategory'] = 'Edit this category'; $string['emptyxml'] = 'Unknown error - empty imsmanifest.xml'; $string['enabled'] = 'Enabled'; $string['erroraccessingcontext'] = 'Cannot access context'; -$string['errordeletingquestionsfromcategory'] = 'Error deleting questions from category {$a}.'; $string['errorduringpost'] = 'Error occurred during post-processing!'; $string['errorduringpre'] = 'Error occurred during pre-processing!'; $string['errorduringproc'] = 'Error occurred during processing!'; @@ -534,3 +533,6 @@ $string['version_selection'] = 'Version {$a->version}'; $string['versioninfo'] = 'Version {$a->version} (of {$a->latestversion})'; $string['versioninfolatest'] = 'Version {$a->version} (latest)'; $string['question_version'] = 'Question version'; + +// Deprecated since Moodle 5.0. +$string['errordeletingquestionsfromcategory'] = 'Error deleting questions from category {$a}.'; diff --git a/lib/classes/event/question_base.php b/lib/classes/event/question_base.php index 0db7da5d7a0..12c24656f2d 100644 --- a/lib/classes/event/question_base.php +++ b/lib/classes/event/question_base.php @@ -51,17 +51,11 @@ abstract class question_base extends base { */ public function get_url() { $cat = $this->other['categoryid'] . ',' . $this->contextid; - if ($this->courseid) { - if ($this->contextlevel == CONTEXT_MODULE) { - return new \moodle_url('/question/edit.php', - ['cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid]); - } - return new \moodle_url('/question/edit.php', - ['courseid' => $this->courseid, 'cat' => $cat, 'lastchanged' => $this->objectid]); - } - // Lets try viewing from the frontpage for contexts above course. - return new \moodle_url('/question/edit.php', - ['courseid' => SITEID, 'edit' => $cat, 'lastchanged' => $this->objectid]); + + return new \moodle_url( + '/question/edit.php', + ['cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid] + ); } /** diff --git a/lib/classes/event/question_category_base.php b/lib/classes/event/question_category_base.php index c3b082c1a10..c516ed6fe3a 100644 --- a/lib/classes/event/question_category_base.php +++ b/lib/classes/event/question_category_base.php @@ -51,14 +51,8 @@ abstract class question_category_base extends base { */ public function get_url() { $cat = $this->objectid . ',' . $this->contextid; - if ($this->courseid) { - if ($this->contextlevel == CONTEXT_MODULE) { - return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); - } - return new \moodle_url('/question/edit.php', ['courseid' => $this->courseid, 'cat' => $cat]); - } - // Lets try viewing from the frontpage for contexts above course. - return new \moodle_url('/question/edit.php', ['courseid' => SITEID, 'cat' => $cat, 'edit' => $this->objectid]); + + return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); } /** diff --git a/lib/classes/event/question_moved.php b/lib/classes/event/question_moved.php index 6956d930f61..710ee8f8efc 100644 --- a/lib/classes/event/question_moved.php +++ b/lib/classes/event/question_moved.php @@ -78,17 +78,10 @@ class question_moved extends question_base { */ public function get_url() { $cat = $this->other['newcategoryid'] . ',' . $this->contextid; - if ($this->courseid) { - if ($this->contextlevel == CONTEXT_MODULE) { - return new \moodle_url('/question/edit.php', - ['cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid]); - } - return new \moodle_url('/question/edit.php', - ['courseid' => $this->courseid, 'cat' => $cat, 'lastchanged' => $this->objectid]); - } - // Lets try viewing from the frontpage for contexts above course. + return new \moodle_url('/question/edit.php', - ['courseid' => SITEID, 'cat' => $cat, 'lastchanged' => $this->objectid]); + ['cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid] + ); } /** diff --git a/lib/classes/event/questions_exported.php b/lib/classes/event/questions_exported.php index 46c608be5e4..0d3a5a66fe5 100644 --- a/lib/classes/event/questions_exported.php +++ b/lib/classes/event/questions_exported.php @@ -77,14 +77,8 @@ class questions_exported extends question_base { */ public function get_url() { $cat = $this->other['categoryid'] . ',' . $this->contextid; - if ($this->courseid) { - if ($this->contextlevel == CONTEXT_MODULE) { - return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); - } - return new \moodle_url('/question/edit.php', ['courseid' => $this->courseid, 'cat' => $cat]); - } - return new \moodle_url('/question/edit.php', - ['courseid' => SITEID, 'cat' => $cat]); + + return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); } /** diff --git a/lib/classes/event/questions_imported.php b/lib/classes/event/questions_imported.php index ef0e0bd0c93..a1844d5e55f 100644 --- a/lib/classes/event/questions_imported.php +++ b/lib/classes/event/questions_imported.php @@ -77,14 +77,8 @@ class questions_imported extends question_base { */ public function get_url() { $cat = $this->other['categoryid'] . ',' . $this->contextid; - if ($this->courseid) { - if ($this->contextlevel == CONTEXT_MODULE) { - return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); - } - return new \moodle_url('/question/edit.php', ['courseid' => $this->courseid, 'cat' => $cat]); - } - return new \moodle_url('/question/edit.php', - ['courseid' => SITEID, 'cat' => $cat]); + + return new \moodle_url('/question/edit.php', ['cmid' => $this->contextinstanceid, 'cat' => $cat]); } /** diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 22fd699a4be..093ee1dfb03 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -4889,12 +4889,6 @@ function remove_course_contents($courseid, $showfeedback = true, ?array $options echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess'); } - // Delete questions and question categories. - question_delete_course($course); - if ($showfeedback) { - echo $OUTPUT->notification($strdeleted.get_string('questions', 'question'), 'notifysuccess'); - } - // Delete content bank contents. $cb = new \core_contentbank\contentbank(); $cbdeleted = $cb->delete_contents($coursecontext); diff --git a/lib/questionlib.php b/lib/questionlib.php index 71b67bc494a..448dcc04ea2 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -438,57 +438,6 @@ function question_delete_context($contextid): array { return []; } -/** - * All question categories and their questions are deleted for this course. - * - * @param stdClass $course an object representing the activity - * @param bool $notused this argument is not used any more. Kept for backwards compatibility. - * @return bool always true. - */ -function question_delete_course($course, $notused = false): bool { - $coursecontext = context_course::instance($course->id); - question_delete_context($coursecontext->id); - return true; -} - -/** - * Category is about to be deleted, - * 1/ All question categories and their questions are deleted for this course category. - * 2/ All questions are moved to new category - * - * @param stdClass|core_course_category $category course category object - * @param stdClass|core_course_category $newcategory empty means everything deleted, otherwise id of - * category where content moved - * @param bool $notused this argument is no longer used. Kept for backwards compatibility. - * @return boolean - */ -function question_delete_course_category($category, $newcategory, $notused=false): bool { - global $DB; - - $context = context_coursecat::instance($category->id); - if (empty($newcategory)) { - question_delete_context($context->id); - - } else { - // Move question categories to the new context. - if (!$newcontext = context_coursecat::instance($newcategory->id)) { - return false; - } - - // Only move question categories if there is any question category at all! - if ($topcategory = question_get_top_category($context->id)) { - $newtopcategory = question_get_top_category($newcontext->id, true); - - question_move_category_to_context($topcategory->id, $context->id, $newcontext->id); - $DB->set_field('question_categories', 'parent', $newtopcategory->id, ['parent' => $topcategory->id]); - // Now delete the top category. - $DB->delete_records('question_categories', ['id' => $topcategory->id]); - } - } - - return true; -} - /** * Creates a new category to save the questions in use. * @@ -502,6 +451,11 @@ function question_delete_course_category($category, $newcategory, $notused=false function question_save_from_deletion($questionids, $newcontextid, $oldplace, $newcategory = null) { global $DB; + $newcontext = context::instance_by_id($newcontextid); + if ($newcontext->contextlevel !== CONTEXT_MODULE) { + throw new moodle_exception("Invalid contextlevel: {$newcontext->contextlevel} for \$newcontextid {$newcontextid}"); + } + // Make a category in the parent context to move the questions to. if (is_null($newcategory)) { $newcategory = new stdClass(); @@ -539,35 +493,17 @@ function question_delete_activity($cm, $notused = false): bool { * This function will handle moving all tag instances to a new context for a * given list of questions. * - * Questions can be tagged in up to two contexts: - * 1.) The context the question exists in. - * 2.) The course context (if the question context is a higher context. - * E.g. course category context or system context. - * - * This means a question that exists in a higher context (e.g. course cat or - * system context) may have multiple groups of tags in any number of child - * course contexts. - * - * Questions in the course category context can be move "down" a context level - * into one of their child course contexts or activity contexts which affects the - * availability of that question in other courses / activities. - * - * In this case it makes the questions no longer available in the other course or - * activity contexts so we need to make sure that the tag instances in those other - * contexts are removed. - * * @param stdClass[] $questions The list of question being moved (must include * the id and contextid) - * @param context $newcontext The Moodle context the questions are being moved to + * @param context $newcontext The Moodle context the questions are being moved to, must be module context. */ function question_move_question_tags_to_new_context(array $questions, context $newcontext): void { - // If the questions are moving to a new course/activity context then we need to - // find any existing tag instances from any unavailable course contexts and - // delete them because they will no longer be applicable (we don't support - // tagging questions across courses). - $instancestodelete = []; + + if ($newcontext->contextlevel !== CONTEXT_MODULE) { + debugging("Invalid contextlevel: {$newcontext->contextlevel}", DEBUG_DEVELOPER); + } + $instancesfornewcontext = []; - $newcontextparentids = $newcontext->get_parent_context_ids(); $questionids = array_map(function($question) { return $question->id; }, $questions); @@ -580,7 +516,6 @@ function question_move_question_tags_to_new_context(array $questions, context $n $tagid = $tagobject->taginstanceid; $tagcontextid = $tagobject->taginstancecontextid; $istaginnewcontext = $tagcontextid == $newcontext->id; - $istaginquestioncontext = $tagcontextid == $question->contextid; if ($istaginnewcontext) { // This tag instance is already in the correct context so we can @@ -588,61 +523,10 @@ function question_move_question_tags_to_new_context(array $questions, context $n continue; } - if ($istaginquestioncontext) { - // This tag instance is in the question context so it needs to be - // updated. - $instancesfornewcontext[] = $tagid; - continue; - } - - // These tag instances are in neither the new context nor the - // question context so we need to determine what to do based on - // the context they are in and the new question context. - $tagcontext = context::instance_by_id($tagcontextid); - $tagcoursecontext = $tagcontext->get_course_context(false); - // The tag is in a course context if get_course_context() returns - // itself. - $istaginstancecontextcourse = !empty($tagcoursecontext) - && $tagcontext->id == $tagcoursecontext->id; - - if ($istaginstancecontextcourse) { - // If the tag instance is in a course context we need to add some - // special handling. - $tagcontextparentids = $tagcontext->get_parent_context_ids(); - $isnewcontextaparent = in_array($newcontext->id, $tagcontextparentids); - $isnewcontextachild = in_array($tagcontext->id, $newcontextparentids); - - if ($isnewcontextaparent) { - // If the tag instance is a course context tag and the new - // context is still a parent context to the tag context then - // we can leave this tag where it is. - continue; - } else if ($isnewcontextachild) { - // If the new context is a child context (e.g. activity) of this - // tag instance then we should move all of this tag instance - // down into the activity context along with the question. - $instancesfornewcontext[] = $tagid; - } else { - // If the tag is in a course context that is no longer a parent - // or child of the new context then this tag instance should be - // removed. - $instancestodelete[] = $tagid; - } - } else { - // This is a catch all for any tag instances not in the question - // context or a course context. These tag instances should be - // updated to the new context id. This will clean up old invalid - // data. - $instancesfornewcontext[] = $tagid; - } + $instancesfornewcontext[] = $tagid; } } - if (!empty($instancestodelete)) { - // Delete any course context tags that may no longer be valid. - core_tag_tag::delete_instances_by_id($instancestodelete); - } - if (!empty($instancesfornewcontext)) { // Update the tag instances to the new context id. core_tag_tag::change_instances_context($instancesfornewcontext, $newcontext); @@ -807,6 +691,11 @@ function move_question_set_references(int $oldcategoryid, int $newcatgoryid, function question_move_category_to_context($categoryid, $oldcontextid, $newcontextid): void { global $DB; + $newcontext = context::instance_by_id($newcontextid); + if ($newcontext->contextlevel !== CONTEXT_MODULE) { + debugging("Invalid contextlevel: {$newcontext->contextlevel}, must use CONTEXT_MODULE", DEBUG_DEVELOPER); + } + $questions = []; $sql = "SELECT q.id, q.qtype FROM {question} q @@ -826,7 +715,6 @@ function question_move_category_to_context($categoryid, $oldcontextid, $newconte ]; } - $newcontext = context::instance_by_id($newcontextid); question_move_question_tags_to_new_context($questions, $newcontext); $subcatids = $DB->get_records_menu('question_categories', ['parent' => $categoryid], '', 'id,1'); @@ -935,9 +823,14 @@ function question_load_questions($questionids, $extrafields = '', $join = '') { * @param object $question the question to tidy. * @param stdClass $category The question_categories record for the given $question. * @param \core_tag_tag[]|null $tagobjects The tags for the given $question. - * @param stdClass[]|null $filtercourses The courses to filter the course tags by. + * @param stdClass[]|null $filtercourses deprecated argument and should not be used */ function _tidy_question($question, $category, ?array $tagobjects = null, ?array $filtercourses = null): void { + + if ($filtercourses !== null) { + debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER); + } + // Convert numeric fields to float. This prevents these being displayed as 1.0000000. $question->defaultmark += 0; $question->penalty += 0; @@ -953,8 +846,6 @@ function _tidy_question($question, $category, ?array $tagobjects = null, ?array if (!is_null($tagobjects)) { $categorycontext = context::instance_by_id($category->contextid); $sortedtagobjects = question_sort_tags($tagobjects, $categorycontext, $filtercourses); - $question->coursetagobjects = $sortedtagobjects->coursetagobjects; - $question->coursetags = $sortedtagobjects->coursetags; $question->tagobjects = $sortedtagobjects->tagobjects; $question->tags = $sortedtagobjects->tags; } @@ -978,12 +869,16 @@ function _tidy_question($question, $category, ?array $tagobjects = null, ?array * @param mixed $questions Either an array of question objects to be updated * or just a single question object * @param bool $loadtags load the question tags from the tags table. Optional, default false. - * @param stdClass[] $filtercourses The courses to filter the course tags by. + * @param stdClass[] $filtercourses deprecated argument and should not be used * @return bool Indicates success or failure. */ function get_question_options(&$questions, $loadtags = false, $filtercourses = null) { global $DB; + if ($filtercourses !== null) { + debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER); + } + $questionlist = is_array($questions) ? $questions : [$questions]; $categoryids = []; $questionids = []; @@ -1037,55 +932,24 @@ function get_question_options(&$questions, $loadtags = false, $filtercourses = n * * @param \core_tag_tag[] $tagobjects The tags for the given $question. * @param stdClass $categorycontext The question categories context. - * @param stdClass[]|null $filtercourses The courses to filter the course tags by. + * @param stdClass[]|null $filtercourses deprecated argument and should not be used. * @return stdClass $sortedtagobjects Sorted tag objects. */ function question_sort_tags($tagobjects, $categorycontext, $filtercourses = null): stdClass { - // Questions can have two sets of tag instances. One set at the - // course context level and another at the context the question - // belongs to (e.g. course category, system etc). + if ($filtercourses !== null) { + debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER); + } + $sortedtagobjects = new stdClass(); - $sortedtagobjects->coursetagobjects = []; - $sortedtagobjects->coursetags = []; $sortedtagobjects->tagobjects = []; $sortedtagobjects->tags = []; $taginstanceidstonormalise = []; - $filtercoursecontextids = []; - $hasfiltercourses = !empty($filtercourses); - - if ($hasfiltercourses) { - // If we're being asked to filter the course tags by a set of courses - // then get the context ids to filter below. - $filtercoursecontextids = array_map(function($course) { - $coursecontext = context_course::instance($course->id); - return $coursecontext->id; - }, $filtercourses); - } foreach ($tagobjects as $tagobject) { $tagcontextid = $tagobject->taginstancecontextid; $tagcontext = context::instance_by_id($tagcontextid); - $tagcoursecontext = $tagcontext->get_course_context(false); - // This is a course tag if the tag context is a course context which - // doesn't match the question's context. Any tag in the question context - // is not considered a course tag, it belongs to the question. - $iscoursetag = $tagcoursecontext - && $tagcontext->id == $tagcoursecontext->id - && $tagcontext->id != $categorycontext->id; - - if ($iscoursetag) { - // Any tag instance in a course context level is considered a course tag. - if (!$hasfiltercourses || in_array($tagcontextid, $filtercoursecontextids)) { - // Add the tag to the list of course tags if we aren't being - // asked to filter or if this tag is in the list of courses - // we're being asked to filter by. - $sortedtagobjects->coursetagobjects[] = $tagobject; - $sortedtagobjects->coursetags[$tagobject->id] = $tagobject->get_display_name(); - } - } else { - // All non course context level tag instances or tags in the question - // context belong to the context that the question was created in. + // All tag instances belong to the context that the question was created in. $sortedtagobjects->tagobjects[] = $tagobject; $sortedtagobjects->tags[$tagobject->id] = $tagobject->get_display_name(); @@ -1100,7 +964,7 @@ function question_sort_tags($tagobjects, $categorycontext, $filtercourses = null // happen below. $tagobject->taginstancecontextid = $categorycontext->id; } - } + } if (!empty($taginstanceidstonormalise)) { @@ -1232,6 +1096,15 @@ function question_get_top_category($contextid, $create = false) { global $DB; $category = $DB->get_record('question_categories', ['contextid' => $contextid, 'parent' => 0]); + $context = context::instance_by_id($contextid); + if ($context->contextlevel !== CONTEXT_MODULE) { + debugging( + "Invalid context level: {$context->contextlevel} for question_get_top_category, must be CONTEXT_MODULE", + DEBUG_DEVELOPER + ); + return false; + } + if (!$category && $create) { // We need to make one. $category = new stdClass(); @@ -1485,6 +1358,15 @@ function question_edit_url($context) { if (!has_any_capability(question_get_question_capabilities(), $context)) { return false; } + + if ($context->contextlevel !== CONTEXT_MODULE) { + debugging( + "Invalid contextlevel: {$context->contextlevel} provided for question_edit_url, must be CONTEXT_MODULE", + DEBUG_DEVELOPER + ); + return false; + } + $baseurl = $CFG->wwwroot . '/question/edit.php?'; $defaultcategory = question_get_default_category($context->id, true); if ($defaultcategory) { @@ -1503,6 +1385,7 @@ function question_edit_url($context) { return $baseurl . 'cmid=' . $context->instanceid; } + return $baseurl . 'cmid=' . $context->instanceid; } /** diff --git a/lib/tests/behat/menu_navigation_topics.feature b/lib/tests/behat/menu_navigation_topics.feature index e1c37a888b2..beb09dd4d5e 100644 --- a/lib/tests/behat/menu_navigation_topics.feature +++ b/lib/tests/behat/menu_navigation_topics.feature @@ -79,7 +79,7 @@ Feature: Menu navigation has accurate checkmarks in topic course format Given I log in as "admin" And I am on "Course 1" course homepage And I update the href of the "//*//a/following-sibling::*//a[contains(text(), 'Filters')]" "xpath" link to "#" - And I navigate to "Question bank" in current page administration + And I navigate to "Question banks" in current page administration Then menu item "Filters" should not be active @javascript diff --git a/lib/tests/navigation/views/secondary_test.php b/lib/tests/navigation/views/secondary_test.php index 21cae6d1100..6036ac3d897 100644 --- a/lib/tests/navigation/views/secondary_test.php +++ b/lib/tests/navigation/views/secondary_test.php @@ -962,7 +962,7 @@ class secondary_test extends \advanced_testcase { ], "Question bank should return nothing" => [ '/question/edit.php', - 'courseid', + 'cmid', false ], "Reports should return nothing" => [ diff --git a/lib/tests/questionlib_test.php b/lib/tests/questionlib_test.php index 601f745bee2..df16d1ddb2c 100644 --- a/lib/tests/questionlib_test.php +++ b/lib/tests/questionlib_test.php @@ -50,13 +50,22 @@ class questionlib_test extends \advanced_testcase { $this->resetAfterTest(); } + /** + * Generate a course and question bank module instance for use in test cases, and return the bank context. + * @return \core\context\module + */ + protected function create_course_and_question_bank(): \core\context\module { + $course = self::getDataGenerator()->create_course(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + return \context_module::instance($qbank->cmid); + } + /** * 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') { + public function setup_quiz_and_questions() { // Create course category. $category = $this->getDataGenerator()->create_category(); @@ -77,23 +86,7 @@ class questionlib_test extends \advanced_testcase { /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); - switch ($type) { - case 'course': - $context = \context_course::instance($course->id); - break; - - case 'category': - $context = \context_coursecat::instance($category->id); - break; - - case 'system': - $context = \context_system::instance(); - break; - - default: - $context = \context_module::instance($quiz->cmid); - break; - } + $context = \context_module::instance($quiz->cmid); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -278,7 +271,7 @@ class questionlib_test extends \advanced_testcase { global $DB; // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -301,7 +294,7 @@ class questionlib_test extends \advanced_testcase { global $DB; // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -328,8 +321,7 @@ class questionlib_test extends \advanced_testcase { public function test_question_delete_question_missing_context(): void { global $DB; - $coursecategory = $this->getDataGenerator()->create_category(); - $context = $coursecategory->get_context(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); @@ -398,6 +390,8 @@ class questionlib_test extends \advanced_testcase { /** * This function tests the question_delete_context function. + * + * @covers ::question_delete_context() */ public function test_question_delete_context(): void { global $DB; @@ -410,110 +404,31 @@ class questionlib_test extends \advanced_testcase { $result = question_delete_context($qcat->contextid); // Verify category deleted. - $criteria = array('id' => $qcat->id); + $criteria = ['id' => $qcat->id]; $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); // Verify questions deleted or moved. $this->assert_category_contains_questions($qcat->id, 0); } - /** - * This function tests the question_delete_course function. - */ - public function test_question_delete_course(): void { - global $DB; - $this->resetAfterTest(true); - $this->setAdminUser(); - - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course'); - - // Test the deletion. - question_delete_course($course); - - // Verify category deleted. - $criteria = array('id' => $qcat->id); - $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); - - // Verify questions deleted or moved. - $this->assert_category_contains_questions($qcat->id, 0); - } - - /** - * This function tests the question_delete_course_category function. - */ - public function test_question_delete_course_category(): void { - global $DB; - $this->resetAfterTest(true); - $this->setAdminUser(); - - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - - // Test that the feedback works. - question_delete_course_category($category, null); - - // Verify category deleted. - $criteria = array('id' => $qcat->id); - $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); - - // Verify questions deleted or moved. - $this->assert_category_contains_questions($qcat->id, 0); - } - - /** - * This function tests the question_delete_course_category function when it is supposed to move question categories. - */ - public function test_question_delete_course_category_move_qcats(): void { - global $DB; - $this->resetAfterTest(true); - $this->setAdminUser(); - - list($category1, $course1, $quiz1, $qcat1, $questions1) = $this->setup_quiz_and_questions('category'); - list($category2, $course2, $quiz2, $qcat2, $questions2) = $this->setup_quiz_and_questions('category'); - - $questionsinqcat1 = count($questions1); - $questionsinqcat2 = count($questions2); - - // Test the delete. - question_delete_course_category($category1, $category2); - - // Verify category not deleted. - $criteria = array('id' => $qcat1->id); - $this->assertEquals(1, $DB->count_records('question_categories', $criteria)); - - // Verify questions are moved. - $params = array($qcat2->contextid); - $actualquestionscount = $DB->count_records_sql("SELECT COUNT(*) - FROM {question} q - JOIN {question_versions} qv ON qv.questionid = q.id - JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid - JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid - WHERE qc.contextid = ?", $params); - $this->assertEquals($questionsinqcat1 + $questionsinqcat2, $actualquestionscount); - - // Verify there is just a single top-level category. - $criteria = array('contextid' => $qcat2->contextid, 'parent' => 0); - $this->assertEquals(1, $DB->count_records('question_categories', $criteria)); - - // Verify there is no question category in previous context. - $criteria = array('contextid' => $qcat1->contextid); - $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); - } - /** * This function tests the question_save_from_deletion function when it is supposed to make a new category and * move question categories to that new category. + * + * @covers ::question_save_from_deletion() */ public function test_question_save_from_deletion(): void { global $DB; $this->resetAfterTest(true); $this->setAdminUser(); - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions(); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbankcontext = \context_module::instance($qbank->cmid); $context = \context::instance_by_id($qcat->contextid); - $newcat = question_save_from_deletion(array_column($questions, 'id'), - $context->get_parent_context()->id, $context->get_context_name()); + $newcat = question_save_from_deletion(array_column($questions, 'id'), $qbankcontext->id, $context->get_context_name()); // Verify that the newcat itself is not a tep level category. $this->assertNotEquals(0, $newcat->parent); @@ -525,13 +440,17 @@ class questionlib_test extends \advanced_testcase { /** * This function tests the question_save_from_deletion function when it is supposed to make a new category and * move question categories to that new category when quiz name is very long but less than 256 characters. + * + * @covers ::question_save_from_deletion() */ public function test_question_save_from_deletion_quiz_with_long_name(): void { global $DB; $this->resetAfterTest(true); $this->setAdminUser(); - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions(); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbankcontext = \context_module::instance($qbank->cmid); // Moodle doesn't allow you to enter a name longer than 255 characters. $quiz->name = shorten_text(str_repeat('123456789 ', 26), 255); @@ -540,8 +459,7 @@ class questionlib_test extends \advanced_testcase { $context = \context::instance_by_id($qcat->contextid); - $newcat = question_save_from_deletion(array_column($questions, 'id'), - $context->get_parent_context()->id, $context->get_context_name()); + $newcat = question_save_from_deletion(array_column($questions, 'id'), $qbankcontext->id, $context->get_context_name()); // Verifying that the inserted record's name is expected or not. $this->assertEquals($DB->get_record('question_categories', ['id' => $newcat->id])->name, $newcat->name); @@ -555,9 +473,11 @@ class questionlib_test extends \advanced_testcase { /** * get_question_options should add the category object to the given question. + * + * @covers ::get_question_options() */ public function test_get_question_options_includes_category_object_single_question(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); $question = array_shift($questions); get_question_options($question); @@ -568,9 +488,11 @@ class questionlib_test extends \advanced_testcase { /** * get_question_options should add the category object to all of the questions in * the given list. + * + * @covers ::get_question_options() */ public function test_get_question_options_includes_category_object_multiple_questions(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); get_question_options($questions); @@ -581,9 +503,11 @@ class questionlib_test extends \advanced_testcase { /** * get_question_options includes the tags for all questions in the list. + * + * @covers ::get_question_options() */ public function test_get_question_options_includes_question_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); $question1 = $questions[0]; $question2 = $questions[1]; $qcontext = \context::instance_by_id($qcat->contextid); @@ -611,130 +535,6 @@ class questionlib_test extends \advanced_testcase { // The question should have a full set of each tag object. $this->assertEquals($tags, $actualtagobjects); - // The question should not have any course tags. - $this->assertEmpty($question->coursetagobjects); - } - } - - /** - * get_question_options includes the course tags for all questions in the list. - */ - public function test_get_question_options_includes_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['baz', 'bop']); - - get_question_options($questions, true); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $expectedcoursetags = []; - $actualcoursetags = $question->coursetags; - foreach ($tags as $tag) { - $expectedcoursetags[$tag->id] = $tag->get_display_name(); - } - - // The question should have a coursetags property populated with each tag id - // and display name as a key vale pair. - $this->assertEquals($expectedcoursetags, $actualcoursetags); - - $actualcoursetagobjects = $question->coursetagobjects; - sort($tags); - sort($actualcoursetagobjects); - - // The question should have a full set of the course tag objects. - $this->assertEquals($tags, $actualcoursetagobjects); - // The question should not have any other tags. - $this->assertEmpty($question->tagobjects); - $this->assertEmpty($question->tags); - } - } - - /** - * get_question_options only categorises a tag as a course tag if it is in a - * course context that is different from the question context. - */ - public function test_get_question_options_course_tags_in_course_question_context(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - - // Create course level tags in the course context that matches the question - // course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['baz', 'bop']); - - get_question_options($questions, true); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - $actualtagobjects = $question->tagobjects; - sort($tags); - sort($actualtagobjects); - - // The tags should not be considered course tags because they are in - // the same context as the question. That makes them question tags. - $this->assertEmpty($question->coursetagobjects); - // The course context tags should be returned in the regular tag object - // list. - $this->assertEquals($tags, $actualtagobjects); - } - } - - /** - * get_question_options includes the tags and course tags for all questions in the list - * if each question has course and question level tags. - */ - public function test_get_question_options_includes_question_and_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['cfoo', 'cbar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['baz', 'bop']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['cbaz', 'cbop']); - - get_question_options($questions, true); - - foreach ($questions as $question) { - $alltags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $tags = array_filter($alltags, function($tag) use ($qcontext) { - return $tag->taginstancecontextid == $qcontext->id; - }); - $coursetags = array_filter($alltags, function($tag) use ($coursecontext) { - return $tag->taginstancecontextid == $coursecontext->id; - }); - - $expectedtags = []; - $actualtags = $question->tags; - foreach ($tags as $tag) { - $expectedtags[$tag->id] = $tag->get_display_name(); - } - - // The question should have a tags property populated with each tag id - // and display name as a key vale pair. - $this->assertEquals($expectedtags, $actualtags); - - $actualtagobjects = $question->tagobjects; - sort($tags); - sort($actualtagobjects); - // The question should have a full set of each tag object. - $this->assertEquals($tags, $actualtagobjects); - - $actualcoursetagobjects = $question->coursetagobjects; - sort($coursetags); - sort($actualcoursetagobjects); - // The question should have a full set of course tag objects. - $this->assertEquals($coursetags, $actualcoursetagobjects); } } @@ -742,9 +542,11 @@ class questionlib_test extends \advanced_testcase { * get_question_options should update the context id to the question category * context id for any non-course context tag that isn't in the question category * context. + * + * @covers ::get_question_options() */ public function test_get_question_options_normalises_question_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); + list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions(); $question1 = $questions[0]; $question2 = $questions[1]; $qcontext = \context::instance_by_id($qcat->contextid); @@ -782,606 +584,33 @@ class questionlib_test extends \advanced_testcase { } /** - * get_question_options if the question is a course level question then tags - * in that context should not be consdered course tags, they are question tags. - */ - public function test_get_question_options_includes_course_context_question_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['baz', 'bop']); - - get_question_options($questions, true); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Tags in a course context that matches the question context should - // not be considered course tags. - $this->assertEmpty($question->coursetagobjects); - $this->assertEmpty($question->coursetags); - - $actualtagobjects = $question->tagobjects; - sort($tags); - sort($actualtagobjects); - // The tags should be considered question tags not course tags. - $this->assertEquals($tags, $actualtagobjects); - } - } - - /** - * get_question_options should return tags from all course contexts by default. - */ - public function test_get_question_options_includes_multiple_courses_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - // Create a sibling course. - $siblingcourse = $this->getDataGenerator()->create_course(['category' => $course->category]); - $siblingcoursecontext = \context_course::instance($siblingcourse->id); - - // Create course tags. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['c1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['c1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $siblingcoursecontext, ['c2']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $siblingcoursecontext, ['c2']); - - get_question_options($questions, true); - - foreach ($questions as $question) { - $this->assertCount(2, $question->coursetagobjects); - - foreach ($question->coursetagobjects as $tag) { - if ($tag->name == 'c1') { - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } else { - $this->assertEquals($siblingcoursecontext->id, $tag->taginstancecontextid); - } - } - } - } - - /** - * get_question_options should filter the course tags by the given list of courses. - */ - public function test_get_question_options_includes_filter_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - // Create a sibling course. - $siblingcourse = $this->getDataGenerator()->create_course(['category' => $course->category]); - $siblingcoursecontext = \context_course::instance($siblingcourse->id); - - // Create course tags. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['bar']); - // Create sibling course tags. These should be filtered out. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $siblingcoursecontext, ['filtered1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $siblingcoursecontext, ['filtered2']); - - // Ask to only receive course tags from $course (ignoring $siblingcourse tags). - get_question_options($questions, true, [$course]); - - foreach ($questions as $question) { - foreach ($question->coursetagobjects as $tag) { - // We should only be seeing course tags from $course. The tags from - // $siblingcourse should have been filtered out. - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the - * question tags contexts when they are moving down (from system to course - * category context). - */ - public function test_question_move_question_tags_to_new_context_system_to_course_cat_qtags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('system'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_coursecat::instance($category->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo', 'bar']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - // All of the tags should have their context id set to the new context. - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the question tags - * contexts when they are moving down (from system to course category context) - * but leave any tags in the course context where they are. - */ - public function test_question_move_question_tags_to_new_context_system_to_course_cat_qtags_and_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('system'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $newcontext = \context_coursecat::instance($category->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - foreach ($tags as $tag) { - if ($tag->name == 'ctag') { - // Course tags should remain in the course context. - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } else { - // Other tags should be updated. - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the question - * contexts tags when they are moving up (from course category to system context). - */ - public function test_question_move_question_tags_to_new_context_course_cat_to_system_qtags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_system::instance(); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the course category context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo', 'bar']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - // All of the tags should have their context id set to the new context. - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the question - * tags contexts when they are moving up (from course category context to system - * context) but leave any tags in the course context where they are. - */ - public function test_question_move_question_tags_to_new_context_course_cat_to_system_qtags_and_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $newcontext = \context_system::instance(); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - foreach ($tags as $tag) { - if ($tag->name == 'ctag') { - // Course tags should remain in the course context. - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } else { - // Other tags should be updated. - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - } - - /** - * question_move_question_tags_to_new_context should merge all tags into the course - * context when moving down from course category context into course context. - */ - public function test_question_move_question_tags_to_new_context_course_cat_to_coures_qtags_and_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $newcontext = $coursecontext; - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have 2 tags. - $this->assertCount(2, $tags); - - foreach ($tags as $tag) { - // All tags should be updated to the course context and merged in. - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should delete all of the tag - * instances from sibling courses when moving the context of a question down - * from a course category into a course context because the other courses will - * no longer have access to the question. - */ - public function test_question_move_question_tags_to_new_context_remove_other_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - // Create a sibling course. - $siblingcourse = $this->getDataGenerator()->create_course(['category' => $course->category]); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $siblingcoursecontext = \context_course::instance($siblingcourse->id); - $newcontext = $coursecontext; - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the target course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - // Create tags in the sibling course context. These should be deleted as - // part of the move. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $siblingcoursecontext, ['stag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $siblingcoursecontext, ['stag']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have 2 tags, 'foo' and 'ctag'. - $this->assertCount(2, $tags); - - foreach ($tags as $tag) { - $tagname = $tag->name; - // The 'stag' should have been deleted because it's in a sibling - // course context. - $this->assertContains($tagname, ['foo', 'ctag']); - // All tags should be in the course context now. - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the question - * tags to be the course category context when moving the tags from a course - * context to a course category context. - */ - public function test_question_move_question_tags_to_new_context_course_to_course_cat(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - // Moving up into the course category context. - $newcontext = \context_coursecat::instance($category->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - // All of the tags should have their context id set to the new context. - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the - * question tags contexts when they are moving down (from system to course - * category context). - */ - public function test_question_move_question_tags_to_new_context_orphaned_tag_contexts(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('system'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $othercategory = $this->getDataGenerator()->create_category(); - $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_coursecat::instance($category->id); - $othercategorycontext = \context_coursecat::instance($othercategory->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the system context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the other course category context. These should be - // update to the next context id because they represent erroneous data - // from a time before context id was mandatory in the tag API. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $othercategorycontext, ['bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $othercategorycontext, ['bar']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have two tags, 'foo' and 'bar'. - $this->assertCount(2, $tags); - - // All of the tags should have their context id set to the new context - // (course category context). - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * When moving from a course category context down into an activity context - * all question context tags and course tags (where the course is a parent of - * the activity) should move into the new context. - */ - public function test_question_move_question_tags_to_new_context_course_cat_to_activity_qtags_and_course_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $newcontext = \context_module::instance($quiz->cmid); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the course category context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Move the questions to the activity context which is a child context of - // $coursecontext. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have 2 tags. - $this->assertCount(2, $tags); - - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * When moving from a course category context down into an activity context - * all question context tags and course tags (where the course is a parent of - * the activity) should move into the new context. Tags in course contexts - * that are not a parent of the activity context should be deleted. - */ - public function test_question_move_question_tags_to_new_context_course_cat_to_activity_orphaned_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); - $newcontext = \context_module::instance($quiz->cmid); - $othercourse = $this->getDataGenerator()->create_course(); - $othercoursecontext = \context_course::instance($othercourse->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the course category context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag']); - // Create tags in the other course context. These should be deleted. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $othercoursecontext, ['delete']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $othercoursecontext, ['delete']); - - // Move the questions to the activity context which is a child context of - // $coursecontext. - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have 2 tags. - $this->assertCount(2, $tags); - - foreach ($tags as $tag) { - // Make sure we don't have any 'delete' tags. - $this->assertContains($tag->name, ['foo', 'ctag']); - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * When moving from a course context down into an activity context all of the - * course tags should move into the activity context. - */ - public function test_question_move_question_tags_to_new_context_course_to_activity_qtags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('course'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_module::instance($quiz->cmid); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the course context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * When moving from a course context down into an activity context all of the - * course tags should move into the activity context. - */ - public function test_question_move_question_tags_to_new_context_activity_to_course_qtags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions(); - $question1 = $questions[0]; - $question2 = $questions[1]; - $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_course::instance($course->id); - - foreach ($questions as $question) { - $question->contextid = $qcat->contextid; - } - - // Create tags in the activity context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - - question_move_question_tags_to_new_context($questions, $newcontext); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - - foreach ($tags as $tag) { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } - } - } - - /** - * question_move_question_tags_to_new_context should update all of the - * question tags contexts when they are moving down (from system to course - * category context). + * When moving all tags from one activity context into another activity context. * - * Course tags within the new category context should remain while any course - * tags in course contexts that can no longer access the question should be - * deleted. + * @covers ::question_move_question_tags_to_new_context() */ - public function test_question_move_question_tags_to_new_context_system_to_course_cat_with_orphaned_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('system'); + public function test_question_move_question_tags_to_new_context_activity_to_activity_qtags(): void { + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); $question1 = $questions[0]; $question2 = $questions[1]; - $othercategory = $this->getDataGenerator()->create_category(); - $othercourse = $this->getDataGenerator()->create_course(['category' => $othercategory->id]); $qcontext = \context::instance_by_id($qcat->contextid); - $newcontext = \context_coursecat::instance($category->id); - $othercategorycontext = \context_coursecat::instance($othercategory->id); - $coursecontext = \context_course::instance($course->id); - $othercoursecontext = \context_course::instance($othercourse->id); + $newcontext = \context_module::instance($qbank->cmid); foreach ($questions as $question) { $question->contextid = $qcat->contextid; } - // Create tags in the system context. + // Create tags in the quiz context. \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['foo']); \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['foo']); - // Create tags in the child course context of the new context. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['bar']); - // Create tags in the other course context. These should be deleted when - // the question moves to the new course category context because this - // course belongs to a different category, which means it will no longer - // have access to the question. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $othercoursecontext, ['delete']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $othercoursecontext, ['delete']); question_move_question_tags_to_new_context($questions, $newcontext); foreach ($questions as $question) { $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - // Each question should have two tags, 'foo' and 'bar'. - $this->assertCount(2, $tags); - // All of the tags should have their context id set to the new context - // (course category context). foreach ($tags as $tag) { - $this->assertContains($tag->name, ['foo', 'bar']); - - if ($tag->name == 'foo') { - $this->assertEquals($newcontext->id, $tag->taginstancecontextid); - } else { - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } + $this->assertEquals($newcontext->id, $tag->taginstancecontextid); } } } @@ -1391,7 +620,7 @@ class questionlib_test extends \advanced_testcase { */ public function test_question_sort_tags_includes_question_tags(): void { - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); + [$category, $course, $quiz, $qcat, $questions] = $this->setup_quiz_and_questions(); $question1 = $questions[0]; $question2 = $questions[1]; $qcontext = \context::instance_by_id($qcat->contextid); @@ -1419,115 +648,6 @@ class questionlib_test extends \advanced_testcase { // The question should have a full set of each tag object. $this->assertEquals($tags, $actualtagobjects); - // The question should not have any course tags. - $this->assertEmpty($tagobjects->coursetagobjects); - } - } - - /** - * question_sort_tags() includes course tags for all questions in the list. - */ - public function test_question_sort_tags_includes_question_course_tags(): void { - global $DB; - - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo', 'bar']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['baz', 'bop']); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $tagobjects = question_sort_tags($tags, $qcat); - - $expectedtags = []; - $actualtags = $tagobjects->coursetags; - foreach ($actualtags as $coursetagid => $coursetagname) { - $expectedtags[$coursetagid] = $coursetagname; - } - - // The question should have a tags property populated with each tag id - // and display name as a key vale pair. - $this->assertEquals($expectedtags, $actualtags); - - $actualtagobjects = $tagobjects->coursetagobjects; - sort($tags); - sort($actualtagobjects); - - // The question should have a full set of each tag object. - $this->assertEquals($tags, $actualtagobjects); - // The question should not have any course tags. - $this->assertEmpty($tagobjects->tagobjects); - } - } - - /** - * question_sort_tags() should return tags from all course contexts by default. - */ - public function test_question_sort_tags_includes_multiple_courses_tags(): void { - global $DB; - - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - // Create a sibling course. - $siblingcourse = $this->getDataGenerator()->create_course(['category' => $course->category]); - $siblingcoursecontext = \context_course::instance($siblingcourse->id); - - // Create course tags. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['c1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['c1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $siblingcoursecontext, ['c2']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $siblingcoursecontext, ['c2']); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $tagobjects = question_sort_tags($tags, $qcat); - $this->assertCount(2, $tagobjects->coursetagobjects); - - foreach ($tagobjects->coursetagobjects as $tag) { - if ($tag->name == 'c1') { - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } else { - $this->assertEquals($siblingcoursecontext->id, $tag->taginstancecontextid); - } - } - } - } - - /** - * question_sort_tags() should filter the course tags by the given list of courses. - */ - public function test_question_sort_tags_includes_filter_course_tags(): void { - global $DB; - - list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions('category'); - $question1 = $questions[0]; - $question2 = $questions[1]; - $coursecontext = \context_course::instance($course->id); - // Create a sibling course. - $siblingcourse = $this->getDataGenerator()->create_course(['category' => $course->category]); - $siblingcoursecontext = \context_course::instance($siblingcourse->id); - - // Create course tags. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['foo']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['bar']); - // Create sibling course tags. These should be filtered out. - \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $siblingcoursecontext, ['filtered1']); - \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $siblingcoursecontext, ['filtered2']); - - foreach ($questions as $question) { - $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $tagobjects = question_sort_tags($tags, $qcat, [$course]); - foreach ($tagobjects->coursetagobjects as $tag) { - - // We should only be seeing course tags from $course. The tags from - // $siblingcourse should have been filtered out. - $this->assertEquals($coursecontext->id, $tag->taginstancecontextid); - } } } @@ -1664,8 +784,7 @@ class questionlib_test extends \advanced_testcase { /** @var \core_question_generator $questiongenerator */ $questiongenerator = $generator->get_plugin_generator('core_question'); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -1748,8 +867,7 @@ class questionlib_test extends \advanced_testcase { $user = $generator->create_user(); $otheruser = $generator->create_user(); $roleid = $generator->create_role(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -1795,8 +913,7 @@ class questionlib_test extends \advanced_testcase { $user = $generator->create_user(); $otheruser = $generator->create_user(); $roleid = $generator->create_role(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -1842,8 +959,7 @@ class questionlib_test extends \advanced_testcase { $user = $generator->create_user(); $otheruser = $generator->create_user(); $roleid = $generator->create_role(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -1889,14 +1005,14 @@ class questionlib_test extends \advanced_testcase { $user = $generator->create_user(); $otheruser = $generator->create_user(); $roleid = $generator->create_role(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); + $coursecontext = $context->get_course_context(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); - $newcategory = $generator->create_category(); - $newcontext = \context_coursecat::instance($newcategory->id); + $qbank2 = $generator->create_module('qbank', ['course' => $coursecontext->instanceid]); + $newcontext = \context_module::instance($qbank2->cmid); $newquestioncat = $questiongenerator->create_question_category([ 'contextid' => $newcontext->id, ]); @@ -1946,8 +1062,7 @@ class questionlib_test extends \advanced_testcase { $user = $generator->create_user(); $otheruser = $generator->create_user(); $roleid = $generator->create_role(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -1982,8 +1097,7 @@ class questionlib_test extends \advanced_testcase { $questiongenerator = $generator->get_plugin_generator('core_question'); $user = $generator->create_user(); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); $questioncat = $questiongenerator->create_question_category([ 'contextid' => $context->id, ]); @@ -2060,10 +1174,9 @@ class questionlib_test extends \advanced_testcase { /** @var \core_question_generator $questiongenerator */ $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); // Create a Course. - $course = $this->getDataGenerator()->create_course(); - $coursecontext = \context_course::instance($course->id); + $context = $this->create_course_and_question_bank(); - $top = question_get_top_category($coursecontext->id, true); + $top = question_get_top_category($context->id, true); $cat1 = $questiongenerator->create_question_category(['parent' => $top->id]); $sub11 = $questiongenerator->create_question_category(['parent' => $cat1->id]); $sub12 = $questiongenerator->create_question_category(['parent' => $cat1->id]); @@ -2094,16 +1207,18 @@ class questionlib_test extends \advanced_testcase { // Create a category tree. $course = $this->getDataGenerator()->create_course(); - $coursecontext = \context_course::instance($course->id); + $qbank1 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $bank1context = \context_module::instance($qbank1->cmid); + $qbank2 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); /** @var \core_question_generator $questiongenerator */ $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = \context_system::instance(); + $wrongcontext = \context_module::instance($qbank2->cmid); - $top = question_get_top_category($coursecontext->id, true); + $top = question_get_top_category($bank1context->id, true); $cat1 = $questiongenerator->create_question_category(['parent' => $top->id]); $sub11 = $questiongenerator->create_question_category(['parent' => $cat1->id]); $sub12 = $questiongenerator->create_question_category(['parent' => $cat1->id]); - $cat2 = $questiongenerator->create_question_category(['parent' => $top->id, 'contextid' => $context->id]); + $cat2 = $questiongenerator->create_question_category(['parent' => $top->id, 'contextid' => $wrongcontext->id]); $sub22 = $questiongenerator->create_question_category(['parent' => $cat2->id]); // Test - returned array has keys and values the same. @@ -2122,8 +1237,7 @@ class questionlib_test extends \advanced_testcase { $generator = $this->getDataGenerator(); /** @var \core_question_generator $questiongenerator */ $questiongenerator = $generator->get_plugin_generator('core_question'); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $context = $this->create_course_and_question_bank(); // Create a top category. $cat0 = question_get_top_category($context->id, true); // Add sub-categories. @@ -2147,15 +1261,18 @@ class questionlib_test extends \advanced_testcase { $generator = $this->getDataGenerator(); /** @var \core_question_generator $questiongenerator */ $questiongenerator = $generator->get_plugin_generator('core_question'); - $category = $generator->create_category(); - $context = \context_coursecat::instance($category->id); + $bank1context = $this->create_course_and_question_bank(); + $coursecontext = $bank1context->get_course_context(); + $qbank2 = self::getDataGenerator()->create_module('qbank', ['course' => $coursecontext->instanceid]); + $bank2context = \context_module::instance($qbank2->cmid); + // Create a top category. - $cat0 = question_get_top_category($context->id, true); + $cat0 = question_get_top_category($bank1context->id, true); // Add sub-categories - but in a different context. $cat1 = $questiongenerator->create_question_category( - ['parent' => $cat0->id, 'contextid' => \context_system::instance()->id]); + ['parent' => $cat0->id, 'contextid' => $bank2context->id]); $cat2 = $questiongenerator->create_question_category( - ['parent' => $cat1->id, 'contextid' => \context_system::instance()->id]); + ['parent' => $cat1->id, 'contextid' => $bank2context->id]); // Test the 'get parents' function only returns categories in the same context. $this->assertEquals([$cat1->id], question_categorylist_parents($cat2->id)); @@ -2311,7 +1428,7 @@ class questionlib_test extends \advanced_testcase { global $DB; $this->resetAfterTest(); // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -2348,7 +1465,7 @@ class questionlib_test extends \advanced_testcase { global $DB; $this->resetAfterTest(); // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -2378,7 +1495,7 @@ class questionlib_test extends \advanced_testcase { global $DB; $this->resetAfterTest(); // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); @@ -2409,7 +1526,7 @@ class questionlib_test extends \advanced_testcase { global $DB; $this->resetAfterTest(); // Setup. - $context = \context_system::instance(); + $context = $this->create_course_and_question_bank(); /** @var \core_question_generator $qgen */ $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $qgen->create_question_category(array('contextid' => $context->id)); diff --git a/mod/quiz/classes/structure.php b/mod/quiz/classes/structure.php index b6a8101b91c..2256fa60408 100644 --- a/mod/quiz/classes/structure.php +++ b/mod/quiz/classes/structure.php @@ -1836,6 +1836,13 @@ class structure { * @throws coding_exception If the context level is unsupported. */ private function get_used_category_description(stdClass $qcategory, bool $includesubcategories): string { + + $context = \context::instance_by_id($qcategory->contextid); + + if ($context->contextlevel != CONTEXT_MODULE) { + throw new coding_exception('Unsupported context.'); + } + if ($qcategory->name === 'top') { // This is a "top" question category. if (!$includesubcategories) { // Question categories labeled as "top" cannot directly contain questions. If the subcategories that may @@ -1843,26 +1850,7 @@ class structure { // that informs the user about the issues associated with these types of generated random questions. return get_string('randomfaultynosubcat', 'mod_quiz'); } - - $context = \context::instance_by_id($qcategory->contextid); - - switch ($context->contextlevel) { - case CONTEXT_MODULE: - return get_string('randommodulewithsubcat', 'mod_quiz'); - - case CONTEXT_COURSE: - return get_string('randomcoursewithsubcat', 'mod_quiz'); - - case CONTEXT_COURSECAT: - $contextname = shorten_text($context->get_context_name(false), 100); - return get_string('randomcoursecatwithsubcat', 'mod_quiz', $contextname); - - case CONTEXT_SYSTEM: - return get_string('randomsystemwithsubcat', 'mod_quiz'); - - default: - throw new coding_exception('Unsupported context.'); - } + return get_string('randommodulewithsubcat', 'mod_quiz'); } // Otherwise, return the description of the used standard question category, also indicating whether subcategories // are included. diff --git a/mod/quiz/edit.php b/mod/quiz/edit.php index 3353ebc615f..39deda908f3 100644 --- a/mod/quiz/edit.php +++ b/mod/quiz/edit.php @@ -50,7 +50,7 @@ require_once($CFG->dirroot . '/question/editlib.php'); $mdlscrollto = optional_param('mdlscrollto', '', PARAM_INT); list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) = - question_edit_setup('editq', '/mod/quiz/edit.php', true); + question_edit_setup('editq', '/mod/quiz/edit.php'); $PAGE->set_url($thispageurl); $PAGE->set_secondary_active_tab("mod_quiz_edit"); diff --git a/mod/quiz/editgrading.php b/mod/quiz/editgrading.php index e3614720e2c..56b74b4a4c4 100644 --- a/mod/quiz/editgrading.php +++ b/mod/quiz/editgrading.php @@ -37,7 +37,7 @@ require_once($CFG->dirroot . '/mod/quiz/locallib.php'); require_once($CFG->dirroot . '/question/editlib.php'); list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) = - question_edit_setup('editq', '/mod/quiz/editgrading.php', true); + question_edit_setup('editq', '/mod/quiz/editgrading.php'); $PAGE->set_url($thispageurl); $PAGE->set_secondary_active_tab('mod_quiz_edit'); diff --git a/mod/quiz/lang/en/deprecated.txt b/mod/quiz/lang/en/deprecated.txt index 27bba327fa8..1cc78afe195 100644 --- a/mod/quiz/lang/en/deprecated.txt +++ b/mod/quiz/lang/en/deprecated.txt @@ -5,3 +5,6 @@ attemptsdeleted,mod_quiz gradesdeleted,mod_quiz useroverridesdeleted,mod_quiz groupoverridesdeleted,mod_quiz +randomcoursecatwithsubcat,mod_quiz +randomcoursewithsubcat,mod_quiz +randomsystemwithsubcat,mod_quiz diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 63b75f7938a..657874f58c4 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -844,8 +844,7 @@ $string['quiztimer'] = 'Quiz Timer'; $string['quizwillopen'] = 'This quiz will open {$a}'; $string['random'] = 'Random question'; $string['randomcatwithsubcat'] = '{$a} and subcategories'; -$string['randomcoursecatwithsubcat'] = 'Any category inside course category {$a}'; -$string['randomcoursewithsubcat'] = 'Any category in this course'; + $string['randomcreate'] = 'Create random questions'; $string['randomediting'] = 'Editing a random question'; $string['randomfaultynosubcat'] = 'Faulty question'; @@ -867,7 +866,6 @@ $string['randomquestiontags_help'] = 'You can restrict the selection criteria fu The "random" questions will be selected from the questions that have all these tags.'; $string['randomquestionusinganewcategory'] = 'Random question using a new category'; -$string['randomsystemwithsubcat'] = 'Any system-level category'; $string['randomwithsubcat'] = 'Questions from this category and its subcategories.'; $string['readytosend'] = 'You are about to send your whole quiz to be graded. Are you sure you want to continue?'; $string['reattemptquiz'] = 'Re-attempt quiz'; @@ -1141,3 +1139,8 @@ $string['attemptsdeleted'] = 'Quiz attempts deleted'; $string['gradesdeleted'] = 'Quiz grades deleted'; $string['useroverridesdeleted'] = 'User overrides deleted'; $string['groupoverridesdeleted'] = 'Group overrides deleted'; + +// Deprecated since Moodle 5.0. +$string['randomcoursecatwithsubcat'] = 'Any category inside course category {$a}'; +$string['randomcoursewithsubcat'] = 'Any category in this course'; +$string['randomsystemwithsubcat'] = 'Any system-level category'; diff --git a/mod/quiz/report/overview/tests/behat/regrading.feature b/mod/quiz/report/overview/tests/behat/regrading.feature index 619b048b3af..4b6b1bfa0a7 100644 --- a/mod/quiz/report/overview/tests/behat/regrading.feature +++ b/mod/quiz/report/overview/tests/behat/regrading.feature @@ -20,12 +20,12 @@ Feature: Regrading quiz attempts using the Grades report | student1 | C1 | student | | student2 | C1 | student | | student3 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: | activity | name | course | idnumber | | quiz | Quiz for testing regrading | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | | Test questions | truefalse | TF | diff --git a/mod/quiz/tests/behat/attempt_basic.feature b/mod/quiz/tests/behat/attempt_basic.feature index 04ebd106617..2006cb8df9a 100644 --- a/mod/quiz/tests/behat/attempt_basic.feature +++ b/mod/quiz/tests/behat/attempt_basic.feature @@ -14,9 +14,16 @@ Feature: Attempt a quiz And the following "course enrolments" exist: | user | course | role | | student | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | grade | navmethod | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | 100 | free | + | quiz | Quiz 2 | Quiz 2 description | C1 | quiz2 | 6 | free | + | quiz | Quiz 3 | Quiz 3 description | C1 | quiz3 | 100 | free | + | quiz | Quiz 4 | Quiz 4 description | C1 | quiz4 | 100 | sequential | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | | | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | @@ -25,12 +32,6 @@ Feature: Attempt a quiz | Test questions | truefalse | TF4 | Fourth question | | Test questions | truefalse | TF5 | Fifth question | | Test questions | truefalse | TF6 | Sixth question | - And the following "activities" exist: - | activity | name | intro | course | idnumber | grade | navmethod | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | 100 | free | - | quiz | Quiz 2 | Quiz 2 description | C1 | quiz2 | 6 | free | - | quiz | Quiz 3 | Quiz 3 description | C1 | quiz3 | 100 | free | - | quiz | Quiz 4 | Quiz 4 description | C1 | quiz4 | 100 | sequential | And quiz "Quiz 1" contains the following questions: | question | page | maxmark | | TF1 | 1 | | diff --git a/mod/quiz/tests/behat/attempt_begin.feature b/mod/quiz/tests/behat/attempt_begin.feature index 1b7508ae1a1..40216bc62a3 100644 --- a/mod/quiz/tests/behat/attempt_begin.feature +++ b/mod/quiz/tests/behat/attempt_begin.feature @@ -11,12 +11,15 @@ Feature: The various checks that may happen when an attept is started And the following "courses" exist: | fullname | shortname | category | | Course 1 | C1 | 0 | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "course enrolments" exist: | user | course | role | | student | C1 | student | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | Text of the first question | diff --git a/mod/quiz/tests/behat/attempt_multiple_grades.feature b/mod/quiz/tests/behat/attempt_multiple_grades.feature index 3942adbda61..2e70c4e7aa5 100644 --- a/mod/quiz/tests/behat/attempt_multiple_grades.feature +++ b/mod/quiz/tests/behat/attempt_multiple_grades.feature @@ -14,12 +14,12 @@ Feature: Attempt a quiz with multiple grades And the following "course enrolments" exist: | user | course | role | | student | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: - | activity | name | course | - | quiz | Quiz 1 | C1 | + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | @javascript Scenario: Navigation to, and display of, grading setup diff --git a/mod/quiz/tests/behat/attempt_require_previous.feature b/mod/quiz/tests/behat/attempt_require_previous.feature index cf715db3482..dde8c467332 100644 --- a/mod/quiz/tests/behat/attempt_require_previous.feature +++ b/mod/quiz/tests/behat/attempt_require_previous.feature @@ -12,13 +12,16 @@ Feature: Attempt a quiz where some questions require that the previous question And the following "courses" exist: | fullname | shortname | category | | Course 1 | C1 | 0 | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "course enrolments" exist: | user | course | role | | student | C1 | student | | teacher | C1 | editingteacher | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | @javascript Scenario Outline: A question that requires the previous one is initially blocked diff --git a/mod/quiz/tests/behat/attempt_review_options.feature b/mod/quiz/tests/behat/attempt_review_options.feature index 2b8c0dce980..86b079c29fa 100644 --- a/mod/quiz/tests/behat/attempt_review_options.feature +++ b/mod/quiz/tests/behat/attempt_review_options.feature @@ -16,9 +16,12 @@ Feature: Allow settings to show Max marks and Marks, Max marks only, or hide the | user | course | role | | teacher | C1 | editingteacher | | student1 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/behat/attempt_sequential.feature b/mod/quiz/tests/behat/attempt_sequential.feature index 5f620c56d5b..10f9bd5e4e7 100644 --- a/mod/quiz/tests/behat/attempt_sequential.feature +++ b/mod/quiz/tests/behat/attempt_sequential.feature @@ -14,18 +14,18 @@ Feature: Attempt a quiz in a sequential mode | user | course | role | | student | C1 | student | | teacher | C1 | teacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | preferredbehaviour | navmethod | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | immediatefeedback | sequential | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | | Test questions | truefalse | TF2 | Second question | | Test questions | truefalse | TF3 | Third question | | Test questions | truefalse | TF4 | Fourth question | - And the following "activities" exist: - | activity | name | intro | course | idnumber | preferredbehaviour | navmethod | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | immediatefeedback | sequential | And quiz "Quiz 1" contains the following questions: | question | page | requireprevious | | TF1 | 1 | 1 | diff --git a/mod/quiz/tests/behat/behat_mod_quiz.php b/mod/quiz/tests/behat/behat_mod_quiz.php index b7c02f28168..f1ce1f6b2e3 100644 --- a/mod/quiz/tests/behat/behat_mod_quiz.php +++ b/mod/quiz/tests/behat/behat_mod_quiz.php @@ -155,7 +155,10 @@ class behat_mod_quiz extends behat_question_base { return new moodle_url('/question/edit.php', [ 'cmid' => $this->get_cm_by_quiz_name($identifier)->id, ]); - + case 'question categories': + return new moodle_url('/question/bank/managecategories/category.php', [ + 'cmid' => $this->get_cm_by_quiz_name($identifier)->id, + ]); default: throw new Exception('Unrecognised quiz page type "' . $type . '."'); diff --git a/mod/quiz/tests/behat/completion_condition_attempts_used.feature b/mod/quiz/tests/behat/completion_condition_attempts_used.feature index 6e42450577d..0b22b50066a 100644 --- a/mod/quiz/tests/behat/completion_condition_attempts_used.feature +++ b/mod/quiz/tests/behat/completion_condition_attempts_used.feature @@ -16,15 +16,15 @@ Feature: Set a quiz to be marked complete when the student uses all attempts all | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | First question | Answer the first question | And the following "activities" exist: | activity | name | course | idnumber | attempts | gradepass | completion | completionusegrade | completionpassgrade | completionattemptsexhausted | | quiz | Test quiz name | C1 | quiz1 | 2 | 5.00 | 2 | 1 | 1 | 1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | First question | Answer the first question | And quiz "Test quiz name" contains the following questions: | question | page | | First question | 1 | diff --git a/mod/quiz/tests/behat/completion_manual.feature b/mod/quiz/tests/behat/completion_manual.feature index d2f3e2c762b..14e06ba0051 100644 --- a/mod/quiz/tests/behat/completion_manual.feature +++ b/mod/quiz/tests/behat/completion_manual.feature @@ -16,15 +16,15 @@ Feature: Manually complete a quiz | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | First question | Answer the first question | And the following "activities" exist: | activity | name | course | idnumber | completion | | quiz | Test quiz name | C1 | quiz1 | 1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | First question | Answer the first question | And quiz "Test quiz name" contains the following questions: | question | page | | First question | 1 | diff --git a/mod/quiz/tests/behat/editing_add.feature b/mod/quiz/tests/behat/editing_add.feature index 6603a5b3ccf..f65696e08d9 100644 --- a/mod/quiz/tests/behat/editing_add.feature +++ b/mod/quiz/tests/behat/editing_add.feature @@ -17,7 +17,9 @@ Feature: Edit quiz page - adding things And the following "activities" exist: | activity | name | intro | course | idnumber | | quiz | Quiz 1 | Quiz 1 for testing the Add menu | C1 | quiz1 | - | qbank | Qbank 1 | Question bank 1 for testing the Add menu | C1 | qbank1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" @javascript @@ -100,21 +102,11 @@ Feature: Edit quiz page - adding things in various categories and add them to the question bank. # Create a couple of sub categories. - When I am on the "Course 1" "core_question > course question categories" page - Then I should see "Add category" - And I follow "Add category" - Then I set the field "Parent category" to "Default for Bank 1" - And I set the field "Name" to "Subcat 1" - And I set the field "Category info" to "This is sub category 1" - And I press "id_submitbutton" - And I should see "Subcat 1" - - And I follow "Add category" - Then I set the field "Parent category" to "Default for C1" - And I set the field "Name" to "Subcat 2" - And I set the field "Category info" to "This is sub category 2" - And I press "id_submitbutton" - And I should see "Subcat 2" + Given the following "question categories" exist: + | contextlevel | reference | questioncategory | name | + | Activity module | quiz1 | Test questions | Subcat 1 | + | Activity module | quiz1 | Test questions | Subcat 2 | + When I am on the "Quiz 1" "mod_quiz > question categories" page And I select "Questions" from the "Question bank tertiary navigation" singleselect And I should see "Question bank" @@ -240,10 +232,7 @@ Feature: Edit quiz page - adding things @accessibility @javascript Scenario: Check the accessibility of the quiz questions page - Given the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: + Given the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | | Test questions | truefalse | Other question | Answer the first question | diff --git a/mod/quiz/tests/behat/editing_move_by_click.feature b/mod/quiz/tests/behat/editing_move_by_click.feature index 7f838d6665b..45986af801e 100644 --- a/mod/quiz/tests/behat/editing_move_by_click.feature +++ b/mod/quiz/tests/behat/editing_move_by_click.feature @@ -14,17 +14,17 @@ Feature: Edit quiz page - drag-and-drop And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | Question A | This is question 01 | | Test questions | truefalse | Question B | This is question 02 | | Test questions | truefalse | Question C | This is question 03 | - And the following "activities" exist: - | activity | name | course | idnumber | - | quiz | Quiz 1 | C1 | quiz1 | And quiz "Quiz 1" contains the following questions: | question | page | | Question A | 1 | diff --git a/mod/quiz/tests/behat/editing_multiple_grades.feature b/mod/quiz/tests/behat/editing_multiple_grades.feature index 6c52da061c6..079b6040d9e 100644 --- a/mod/quiz/tests/behat/editing_multiple_grades.feature +++ b/mod/quiz/tests/behat/editing_multiple_grades.feature @@ -14,18 +14,18 @@ Feature: Setup multiple grades for a quiz And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | description | Info | Some information | | Test questions | truefalse | Question A | This is question 01 | | Test questions | truefalse | Question B | This is question 02 | | Test questions | truefalse | Question C | This is question 03 | - And the following "activities" exist: - | activity | name | course | - | quiz | Quiz 1 | C1 | @javascript Scenario: Navigation to, and display of, grading setup diff --git a/mod/quiz/tests/behat/editing_question_numbering.feature b/mod/quiz/tests/behat/editing_question_numbering.feature index 03c8e81ad2e..f6d1a372bee 100644 --- a/mod/quiz/tests/behat/editing_question_numbering.feature +++ b/mod/quiz/tests/behat/editing_question_numbering.feature @@ -15,13 +15,14 @@ Feature: Editing question numbering of the existing questions already in a quiz | user | course | role | | teacher1 | C1 | editingteacher | And the following "activities" exist: - | activity | name | intro | course | idnumber | - | quiz | Quiz 1 | Quiz 1 for testing | C1 | quiz1 | - | quiz | Quiz 2 | Quiz 2 for testing | C1 | quiz2 | + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 for testing | C1 | quiz1 | + | quiz | Quiz 2 | Quiz 2 for testing | C1 | quiz2 | + | qbank | Qbank 1 | Qbank for testing | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Questions Category 1| - | Course | C1 | Questions Category 2| + | contextlevel | reference | name | + | Activity module | qbank1 | Questions Category 1| + | Activity module | qbank1 | Questions Category 2| Given the following "questions" exist: | questioncategory | qtype | name | questiontext | | Questions Category 1 | description | Description | This is decription ... | diff --git a/mod/quiz/tests/behat/editing_remove_multiple_questions.feature b/mod/quiz/tests/behat/editing_remove_multiple_questions.feature index 7a87eafc1f4..0ee5bb119dd 100644 --- a/mod/quiz/tests/behat/editing_remove_multiple_questions.feature +++ b/mod/quiz/tests/behat/editing_remove_multiple_questions.feature @@ -14,12 +14,12 @@ Feature: Edit quiz page - remove multiple questions And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: - | activity | name | course | idnumber | - | quiz | Quiz 1 | C1 | quiz1 | + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And I log in as "teacher1" @javascript diff --git a/mod/quiz/tests/behat/editing_remove_question.feature b/mod/quiz/tests/behat/editing_remove_question.feature index 1ae73b6e90a..05091ed1369 100644 --- a/mod/quiz/tests/behat/editing_remove_question.feature +++ b/mod/quiz/tests/behat/editing_remove_question.feature @@ -14,12 +14,12 @@ Feature: Edit quiz page - remove questions And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: - | activity | name | course | idnumber | - | quiz | Quiz 1 | C1 | quiz1 | + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And I log in as "teacher1" @javascript diff --git a/mod/quiz/tests/behat/editing_require_previous.feature b/mod/quiz/tests/behat/editing_require_previous.feature index 1774edc643b..a0c07568928 100644 --- a/mod/quiz/tests/behat/editing_require_previous.feature +++ b/mod/quiz/tests/behat/editing_require_previous.feature @@ -14,9 +14,12 @@ Feature: Edit quizzes where some questions require the previous one to have been And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And I log in as "teacher1" @javascript diff --git a/mod/quiz/tests/behat/editing_section_headings.feature b/mod/quiz/tests/behat/editing_section_headings.feature index 2aa55e9b0ff..37ab09408f5 100644 --- a/mod/quiz/tests/behat/editing_section_headings.feature +++ b/mod/quiz/tests/behat/editing_section_headings.feature @@ -15,9 +15,12 @@ Feature: Edit quiz page - section headings And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And I log in as "teacher1" @javascript diff --git a/mod/quiz/tests/behat/editing_set_marks_no_attempts.feature b/mod/quiz/tests/behat/editing_set_marks_no_attempts.feature index 5fcde062bee..1291409ebd7 100644 --- a/mod/quiz/tests/behat/editing_set_marks_no_attempts.feature +++ b/mod/quiz/tests/behat/editing_set_marks_no_attempts.feature @@ -15,12 +15,12 @@ Feature: Edit quiz marks with no attempts | user | course | role | | teacher1 | C1 | editingteacher | And the following "activities" exist: - | activity | name | course | idnumber | grade | decimalpoints | questiondecimalpoints | - | quiz | Quiz 1 | C1 | quiz1 | 20 | 2 | -1 | + | activity | name | course | idnumber | grade | decimalpoints | questiondecimalpoints | + | quiz | Quiz 1 | C1 | quiz1 | 20 | 2 | -1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer me | diff --git a/mod/quiz/tests/behat/editing_switch_bank.feature b/mod/quiz/tests/behat/editing_switch_bank.feature index bba10db2f04..8b66759862b 100644 --- a/mod/quiz/tests/behat/editing_switch_bank.feature +++ b/mod/quiz/tests/behat/editing_switch_bank.feature @@ -31,13 +31,6 @@ Feature: Switching question bank when adding questions to a quiz | Activity module | qbank3 | Test questions 3 | | Activity module | qbank4 | Test questions 4 | | Activity module | quiz1 | Test questions 5 | - And the following "questions" exist: - | questioncategory | qtype | name | user | questiontext | idnumber | - | Test questions 1 | essay | question 01 name | admin | Question 01 text | qidnum1 | - | Test questions 2 | essay | question 02 name | teacher1 | Question 02 text | qidnum2 | - | Test questions 3 | essay | question 03 name | teacher1 | Question 03 text | qidnum3 | - | Test questions 4 | essay | question 04 name | admin | Question 04 text | qidnum4 | - | Test questions 5 | essay | question 05 name | teacher1 | Question 05 text | qidnum5 | And I log in as "teacher1" And I am on the "Quiz 1" "mod_quiz > Edit" page diff --git a/mod/quiz/tests/behat/feedback_on_override_mark.feature b/mod/quiz/tests/behat/feedback_on_override_mark.feature index c2620cd659a..b94c5807c22 100644 --- a/mod/quiz/tests/behat/feedback_on_override_mark.feature +++ b/mod/quiz/tests/behat/feedback_on_override_mark.feature @@ -16,15 +16,15 @@ Feature: Teachers see correct answers when overriding marks | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | + And the following "activities" exist: + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | generalfeedback | | Test questions | shortanswer | Kermit | What kind of animal is Kermit? | | - And the following "activities" exist: - | activity | name | course | idnumber | - | quiz | Quiz 1 | C1 | quiz1 | And quiz "Quiz 1" contains the following questions: | question | page | | Kermit | 1 | diff --git a/mod/quiz/tests/behat/flag_questions.feature b/mod/quiz/tests/behat/flag_questions.feature index 7870e199aaa..aecdf6edee5 100644 --- a/mod/quiz/tests/behat/flag_questions.feature +++ b/mod/quiz/tests/behat/flag_questions.feature @@ -16,14 +16,6 @@ Feature: Flag quiz questions | user | course | role | | student1 | C1 | student | | teacher1 | C1 | teacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | TF1 | First question | - | Test questions | truefalse | TF2 | Second question | - | Test questions | truefalse | TF3 | Third question | And the following "activity" exists: | activity | quiz | | name | Quiz 1 | @@ -36,6 +28,15 @@ Feature: Flag quiz questions | generalfeedbackimmediately | 1 | | rightanswerimmediately | 1 | | overallfeedbackimmediately | 1 | + | idnumber | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | TF1 | First question | + | Test questions | truefalse | TF2 | Second question | + | Test questions | truefalse | TF3 | Third question | And quiz "Quiz 1" contains the following questions: | question | page | | TF1 | 1 | diff --git a/mod/quiz/tests/behat/group_mode.feature b/mod/quiz/tests/behat/group_mode.feature index 21a2fbc82f9..db4faf585f4 100644 --- a/mod/quiz/tests/behat/group_mode.feature +++ b/mod/quiz/tests/behat/group_mode.feature @@ -42,9 +42,10 @@ Feature: Viewing results by group | activity | name | intro | course | idnumber | groupmode | | quiz | Separate quiz | quiz with separate groups | C1 | quiz1 | 1 | | quiz | Visible quiz | quiz with visible groups | C1 | quiz2 | 2 | + | qbank | Qbank 1 | | C1 | qbank1 | | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/behat/info_page.feature b/mod/quiz/tests/behat/info_page.feature index 318f7768d99..fc95a17e183 100644 --- a/mod/quiz/tests/behat/info_page.feature +++ b/mod/quiz/tests/behat/info_page.feature @@ -14,9 +14,12 @@ Feature: Display of information before starting a quiz And the following "course enrolments" exist: | user | course | role | | student | C1 | student | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | Text of the first question | diff --git a/mod/quiz/tests/behat/manually_mark_question.feature b/mod/quiz/tests/behat/manually_mark_question.feature index 981b2d29715..9bbfccf554d 100644 --- a/mod/quiz/tests/behat/manually_mark_question.feature +++ b/mod/quiz/tests/behat/manually_mark_question.feature @@ -16,15 +16,16 @@ Feature: Teachers can override the grade for any question | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | grade | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | 20 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | defaultmark | | Test questions | essay | TF1 | First question | 20 | - And the following "activities" exist: - | activity | name | intro | course | idnumber | grade | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | 20 | + And quiz "Quiz 1" contains the following questions: | question | page | | TF1 | 1 | diff --git a/mod/quiz/tests/behat/preview.feature b/mod/quiz/tests/behat/preview.feature index 3d6fdc495f2..5266f474802 100644 --- a/mod/quiz/tests/behat/preview.feature +++ b/mod/quiz/tests/behat/preview.feature @@ -14,12 +14,12 @@ Feature: Preview a quiz as a teacher And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: - | activity | name | intro | course | idnumber | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/behat/quiz_activity_availability.feature b/mod/quiz/tests/behat/quiz_activity_availability.feature index bb07940006d..d6fe0f90a8d 100644 --- a/mod/quiz/tests/behat/quiz_activity_availability.feature +++ b/mod/quiz/tests/behat/quiz_activity_availability.feature @@ -16,9 +16,12 @@ Feature: Quiz availability can be set | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/behat/quiz_activity_certainty.feature b/mod/quiz/tests/behat/quiz_activity_certainty.feature index c8de86c9560..c9a377ab7ce 100644 --- a/mod/quiz/tests/behat/quiz_activity_certainty.feature +++ b/mod/quiz/tests/behat/quiz_activity_certainty.feature @@ -16,17 +16,17 @@ Feature: Set a quiz with certainty-based marking | user | course | role | | student1 | C1 | student | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | | Test questions | truefalse | TF2 | Second question | | Test questions | truefalse | TF3 | Third question | - And the following "activities" exist: - | activity | name | course | - | quiz | Quiz 1 | C1 | And quiz "Quiz 1" contains the following questions: | question | page | | TF1 | 1 | diff --git a/mod/quiz/tests/behat/quiz_activity_completion.feature b/mod/quiz/tests/behat/quiz_activity_completion.feature index f9047398d7e..2c52d187a2e 100644 --- a/mod/quiz/tests/behat/quiz_activity_completion.feature +++ b/mod/quiz/tests/behat/quiz_activity_completion.feature @@ -16,12 +16,6 @@ Feature: View activity completion in the quiz activity | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | First question | Answer the first question | And the following "activity" exists: | activity | quiz | | course | C1 | @@ -37,6 +31,12 @@ Feature: View activity completion in the quiz activity | completionattemptsexhausted | 1 | | completionminattemptsenabled | 1 | | completionminattempts | 1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | First question | Answer the first question | And quiz "Test quiz name" contains the following questions: | question | page | | First question | 1 | diff --git a/mod/quiz/tests/behat/quiz_activity_completion_locked.feature b/mod/quiz/tests/behat/quiz_activity_completion_locked.feature index dd60560f310..7ea12aa7b53 100644 --- a/mod/quiz/tests/behat/quiz_activity_completion_locked.feature +++ b/mod/quiz/tests/behat/quiz_activity_completion_locked.feature @@ -17,12 +17,6 @@ Feature: Ensure saving a quiz does not modify the completion settings. | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | First question | Answer the first question | And the following "activity" exists: | activity | quiz | | course | C1 | @@ -36,6 +30,12 @@ Feature: Ensure saving a quiz does not modify the completion settings. | completionusegrade | 1 | | completionpassgrade | 1 | | completionattemptsexhausted | 1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | First question | Answer the first question | And quiz "Test quiz" contains the following questions: | question | page | | First question | 1 | diff --git a/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature b/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature index 79943ed6094..69a11262d7c 100644 --- a/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature +++ b/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature @@ -16,13 +16,6 @@ Feature: Activity completion in the quiz activity with unlocked and re-grading. | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | defaultmark | - | Test questions | truefalse | First question | Answer the first question | 8 | - | Test questions | truefalse | Second question | Answer the second question | 2 | And the following "activity" exists: | activity | quiz | | course | C1 | @@ -35,6 +28,13 @@ Feature: Activity completion in the quiz activity with unlocked and re-grading. | completion | 2 | | completionusegrade | 1 | | completionpassgrade | 1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | defaultmark | + | Test questions | truefalse | First question | Answer the first question | 8 | + | Test questions | truefalse | Second question | Answer the second question | 2 | And quiz "Test quiz name" contains the following questions: | question | page | | First question | 1 | diff --git a/mod/quiz/tests/behat/quiz_feedback.feature b/mod/quiz/tests/behat/quiz_feedback.feature index e7f5af11ae3..ed4185780b1 100644 --- a/mod/quiz/tests/behat/quiz_feedback.feature +++ b/mod/quiz/tests/behat/quiz_feedback.feature @@ -15,9 +15,12 @@ Feature: Enable deferred or immediate feedback for quiz | user | course | role | | student1 | C1 | student | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/behat/quiz_interactive_multiple_tries.feature b/mod/quiz/tests/behat/quiz_interactive_multiple_tries.feature index ba6e923d26b..efcb5b4bc71 100644 --- a/mod/quiz/tests/behat/quiz_interactive_multiple_tries.feature +++ b/mod/quiz/tests/behat/quiz_interactive_multiple_tries.feature @@ -16,16 +16,16 @@ Feature: Set a quiz to be interactive with multiple tries | user | course | role | | student1 | C1 | student | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | preferredbehaviour | specificfeedbackduring | idnumber | + | quiz | Quiz 1 | C1 | interactive | 1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | template | hint1 | hint2 | shuffleanswers | | Test questions | multichoice | MC1 | First question | one_of_four | Hint 1 | Hint 2 | 0 | | Test questions | multichoice | MC2 | Second question | one_of_four | Hint 1 | Hint 2 | 0 | - And the following "activities" exist: - | activity | name | course | preferredbehaviour | specificfeedbackduring | - | quiz | Quiz 1 | C1 | interactive | 1 | And quiz "Quiz 1" contains the following questions: | question | page | | MC1 | 1 | diff --git a/mod/quiz/tests/behat/quiz_question_versions.feature b/mod/quiz/tests/behat/quiz_question_versions.feature index 739da2fca53..c8c22e35474 100644 --- a/mod/quiz/tests/behat/quiz_question_versions.feature +++ b/mod/quiz/tests/behat/quiz_question_versions.feature @@ -14,12 +14,12 @@ Feature: Quiz question versioning And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: - | activity | name | course | idnumber | - | quiz | Quiz 1 | C1 | quiz1 | + | activity | name | course | idnumber | + | quiz | Quiz 1 | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/mod/quiz/tests/behat/quiz_reset.feature b/mod/quiz/tests/behat/quiz_reset.feature index b70e6b48435..98fa9629e8f 100644 --- a/mod/quiz/tests/behat/quiz_reset.feature +++ b/mod/quiz/tests/behat/quiz_reset.feature @@ -20,15 +20,15 @@ Feature: Quiz reset | name | course | idnumber | | Group 1 | C1 | G1 | | Group 2 | C1 | G2 | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | TF1 | First question | And the following "activities" exist: | activity | name | intro | course | idnumber | | quiz | Test quiz name | Test quiz description | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | TF1 | First question | And quiz "Test quiz name" contains the following questions: | question | page | | TF1 | 1 | diff --git a/mod/quiz/tests/behat/random_question.feature b/mod/quiz/tests/behat/random_question.feature index ae5080fdd58..8474261d025 100644 --- a/mod/quiz/tests/behat/random_question.feature +++ b/mod/quiz/tests/behat/random_question.feature @@ -16,14 +16,15 @@ Feature: Moving a question to another category should not affect random question | user | course | role | | teacher1 | C1 | editingteacher | And the following "activities" exist: - | activity | name | intro | course | idnumber | - | quiz | Quiz 1 | Quiz 1 for testing the Add random question form | C1 | quiz1 | + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 for testing the Add random question form | C1 | quiz1 | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | questioncategory | name | - | Course | C1 | Top | top | - | Course | C1 | top | Default for C1 | - | Course | C1 | Default for C1 | Subcategory | - | Course | C1 | top | Used category | + | contextlevel | reference | questioncategory | name | + | Activity module | qbank1 | Top | top | + | Activity module | qbank1 | top | Default for Qbank 1 | + | Activity module | qbank1 | Default for Qbank 1 | Subcategory | + | Activity module | qbank1 | top | Used category | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Used category | essay | Test question to be moved | Write about whatever you want | @@ -33,21 +34,27 @@ Feature: Moving a question to another category should not affect random question Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" When I open the "last" add to quiz menu And I follow "a random question" + And I click on "Switch bank" "button" + And I click on "Qbank 1" "link" in the "Select question bank" "dialogue" And I apply question bank filter "Category" with value "Used category" And I press "Add random question" And I should see "Random (Used category) based on filter condition" on quiz page "1" And I click on "Configure question" "link" in the "Random (Used category) based on filter condition" "list_item" And I should see "Used category" And I am on "Course 1" course homepage - And I navigate to "Question bank" in current page administration + And I navigate to "Question banks" in current page administration + And I click on "Qbank 1" "link" And I apply question bank filter "Category" with value "Used category" And I click on "Test question to be moved" "checkbox" in the "Test question to be moved" "table_row" And I click on "With selected" "button" And I click on question bulk action "move" - And I set the field "Question category" to "Subcategory" - And I press "Move to" - Then I should see "Test question to be moved" - And I should see "Subcategory (1)" + And I open the autocomplete suggestions list in the ".search-categories" "css_element" + And I click on "Subcategory" item in the autocomplete list + And I click on "Move questions" "button" + And I should see "Are you sure you want to move these questions?" + And I click on "Confirm" "button" + And I wait until the page is ready + Then I should see "Subcategory (1)" And I am on the "Quiz 1" "mod_quiz > Edit" page And I should see "Random (Used category) based on filter condition" on quiz page "1" And I click on "Configure question" "link" in the "Random (Used category) based on filter condition" "list_item" @@ -58,10 +65,12 @@ Feature: Moving a question to another category should not affect random question Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" When I open the "last" add to quiz menu And I follow "a random question" + Then I click on "Switch bank" "button" + And I click on "Qbank 1" "link" in the "Select question bank" "dialogue" And I apply question bank filter "Category" with value "Used category" And I press "Add random question" And I should see "Random (Used category) based on filter condition" on quiz page "1" - And I am on the "Course 1" "core_question > course question categories" page + And I am on the "Qbank 1" "core_question > question categories" page And I open the action menu in "Used category" "list_item" And I choose "Edit" in the open action menu And I set the following fields to these values: diff --git a/mod/quiz/tests/behat/reattempt_quiz.feature b/mod/quiz/tests/behat/reattempt_quiz.feature index 36cd2b3bcc9..929ff27166c 100644 --- a/mod/quiz/tests/behat/reattempt_quiz.feature +++ b/mod/quiz/tests/behat/reattempt_quiz.feature @@ -18,16 +18,16 @@ Feature: Several attempts in a quiz | student1 | C1 | student | | student2 | C1 | student | | teacher | C1 | teacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | preferredbehaviour | navmethod | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | immediatefeedback | free | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | | Test questions | truefalse | TF2 | Second question | - And the following "activities" exist: - | activity | name | intro | course | idnumber | preferredbehaviour | navmethod | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | immediatefeedback | free | And quiz "Quiz 1" contains the following questions: | question | page | requireprevious | | TF1 | 1 | 1 | diff --git a/mod/quiz/tests/behat/settings_form_fields_disableif.feature b/mod/quiz/tests/behat/settings_form_fields_disableif.feature index 059e912251d..cd4fadaba3d 100644 --- a/mod/quiz/tests/behat/settings_form_fields_disableif.feature +++ b/mod/quiz/tests/behat/settings_form_fields_disableif.feature @@ -125,11 +125,12 @@ Feature: Settings form fields disabled if not required @javascript Scenario: If there are quiz attempts, there is not option to repaginate. Given the following "activities" exist: - | activity | name | intro | course | idnumber | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | + | qbank | Qbank 1 | Qbank for testing | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | diff --git a/mod/quiz/tests/quiz_question_restore_test.php b/mod/quiz/tests/quiz_question_restore_test.php index ac115904704..67de8cdfb8c 100644 --- a/mod/quiz/tests/quiz_question_restore_test.php +++ b/mod/quiz/tests/quiz_question_restore_test.php @@ -151,19 +151,20 @@ class quiz_question_restore_test extends \advanced_testcase { } /** - * Test if a duplicate does not duplicate questions in course question bank. + * Test if a duplicate does not duplicate questions from a shared question bank. * * @covers ::duplicate_module */ - public function test_quiz_duplicate_does_not_duplicate_course_question_bank_questions(): void { + public function test_quiz_duplicate_does_not_duplicate_questions_from_shared_banks(): void { $this->resetAfterTest(); $quiz = $this->create_test_quiz($this->course); - // Test for questions from a different context. - $context = \context_course::instance($this->course->id); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $this->course->id]); + // Test for questions from a qbank context. + $context = \context_module::instance($qbank->cmid); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $this->add_two_regular_questions($questiongenerator, $quiz, ['contextid' => $context->id]); $this->add_one_random_question($questiongenerator, $quiz, ['contextid' => $context->id]); - // Count the questions in course context. + // Count the questions in qbank context. $this->assertEquals(7, $this->question_count($context->id)); $newquiz = $this->duplicate_quiz($this->course, $quiz); $this->assertEquals(7, $this->question_count($context->id)); diff --git a/question/bank/columnsortorder/classes/column_manager.php b/question/bank/columnsortorder/classes/column_manager.php index badd6cc24d7..7676d9bc449 100644 --- a/question/bank/columnsortorder/classes/column_manager.php +++ b/question/bank/columnsortorder/classes/column_manager.php @@ -184,7 +184,7 @@ class column_manager extends column_manager_base { $contexts, new moodle_url('/question/bank/columnsortorder/sortcolumns.php'), $course, - null, + $cm, $params ); return $questionbank; diff --git a/question/bank/columnsortorder/tests/behat/question_bank.feature b/question/bank/columnsortorder/tests/behat/question_bank.feature index fed6ab56be4..7a7ccc0db09 100644 --- a/question/bank/columnsortorder/tests/behat/question_bank.feature +++ b/question/bank/columnsortorder/tests/behat/question_bank.feature @@ -137,7 +137,7 @@ Feature: Set question bank column order and size And I click on "Disable" "link" in the "Question statistics" "table_row" And I click on "Enable" "link" in the "Question statistics" "table_row" And I click on "Disable" "link" in the "Question statistics" "table_row" - And I am on the "Course 1" "core_question > course question bank" page + And I am on the "Test quiz Q001" "mod_quiz > question bank" page Then I should see "Question bank" And "Create a new question" "button" should exist # Really, we are just checking the question bank displayed without errors. diff --git a/question/bank/columnsortorder/tests/column_manager_test.php b/question/bank/columnsortorder/tests/column_manager_test.php index c1816bffe33..c6ddfc44db1 100644 --- a/question/bank/columnsortorder/tests/column_manager_test.php +++ b/question/bank/columnsortorder/tests/column_manager_test.php @@ -48,10 +48,12 @@ class column_manager_test extends advanced_testcase { protected static function get_question_bank(): view { $course = self::getDataGenerator()->create_course(); $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $cm = get_coursemodule_from_id('qbank', $qbank->cmid); $questionbank = new view( new question_edit_contexts(\context_module::instance($qbank->cmid)), new moodle_url('/'), - $course + $course, + $cm ); return $questionbank; } diff --git a/question/bank/comment/tests/backup_test.php b/question/bank/comment/tests/backup_test.php index f908e16979c..aecd0f1f007 100644 --- a/question/bank/comment/tests/backup_test.php +++ b/question/bank/comment/tests/backup_test.php @@ -74,7 +74,8 @@ class backup_test extends \advanced_testcase { // Question initial set up. $this->category = $this->getDataGenerator()->create_category(); $this->course = $this->getDataGenerator()->create_course(['category' => $this->category->id]); - $context = \context_coursecat::instance($this->category->id); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $this->course->id]); + $context = \context_module::instance($qbank->cmid); $this->qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $this->qgen->create_question_category(['contextid' => $context->id]); diff --git a/question/bank/comment/tests/behat/question_comment.feature b/question/bank/comment/tests/behat/question_comment.feature index 702e88df9e1..f5958879fbf 100644 --- a/question/bank/comment/tests/behat/question_comment.feature +++ b/question/bank/comment/tests/behat/question_comment.feature @@ -17,8 +17,8 @@ Feature: A Teacher can comment in a question | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/comment/tests/behat/question_comment_column.feature b/question/bank/comment/tests/behat/question_comment_column.feature index 74006caf219..243be8bd412 100644 --- a/question/bank/comment/tests/behat/question_comment_column.feature +++ b/question/bank/comment/tests/behat/question_comment_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for comment | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/comment/tests/lib_test.php b/question/bank/comment/tests/lib_test.php index 5cad82bab9a..ebf28e72c65 100644 --- a/question/bank/comment/tests/lib_test.php +++ b/question/bank/comment/tests/lib_test.php @@ -84,7 +84,8 @@ class lib_test extends \advanced_testcase { $category = $this->getDataGenerator()->create_category(); $course = $this->getDataGenerator()->create_course(['category' => $category->id]); $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = \context_coursecat::instance($category->id); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $context = \context_module::instance($qbank->cmid); $qcat = $qgen->create_question_category(['contextid' => $context->id]); $question = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q1']); @@ -111,8 +112,9 @@ class lib_test extends \advanced_testcase { // Make a test question. $category = $this->getDataGenerator()->create_category(); $course = $this->getDataGenerator()->create_course(['category' => $category->id]); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $context = \context_module::instance($qbank->cmid); $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = \context_coursecat::instance($category->id); $qcat = $qgen->create_question_category(['contextid' => $context->id]); $question = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q1']); $args = [ diff --git a/question/bank/customfields/tests/customfield_test.php b/question/bank/customfields/tests/customfield_test.php index 962ba0c8f6a..6fbd52c4ba5 100644 --- a/question/bank/customfields/tests/customfield_test.php +++ b/question/bank/customfields/tests/customfield_test.php @@ -91,7 +91,8 @@ class customfield_test extends \advanced_testcase { // Question initial set up. $this->category = $this->getDataGenerator()->create_category(); $this->course = $this->getDataGenerator()->create_course(['category' => $this->category->id]); - $context = \context_coursecat::instance($this->category->id); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $this->course->id]); + $context = \context_module::instance($qbank->cmid); $this->qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); $qcat = $this->qgen->create_question_category(['contextid' => $context->id]); diff --git a/question/bank/customfields/tests/question_handler_test.php b/question/bank/customfields/tests/question_handler_test.php index 9ebd0e559e8..e0f62f10af1 100644 --- a/question/bank/customfields/tests/question_handler_test.php +++ b/question/bank/customfields/tests/question_handler_test.php @@ -33,9 +33,10 @@ class question_handler_test extends \advanced_testcase { * @throws coding_exception */ protected function setup_question(): int { - $category = $this->getDataGenerator()->create_category(); + $course = self::getDataGenerator()->create_course(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = \context_coursecat::instance($category->id); + $context = \context_module::instance($qbank->cmid); $questioncategory = $questiongenerator->create_question_category(['contextid' => $context->id]); $questiondata = ['category' => $questioncategory->id, 'idnumber' => 'q1']; $question = $questiongenerator->create_question('shortanswer', null, $questiondata); diff --git a/question/bank/deletequestion/classes/delete_action.php b/question/bank/deletequestion/classes/delete_action.php index b1fc73b5474..b98ad9cb528 100644 --- a/question/bank/deletequestion/classes/delete_action.php +++ b/question/bank/deletequestion/classes/delete_action.php @@ -64,12 +64,8 @@ class delete_action extends question_action_base { $this->strdelete = get_string('delete'); $this->strrestore = get_string('restore'); $this->deletequestionurl = new \moodle_url('/question/bank/deletequestion/delete.php'); - if (!empty($this->qbank->cm->id)) { - $this->returnparams['cmid'] = $this->qbank->cm->id; - } - if (!empty($this->qbank->course->id)) { - $this->returnparams['courseid'] = $this->qbank->course->id; - } + $this->returnparams['cmid'] = $this->qbank->cm->id; + if (!empty($this->qbank->returnurl)) { $this->returnparams['returnurl'] = $this->qbank->returnurl; } diff --git a/question/bank/deletequestion/delete.php b/question/bank/deletequestion/delete.php index 396dda83f99..99e3566359d 100644 --- a/question/bank/deletequestion/delete.php +++ b/question/bank/deletequestion/delete.php @@ -31,8 +31,7 @@ global $DB, $OUTPUT, $PAGE, $COURSE; $deleteselected = optional_param('deleteselected', false, PARAM_BOOL); $returnurl = optional_param('returnurl', 0, PARAM_LOCALURL); -$cmid = optional_param('cmid', 0, PARAM_INT); -$courseid = optional_param('courseid', 0, PARAM_INT); +$cmid = required_param('cmid', PARAM_INT); $deleteall = optional_param('deleteall', false, PARAM_BOOL); if ($returnurl) { @@ -41,16 +40,9 @@ if ($returnurl) { \core_question\local\bank\helper::require_plugin_enabled('qbank_deletequestion'); -if ($cmid) { - list($module, $cm) = get_module_from_cmid($cmid); - require_login($cm->course, false, $cm); - $thiscontext = context_module::instance($cmid); -} else if ($courseid) { - require_login($courseid, false); - $thiscontext = context_course::instance($courseid); -} else { - throw new moodle_exception('missingcourseorcmid', 'question'); -} +[$module, $cm] = get_module_from_cmid($cmid); +require_login($cm->course, false, $cm); +$thiscontext = context_module::instance($cmid); $contexts = new core_question\local\bank\question_edit_contexts($thiscontext); $url = new moodle_url('/question/bank/deletequestion/delete.php'); @@ -115,7 +107,6 @@ if ($deleteselected) { 'sesskey' => sesskey(), 'returnurl' => $returnurl->out_as_local_url(false), 'cmid' => $cmid, - 'courseid' => $courseid, ], ); $continue = new \single_button($deleteurl, get_string('delete'), 'post'); diff --git a/question/bank/deletequestion/tests/behat/delete_question_column.feature b/question/bank/deletequestion/tests/behat/delete_question_column.feature index 2e09b09a464..9c36f6ae240 100644 --- a/question/bank/deletequestion/tests/behat/delete_question_column.feature +++ b/question/bank/deletequestion/tests/behat/delete_question_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for deletequestion | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | @@ -58,6 +58,7 @@ Feature: Use the qbank plugin manager page for deletequestion @javascript Scenario: I should be able to delete a question when filtered using tags Given I am on the "First question" "core_question > edit" page logged in as "admin" + And I change window size to "large" And I set the following fields to these values: | Tags | foo | And I click on "Save changes" "button" diff --git a/question/bank/editquestion/addquestion.php b/question/bank/editquestion/addquestion.php index 7b8502bab44..102cd916041 100644 --- a/question/bank/editquestion/addquestion.php +++ b/question/bank/editquestion/addquestion.php @@ -29,8 +29,7 @@ use qbank_editquestion\editquestion_helper; // Read URL parameters. $categoryid = required_param('category', PARAM_INT); -$cmid = optional_param('cmid', 0, PARAM_INT); -$courseid = optional_param('courseid', 0, PARAM_INT); +$cmid = required_param('cmid', PARAM_INT); $returnurl = optional_param('returnurl', 0, PARAM_LOCALURL); $appendqnumstring = optional_param('appendqnumstring', '', PARAM_ALPHA); $validationerror = optional_param('validationerror', false, PARAM_BOOL); @@ -45,20 +44,10 @@ if (!$category = $DB->get_record('question_categories', array('id' => $categoryi throw new moodle_exception('categorydoesnotexist', 'question', $returnurl); } -if ($cmid) { - list($module, $cm) = get_module_from_cmid($cmid); - require_login($cm->course, false, $cm); - $thiscontext = context_module::instance($cmid); - $hiddenparams['cmid'] = $cmid; -} else if ($courseid) { - require_login($courseid, false); - $thiscontext = context_course::instance($courseid); - $module = null; - $cm = null; - $hiddenparams['courseid'] = $courseid; -} else { - throw new moodle_exception('missingcourseorcmid', 'question'); -} +list($module, $cm) = get_module_from_cmid($cmid); +require_login($cm->course, false, $cm); +$thiscontext = context_module::instance($cmid); +$hiddenparams['cmid'] = $cmid; // Check permissions. $categorycontext = context::instance_by_id($category->contextid); @@ -73,11 +62,9 @@ if (!empty($appendqnumstring)) { } $PAGE->set_url('/question/bank/editquestion/addquestion.php', $hiddenparams); -if ($cmid) { - $questionbankurl = new moodle_url('/question/edit.php', array('cmid' => $cmid)); -} else { - $questionbankurl = new moodle_url('/question/edit.php', array('courseid' => $courseid)); -} + +$questionbankurl = new moodle_url('/question/edit.php', ['cmid' => $cmid]); + navigation_node::override_active_url($questionbankurl); $chooseqtype = get_string('chooseqtypetoadd', 'question'); diff --git a/question/bank/editquestion/classes/copy_action.php b/question/bank/editquestion/classes/copy_action.php index 514e91005b3..5b7a3058b3e 100644 --- a/question/bank/editquestion/classes/copy_action.php +++ b/question/bank/editquestion/classes/copy_action.php @@ -50,11 +50,7 @@ class copy_action extends question_action_base { $this->strcopy = get_string('duplicate'); $this->duplicatequestionurl = new \moodle_url('/question/bank/editquestion/question.php', array('returnurl' => $this->qbank->returnurl)); - if ($this->qbank->cm !== null) { - $this->duplicatequestionurl->param('cmid', $this->qbank->cm->id); - } else { - $this->duplicatequestionurl->param('courseid', $this->qbank->course->id); - } + $this->duplicatequestionurl->param('cmid', $this->qbank->cm->id); } public function get_menu_position(): int { diff --git a/question/bank/editquestion/classes/edit_action.php b/question/bank/editquestion/classes/edit_action.php index d642e37ce0a..09155d1f245 100644 --- a/question/bank/editquestion/classes/edit_action.php +++ b/question/bank/editquestion/classes/edit_action.php @@ -60,11 +60,7 @@ class edit_action extends question_action_base { $this->strview = get_string('view'); $this->editquestionurl = new \moodle_url('/question/bank/editquestion/question.php', array('returnurl' => $this->qbank->returnurl)); - if ($this->qbank->cm !== null) { - $this->editquestionurl->param('cmid', $this->qbank->cm->id); - } else { - $this->editquestionurl->param('courseid', $this->qbank->course->id); - } + $this->editquestionurl->param('cmid', $this->qbank->cm->id); } public function get_menu_position(): int { diff --git a/question/bank/editquestion/question.php b/question/bank/editquestion/question.php index ee2efa87487..dfade996968 100644 --- a/question/bank/editquestion/question.php +++ b/question/bank/editquestion/question.php @@ -31,8 +31,7 @@ $id = optional_param('id', 0, PARAM_INT); // Question id. $makecopy = optional_param('makecopy', 0, PARAM_BOOL); $qtype = optional_param('qtype', '', PARAM_COMPONENT); $categoryid = optional_param('category', 0, PARAM_INT); -$cmid = optional_param('cmid', 0, PARAM_INT); -$courseid = optional_param('courseid', 0, PARAM_INT); +$cmid = required_param('cmid', PARAM_INT); $wizardnow = optional_param('wizardnow', '', PARAM_ALPHA); $originalreturnurl = optional_param('returnurl', 0, PARAM_LOCALURL); $appendqnumstring = optional_param('appendqnumstring', '', PARAM_ALPHA); @@ -57,9 +56,6 @@ if ($categoryid !== 0) { if ($cmid !== 0) { $url->param('cmid', $cmid); } -if ($courseid !== 0) { - $url->param('courseid', $courseid); -} if ($wizardnow !== '') { $url->param('wizardnow', $wizardnow); } @@ -77,11 +73,8 @@ if ($mdlscrollto) { } $PAGE->set_url($url); -if ($cmid) { - $questionbankurl = new moodle_url('/question/edit.php', array('cmid' => $cmid)); -} else { - $questionbankurl = new moodle_url('/question/edit.php', array('courseid' => $courseid)); -} +$questionbankurl = new moodle_url('/question/edit.php', ['cmid' => $cmid]); + navigation_node::override_active_url($questionbankurl); if ($originalreturnurl) { @@ -96,18 +89,10 @@ if ($mdlscrollto) { $returnurl->param('mdlscrollto', $mdlscrollto); } -if ($cmid) { - list($module, $cm) = get_module_from_cmid($cmid); - require_login($cm->course, false, $cm); - $thiscontext = context_module::instance($cmid); -} else if ($courseid) { - require_login($courseid, false); - $thiscontext = context_course::instance($courseid); - $module = null; - $cm = null; -} else { - throw new moodle_exception('missingcourseorcmid', 'question'); -} +list($module, $cm) = get_module_from_cmid($cmid); +require_login($cm->course, false, $cm); +$thiscontext = context_module::instance($cmid); + $contexts = new core_question\local\bank\question_edit_contexts($thiscontext); $PAGE->set_pagelayout('admin'); @@ -119,10 +104,7 @@ if ($id) { if (!$question = $DB->get_record('question', array('id' => $id))) { throw new moodle_exception('questiondoesnotexist', 'question', $returnurl); } - // We can use $COURSE here because it's been initialised as part of the - // require_login above. Passing it as the third parameter tells the function - // to filter the course tags by that course. - get_question_options($question, true, [$COURSE]); + get_question_options($question, true); } else if ($categoryid && $qtype) { // Only for creating new questions. $question = new stdClass(); diff --git a/question/bank/editquestion/tests/behat/qbank_editquestion.feature b/question/bank/editquestion/tests/behat/qbank_editquestion.feature index 7614c0989b7..13b6b939a73 100644 --- a/question/bank/editquestion/tests/behat/qbank_editquestion.feature +++ b/question/bank/editquestion/tests/behat/qbank_editquestion.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for editquestion | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/editquestion/tests/behat/question_status.feature b/question/bank/editquestion/tests/behat/question_status.feature index 92fe44cfe12..a040c68d4d5 100644 --- a/question/bank/editquestion/tests/behat/question_status.feature +++ b/question/bank/editquestion/tests/behat/question_status.feature @@ -10,8 +10,8 @@ Feature: Use the qbank base view to test the status change using | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/exporttoxml/classes/helper.php b/question/bank/exporttoxml/classes/helper.php index e2276992e73..431bc822d2c 100644 --- a/question/bank/exporttoxml/classes/helper.php +++ b/question/bank/exporttoxml/classes/helper.php @@ -35,22 +35,10 @@ class helper { * @return \moodle_url the requested URL. */ public static function question_get_export_single_question_url($question): \moodle_url { - $params = ['id' => $question->id, 'sesskey' => sesskey()]; $context = \context::instance_by_id($question->contextid); - switch ($context->contextlevel) { - case CONTEXT_MODULE: - $params['cmid'] = $context->instanceid; - break; - - case CONTEXT_COURSE: - $params['courseid'] = $context->instanceid; - break; - - default: - $params['courseid'] = SITEID; - } - - return new \moodle_url('/question/bank/exporttoxml/exportone.php', $params); + return new \moodle_url('/question/bank/exporttoxml/exportone.php', + ['id' => $question->id, 'sesskey' => sesskey(), 'cmid' => $context->instanceid] + ); } } diff --git a/question/bank/exporttoxml/exportone.php b/question/bank/exporttoxml/exportone.php index 6fc3cab8910..768873ad032 100644 --- a/question/bank/exporttoxml/exportone.php +++ b/question/bank/exporttoxml/exportone.php @@ -29,24 +29,16 @@ require_once($CFG->dirroot . '/question/format/xml/format.php'); // Get the parameters from the URL. $questionid = required_param('id', PARAM_INT); -$cmid = optional_param('cmid', 0, PARAM_INT); -$courseid = optional_param('courseid', 0, PARAM_INT); +$cmid = required_param('cmid', PARAM_INT); $urlparams = ['id' => $questionid, 'sesskey' => sesskey()]; \core_question\local\bank\helper::require_plugin_enabled('qbank_exporttoxml'); -if ($cmid) { - $cm = get_coursemodule_from_id(null, $cmid); - require_login($cm->course, false, $cm); - $thiscontext = context_module::instance($cmid); - $urlparams['cmid'] = $cmid; -} else if ($courseid) { - require_login($courseid, false); - $thiscontext = context_course::instance($courseid); - $urlparams['courseid'] = $courseid; -} else { - throw new moodle_exception('missingcourseorcmid', 'question'); -} +$cm = get_coursemodule_from_id(null, $cmid); +require_login($cm->course, false, $cm); +$thiscontext = context_module::instance($cmid); +$urlparams['cmid'] = $cmid; + require_sesskey(); // Load the necessary data. diff --git a/question/bank/exporttoxml/tests/behat/export_to_xml_action.feature b/question/bank/exporttoxml/tests/behat/export_to_xml_action.feature index 64ef39857c7..9185882cfee 100644 --- a/question/bank/exporttoxml/tests/behat/export_to_xml_action.feature +++ b/question/bank/exporttoxml/tests/behat/export_to_xml_action.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for exporttoxml | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/exporttoxml/tests/behat/preview_question.feature b/question/bank/exporttoxml/tests/behat/preview_question.feature index 9420ecc64ee..72e88432c63 100644 --- a/question/bank/exporttoxml/tests/behat/preview_question.feature +++ b/question/bank/exporttoxml/tests/behat/preview_question.feature @@ -14,9 +14,12 @@ Feature: A teacher can export a question as XML from the preview question screen And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | name | contextlevel | reference | - | Test questions | Course | C1 | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | | Test questions | numerical | Test question to be previewed | diff --git a/question/bank/history/classes/helper.php b/question/bank/history/classes/helper.php index 58dd7563bb0..834c93b6eec 100644 --- a/question/bank/history/classes/helper.php +++ b/question/bank/history/classes/helper.php @@ -34,8 +34,12 @@ class helper { * @param int $courseid id of the course * @param ?string $filter filter param to pass to the History view * @return \moodle_url + * @deprecated since Moodle 5.0. + * @todo MDL-82413 Final deprecation in Moodle 6.0. */ + #[\core\attribute\deprecated(replacement: 'qbank_history\helper::get_question_history_url', since: '5.0', mdl: 'MDL-71378')] public static function question_history_url(int $entryid, string $returnrul, int $courseid, ?string $filter): \moodle_url { + \core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]); $params = [ 'entryid' => $entryid, 'returnurl' => $returnrul, @@ -48,4 +52,27 @@ class helper { return new \moodle_url('/question/bank/history/history.php', $params); } + /** + * Get the question history url. + * + * @param int $entryid id of the question entry + * @param string $returnrul url of the page to return to + * @param int $cmid id of the coursemodule holding the question bank. + * @param ?string $filter filter param to pass to the History view + * @return \moodle_url + */ + public static function get_question_history_url(int $entryid, string $returnrul, int $cmid, ?string $filter) { + + $params = [ + 'entryid' => $entryid, + 'returnurl' => $returnrul, + 'cmid' => $cmid, + ]; + if (!is_null($filter)) { + $params['filter'] = $filter; + } + + return new \moodle_url('/question/bank/history/history.php', $params); + } + } diff --git a/question/bank/history/classes/history_action.php b/question/bank/history/classes/history_action.php index 7157cf806d6..894712ab6f5 100644 --- a/question/bank/history/classes/history_action.php +++ b/question/bank/history/classes/history_action.php @@ -49,10 +49,10 @@ class history_action extends question_action_base { } if (question_has_capability_on($question, 'use')) { - $url = helper::question_history_url( + $url = helper::get_question_history_url( $question->questionbankentryid, $this->qbank->returnurl, - $this->qbank->course->id, + $this->qbank->cm->id, $this->qbank->base_url()->param('filter'), ); return [$url, 't/log', $this->strpreview]; diff --git a/question/bank/history/classes/question_history_view.php b/question/bank/history/classes/question_history_view.php index 769de64b7c1..6931512a942 100644 --- a/question/bank/history/classes/question_history_view.php +++ b/question/bank/history/classes/question_history_view.php @@ -63,6 +63,10 @@ class question_history_view extends view { array $params = [], array $extraparams = [], ) { + if ($cm === null) { + debugging('$cm is now a required field', DEBUG_DEVELOPER); + } + $this->entryid = $extraparams['entryid']; $this->basereturnurl = new \moodle_url($extraparams['returnurl']); parent::__construct($contexts, $pageurl, $course, $cm, $params, $extraparams); diff --git a/question/bank/history/history.php b/question/bank/history/history.php index 6e80d200765..9ad76f75255 100644 --- a/question/bank/history/history.php +++ b/question/bank/history/history.php @@ -42,7 +42,7 @@ $PAGE->set_url($url); // Additional param to differentiate with other question bank view. $extraparams['entryid'] = $entryid; $extraparams['returnurl'] = $returnurl; -$questionbank = new \qbank_history\question_history_view($contexts, $url, $COURSE, null, $pagevars, $extraparams); +$questionbank = new \qbank_history\question_history_view($contexts, $url, $COURSE, $cm, $pagevars, $extraparams); $streditingquestions = get_string('history_header', 'qbank_history'); $PAGE->set_title($streditingquestions); diff --git a/question/bank/history/tests/behat/question_history_action.feature b/question/bank/history/tests/behat/question_history_action.feature index ac1ca5877f0..fd2f7bb0777 100644 --- a/question/bank/history/tests/behat/question_history_action.feature +++ b/question/bank/history/tests/behat/question_history_action.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for question history | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | @@ -42,8 +42,8 @@ Feature: Use the qbank plugin manager page for question history @javascript Scenario: Viewing history for a question in a non-default category Given the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions 2 | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions 2 | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions 2 | truefalse | Second question | Answer the second question | diff --git a/question/bank/history/tests/behat/question_version_column.feature b/question/bank/history/tests/behat/question_version_column.feature index 81ddace0cb7..c18e2485252 100644 --- a/question/bank/history/tests/behat/question_version_column.feature +++ b/question/bank/history/tests/behat/question_version_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for version column | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/history/tests/helper_test.php b/question/bank/history/tests/helper_test.php index 331423e0b1b..8bff1a7896b 100644 --- a/question/bank/history/tests/helper_test.php +++ b/question/bank/history/tests/helper_test.php @@ -80,16 +80,16 @@ class helper_test extends \advanced_testcase { public function test_question_history_url(): void { $this->resetAfterTest(); $filter = urlencode('filters[]'); - $actionurl = helper::question_history_url( + $actionurl = helper::get_question_history_url( $this->questiondata->questionbankentryid, $this->returnurl, - $this->courseid, + $this->context->instanceid, $filter, ); $params = [ 'entryid' => $this->questiondata->questionbankentryid, 'returnurl' => $this->returnurl, - 'courseid' => $this->courseid, + 'cmid' => $this->context->instanceid, 'filter' => $filter, ]; $expectedurl = new \moodle_url('/question/bank/history/history.php', $params); @@ -103,16 +103,16 @@ class helper_test extends \advanced_testcase { */ public function test_question_history_url_null_filter(): void { $this->resetAfterTest(); - $actionurl = helper::question_history_url( + $actionurl = helper::get_question_history_url( $this->questiondata->questionbankentryid, $this->returnurl, - $this->courseid, + $this->context->instanceid, null, ); $params = [ 'entryid' => $this->questiondata->questionbankentryid, 'returnurl' => $this->returnurl, - 'courseid' => $this->courseid, + 'cmid' => $this->context->instanceid, ]; $expectedurl = new \moodle_url('/question/bank/history/history.php', $params); $this->assertEquals($expectedurl, $actionurl); diff --git a/question/bank/history/tests/question_history_view_test.php b/question/bank/history/tests/question_history_view_test.php index e33484ef25f..265fe283461 100644 --- a/question/bank/history/tests/question_history_view_test.php +++ b/question/bank/history/tests/question_history_view_test.php @@ -46,6 +46,7 @@ class question_history_view_test extends \advanced_testcase { // Create a course. $course = $generator->create_course(); $qbank = $generator->create_module('qbank', ['course' => $course->id]); + $cm = get_coursemodule_from_id('qbank', $qbank->cmid); $context = \context_module::instance($qbank->cmid); // Create a question in the default category. @@ -73,7 +74,7 @@ class question_history_view_test extends \advanced_testcase { 'entryid' => $entry->id, 'returnurl' => "/", ]; - $view = new $viewclass($contexts, new \moodle_url('/'), $course, null, $pagevars, $extraparams); + $view = new $viewclass($contexts, new \moodle_url('/'), $course, $cm, $pagevars, $extraparams); ob_start(); $view->display(); $html = ob_get_clean(); @@ -98,6 +99,8 @@ class question_history_view_test extends \advanced_testcase { // Create a course. $course = $generator->create_course(); + $qbank = $generator->create_module('qbank', ['course' => $course->id]); + $cm = get_coursemodule_from_id('qbank', $qbank->cmid); $context = \context_course::instance($course->id); // Create a question in the default category. @@ -120,7 +123,7 @@ class question_history_view_test extends \advanced_testcase { 'entryid' => $entry->id, 'returnurl' => "/", ]; - $view = new $viewclass($contexts, new \moodle_url('/'), $course, null, $pagevars, $extraparams); + $view = new $viewclass($contexts, new \moodle_url('/'), $course, $cm, $pagevars, $extraparams); ob_start(); $view->display_question_bank_header(); $headerhtml = ob_get_clean(); @@ -129,7 +132,7 @@ class question_history_view_test extends \advanced_testcase { $questiondata2 = $questiongenerator->update_question($questiondata1, null, ['name' => 'Second version']); - $view = new $viewclass($contexts, new \moodle_url('/'), $course, null, $pagevars, $extraparams); + $view = new $viewclass($contexts, new \moodle_url('/'), $course, $cm, $pagevars, $extraparams); ob_start(); $view->display_question_bank_header(); $headerhtml = ob_get_clean(); diff --git a/question/bank/importquestions/import.php b/question/bank/importquestions/import.php index dea5b4c2a7c..3d7c9340002 100644 --- a/question/bank/importquestions/import.php +++ b/question/bank/importquestions/import.php @@ -48,17 +48,13 @@ if (!$category = $DB->get_record("question_categories", ['id' => $catid])) { $categorycontext = context::instance_by_id($category->contextid); $category->context = $categorycontext; -// This page can be called without courseid or cmid in which case. +// This page can be called without cmid in which case. // We get the context from the category object. if ($contexts === null) { // Need to get the course from the chosen category. $contexts = new core_question\local\bank\question_edit_contexts($categorycontext); $thiscontext = $contexts->lowest(); - if ($thiscontext->contextlevel == CONTEXT_COURSE) { - require_login($thiscontext->instanceid, false); - } else if ($thiscontext->contextlevel == CONTEXT_MODULE) { - list($module, $cm) = get_module_from_cmid($thiscontext->instanceid); - require_login($cm->course, false, $cm); - } + [$module, $cm] = get_module_from_cmid($thiscontext->instanceid); + require_login($cm->course, false, $cm); $contexts->require_one_edit_tab_cap($edittab); } diff --git a/question/bank/managecategories/category.php b/question/bank/managecategories/category.php index 0f12841f68c..9afd4626867 100644 --- a/question/bank/managecategories/category.php +++ b/question/bank/managecategories/category.php @@ -37,16 +37,15 @@ use qbank_managecategories\question_categories; require_login(); core_question\local\bank\helper::require_plugin_enabled(helper::PLUGINNAME); +// Since Moodle 5.0 any request with the courseid parameter is deprecated and will redirect to the banks management page. +if ($courseid = optional_param('courseid', 0, PARAM_INT)) { + redirect(new moodle_url('/question/banks.php', ['courseid' => $courseid])); +} + list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('categories', '/question/bank/managecategories/category.php'); -$courseid = optional_param('courseid', 0, PARAM_INT); -if (!is_null($cmid)) { - $thiscontext = context_module::instance($cmid)->id; -} else { - $course = get_course($courseid); - $thiscontext = context_course::instance($course->id)->id; -} +$thiscontext = context_module::instance($cmid)->id; $todelete = optional_param('delete', 0, PARAM_INT); // The ID of a category to delete. @@ -112,10 +111,13 @@ if ($questionstomove) { echo $OUTPUT->box(get_string('categorymove', 'question', $vars), 'generalbox boxaligncenter'); $moveform->display(); } else { + // Get module contexts we have capabilities to manage. + $contextswithcaps = $contexts->having_one_edit_tab_cap('categories'); + $modcontexts = array_filter($contextswithcaps, static fn ($context) => $context->contextlevel === CONTEXT_MODULE); // Display the user interface. $questioncategories = new question_categories( $thispageurl, - $contexts->having_one_edit_tab_cap('categories'), + $modcontexts, $cmid, $courseid, $thiscontext, diff --git a/question/bank/managecategories/classes/category_condition.php b/question/bank/managecategories/classes/category_condition.php index 6ff8882e556..a8d82c98a52 100644 --- a/question/bank/managecategories/classes/category_condition.php +++ b/question/bank/managecategories/classes/category_condition.php @@ -32,6 +32,9 @@ class category_condition extends condition { /** @var \stdClass The course record. */ protected $course; + /** @var \stdClass The course_modules record. */ + protected \stdClass $cm; + /** @var \stdClass The category record. */ protected $category; @@ -61,6 +64,7 @@ class category_condition extends condition { static fn($context) => $context->contextlevel === CONTEXT_MODULE ); $this->course = $qbank->course; + $this->cm = $qbank->cm; [$categoryid, $contextid] = self::validate_category_param($this->cat); if (is_null($categoryid)) { @@ -74,14 +78,13 @@ class category_condition extends condition { } /** - * MDL-71378 TODO: Not used and to be deprecated anyway * Return default category * * @return \stdClass default category */ public function get_default_category(): \stdClass { if (empty($this->category)) { - return question_get_default_category(\context_course::instance($this->course->id)->id); + return question_get_default_category(\context_module::instance($this->cm->id)->id, true); } return $this->category; diff --git a/question/bank/managecategories/classes/question_categories.php b/question/bank/managecategories/classes/question_categories.php index 2a642a01fde..55e588818d7 100644 --- a/question/bank/managecategories/classes/question_categories.php +++ b/question/bank/managecategories/classes/question_categories.php @@ -86,6 +86,10 @@ class question_categories { fields: 'contextid, id' ); foreach ($contexts as $context) { + // We can only have categories in module context. + if ($context->contextlevel !== CONTEXT_MODULE) { + continue; + } $items = helper::get_categories_for_contexts($context->id); // Create an ordered tree with children correctly nested under parents. foreach ($items as $item) { diff --git a/question/bank/managecategories/classes/question_category_list_item.php b/question/bank/managecategories/classes/question_category_list_item.php index 1f288a271a2..00d012add20 100644 --- a/question/bank/managecategories/classes/question_category_list_item.php +++ b/question/bank/managecategories/classes/question_category_list_item.php @@ -87,12 +87,8 @@ class question_category_list_item extends \list_item { $str = $extraargs['str']; $category = $this->item; - // Each section adds html to be displayed as part of this list item. - $nodeparent = $PAGE->settingsnav->find('questionbank', \navigation_node::TYPE_CONTAINER); + $questionbankurl = new moodle_url('/question/edit.php', $this->parentlist->pageurl->params()); - // The category URL is based on the node action. - $questionbankurl = new moodle_url($nodeparent->action->out_omit_querystring(), - $this->parentlist->pageurl->params()); $questionbankurl->param('cat', $category->id . ',' . $category->contextid); $categoryname = format_string($category->name, true, ['context' => $this->parentlist->context]); diff --git a/question/bank/managecategories/tests/behat/move_question_categories.feature b/question/bank/managecategories/tests/behat/move_question_categories.feature index 1279686dceb..9e5c6aa43cc 100644 --- a/question/bank/managecategories/tests/behat/move_question_categories.feature +++ b/question/bank/managecategories/tests/behat/move_question_categories.feature @@ -11,9 +11,9 @@ Feature: A teacher can reorder question categories And the following "courses" exist: | fullname | shortname | format | | Course 1 | C1 | weeks | - And the following "categories" exist: - | name | category | idnumber | - | Category 1 | 0 | CAT1 | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | @@ -21,16 +21,15 @@ Feature: A teacher can reorder question categories | user | role | contextlevel | | teacher1 | editingteacher | System | And the following "question categories" exist: - | contextlevel | reference | name | idnumber | - | Course | C1 | Course category 1 | questioncat1 | - | Course | C1 | Course category 2 | questioncat2 | - | Course | C1 | Course category 3 | questioncat3 | - | Category | CAT1 | Default for Category 1 | | - | System | S1 | System category | | + | contextlevel | reference | name | idnumber | + | Course | C1 | Course category 1 | questioncat1 | + | Course | C1 | Course category 2 | questioncat2 | + | Course | C1 | Course category 3 | questioncat3 | And I am on the "Course 1" "core_question > course question categories" page logged in as "teacher1" Scenario: Teacher cannot move or delete single category under context - When I open the action menu in "Default for Category 1" "list_item" + Given I am on the "Qbank 1" "core_question > question categories" page logged in as "teacher1" + When I open the action menu in "Default for Qbank 1" "list_item" Then I should not see "Delete" Scenario: Teacher can see complete edit menu if multiples categories exist under context @@ -67,35 +66,9 @@ Feature: A teacher can reorder question categories And "Course category 3" "list_item" should appear before "Course category 2" "list_item" And "Course category 3" "list_item" should exist in the "Course category 1" "list_item" - Scenario: Teacher can move a category between contexts - Given "Course: Course 1" "text" should appear before "Course category 1" "list_item" - And "Category: Category 1" "text" should appear after "Course category 1" "list_item" - And "Category: Category 1" "text" should appear before "Default for Category 1" "list_item" - And "Course category 2" "list_item" should appear before "Course category 3" "list_item" - When I open the action menu in "Course category 1" "list_item" - And I choose "Move" in the open action menu - And I click on "After Default for Category 1" "link" in the "Move Course category 1" "dialogue" - Then "Course: Course 1" "text" should appear before "Course category 1" "list_item" - And "Category: Category 1" "text" should appear before "Course category 1" "list_item" - And "Default for Category 1" "list_item" should appear before "Course category 1" "list_item" - Scenario: Teacher can display and hide category descriptions + Given I am on the "Qbank 1" "core_question > question categories" page logged in as "teacher1" When I click on "Show descriptions" "checkbox" - Then I should see "The default category for questions shared in context 'Category 1'." + Then I should see "The default category for questions shared in context 'Qbank 1'." And I click on "Show descriptions" "checkbox" - And I should not see "The default category for questions shared in context 'Category 1'." - - Scenario: Teacher cannot create a duplicate idnumber within a context by moving a category - Given "Course category 1" "list_item" should appear before "System category" "list_item" - And I open the action menu in "Course category 1" "list_item" - And I choose "Move" in the open action menu - And I click on "After System category" "link" in the "Move Course category 1" "dialogue" - Then "Course category 1" "list_item" should appear after "System category" "list_item" - And I open the action menu in "Course category 2" "list_item" - And I choose "Edit settings" in the open action menu - And I set the field "ID number" to "questioncat1" - And I click on "Save changes" "button" in the "Edit category" "dialogue" - And I open the action menu in "Course category 2" "list_item" - And I choose "Move" in the open action menu - And I click on "After Course category 1" "link" in the "Move Course category 2" "dialogue" - And I should see "ID number already in use. Change it to move or update category." + And I should not see "The default category for questions shared in context 'Qbank 1'." diff --git a/question/bank/managecategories/tests/behat/question_categories.feature b/question/bank/managecategories/tests/behat/question_categories.feature index d01caa7bdde..f0b0f830f8c 100644 --- a/question/bank/managecategories/tests/behat/question_categories.feature +++ b/question/bank/managecategories/tests/behat/question_categories.feature @@ -12,16 +12,19 @@ Feature: A teacher can put questions in categories in the question bank | fullname | shortname | format | | Course 1 | C1 | weeks | And the following "course enrolments" exist: - | user | course | role | - | teacher1 | C1 | editingteacher | + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | questioncategory | name | sortorder | desciption | - | Course | C1 | Top | top | 0 | | - | Course | C1 | top | Default for C1 | 0 | Description for default for C1 | - | Course | C1 | Default for C1 | Subcategory | 0 | Description for Subcategory | - | Course | C1 | Default for C1 | Another subcat | 1 | Description for Another subcat | - | Course | C1 | top | Used category | 1 | | - | Course | C1 | top | Default & testing | 2 | | + | contextlevel | reference | questioncategory | name | + | Activity module | qbank1 | Top | top | + | Activity module | qbank1 | top | Default for qbank1 | + | Activity module | qbank1 | Default for qbank1 | Subcategory | + | Activity module | qbank1 | Default for qbank1 | Another subcat | + | Activity module | qbank1 | top | Used category | + | Activity module | qbank1 | top | Default & testing | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Used category | essay | Test question to be moved | Write about whatever you want | @@ -29,7 +32,7 @@ Feature: A teacher can put questions in categories in the question bank And I log in as "teacher1" Scenario: A new question category can be created - When I am on the "Course 1" "core_question > course question categories" page + When I am on the "Qbank 1" "core_question > question categories" page And I press "Add category" And I set the following fields to these values: | Name | 'Test' & 'display' | @@ -47,14 +50,14 @@ Feature: A teacher can put questions in categories in the question bank And the "Parent category" select box should contain "'Test' & 'display' [newcatidnumber]" Scenario: A question category can be edited - When I am on the "Course 1" "core_question > course question categories" page + When I am on the "Qbank 1" "core_question > question categories" page # There have been bugs which only happened if a question category was not empty, so add a question. And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Subcategory | essay | Test question for renaming category | Write about whatever you want | And I open the action menu in "Subcategory" "list_item" And I choose "Edit settings" in the open action menu - And the field "parent" matches value "   Default for C1" + And the field "parent" matches value "   Default for qbank1" And I set the following fields to these values: | Name | New name | | Category info | I was edited | @@ -64,32 +67,32 @@ Feature: A teacher can put questions in categories in the question bank And I should see "I was edited" Scenario: An empty question category can be deleted - When I am on the "Course 1" "core_question > course question categories" page + When I am on the "Qbank 1" "core_question > question categories" page And I open the action menu in "Subcategory" "list_item" And I choose "Delete" in the open action menu And I click on "Delete" "button" in the "Delete" "dialogue" Then I should not see "Subcategory" Scenario: An non-empty question category can be deleted if you move the contents elsewhere - When I am on the "Course 1" "core_question > course question categories" page + When I am on the "Qbank 1" "core_question > question categories" page And I open the action menu in "Used category" "list_item" And I choose "Delete" in the open action menu And I click on "Delete" "button" in the "Delete" "dialogue" And I should see "The category 'Used category' contains 1 questions" - And I select "Default for C1" from the "Category" singleselect + And I select "Default for qbank1" from the "Category" singleselect And I press "Save in category" Then I should not see "Used category" - And I should see "Default for C1" - And I should see "(1)" + And I press "Add category" + And I should see "Default for qbank1 (1)" @_file_upload Scenario: Multi answer questions with their child questions can be moved to another category when the current category is deleted - When I am on the "Course 1" "core_question > course question import" page + When I am on the "Qbank 1" "core_question > question import" page And I set the field "id_format_xml" to "1" And I upload "question/format/xml/tests/fixtures/multianswer.xml" file to "Import" filemanager And I press "id_submitbutton" And I press "Continue" - And I am on the "Course 1" "core_question > course question categories" page + And I am on the "Qbank 1" "core_question > question categories" page And I open the action menu in "Default for Test images in backup" "list_item" And I choose "Delete" in the open action menu And I click on "Delete" "button" in the ".modal-footer" "css_element" @@ -101,8 +104,8 @@ Feature: A teacher can put questions in categories in the question bank And I should see "Used category (2)" Scenario: Filter questions by category and subcategories - When I am on the "Course 1" "core_question > course question bank" page - And I apply question bank filter "Category" with value "Default for C1" + When I am on the "Qbank 1" "core_question > question bank" page + And I apply question bank filter "Category" with value "Default for qbank1" Then I should not see "Question 1" When I set the field "Also show questions from subcategories" to "1" And I click on "Apply filters" "button" diff --git a/question/bank/managecategories/tests/behat/question_categories_idnumber.feature b/question/bank/managecategories/tests/behat/question_categories_idnumber.feature index 2db6176742c..b38d4e6aef9 100644 --- a/question/bank/managecategories/tests/behat/question_categories_idnumber.feature +++ b/question/bank/managecategories/tests/behat/question_categories_idnumber.feature @@ -14,20 +14,23 @@ Feature: A teacher can put questions with idnumbers in categories with idnumbers And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And I log in as "teacher1" And I am on "Course 1" course homepage Scenario: A new question category can only be created with a unique idnumber for a context # Note need to create the top category each time. When the following "question categories" exist: - | contextlevel | reference | questioncategory | name | idnumber | - | Course | C1 | Top | top | | - | Course | C1 | top | Used category | c1used | - And I am on the "Course 1" "core_question > course question categories" page + | contextlevel | reference | questioncategory | name | idnumber | + | Activity module | qbank1 | Top | top | | + | Activity module | qbank1 | top | Used category | c1used | + And I am on the "Qbank 1" "core_question > question categories" page And I press "Add category" And I set the following fields to these values: | Name | New cat | - | Parent category | Top for Course 1 | + | Parent category | Top for Qbank 1 | | Category info | Created as a test | | ID number | c1used | And I click on "Add category" "button" in the "Add category" "dialogue" @@ -45,10 +48,10 @@ Feature: A teacher can put questions with idnumbers in categories with idnumbers Scenario: A question category can be edited and saved without changing the idnumber When the following "question categories" exist: - | contextlevel | reference | questioncategory | name | idnumber | - | Course | C1 | Top | top | | - | Course | C1 | top | Used category | c1used | - And I am on the "Course 1" "core_question > course question categories" page + | contextlevel | reference | questioncategory | name | idnumber | + | Activity module | qbank1 | Top | top | | + | Activity module | qbank1 | top | Used category | c1used | + And I am on the "Qbank 1" "core_question > question categories" page Then I open the action menu in "Used category" "list_item" And I choose "Edit settings" in the open action menu And I click on "Save changes" "button" in the "Edit category" "dialogue" diff --git a/question/bank/managecategories/tests/behat/view_manage_categories_plugin.feature b/question/bank/managecategories/tests/behat/view_manage_categories_plugin.feature index 457108cf720..f827bb8f122 100644 --- a/question/bank/managecategories/tests/behat/view_manage_categories_plugin.feature +++ b/question/bank/managecategories/tests/behat/view_manage_categories_plugin.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for managecategories | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/managecategories/tests/external/move_category_test.php b/question/bank/managecategories/tests/external/move_category_test.php index ea0b5ee1182..614caf71b55 100644 --- a/question/bank/managecategories/tests/external/move_category_test.php +++ b/question/bank/managecategories/tests/external/move_category_test.php @@ -17,7 +17,7 @@ namespace qbank_managecategories\external; use context; -use context_course; +use context_module; use moodle_url; use qbank_managecategories\question_categories; @@ -75,13 +75,14 @@ final class move_category_test extends \qbank_managecategories\manage_category_t // Create context for question categories. $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($course); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); // Check current order. $currentorder = $this->get_current_order($context); @@ -136,14 +137,14 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->resetAfterTest(); // Create context for question categories. - $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); // Check current order. $currentorder = $this->get_current_order($context); @@ -205,14 +206,14 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->resetAfterTest(); // Create context for question categories. - $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); // Check current order. $currentorder = $this->get_current_order($context); @@ -278,15 +279,15 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->resetAfterTest(); // Create context for question categories. - $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course, ['parent' => $qcat1->id]); - $qcat4 = $this->create_question_category_for_a_course($course, ['parent' => $qcat2->id]); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat1->id]); + $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat2->id]); // Check current order. $currentorder = $this->get_current_order($context); @@ -329,77 +330,6 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->assertEquals($stateupdates, $expectedstateupdates); } - /** - * Move a category from the top category of one context to another. - * - * @return void - */ - public function test_move_category_between_contexts(): void { - $this->setAdminUser(); - $this->resetAfterTest(); - - // Create context for question categories. - $course = $this->create_course(); - $coursecontext = context_course::instance($course->id); - $coursecategory = $this->create_course_category(); - $categorycontext = \context_coursecat::instance($coursecategory->id); - - // Question categories. - $coursecatqcat = question_get_top_category($categorycontext->id, true); - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - - $currentcourseorder = $this->get_current_order($coursecontext); - $expectedorder = [ - $qcat1->id => [], - $qcat2->id => [], - ]; - $this->assertEquals($expectedorder, $currentcourseorder); - $currentcoursecatorder = $this->get_current_order($categorycontext); - $expectedcoursecatorder = [ - ]; - $this->assertEquals($expectedcoursecatorder, $currentcoursecatorder); - - $stateupdates = move_category::execute($coursecontext->id, $qcat1->id, $coursecatqcat->id); - - $newcourseorder = $this->get_current_order($coursecontext); - $newexpectedorder = [ - $qcat2->id => [], - ]; - $this->assertEquals($newcourseorder, $newexpectedorder); - $newcoursecatorder = $this->get_current_order($categorycontext); - $newexpectedcoursecatorder = [ - $qcat1->id => [], - ]; - $this->assertEquals($newcoursecatorder, $newexpectedcoursecatorder); - - // Expect an update to the sortorder, parent and context of the moved category. - // Since the original sibling is the only remaining child of a top-level category, it has its draghandle property - // updated as well as its sortorder. - $expectedstateupdates = [ - (object)[ - 'name' => 'categories', - 'action' => 'put', - 'fields' => (object)[ - 'id' => $qcat1->id, - 'sortorder' => 1, - 'parent' => $coursecatqcat->id, - 'context' => $categorycontext->id, - ], - ], - (object)[ - 'name' => 'categories', - 'action' => 'put', - 'fields' => (object)[ - 'id' => $qcat2->id, - 'sortorder' => 1, - 'draghandle' => false, - ], - ], - ]; - $this->assertEquals($stateupdates, $expectedstateupdates); - } - /** * Move a category that has its own children. * @@ -412,16 +342,16 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->resetAfterTest(); // Create context for question categories. - $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course); - $qcat4 = $this->create_question_category_for_a_course($course, ['parent' => $qcat3->id]); - $qcat5 = $this->create_question_category_for_a_course($course, ['parent' => $qcat3->id]); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); + $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]); + $qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]); // Check current order. $currentorder = $this->get_current_order($context); @@ -482,16 +412,16 @@ final class move_category_test extends \qbank_managecategories\manage_category_t $this->resetAfterTest(); // Create context for question categories. - $course = $this->create_course(); - $context = context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $context = context_module::instance($qbank->cmid); $this->create_course_category(); // Question categories. - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course); - $qcat3 = $this->create_question_category_for_a_course($course); - $qcat4 = $this->create_question_category_for_a_course($course, ['parent' => $qcat3->id]); - $qcat5 = $this->create_question_category_for_a_course($course, ['parent' => $qcat4->id]); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); + $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]); + $qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat4->id]); // Check current order. $currentorder = $this->get_current_order($context); diff --git a/question/bank/managecategories/tests/helper_test.php b/question/bank/managecategories/tests/helper_test.php index ed8be52e7ce..cb687f1175a 100644 --- a/question/bank/managecategories/tests/helper_test.php +++ b/question/bank/managecategories/tests/helper_test.php @@ -166,7 +166,7 @@ final class helper_test extends manage_category_test_base { $qcategory1 = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); // Try to delete a top category. - $categorytop = question_get_top_category($qcategory1->id, true)->id; + $categorytop = question_get_top_category($qcategory1->contextid, true)->id; try { helper::question_can_delete_cat($categorytop); } catch (moodle_exception $e) { diff --git a/question/bank/managecategories/tests/manage_category_test_base.php b/question/bank/managecategories/tests/manage_category_test_base.php index 360dcfbd3ed..51219b148af 100644 --- a/question/bank/managecategories/tests/manage_category_test_base.php +++ b/question/bank/managecategories/tests/manage_category_test_base.php @@ -16,8 +16,6 @@ namespace qbank_managecategories; -use core_question\local\bank\question_edit_contexts; -use moodle_url; /** * Test base for category tests * @@ -39,17 +37,6 @@ abstract class manage_category_test_base extends \advanced_testcase { return $questiongenerator->create_question_category($categorydetails); } - /** - * Create a question category for the system. - * - * @param array $categorydetails details of the category - * @return \stdClass question category record - */ - protected function create_question_category_for_the_system(array $categorydetails = []): \stdClass { - $context = \context_system::instance(); - return $this->create_new_question_category_for_a_context($context->id, $categorydetails); - } - /** * Create a course category * @@ -60,21 +47,6 @@ abstract class manage_category_test_base extends \advanced_testcase { return $this->getDataGenerator()->create_category(); } - /** - * Create a question category for a course category. - * - * @param \core_course_category $coursecategory the course category that new question category will be created for - * @param array $categorydetails details of the category - * @return \stdClass question category record - */ - protected function create_question_category_for_a_course_category( - \core_course_category $coursecategory, - array $categorydetails = [], - ): \stdClass { - $context = \context_coursecat::instance($coursecategory->id); - return $this->create_new_question_category_for_a_context($context->id, $categorydetails); - } - /** * Create a course * @@ -86,14 +58,24 @@ abstract class manage_category_test_base extends \advanced_testcase { } /** - * Create a question category for a course. + * Create a mod_qbank instance. * - * @param \stdClass $course the course that new question category will be created for + * @param \stdClass $course to add the mod_qbank instance to. + * @return \stdClass new qbank module + */ + protected function create_qbank(\stdClass $course): \stdClass { + return self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + } + + /** + * Create a question category for a qbank module. + * + * @param \stdClass $qbank the qbank module that new question category will be created for * @param array $categorydetails details of the category * @return \stdClass category record */ - protected function create_question_category_for_a_course(\stdClass $course, array $categorydetails = []): \stdClass { - $context = \context_course::instance($course->id); + protected function create_question_category_for_a_qbank(\stdClass $qbank, array $categorydetails = []): \stdClass { + $context = \context_module::instance($qbank->cmid); return $this->create_new_question_category_for_a_context($context->id, $categorydetails); } diff --git a/question/bank/managecategories/tests/question_categories_test.php b/question/bank/managecategories/tests/question_categories_test.php index 03c6b23d783..8a2e64b314d 100644 --- a/question/bank/managecategories/tests/question_categories_test.php +++ b/question/bank/managecategories/tests/question_categories_test.php @@ -39,19 +39,19 @@ final class question_categories_test extends manage_category_test_base { $this->resetAfterTest(); // Create question categories for a course. - $course = $this->create_course(); - $qcat1 = $this->create_question_category_for_a_course($course); - $qcat2 = $this->create_question_category_for_a_course($course, ['parent' => $qcat1->id]); - $qcat3 = $this->create_question_category_for_a_course($course); - $qcat4 = $this->create_question_category_for_a_course($course, ['parent' => $qcat2->id]); - $coursecontext = \context_course::instance($course->id); + $qbank = $this->create_qbank($this->create_course()); + $qcat1 = $this->create_question_category_for_a_qbank($qbank); + $qcat2 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat1->id]); + $qcat3 = $this->create_question_category_for_a_qbank($qbank); + $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat2->id]); + $context = \context_module::instance($qbank->cmid); // Create ordered tree. $questioncategories = new question_categories( new \moodle_url('/'), - [$coursecontext], + [$context], ); - $items = $questioncategories->editlists[$coursecontext->id]->items; + $items = $questioncategories->editlists[$context->id]->items; // Two top categories (1 and 3) in the course. $this->assertCount(2, $items); diff --git a/question/bank/previewquestion/classes/helper.php b/question/bank/previewquestion/classes/helper.php index 3b0849a428e..29f5e761ed1 100644 --- a/question/bank/previewquestion/classes/helper.php +++ b/question/bank/previewquestion/classes/helper.php @@ -101,7 +101,7 @@ class helper { * @param int $questionid the question being previewed * @param int $qubaid the id of the question usage for this preview * @param question_preview_options $options the options in use - * @param context $context context for the question preview + * @param context $context module context for the question preview * @param moodle_url $returnurl url of the page to return to * @param int|null $restartversion version of the question to use when next restarting the preview. * @return moodle_url @@ -112,11 +112,13 @@ class helper { 'id' => $questionid, 'previewid' => $qubaid, ]; - if ($context->contextlevel == CONTEXT_MODULE) { - $params['cmid'] = $context->instanceid; - } else if ($context->contextlevel == CONTEXT_COURSE) { - $params['courseid'] = $context->instanceid; + + if ($context->contextlevel !== CONTEXT_MODULE) { + debugging("Invalid contextlevel: {$context->contextlevel} must be CONTEXT_MODULE"); } + + $params['cmid'] = $context->instanceid; + if ($returnurl !== null) { $params['returnurl'] = $returnurl; } @@ -131,7 +133,7 @@ class helper { * The the URL to use for actions relating to this preview. * * @param int $questionid the question being previewed - * @param context $context the current moodle context + * @param context $context the current moodle module context * @param int $previewid optional previewid to sign post saved previewed answers * @param moodle_url $returnurl url of the page to return to * @return moodle_url @@ -140,11 +142,13 @@ class helper { $params = [ 'id' => $questionid, ]; - if ($context->contextlevel == CONTEXT_MODULE) { - $params['cmid'] = $context->instanceid; - } else if ($context->contextlevel == CONTEXT_COURSE) { - $params['courseid'] = $context->instanceid; + + if ($context->contextlevel !== CONTEXT_MODULE) { + debugging("Invalid contextlevel: {$context->contextlevel} must be CONTEXT_MODULE"); } + + $params['cmid'] = $context->instanceid; + if ($previewid) { $params['previewid'] = $previewid; } diff --git a/question/bank/previewquestion/preview.php b/question/bank/previewquestion/preview.php index 2ee19639192..a7a9e10d8d5 100644 --- a/question/bank/previewquestion/preview.php +++ b/question/bank/previewquestion/preview.php @@ -63,11 +63,6 @@ if ($cmid = optional_param('cmid', 0, PARAM_INT)) { $cm = get_coursemodule_from_id(false, $cmid); require_login($cm->course, false, $cm); $context = context_module::instance($cmid); - -} else if ($courseid = optional_param('courseid', 0, PARAM_INT)) { - require_login($courseid); - $context = context_course::instance($courseid); - } else { require_login(); $category = $DB->get_record('question_categories', ['id' => $question->category], '*', MUST_EXIST); @@ -321,7 +316,7 @@ if (!is_null($returnurl)) { $previewdata['redirect'] = true; $previewdata['redirecturl'] = $returnurl; } -$closeurl = new moodle_url('/question/edit.php', ['courseid' => $COURSE->id]); +$closeurl = new moodle_url('/question/edit.php', ['cmid' => $context->instanceid]); echo $PAGE->get_renderer('qbank_previewquestion')->render_preview_page($previewdata); // Log the preview of this question. diff --git a/question/bank/previewquestion/tests/behat/preview_question.feature b/question/bank/previewquestion/tests/behat/preview_question.feature index 883886ef92c..54bbad8da42 100644 --- a/question/bank/previewquestion/tests/behat/preview_question.feature +++ b/question/bank/previewquestion/tests/behat/preview_question.feature @@ -18,8 +18,8 @@ Feature: A teacher can preview questions in the question bank | user | course | role | | teacher1 | C1 | editingteacher | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | | Test questions | numerical | Test question to be previewed | diff --git a/question/bank/previewquestion/tests/behat/preview_question_action.feature b/question/bank/previewquestion/tests/behat/preview_question_action.feature index 8123cb5e943..02cc81fd6be 100644 --- a/question/bank/previewquestion/tests/behat/preview_question_action.feature +++ b/question/bank/previewquestion/tests/behat/preview_question_action.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for previewquestion | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/previewquestion/tests/qbank_preview_helper_test.php b/question/bank/previewquestion/tests/qbank_preview_helper_test.php index 93d37082264..f171bf47c5a 100644 --- a/question/bank/previewquestion/tests/qbank_preview_helper_test.php +++ b/question/bank/previewquestion/tests/qbank_preview_helper_test.php @@ -75,7 +75,7 @@ class qbank_preview_helper_test extends \advanced_testcase { $course = $generator->create_course(); $qbank = $generator->create_module('qbank', ['course' => $course->id]); $qbankcontext = \context_module::instance($qbank->cmid); - $this->context = context_course::instance($course->id); + $this->context = $qbankcontext; // Create a question in the default category. $contexts = new \core_question\local\bank\question_edit_contexts($qbankcontext); $cat = question_get_default_category($contexts->lowest()->id, true); @@ -101,7 +101,7 @@ class qbank_preview_helper_test extends \advanced_testcase { 'id' => $this->questiondata->id, 'previewid' => $this->quba->get_id(), 'returnurl' => $this->returnurl, - 'courseid' => $this->context->instanceid, + 'cmid' => $this->context->instanceid, 'restartversion' => question_preview_options::ALWAYS_LATEST, ]; $params = array_merge($params, $this->options->get_url_params()); @@ -121,7 +121,7 @@ class qbank_preview_helper_test extends \advanced_testcase { 'id' => $this->questiondata->id, 'previewid' => $this->quba->get_id(), 'returnurl' => $this->returnurl, - 'courseid' => $this->context->instanceid + 'cmid' => $this->context->instanceid, ]; $params = array_merge($params, $this->options->get_url_params()); $expectedurl = new moodle_url('/question/bank/previewquestion/preview.php', $params); @@ -140,7 +140,7 @@ class qbank_preview_helper_test extends \advanced_testcase { 'id' => $this->questiondata->id, 'previewid' => $this->quba->get_id(), 'returnurl' => $this->returnurl, - 'courseid' => $this->context->instanceid + 'cmid' => $this->context->instanceid, ]; $expectedurl = new moodle_url('/question/bank/previewquestion/preview.php', $params); $this->assertEquals($expectedurl, $formurl); @@ -158,7 +158,7 @@ class qbank_preview_helper_test extends \advanced_testcase { 'id' => $this->questiondata->id, 'behaviour' => $this->options->behaviour, 'maxmark' => $this->options->maxmark, - 'courseid' => $this->context->instanceid, + 'cmid' => $this->context->instanceid, 'restartversion' => question_preview_options::ALWAYS_LATEST, ]; // Extra params for options. @@ -186,7 +186,7 @@ class qbank_preview_helper_test extends \advanced_testcase { 'id' => $this->questiondata->id, 'behaviour' => $this->options->behaviour, 'maxmark' => $this->options->maxmark, - 'courseid' => $this->context->instanceid, + 'cmid' => $this->context->instanceid, ]; // Extra params for options. $params['correctness'] = $this->options->correctness; diff --git a/question/bank/statistics/tests/behat/statistics_action.feature b/question/bank/statistics/tests/behat/statistics_action.feature index 9a5b274640d..59a2e0f4c30 100644 --- a/question/bank/statistics/tests/behat/statistics_action.feature +++ b/question/bank/statistics/tests/behat/statistics_action.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for statistics | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/statistics/tests/behat/statistics_values.feature b/question/bank/statistics/tests/behat/statistics_values.feature index 4b2b030b93a..bce78b1a2ed 100644 --- a/question/bank/statistics/tests/behat/statistics_values.feature +++ b/question/bank/statistics/tests/behat/statistics_values.feature @@ -16,19 +16,20 @@ Feature: Show statistics in question bank | user | course | role | | student1 | C1 | student | | student2 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | + | quiz | Quiz 2 | Quiz 2 description | C1 | quiz2 | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | TF1 | First question | | Test questions | truefalse | TF2 | Second question | | Test questions | truefalse | TF3 | Third question | | Test questions | truefalse | TF4 | Fourth question | - And the following "activities" exist: - | activity | name | intro | course | idnumber | - | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | - | quiz | Quiz 2 | Quiz 2 description | C1 | quiz2 | And quiz "Quiz 1" contains the following questions: | question | page | maxmark | | TF1 | 1 | 1.0 | @@ -74,7 +75,7 @@ Feature: Show statistics in question bank | 1 | True | | 2 | True | And I run pending statistics recalculation tasks - When I am on the "Course 1" "core_question > course question bank" page logged in as "admin" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "admin" Then I should see "50.00%" in the "TF1" "table_row" And I should see "75.00%" in the "TF2" "table_row" And I should see "75.00%" in the "TF3" "table_row" @@ -90,7 +91,7 @@ Feature: Show statistics in question bank | 1 | True | | 2 | True | And I run pending statistics recalculation tasks - When I am on the "Course 1" "core_question > course question bank" page logged in as "admin" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "admin" Then I should see "50.00%" in the "TF1" "table_row" And I should see "75.00%" in the "TF2" "table_row" And I should see "75.00%" in the "TF3" "table_row" @@ -106,7 +107,7 @@ Feature: Show statistics in question bank | 1 | True | | 2 | True | And I run pending statistics recalculation tasks - When I am on the "Course 1" "core_question > course question bank" page logged in as "admin" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "admin" Then I should see "Likely" in the "TF1" "table_row" And I should see "Unlikely" in the "TF2" "table_row" And I should see "Unlikely" in the "TF3" "table_row" @@ -128,7 +129,7 @@ Feature: Show statistics in question bank | 1 | True | | 2 | False | And I run pending statistics recalculation tasks - When I am on the "Course 1" "core_question > course question bank" page logged in as "admin" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "admin" Then I should see "Likely" in the "TF1" "table_row" And I should see "Very likely" in the "TF2" "table_row" And I should see "Very likely" in the "TF3" "table_row" @@ -152,7 +153,7 @@ Feature: Show statistics in question bank | quiz | Quiz 3 | C2 | quiz3 | And the following "question categories" exist: | contextlevel | reference | name | - | Course | C2 | Quiz questions | + | Activity module | quiz3 | Quiz questions | And the following "questions" exist: | questioncategory | qtype | template | name | | Quiz questions | multichoice | one_of_four | MCA | diff --git a/question/bank/tagquestion/classes/external/submit_tags.php b/question/bank/tagquestion/classes/external/submit_tags.php index 0d69ad5af71..c6d43487143 100644 --- a/question/bank/tagquestion/classes/external/submit_tags.php +++ b/question/bank/tagquestion/classes/external/submit_tags.php @@ -99,25 +99,12 @@ class submit_tags extends external_api { $mform = new tags_form(null, $formoptions, 'post', '', null, $cantag, $data); if ($validateddata = $mform->get_data()) { - if ($cantag) { - if (isset($validateddata->tags)) { - // Due to a mform bug, if there's no tags set on the tag element, it submits the name as the value. - // The only way to discover is checking if the tag element is an array. - $tags = is_array($validateddata->tags) ? $validateddata->tags : []; - - core_tag_tag::set_item_tags('core_question', 'question', $validateddata->id, - $questioncontext, $tags); - - $result['status'] = true; - } - - if (isset($validateddata->coursetags)) { - $coursetags = is_array($validateddata->coursetags) ? $validateddata->coursetags : []; - core_tag_tag::set_item_tags('core_question', 'question', $validateddata->id, - $editingcontext->get_course_context(false), $coursetags); - - $result['status'] = true; - } + if ($cantag && isset($validateddata->tags)) { + // Due to a mform bug, if there's no tags set on the tag element, it submits the name as the value. + // The only way to discover is checking if the tag element is an array. + $tags = is_array($validateddata->tags) ? $validateddata->tags : []; + core_tag_tag::set_item_tags('core_question', 'question', $validateddata->id, $questioncontext, $tags); + $result['status'] = true; } } diff --git a/question/bank/tagquestion/classes/form/tags_form.php b/question/bank/tagquestion/classes/form/tags_form.php index 7269b51935d..4aaa067e27f 100644 --- a/question/bank/tagquestion/classes/form/tags_form.php +++ b/question/bank/tagquestion/classes/form/tags_form.php @@ -63,25 +63,6 @@ class tags_form extends \moodleform { 'noselectionstring' => get_string('anytags', 'quiz'), ]; $mform->addElement('autocomplete', 'tags', get_string('tags'), $tagstrings, $options); - - // Is the question category in a course context? - $qcontext = $customdata['questioncontext']; - $qcoursecontext = $qcontext->get_course_context(false); - $iscourseoractivityquestion = !empty($qcoursecontext); - // Is the current context we're editing in a course context? - $editingcontext = $customdata['editingcontext']; - $editingcoursecontext = $editingcontext->get_course_context(false); - $iseditingcontextcourseoractivity = !empty($editingcoursecontext); - - if ($iseditingcontextcourseoractivity && !$iscourseoractivityquestion) { - // If the question is being edited in a course or activity context - // and the question isn't a course or activity level question then - // allow course tags to be added to the course. - $coursetagheader = get_string('questionformtagheader', 'core_question', - $editingcoursecontext->get_context_name(true)); - $mform->addElement('autocomplete', 'coursetags', $coursetagheader, $tagstrings, $options); - - } } } diff --git a/question/bank/tagquestion/lib.php b/question/bank/tagquestion/lib.php index c8390147543..bf8bfb8c70e 100644 --- a/question/bank/tagquestion/lib.php +++ b/question/bank/tagquestion/lib.php @@ -46,13 +46,6 @@ function qbank_tagquestion_output_fragment_tags_form($args) { // Load the question and some related information. $question = $DB->get_record('question', ['id' => $id]); - if ($coursecontext = $editingcontext->get_course_context(false)) { - $course = $DB->get_record('course', ['id' => $coursecontext->instanceid]); - $filtercourses = [$course]; - } else { - $filtercourses = null; - } - $sql = "SELECT qc.* FROM {question} q JOIN {question_versions} qv ON qv.questionid = q.id @@ -68,8 +61,7 @@ function qbank_tagquestion_output_fragment_tags_form($args) { $tagobjectsbyquestion = core_tag_tag::get_items_tags('core_question', 'question', [$question->id]); if (!empty($tagobjectsbyquestion[$question->id])) { $tagobjects = $tagobjectsbyquestion[$question->id]; - $sortedtagobjects = question_sort_tags($tagobjects, - context::instance_by_id($category->contextid), $filtercourses); + $sortedtagobjects = question_sort_tags($tagobjects, context::instance_by_id($category->contextid)); } } $formoptions = [ diff --git a/question/bank/tagquestion/tests/behat/tag_question_action.feature b/question/bank/tagquestion/tests/behat/tag_question_action.feature index f62bf00a65d..9ab3ea1da08 100644 --- a/question/bank/tagquestion/tests/behat/tag_question_action.feature +++ b/question/bank/tagquestion/tests/behat/tag_question_action.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for tagquestion | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/tagquestion/tests/external/submit_tags_test.php b/question/bank/tagquestion/tests/external/submit_tags_test.php index 4113a06bc91..126e0050931 100644 --- a/question/bank/tagquestion/tests/external/submit_tags_test.php +++ b/question/bank/tagquestion/tests/external/submit_tags_test.php @@ -196,182 +196,6 @@ class submit_tags_test extends \externallib_advanced_testcase { $this->assertFalse($result['status']); } - /** - * Data provided for the submit_tags_form test to check that course tags are - * only created in the correct editing and question context combinations. - * - * @return array Test cases - */ - public function get_submit_tags_form_testcases() { - return [ - 'course - course' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course - course - empty tags' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'course', - 'questiontags' => [], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course - course category' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => true - ], - 'course - system' => [ - 'editingcontext' => 'course', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => true - ], - 'course category - course' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course category - course category' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'course category - system' => [ - 'editingcontext' => 'category', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - course' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'course', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - course category' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'category', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - 'system - system' => [ - 'editingcontext' => 'system', - 'questioncontext' => 'system', - 'questiontags' => ['foo'], - 'coursetags' => ['bar'], - 'expectcoursetags' => false - ], - ]; - } - - /** - * Tests that submit_tags_form only creates course tags when the correct combination - * of editing context and question context is provided. - * - * Course tags can only be set on a course category or system context question that - * is being editing in a course context. - * - * @dataProvider get_submit_tags_form_testcases() - * @param string $editingcontext The type of the context the question is being edited in - * @param string $questioncontext The type of the context the question belongs to - * @param string[] $questiontags The tag names to set as question tags - * @param string[] $coursetags The tag names to set as course tags - * @param bool $expectcoursetags If the given course tags should have been set or not - */ - public function test_submit_tags_form_context_combinations( - $editingcontext, - $questioncontext, - $questiontags, - $coursetags, - $expectcoursetags - ): void { - $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); - list ($category, $course, $qcat, $questions) = $questiongenerator->setup_course_and_questions($questioncontext); - $coursecontext = \context_course::instance($course->id); - $questioncontext = \context::instance_by_id($qcat->contextid); - - switch($editingcontext) { - case 'system': - $editingcontext = \context_system::instance(); - break; - - case 'category': - $editingcontext = \context_coursecat::instance($category->id); - break; - - default: - $editingcontext = \context_course::instance($course->id); - } - - $user = $this->create_user_can_tag($course); - $question = $questions[0]; - $formdata = $this->generate_encoded_submit_tags_form_string( - $question, - $qcat, - $questioncontext, - $questiontags, // Question tags. - $coursetags // Course tags. - ); - - $this->setUser($user); - - $result = submit_tags::execute($question->id, $editingcontext->id, $formdata); - - $this->assertTrue($result['status']); - - $tagobjects = \core_tag_tag::get_item_tags('core_question', 'question', $question->id); - $coursetagobjects = []; - $questiontagobjects = []; - - if ($expectcoursetags) { - // If the use case is expecting course tags to be created then split - // the tags into course tags and question tags and ensure we have - // the correct number of course tags. - - while ($tagobject = array_shift($tagobjects)) { - if ($tagobject->taginstancecontextid == $questioncontext->id) { - $questiontagobjects[] = $tagobject; - } else if ($tagobject->taginstancecontextid == $coursecontext->id) { - $coursetagobjects[] = $tagobject; - } - } - - $this->assertCount(count($coursetags), $coursetagobjects); - } else { - $questiontagobjects = $tagobjects; - } - - // Ensure the expected number of question tags was created. - $this->assertCount(count($questiontags), $questiontagobjects); - - foreach ($questiontagobjects as $tagobject) { - // If we have any question tags then make sure they are in the list - // of expected tags and have the correct context. - $this->assertContains($tagobject->name, $questiontags); - $this->assertEquals($questioncontext->id, $tagobject->taginstancecontextid); - } - - foreach ($coursetagobjects as $tagobject) { - // If we have any course tags then make sure they are in the list - // of expected course tags and have the correct context. - $this->assertContains($tagobject->name, $coursetags); - $this->assertEquals($coursecontext->id, $tagobject->taginstancecontextid); - } - } - /** * Build the encoded form data expected by the submit_tags_form external function. * diff --git a/question/bank/usage/tests/behat/question_last_used_column.feature b/question/bank/usage/tests/behat/question_last_used_column.feature index a6e13db01e2..45c6706bfec 100644 --- a/question/bank/usage/tests/behat/question_last_used_column.feature +++ b/question/bank/usage/tests/behat/question_last_used_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for question last used | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/usage/tests/behat/question_usage_column.feature b/question/bank/usage/tests/behat/question_usage_column.feature index 39cc9dd6174..2310e78d239 100644 --- a/question/bank/usage/tests/behat/question_usage_column.feature +++ b/question/bank/usage/tests/behat/question_usage_column.feature @@ -10,11 +10,12 @@ Feature: Use the qbank plugin manager page for question usage | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | + And I change window size to "large" Scenario: Enable/disable question usage column from the base view Given I log in as "admin" diff --git a/question/bank/viewcreator/tests/behat/view_view_creator_column.feature b/question/bank/viewcreator/tests/behat/view_view_creator_column.feature index 7d1cd3f293b..3fe1373dc14 100644 --- a/question/bank/viewcreator/tests/behat/view_view_creator_column.feature +++ b/question/bank/viewcreator/tests/behat/view_view_creator_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for viewcreator plugin | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/viewquestionname/tests/behat/question_in_place_editing.feature b/question/bank/viewquestionname/tests/behat/question_in_place_editing.feature index 0fcfd19a8af..5b0b6a9ce04 100644 --- a/question/bank/viewquestionname/tests/behat/question_in_place_editing.feature +++ b/question/bank/viewquestionname/tests/behat/question_in_place_editing.feature @@ -15,8 +15,8 @@ Feature: Use the qbank view page to edit question title using in place edit feat | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/viewquestionname/tests/behat/view_question_name_column.feature b/question/bank/viewquestionname/tests/behat/view_question_name_column.feature index a51cb3ada94..0a6e7cc6f2f 100644 --- a/question/bank/viewquestionname/tests/behat/view_question_name_column.feature +++ b/question/bank/viewquestionname/tests/behat/view_question_name_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for viewquestionname | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/bank/viewquestiontext/tests/behat/view_question_text_column.feature b/question/bank/viewquestiontext/tests/behat/view_question_text_column.feature index fc653219921..50ed7969cf4 100644 --- a/question/bank/viewquestiontext/tests/behat/view_question_text_column.feature +++ b/question/bank/viewquestiontext/tests/behat/view_question_text_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for viewquestiontext | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first <question> | diff --git a/question/bank/viewquestiontype/tests/behat/view_question_type_column.feature b/question/bank/viewquestiontype/tests/behat/view_question_type_column.feature index faa5b4f9bfc..9279c3d51da 100644 --- a/question/bank/viewquestiontype/tests/behat/view_question_type_column.feature +++ b/question/bank/viewquestiontype/tests/behat/view_question_type_column.feature @@ -10,8 +10,8 @@ Feature: Use the qbank plugin manager page for viewquestiontype | activity | name | course | idnumber | | quiz | Test quiz | C1 | quiz1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | truefalse | First question | Answer the first question | diff --git a/question/classes/local/bank/view.php b/question/classes/local/bank/view.php index 6d51e2dfb76..af283d1edd0 100644 --- a/question/classes/local/bank/view.php +++ b/question/classes/local/bank/view.php @@ -235,6 +235,10 @@ class view { $this->cm = $cm; $this->extraparams = $extraparams; + if ($cm === null) { + debugging('Passing $cm to the view constructor is now required.', DEBUG_DEVELOPER); + } + // Default filter condition. if (!isset($params['filter']) && isset($params['cat'])) { $params['filter'] = filter_condition_manager::get_default_filter($params['cat']); @@ -251,12 +255,7 @@ class view { // Create the url of the new question page to forward to. $this->returnurl = $pageurl->out_as_local_url(false); $this->editquestionurl = new \moodle_url('/question/bank/editquestion/question.php', ['returnurl' => $this->returnurl]); - // MDL-71378 TODO: refactor this. Must require cm. - if ($this->cm !== null) { - $this->editquestionurl->param('cmid', $this->cm->id); - } else { - $this->editquestionurl->param('courseid', $this->course->id); - } + $this->editquestionurl->param('cmid', $this->cm->id); $this->lastchangedid = clean_param($pageurl->param('lastchanged'), PARAM_INT); @@ -560,7 +559,7 @@ class view { protected function parse_subsort($sort): array { // Do the parsing. if (strpos($sort, '-') !== false) { - list($colname, $subsort) = explode('-', $sort, 2); + [$colname, $subsort] = explode('-', $sort, 2); } else { $colname = $sort; $subsort = ''; @@ -957,7 +956,7 @@ class view { DEBUG_DEVELOPER ); global $DB, $OUTPUT; - list($categoryid, $contextid) = explode(',', $categoryandcontext); + [$categoryid, $contextid] = explode(',', $categoryandcontext); if (!$categoryid) { $this->print_choose_category_message(); return false; diff --git a/question/classes/output/question_bank_filter_ui.php b/question/classes/output/question_bank_filter_ui.php index 20e511c548c..7535feec8df 100644 --- a/question/classes/output/question_bank_filter_ui.php +++ b/question/classes/output/question_bank_filter_ui.php @@ -114,14 +114,14 @@ class question_bank_filter_ui extends datafilter { * @return stdClass Data in a format compatible with a mustache template. */ public function export_for_template(renderer_base $output): stdClass { - $defaultcategory = question_get_default_category($this->context->id); - $courseid = null; - if ($this->context->contextlevel == CONTEXT_COURSE) { - $courseid = $this->context->instanceid; - } - if (empty($courseid)) { - $courseid = $this->searchconditions['category']->get_course_id(); + $defaultcategory = question_get_default_category($this->context->id, true); + + if ($this->context->contextlevel != CONTEXT_MODULE) { + debugging("Context level: {$this->context->contextlevel} is now deprecated, please use CONTEXT_MODULE"); } + + $courseid = $this->context->get_course_context()->instanceid; + return (object) [ 'tableregionid' => $this->tableregionid, 'courseid' => $courseid, diff --git a/question/edit.php b/question/edit.php index eb05c2479a3..f896fd67931 100644 --- a/question/edit.php +++ b/question/edit.php @@ -30,6 +30,11 @@ use core_question\local\bank\view; require_once(__DIR__ . '/../config.php'); require_once($CFG->dirroot . '/question/editlib.php'); +// Since Moodle 5.0 any request with the courseid parameter is deprecated and will redirect to the banks management page. +if ($courseid = optional_param('courseid', 0, PARAM_INT)) { + redirect(new moodle_url('/question/banks.php', ['courseid' => $courseid])); +} + list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('questions', '/question/edit.php'); diff --git a/question/editlib.php b/question/editlib.php index 60cc2f9c4c3..a50f83ab048 100644 --- a/question/editlib.php +++ b/question/editlib.php @@ -117,29 +117,20 @@ function get_questions_category(object $category, bool $noparent, bool $recurse * Common setup for all pages for editing questions. * @param string $baseurl the name of the script calling this funciton. For examle 'qusetion/edit.php'. * @param string $edittab code for this edit tab - * @param bool $requirecmid require cmid? default false + * @param bool $requirecmid This parameter has been deprecated since 4.5 and should not be used anymore. * @param bool $unused no longer used, do no pass * @return array $thispageurl, $contexts, $cmid, $cm, $module, $pagevars */ -function question_edit_setup($edittab, $baseurl, $requirecmid = false, $unused = null) { +function question_edit_setup($edittab, $baseurl, $requirecmid = null, $unused = null) { global $PAGE; - if ($unused !== null) { + if ($unused !== null || $requirecmid !== null) { debugging('Deprecated argument passed to question_edit_setup()', DEBUG_DEVELOPER); } $params = []; - if ($requirecmid) { - $params['cmid'] = required_param('cmid', PARAM_INT); - } else { - $params['cmid'] = optional_param('cmid', null, PARAM_INT); - } - - if (!$params['cmid']) { - $params['courseid'] = required_param('courseid', PARAM_INT); - } - + $params['cmid'] = required_param('cmid', PARAM_INT); $params['qpage'] = optional_param('qpage', null, PARAM_INT); // Pass 'cat' from page to page and when 'category' comes from a drop down menu @@ -167,14 +158,12 @@ function question_edit_setup($edittab, $baseurl, $requirecmid = false, $unused = * Common function for building the generic resources required by the * editing questions pages. * - * Either a cmid or a course id must be provided as keys in $params or - * an exception will be thrown. All other params are optional and will have - * sane default applied if not provided. + * A cmid must be provided as a key in $params or an exception will be thrown. + * All other params are optional and will have sane default applied if not provided. * * The acceptable keys for $params are: * [ * 'cmid' => PARAM_INT, - * 'courseid' => PARAM_INT, * 'qpage' => PARAM_INT, * 'cat' => PARAM_SEQUENCE, * 'category' => PARAM_SEQUENCE, @@ -209,7 +198,6 @@ function question_build_edit_resources($edittab, $baseurl, $params, ]; $paramtypes = [ 'cmid' => PARAM_INT, - 'courseid' => PARAM_INT, 'qpage' => PARAM_INT, 'cat' => PARAM_SEQUENCE, 'category' => PARAM_SEQUENCE, @@ -237,28 +225,20 @@ function question_build_edit_resources($edittab, $baseurl, $params, } $cmid = $cleanparams['cmid']; - $courseid = $cleanparams['courseid']; $qpage = $cleanparams['qpage'] ?: -1; $cat = $cleanparams['cat'] ?: 0; $category = $cleanparams['category'] ?: 0; $qperpage = $cleanparams['qperpage']; $cpage = $cleanparams['cpage'] ?: 1; - if (is_null($cmid) && is_null($courseid)) { - throw new \moodle_exception('Must provide a cmid or courseid'); + if (is_null($cmid)) { + throw new \moodle_exception('Must provide a cmid'); } - if ($cmid) { - list($module, $cm) = get_module_from_cmid($cmid); - $courseid = $cm->course; - $thispageurl->params(compact('cmid')); - $thiscontext = context_module::instance($cmid); - } else { - $module = null; - $cm = null; - $thispageurl->params(compact('courseid')); - $thiscontext = context_course::instance($courseid); - } + list($module, $cm) = get_module_from_cmid($cmid); + $courseid = $cm->course; + $thispageurl->params(compact('cmid')); + $thiscontext = context_module::instance($cmid); if (defined('AJAX_SCRIPT') && AJAX_SCRIPT) { // For AJAX, we don't need to set up the course page for output. diff --git a/question/format.php b/question/format.php index a24d9e9d9d7..68a8f46ad05 100644 --- a/question/format.php +++ b/question/format.php @@ -141,8 +141,12 @@ class qformat_default { if (count($this->questions)) { debugging('You shouldn\'t call setCategory after setQuestions'); } + $context = context::instance_by_id($category->contextid); + if ($context->contextlevel !== CONTEXT_MODULE) { + throw new \core\exception\moodle_exception('Invalid contextlevel for target category, must be CONTEXT_MODULE'); + } $this->category = $category; - $this->importcontext = context::instance_by_id($this->category->contextid); + $this->importcontext = $context; } /** @@ -508,30 +512,14 @@ class qformat_default { $event->trigger(); if (core_tag_tag::is_enabled('core_question', 'question')) { - // Is the current context we're importing in a course context? - $importingcontext = $this->importcontext; - $importingcoursecontext = $importingcontext->get_course_context(false); - $isimportingcontextcourseoractivity = !empty($importingcoursecontext); - - if (!empty($question->coursetags)) { - if ($isimportingcontextcourseoractivity) { - $mergedtags = array_merge($question->coursetags, $question->tags); - - core_tag_tag::set_item_tags('core_question', 'question', $question->id, - $question->context, $mergedtags); - } else { - core_tag_tag::set_item_tags('core_question', 'question', $question->id, - context_course::instance($this->course->id), $question->coursetags); - - if (!empty($question->tags)) { - core_tag_tag::set_item_tags('core_question', 'question', $question->id, - $importingcontext, $question->tags); - } - } - } else if (!empty($question->tags)) { - core_tag_tag::set_item_tags('core_question', 'question', $question->id, - $question->context, $question->tags); - } + // Course tags on question now deprecated so merge them into the question tags. + $mergedtags = array_merge($question->coursetags ?? [], $question->tags ?? []); + core_tag_tag::set_item_tags('core_question', + 'question', + $question->id, + $question->context, + $mergedtags + ); } if (!empty($result->error)) { @@ -613,6 +601,14 @@ class qformat_default { } else { $context = context::instance_by_id($this->category->contextid); } + + // If the file had categories in a deprecated context then the categories need to be applied to a default system type + // mod_qbank instance on the course instead. + if ($context->contextlevel !== CONTEXT_MODULE) { + $qbank = \core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($this->course, true); + $context = context_module::instance($qbank->id); + } + $this->importcontext = $context; // Now create any categories that need to be created. @@ -1026,12 +1022,10 @@ class qformat_default { } } - // continue path for following error checks - $course = $this->course; - $continuepath = "{$CFG->wwwroot}/question/bank/exportquestions/export.php?courseid={$course->id}"; - - // did we actually process anything + // Did we actually process anything? Then continue path for following error checks. if ($count==0) { + $context = context::instance_by_id($contextid); + $continuepath = "{$CFG->wwwroot}/question/bank/exportquestions/export.php?cmid={$context->instanceid}"; throw new \moodle_exception('noquestions', 'question', $continuepath); } diff --git a/question/format/aiken/tests/behat/aiken_export.feature b/question/format/aiken/tests/behat/aiken_export.feature index 7c3380ee986..de78f70f9c6 100644 --- a/question/format/aiken/tests/behat/aiken_export.feature +++ b/question/format/aiken/tests/behat/aiken_export.feature @@ -14,16 +14,19 @@ Feature: Test exporting questions using Aiken format. And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | template | | Test questions | multichoice | Multi-choice-001 | two_of_four | | Test questions | multichoice | Multi-choice-002 | one_of_four | Scenario: Aiken export - When I am on the "Course 1" "core_question > course question export" page logged in as "teacher1" + When I am on the "Qbank 1" "core_question > question export" page logged in as "teacher1" And I set the field "id_format_aiken" to "1" When I press "Export questions to file" Then following "click here" should download a file that: diff --git a/question/format/aiken/tests/behat/aiken_import.feature b/question/format/aiken/tests/behat/aiken_import.feature index 9b3ef2e0a3d..87b23c1b0f4 100644 --- a/question/format/aiken/tests/behat/aiken_import.feature +++ b/question/format/aiken/tests/behat/aiken_import.feature @@ -14,10 +14,13 @@ Feature: Test importing questions from Aiken format. And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | @javascript @_file_upload Scenario: import some Aiken questions - Given I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + Given I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" And I set the field "id_format_aiken" to "1" And I upload "question/format/aiken/tests/fixtures/questions.aiken.txt" file to "Import" filemanager When I press "id_submitbutton" diff --git a/question/format/aiken/tests/qformat_aiken_export_test.php b/question/format/aiken/tests/qformat_aiken_export_test.php index 785d2c0a5a6..fe761e12485 100644 --- a/question/format/aiken/tests/qformat_aiken_export_test.php +++ b/question/format/aiken/tests/qformat_aiken_export_test.php @@ -59,8 +59,9 @@ class qformat_aiken_export_test extends advanced_testcase { // Create a new course category and and a new course in that. $category = $this->getDataGenerator()->create_category(); $course = $this->getDataGenerator()->create_course(array('category' => $category->id)); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $context = context_module::instance($qbank->cmid); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = context_coursecat::instance($category->id); $cat = $generator->create_question_category(array('contextid' => $context->id)); $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); @@ -94,7 +95,8 @@ EOT; $category = $this->getDataGenerator()->create_category(); $course = $this->getDataGenerator()->create_course(array('category' => $category->id)); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $context = context_coursecat::instance($category->id); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $context = context_module::instance($qbank->cmid); $cat = $generator->create_question_category(array('contextid' => $context->id)); $question = $generator->create_question('multichoice', 'one_of_four', array('category' => $cat->id)); diff --git a/question/format/gift/format.php b/question/format/gift/format.php index 364ca19a3d1..016e38d7d21 100644 --- a/question/format/gift/format.php +++ b/question/format/gift/format.php @@ -864,7 +864,7 @@ class qformat_gift extends qformat_default { if (!empty($tagobjects)) { $context = context::instance_by_id($questiondata->contextid); - $sortedtagobjects = question_sort_tags($tagobjects, $context, [$this->course]); + $sortedtagobjects = question_sort_tags($tagobjects, $context); // Currently we ignore course tags. This should probably be fixed in future. diff --git a/question/format/gift/tests/behat/import_export.feature b/question/format/gift/tests/behat/import_export.feature index c39a92ce65a..bf5aae833df 100644 --- a/question/format/gift/tests/behat/import_export.feature +++ b/question/format/gift/tests/behat/import_export.feature @@ -14,7 +14,10 @@ Feature: Test importing questions from GIFT format. And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | - And I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | + And I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" @javascript @_file_upload Scenario: import some GIFT questions @@ -28,7 +31,7 @@ Feature: Test importing questions from GIFT format. Then I should see "colours" # Now export again. - And I am on the "Course 1" "core_question > course question export" page + And I am on the "Qbank 1" "core_question > question export" page And I set the field "id_format_gift" to "1" And I press "Export questions to file" And following "click here" should download a file that: diff --git a/question/format/xml/format.php b/question/format/xml/format.php index 90d2f1ffc14..fca520c2468 100644 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -1575,7 +1575,7 @@ class qformat_xml extends qformat_default { if (!empty($tagobjects)) { $context = context::instance_by_id($contextid); - $sortedtagobjects = question_sort_tags($tagobjects, $context, [$this->course]); + $sortedtagobjects = question_sort_tags($tagobjects, $context); if (!empty($sortedtagobjects->coursetags)) { // Set them on the form to be rendered as existing tags. diff --git a/question/format/xml/tests/behat/import_export.feature b/question/format/xml/tests/behat/import_export.feature index 9e68c3684d8..4b50184d97f 100644 --- a/question/format/xml/tests/behat/import_export.feature +++ b/question/format/xml/tests/behat/import_export.feature @@ -14,10 +14,13 @@ Feature: Test importing questions from Moodle XML format. And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | @javascript @_file_upload Scenario: import some true/false questions from Moodle XML format - When I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + When I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" And I set the field "id_format_xml" to "1" And I upload "question/format/xml/tests/fixtures/truefalse.xml" file to "Import" filemanager And I press "id_submitbutton" @@ -30,7 +33,7 @@ Feature: Test importing questions from Moodle XML format. Then I should see "Moodle acronym (True)" # Now export again. - When I am on the "Course 1" "core_question > course question export" page logged in as "teacher" + When I am on the "Qbank 1" "core_question > question export" page logged in as "teacher" And I set the field "id_format_xml" to "1" And I set the field "Export category" to "TrueFalse" And I press "Export questions to file" @@ -41,7 +44,7 @@ Feature: Test importing questions from Moodle XML format. @javascript @_file_upload Scenario: import some multiple choice questions from Moodle XML format - When I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + When I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" And I set the field "id_format_xml" to "1" And I upload "question/format/xml/tests/fixtures/multichoice.xml" file to "Import" filemanager And I press "id_submitbutton" @@ -53,7 +56,7 @@ Feature: Test importing questions from Moodle XML format. @javascript @_file_upload Scenario: import some multi-answer questions from Moodle XML format - When I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + When I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" And I set the field "id_format_xml" to "1" And I upload "question/format/xml/tests/fixtures/multianswer.xml" file to "Import" filemanager And I press "id_submitbutton" @@ -65,7 +68,7 @@ Feature: Test importing questions from Moodle XML format. @javascript @_file_upload Scenario: import some questions with legacy-style images from Moodle XML format - When I am on the "Course 1" "core_question > course question import" page logged in as "teacher" + When I am on the "Qbank 1" "core_question > question import" page logged in as "teacher" And I set the field "id_format_xml" to "1" And I upload "question/format/xml/tests/fixtures/sample_questions_with_old_image_tag.xml" file to "Import" filemanager And I press "id_submitbutton" diff --git a/question/format/xml/tests/fixtures/categories_reverse_order.xml b/question/format/xml/tests/fixtures/categories_reverse_order.xml index 77e003287de..3d4dcc629fc 100644 --- a/question/format/xml/tests/fixtures/categories_reverse_order.xml +++ b/question/format/xml/tests/fixtures/categories_reverse_order.xml @@ -3,7 +3,7 @@ - $course$/Sigma/Tau + $module$/Sigma/Tau This is Tau category for test @@ -40,7 +40,7 @@ - $course$/Sigma + $module$/Sigma This is Sigma category for test diff --git a/question/format/xml/tests/fixtures/category_with_description.xml b/question/format/xml/tests/fixtures/category_with_description.xml index 674457bab42..31610569772 100644 --- a/question/format/xml/tests/fixtures/category_with_description.xml +++ b/question/format/xml/tests/fixtures/category_with_description.xml @@ -3,7 +3,7 @@ - $course$/Alpha + $module$/Alpha This is Alpha category for test diff --git a/question/format/xml/tests/fixtures/deprecated_category.xml b/question/format/xml/tests/fixtures/deprecated_category.xml new file mode 100644 index 00000000000..40f547d3b91 --- /dev/null +++ b/question/format/xml/tests/fixtures/deprecated_category.xml @@ -0,0 +1,40 @@ + + + + + + $course$/Alpha + + + This is Alpha category for test + + alpha-idnumber + + + + + Alpha Question + + + Testing Alpha Question

]]>
+
+ + + + 1.0000000 + 1.0000000 + 0 + + true + + + + + + false + + + + +
+
diff --git a/question/format/xml/tests/fixtures/export_category.xml b/question/format/xml/tests/fixtures/export_category.xml index 63087cf7264..890ab7f15ef 100644 --- a/question/format/xml/tests/fixtures/export_category.xml +++ b/question/format/xml/tests/fixtures/export_category.xml @@ -3,7 +3,7 @@ - $course$/Alpha + $module$/Alpha This is Alpha category for test diff --git a/question/format/xml/tests/fixtures/html_chars_in_idnumbers.xml b/question/format/xml/tests/fixtures/html_chars_in_idnumbers.xml index cc1d37676e9..323032917ad 100644 --- a/question/format/xml/tests/fixtures/html_chars_in_idnumbers.xml +++ b/question/format/xml/tests/fixtures/html_chars_in_idnumbers.xml @@ -3,7 +3,7 @@ - $course$/Alpha + $module$/Alpha This is Alpha category for test diff --git a/question/format/xml/tests/fixtures/multianswer.xml b/question/format/xml/tests/fixtures/multianswer.xml index 5a3cc3422a6..a96071a190c 100644 --- a/question/format/xml/tests/fixtures/multianswer.xml +++ b/question/format/xml/tests/fixtures/multianswer.xml @@ -3,7 +3,7 @@ - $course$/Default for Test images in backup + $module$/Default for Test images in backup diff --git a/question/format/xml/tests/fixtures/multichoice.xml b/question/format/xml/tests/fixtures/multichoice.xml index dbf58455cf9..a023996dda2 100644 --- a/question/format/xml/tests/fixtures/multichoice.xml +++ b/question/format/xml/tests/fixtures/multichoice.xml @@ -3,7 +3,7 @@ - $course$/Default for testhead/Multichoice + $module$/Default for testhead/Multichoice diff --git a/question/format/xml/tests/fixtures/nested_categories.xml b/question/format/xml/tests/fixtures/nested_categories.xml index ac6db64d315..5db1145f635 100644 --- a/question/format/xml/tests/fixtures/nested_categories.xml +++ b/question/format/xml/tests/fixtures/nested_categories.xml @@ -3,7 +3,7 @@ - $course$/Delta + $module$/Delta This is Delta category for test @@ -14,7 +14,7 @@ - $course$/Delta/Epsilon + $module$/Delta/Epsilon This is Epsilon category for test @@ -25,7 +25,7 @@ - $course$/Delta/Epsilon/Zeta + $module$/Delta/Epsilon/Zeta This is Zeta category for test diff --git a/question/format/xml/tests/fixtures/nested_categories_with_questions.xml b/question/format/xml/tests/fixtures/nested_categories_with_questions.xml index cca8027ec0b..9032fa715da 100644 --- a/question/format/xml/tests/fixtures/nested_categories_with_questions.xml +++ b/question/format/xml/tests/fixtures/nested_categories_with_questions.xml @@ -3,7 +3,7 @@ - $course$/Iota + $module$/Iota This is Iota category for test @@ -43,7 +43,7 @@ - $course$/Iota/Kappa + $module$/Iota/Kappa This is Kappa category for test @@ -115,7 +115,7 @@ - $course$/Iota/Kappa/Lambda + $module$/Iota/Kappa/Lambda This is Lambda category for test @@ -155,7 +155,7 @@ - $course$/Iota/Mu + $module$/Iota/Mu This is Mu category for test diff --git a/question/format/xml/tests/fixtures/old_format_file.xml b/question/format/xml/tests/fixtures/old_format_file.xml index efc900702b3..3665f644524 100644 --- a/question/format/xml/tests/fixtures/old_format_file.xml +++ b/question/format/xml/tests/fixtures/old_format_file.xml @@ -3,7 +3,7 @@ - $course$/Pi + $module$/Pi @@ -38,7 +38,7 @@ - $course$/Pi/Rho + $module$/Pi/Rho diff --git a/question/format/xml/tests/fixtures/sample_questions_with_old_image_tag.xml b/question/format/xml/tests/fixtures/sample_questions_with_old_image_tag.xml index 852e5bc1979..69cc125ecdd 100644 --- a/question/format/xml/tests/fixtures/sample_questions_with_old_image_tag.xml +++ b/question/format/xml/tests/fixtures/sample_questions_with_old_image_tag.xml @@ -5,7 +5,7 @@ - $course$/Default for Test images in backup + $module$/Default for Test images in backup diff --git a/question/format/xml/tests/fixtures/truefalse.xml b/question/format/xml/tests/fixtures/truefalse.xml index 97b099a42fa..bee898d1a7f 100644 --- a/question/format/xml/tests/fixtures/truefalse.xml +++ b/question/format/xml/tests/fixtures/truefalse.xml @@ -3,7 +3,7 @@ - $course$/Default for testhead/TrueFalse + $module$/Default for testhead/TrueFalse diff --git a/question/format/xml/tests/qformat_xml_import_export_test.php b/question/format/xml/tests/qformat_xml_import_export_test.php index 99a39c8b635..1296f9e2c77 100644 --- a/question/format/xml/tests/qformat_xml_import_export_test.php +++ b/question/format/xml/tests/qformat_xml_import_export_test.php @@ -21,7 +21,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use core_question\local\bank\question_edit_contexts; defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -39,15 +38,21 @@ require_once($CFG->dirroot . '/question/editlib.php'); * @covers \qformat_xml */ class qformat_xml_import_export_test extends advanced_testcase { + + /** @var stdClass mod_qbank instance */ + private stdClass $qbank; + /** * Create object qformat_xml for test. * @param string $filename with name for testing file. - * @param stdClass $course * @return qformat_xml XML question format object. */ - public function create_qformat($filename, $course) { + public function create_qformat($filename) { + $course = self::getDataGenerator()->create_course(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qformat = new qformat_xml(); - $qformat->setContexts((new question_edit_contexts(context_course::instance($course->id)))->all()); + $qformat->setContexts([context_module::instance($qbank->cmid)]); $qformat->setCourse($course); $qformat->setFilename(__DIR__ . '/fixtures/' . $filename); $qformat->setRealfilename($filename); @@ -59,6 +64,8 @@ class qformat_xml_import_export_test extends advanced_testcase { $qformat->setContexttofile(1); $qformat->set_display_progress(false); + $this->qbank = $qbank; + return $qformat; } @@ -150,7 +157,7 @@ class qformat_xml_import_export_test extends advanced_testcase { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('category_with_description.xml', $course); + $qformat = $this->create_qformat('category_with_description.xml'); $imported = $qformat->importprocess(); $this->assertTrue($imported); $this->assert_category_imported('Alpha', @@ -168,8 +175,7 @@ class qformat_xml_import_export_test extends advanced_testcase { $this->resetAfterTest(); self::setAdminUser(); - $course = self::getDataGenerator()->create_course(); - $qformat = $this->create_qformat('deprecated_category.xml', $course); + $qformat = $this->create_qformat('deprecated_category.xml'); $cat = question_get_default_category($qformat->contexts[0]->id, true); $qformat->setCategory($cat); $imported = $qformat->importprocess(); @@ -183,9 +189,8 @@ class qformat_xml_import_export_test extends advanced_testcase { */ public function test_import_nested_categories(): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('nested_categories.xml', $course); + $qformat = $this->create_qformat('nested_categories.xml'); $imported = $qformat->importprocess(); $this->assertTrue($imported); $this->assert_category_imported('Delta', 'This is Delta category for test', FORMAT_PLAIN); @@ -201,9 +206,8 @@ class qformat_xml_import_export_test extends advanced_testcase { */ public function test_import_nested_categories_with_questions(): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('nested_categories_with_questions.xml', $course); + $qformat = $this->create_qformat('nested_categories_with_questions.xml'); $imported = $qformat->importprocess(); $this->assertTrue($imported); $this->assert_category_imported('Iota', 'This is Iota category for test', FORMAT_PLAIN); @@ -225,9 +229,8 @@ class qformat_xml_import_export_test extends advanced_testcase { */ public function test_import_old_format(): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('old_format_file.xml', $course); + $qformat = $this->create_qformat('old_format_file.xml'); $imported = $qformat->importprocess(); $this->assertTrue($imported); $this->assert_category_imported('Pi', '', FORMAT_MOODLE); @@ -243,9 +246,8 @@ class qformat_xml_import_export_test extends advanced_testcase { */ public function test_import_categories_in_reverse_order(): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('categories_reverse_order.xml', $course); + $qformat = $this->create_qformat('categories_reverse_order.xml'); $imported = $qformat->importprocess(); $this->assertTrue($imported); $this->assert_category_imported('Sigma', 'This is Sigma category for test', FORMAT_HTML); @@ -265,9 +267,8 @@ class qformat_xml_import_export_test extends advanced_testcase { global $OUTPUT; $this->resetAfterTest(true); - $course = $this->getDataGenerator()->create_course(); $this->setAdminUser(); - $qformat = $this->create_qformat('error_invalid_grades.xml', $course); + $qformat = $this->create_qformat('error_invalid_grades.xml'); ob_start(); $imported = $qformat->importprocess(); @@ -293,11 +294,11 @@ class qformat_xml_import_export_test extends advanced_testcase { // Note while this loads $qformat with all the 'right' data from the xml file, // the call to setCategory, followed by exportprocess will actually only export data // from the database (created by the generator). - $qformat = $this->create_qformat('export_category.xml', $SITE); + $qformat = $this->create_qformat('export_category.xml'); $category = $generator->create_question_category([ 'name' => 'Alpha', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Alpha category for test', 'infoformat' => '0', 'idnumber' => 'alpha-idnumber', @@ -328,11 +329,11 @@ class qformat_xml_import_export_test extends advanced_testcase { $this->resetAfterTest(); $this->setAdminUser(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $qformat = $this->create_qformat('nested_categories.zml', $SITE); + $qformat = $this->create_qformat('nested_categories.xml'); $categorydelta = $generator->create_question_category([ 'name' => 'Delta', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Delta category for test', 'infoformat' => '2', 'stamp' => make_unique_id_code(), @@ -340,7 +341,7 @@ class qformat_xml_import_export_test extends advanced_testcase { 'sortorder' => '999']); $categoryepsilon = $generator->create_question_category([ 'name' => 'Epsilon', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Epsilon category for test', 'infoformat' => '4', 'stamp' => make_unique_id_code(), @@ -348,7 +349,7 @@ class qformat_xml_import_export_test extends advanced_testcase { 'sortorder' => '999']); $categoryzeta = $generator->create_question_category([ 'name' => 'Zeta', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Zeta category for test', 'infoformat' => '0', 'stamp' => make_unique_id_code(), @@ -382,11 +383,11 @@ class qformat_xml_import_export_test extends advanced_testcase { $this->resetAfterTest(); $this->setAdminUser(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); - $qformat = $this->create_qformat('nested_categories_with_questions.xml', $SITE); + $qformat = $this->create_qformat('nested_categories_with_questions.xml'); $categoryiota = $generator->create_question_category([ 'name' => 'Iota', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Iota category for test', 'infoformat' => '2', 'stamp' => make_unique_id_code(), @@ -405,7 +406,7 @@ class qformat_xml_import_export_test extends advanced_testcase { 'penalty' => '1']); $categorykappa = $generator->create_question_category([ 'name' => 'Kappa', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Kappa category for test', 'infoformat' => '4', 'stamp' => make_unique_id_code(), @@ -441,7 +442,7 @@ class qformat_xml_import_export_test extends advanced_testcase { 'idnumber' => '']); $categorylambda = $generator->create_question_category([ 'name' => 'Lambda', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Lambda category for test', 'infoformat' => '0', 'stamp' => make_unique_id_code(), @@ -460,7 +461,7 @@ class qformat_xml_import_export_test extends advanced_testcase { 'penalty' => '1']); $categorymu = $generator->create_question_category([ 'name' => 'Mu', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Mu category for test', 'infoformat' => '0', 'stamp' => make_unique_id_code(), @@ -495,11 +496,11 @@ class qformat_xml_import_export_test extends advanced_testcase { // Note while this loads $qformat with all the 'right' data from the xml file, // the call to setCategory, followed by exportprocess will actually only export data // from the database (created by the generator). - $qformat = $this->create_qformat('export_category.xml', $SITE); + $qformat = $this->create_qformat('export_category.xml'); $category = $generator->create_question_category([ 'name' => 'Alpha', - 'contextid' => context_course::instance($SITE->id)->id, + 'contextid' => context_module::instance($this->qbank->cmid)->id, 'info' => 'This is Alpha category for test', 'infoformat' => '0', 'idnumber' => 'The inequalities < & >', diff --git a/question/templates/qbank_action_menu.mustache b/question/templates/qbank_action_menu.mustache index dde72909e32..0d1d93bbb9f 100644 --- a/question/templates/qbank_action_menu.mustache +++ b/question/templates/qbank_action_menu.mustache @@ -36,22 +36,22 @@ "options": [{ "name": "Questions", "selected": true, - "value": "/question/edit.php?courseid=7" + "value": "/question/edit.php?cmid=7" }, { "name": "Categories", "selected": false, - "value": "/question/bank/managecategories/category.php?courseid=7" + "value": "/question/bank/managecategories/category.php?cmid=7" }, { "name": "Import", "selected": false, - "value": "/question/bank/importquestions/import.php?courseid=7" + "value": "/question/bank/importquestions/import.php?cmid=7" }, { "name": "Export", "selected": false, - "value": "/question/bank/exportquestions/export.php?courseid=7" + "value": "/question/bank/exportquestions/export.php?cmid=7" } ] }, diff --git a/question/tests/backup_test.php b/question/tests/backup_test.php index f06775a308e..b166afbfe15 100644 --- a/question/tests/backup_test.php +++ b/question/tests/backup_test.php @@ -431,6 +431,7 @@ class backup_test extends \advanced_testcase { // Create a course and a category. $course = $this->getDataGenerator()->create_course(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); $category = $this->getDataGenerator()->create_category(); // Create a question with links in all the places that should be recoded. @@ -438,7 +439,7 @@ class backup_test extends \advanced_testcase { $testcontent = 'Look at the course.'; $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $questioncategory = $questiongenerator->create_question_category( - ['contextid' => context_course::instance($course->id)->id]); + ['contextid' => \context_module::instance($qbank->cmid)->id]); $question = $questiongenerator->create_question('multichoice', null, [ 'name' => 'Test question', 'category' => $questioncategory->id, @@ -461,7 +462,11 @@ class backup_test extends \advanced_testcase { // Backup and restore the course. $backupid = $this->backup_course($course); - $newcourseid = $this->restore_course($backupid, 'New course', 'C2', $category->id); + $newcourse = $this->getDataGenerator()->create_course(); + $this->restore_to_course($backupid, $newcourse->id); + $modinfo = get_fast_modinfo($newcourse); + $qbanks = $modinfo->get_instances_of('qbank'); + $qbank = reset($qbanks); // Get the question from the restored course - we are expecting just one, but that is not the real test here. $restoredquestions = $DB->get_records_sql(" @@ -471,14 +476,14 @@ class backup_test extends \advanced_testcase { JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid WHERE qc.contextid = ? - ", [context_course::instance($newcourseid)->id]); + ", [\context_module::instance($qbank->id)->id]); $this->assertCount(1, $restoredquestions); $questionid = array_key_first($restoredquestions); $this->assertEquals('Test question', $restoredquestions[$questionid]->name); // Verify the links have been recoded. $restoredquestion = question_bank::load_question_data($questionid); - $recodedlink = new moodle_url('/course/view.php', ['id' => $newcourseid]); + $recodedlink = new moodle_url('/course/view.php', ['id' => $newcourse->id]); $recodedcontent = 'Look at the course.'; $firstanswerid = array_key_first($restoredquestion->options->answers); $firsthintid = array_key_first($restoredquestion->hints); diff --git a/question/tests/behat/bank_add_default_shared.feature b/question/tests/behat/bank_add_default_shared.feature index 24465c1549f..fb23c9407bc 100644 --- a/question/tests/behat/bank_add_default_shared.feature +++ b/question/tests/behat/bank_add_default_shared.feature @@ -14,7 +14,6 @@ Feature: Add a default question bank And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | - | student1 | C1 | student | Scenario: Add a default question bank to a course Given I am on the "C1" "Course" page logged in as "teacher1" diff --git a/question/tests/behat/bank_view.feature b/question/tests/behat/bank_view.feature index 37fdd59e3e2..c28ab843156 100644 --- a/question/tests/behat/bank_view.feature +++ b/question/tests/behat/bank_view.feature @@ -14,9 +14,12 @@ Feature: A bank view with questions can be managed And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | @javascript Scenario: Viewing question bank should not load individual questions @@ -25,7 +28,8 @@ Feature: A bank view with questions can be managed | Test questions | essay | Essay test question | Write about whatever you want | qid | | Test questions | numerical | Numerical test question | Write about whatever you want | qid | And I am on the "C1" "Course" page logged in as "teacher1" - And I navigate to "Question bank" in current page administration + And I navigate to "Question banks" in current page administration + And I click on "Qbank 1" "link" And I should see "Essay test question" And I should see "Numerical test question" And I choose "Delete" action for "Essay test question" in the question bank @@ -42,7 +46,8 @@ Feature: A bank view with questions can be managed | Test questions | truefalse | Truefalse type question | Write about whatever you want | | Test questions | essay | Essay type question | Write about whatever you want | And I am on the "C1" "Course" page logged in as "teacher1" - And I navigate to "Question bank" in current page administration + And I navigate to "Question banks" in current page administration + And I click on "Qbank 1" "link" And I should see "Unknown type question" And I should see "Truefalse type question" And I should see "Essay type question" diff --git a/question/tests/behat/behat_core_question.php b/question/tests/behat/behat_core_question.php index d2476c12da7..1131437f83c 100644 --- a/question/tests/behat/behat_core_question.php +++ b/question/tests/behat/behat_core_question.php @@ -53,12 +53,15 @@ class behat_core_question extends behat_question_base { * Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'. * * Recognised page names are: - * | pagetype | name meaning | description | - * | course question bank | Course name | The question bank for a course | - * | course question import | Course name | The import questions screen for a course | - * | course question export | Course name | The export questions screen for a course | - * | preview | Question name | The screen to preview a question | - * | edit | Question name | The screen to edit a question | + * | pagetype | name meaning | description | + * | course question bank | Course name | The default question bank for a course | + * | course question import | Course name | The import questions screen for a course default bank | + * | course question export | Course name | The export questions screen for a course default bank | + * | question bank | Question bank name | The question bank module | + * | question import | Question bank name | The import questions screen for a question bank | + * | question export | Question bank name | The export questions screen for a question bank | + * | preview | Question name | The screen to preview a question | + * | edit | Question name | The screen to edit a question | * * @param string $type identifies which type of page this is, e.g. 'Preview'. * @param string $identifier identifies the particular page, e.g. 'My question'. @@ -71,21 +74,51 @@ class behat_core_question extends behat_question_base { // The question bank does not handle fields at the edge of the viewport well. // Increase the size to avoid this. $this->execute('behat_general::i_change_window_size_to', ['window', 'large']); + $qbank = $this->get_default_bank_for_course_identifier($identifier); return new moodle_url('/question/edit.php', [ - 'courseid' => $this->get_course_id($identifier), + 'cmid' => $qbank->id, ]); + case 'question bank': + // The question bank does not handle fields at the edge of the viewport well. + // Increase the size to avoid this. + $this->execute('behat_general::i_change_window_size_to', ['window', 'large']); + return new moodle_url('/question/edit.php', + ['cmid' => $this->get_cm_by_activity_name('qbank', $identifier)->id] + ); + case 'course question categories': + $qbank = $this->get_default_bank_for_course_identifier($identifier); return new moodle_url('/question/bank/managecategories/category.php', - ['courseid' => $this->get_course_id($identifier)]); + ['cmid' => $qbank->id] + ); + + case 'question categories': + return new moodle_url('/question/bank/managecategories/category.php', + ['cmid' => $this->get_cm_by_activity_name('qbank', $identifier)->id] + ); case 'course question import': + $qbank = $this->get_default_bank_for_course_identifier($identifier); return new moodle_url('/question/bank/importquestions/import.php', - ['courseid' => $this->get_course_id($identifier)]); + ['cmid' => $qbank->id] + ); + + case 'question import': + return new moodle_url('/question/bank/importquestions/import.php', + ['cmid' => $this->get_cm_by_activity_name('qbank', $identifier)->id] + ); case 'course question export': + $qbank = $this->get_default_bank_for_course_identifier($identifier); return new moodle_url('/question/bank/exportquestions/export.php', - ['courseid' => $this->get_course_id($identifier)]); + ['cmid' => $qbank->id] + ); + + case 'question export': + return new moodle_url('/question/bank/exportquestions/export.php', + ['cmid' => $this->get_cm_by_activity_name('qbank', $identifier)->id] + ); case 'preview': [$questionid, $otheridtype, $otherid] = $this->find_question_by_name($identifier); @@ -102,6 +135,17 @@ class behat_core_question extends behat_question_base { } } + /** + * Get the course default question bank, creating it if it doesn't yet exist + * + * @param string $identifier The name of the course to add a default bank to. + * @return cm_info The newly created default question bank. + */ + private function get_default_bank_for_course_identifier(string $identifier): cm_info { + $course = get_course($this->get_course_id($identifier)); + return \core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true); + } + /** * Find a question, and where it is, from the question name. * diff --git a/question/tests/behat/delete_questions.feature b/question/tests/behat/delete_questions.feature index 7cb1f6f8e10..26a7f921c7b 100644 --- a/question/tests/behat/delete_questions.feature +++ b/question/tests/behat/delete_questions.feature @@ -14,13 +14,16 @@ Feature: A teacher can delete questions in the question bank And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | essay | Test question to be deleted | Write about whatever you want | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" @javascript Scenario: A question not used anywhere can really be deleted @@ -49,7 +52,7 @@ Feature: A teacher can delete questions in the question bank And quiz "Test quiz" contains the following questions: | question | page | requireprevious | | Test used question to be deleted | 1 | 0 | - When I am on the "Course 1" "core_question > course question bank" page + When I am on the "Qbank 1" "core_question > question bank" page And I choose "Delete" action for "Test used question to be deleted" in the question bank And I should see "This will delete the following question and all its versions:" And I should see "* Denotes questions which can't be deleted because they are in use. Instead, they will be hidden in the question bank unless you set 'Show hidden questions' to 'Yes'." @@ -74,7 +77,7 @@ Feature: A teacher can delete questions in the question bank @javascript Scenario: Delete question has multiple versions in question bank page - Given I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + Given I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" When the following "core_question > updated questions" exist: | questioncategory | question | questiontext | | Test questions | Test question to be deleted | Test question to be deleted version 2 | diff --git a/question/tests/behat/duplicate_questions.feature b/question/tests/behat/duplicate_questions.feature index 0b762b548fc..eacef35d1bd 100644 --- a/question/tests/behat/duplicate_questions.feature +++ b/question/tests/behat/duplicate_questions.feature @@ -14,13 +14,16 @@ Feature: A teacher can duplicate questions in the question bank And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | idnumber | | Test questions | essay | Test question to be copied | Write about whatever you want | qid | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher" Scenario: Duplicating a previously created question When I choose "Duplicate" action for "Test question to be copied" in the question bank diff --git a/question/tests/behat/edit_question_tags.feature b/question/tests/behat/edit_question_tags.feature index f623128010c..9d09432a8ea 100644 --- a/question/tests/behat/edit_question_tags.feature +++ b/question/tests/behat/edit_question_tags.feature @@ -14,13 +14,16 @@ Feature: A teacher can manage tags on questions in the question bank And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | essay | Test question to be tagged | Write about whatever you want | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" @javascript Scenario: Manage tags on a question diff --git a/question/tests/behat/edit_question_versioning.feature b/question/tests/behat/edit_question_versioning.feature index 4fa0bf7f561..9717cf6b1cb 100644 --- a/question/tests/behat/edit_question_versioning.feature +++ b/question/tests/behat/edit_question_versioning.feature @@ -14,12 +14,12 @@ Feature: Questions in the question bank have versions And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: | activity | name | course | idnumber | | quiz | Quiz 1 | C1 | quiz1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | answer 1 | | Test questions | truefalse | First question | Answer the first question | True | @@ -29,13 +29,13 @@ Feature: Questions in the question bank have versions @javascript Scenario: Question version is displayed - Given I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + Given I am on the "Quiz 1" "mod_quiz > question bank" page logged in as "teacher1" When I choose "Edit question" action for "First question" in the question bank Then I should see "Version 1" @javascript Scenario: Question version change when question is altered - Given I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + Given I am on the "Quiz 1" "mod_quiz > question bank" page logged in as "teacher1" When I choose "Edit question" action for "First question" in the question bank And I should see "Version 1" When I set the field "id_name" to "Renamed question v2" diff --git a/question/tests/behat/edit_questions.feature b/question/tests/behat/edit_questions.feature index 0414ff30d34..2df7036a627 100644 --- a/question/tests/behat/edit_questions.feature +++ b/question/tests/behat/edit_questions.feature @@ -14,13 +14,16 @@ Feature: A teacher can edit questions in the question bank And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | essay | Test question to be edited | Write about whatever you want | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" @javascript Scenario: Edit a previously created question @@ -72,7 +75,7 @@ Feature: A teacher can edit questions in the question bank Given the following "questions" exist: | questioncategory | qtype | name | idnumber | | Test questions | essay | Question with idnumber | frog | - When I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" Then I should see "frog" in the "Question with idnumber" "table_row" When I choose "Edit question" action for "Question with idnumber" in the question bank And I set the field "ID number" to "" @@ -83,7 +86,7 @@ Feature: A teacher can edit questions in the question bank Given the following "questions" exist: | questioncategory | qtype | name | questiontext | | Test questions | missingtype | Broken question | Write something | - When I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" Then the "Edit question" item should not exist in the "Edit" action menu of the "Broken question" "table_row" And the "Duplicate" item should not exist in the "Edit" action menu of the "Broken question" "table_row" And the "Preview" item should not exist in the "Edit" action menu of the "Broken question" "table_row" diff --git a/question/tests/behat/edit_questions_standard_tags.feature b/question/tests/behat/edit_questions_standard_tags.feature index 0aacb5db6f0..dcfd86a4999 100644 --- a/question/tests/behat/edit_questions_standard_tags.feature +++ b/question/tests/behat/edit_questions_standard_tags.feature @@ -14,13 +14,16 @@ Feature: The questions can be tagged And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "tags" exist: | name | isstandard | | foo | 1 | | bar | 1 | Scenario: The tags autocomplete should include standard tags - When I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + When I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" And I press "Create a new question ..." And I set the field "item_qtype_truefalse" to "1" And I click on "Add" "button" in the "Choose a question type to add" "dialogue" diff --git a/question/tests/behat/filter_questions_by_tag.feature b/question/tests/behat/filter_questions_by_tag.feature index 4432999b305..5de2b7a7d74 100644 --- a/question/tests/behat/filter_questions_by_tag.feature +++ b/question/tests/behat/filter_questions_by_tag.feature @@ -14,9 +14,12 @@ Feature: The questions in the question bank can be filtered by tags And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | user | questiontext | | Test questions | essay | question 1 name | admin | Question 1 text | @@ -26,6 +29,7 @@ Feature: The questions in the question bank can be filtered by tags | Tags | foo | And I press "id_submitbutton" And I am on the "question 2 name" "core_question > edit" page + And I change window size to "large" And I set the following fields to these values: | Tags | bar | And I press "id_submitbutton" @@ -38,7 +42,7 @@ Feature: The questions in the question bank can be filtered by tags @javascript Scenario: Empty condition should not result in exception - When I am on the "Course 1" "core_question > course question bank" page + When I am on the "Qbank 1" "core_question > question bank" page And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Test questions" When I click on "Add condition" "button" And I set the field "type" in the "Filter 2" "fieldset" to "Tag" diff --git a/question/tests/behat/filter_questions_combined_conditions.feature b/question/tests/behat/filter_questions_combined_conditions.feature index 046d2550d8c..1afe2567611 100644 --- a/question/tests/behat/filter_questions_combined_conditions.feature +++ b/question/tests/behat/filter_questions_combined_conditions.feature @@ -14,10 +14,13 @@ Feature: The questions in the question bank can be filtered by combine various c And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions 1| - | Course | C1 | Test questions 2| + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions 1 | + | Activity module | qbank1 | Test questions 2 | And the following "questions" exist: | questioncategory | qtype | name | user | questiontext | | Test questions 1 | essay | question 1 name | teacher1 | Question 1 text | @@ -28,11 +31,12 @@ Feature: The questions in the question bank can be filtered by combine various c | question | tag | | question 1 name | foo | | question 3 name | foo | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" @javascript Scenario: The questions can be filtered by matching all conditions When I apply question bank filter "Category" with value "Test questions 1" + And I change window size to "large" And I apply question bank filter "Tag" with value "foo" Then I should see "question 1 name" in the "categoryquestions" "table" And I should not see "question 2 name" in the "categoryquestions" "table" diff --git a/question/tests/behat/question_categories.feature b/question/tests/behat/question_categories.feature deleted file mode 100644 index ba147b8f1fb..00000000000 --- a/question/tests/behat/question_categories.feature +++ /dev/null @@ -1,39 +0,0 @@ -@core @core_question -Feature: A teacher can move questions between categories in the question bank - In order to organize my questions - As a teacher - I move questions between categories - - Background: - Given the following "users" exist: - | username | firstname | lastname | email | - | teacher1 | Teacher | 1 | teacher1@example.com | - And the following "courses" exist: - | fullname | shortname | format | - | Course 1 | C1 | weeks | - And the following "course enrolments" exist: - | user | course | role | - | teacher1 | C1 | editingteacher | - And the following "question categories" exist: - | contextlevel | reference | questioncategory | name | - | Course | C1 | Top | top | - | Course | C1 | top | Default for C1 | - | Course | C1 | Default for C1 | Subcategory | - | Course | C1 | top | Used category | - And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Used category | essay | Test question to be moved | Write about whatever you want | - And I log in as "teacher1" - And I am on "Course 1" course homepage - - @javascript - Scenario: Move a question between categories via the question page - When I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" - And I apply question bank filter "Category" with value "Used category" - And I click on "Test question to be moved" "checkbox" in the "Test question to be moved" "table_row" - And I click on "With selected" "button" - And I click on question bulk action "move" - And I set the field "Question category" to "Subcategory" - And I press "Move to" - Then I should see "Test question to be moved" - And I should see "Subcategory (1)" in the ".form-autocomplete-selection" "css_element" diff --git a/question/tests/behat/question_categories_idnumber.feature b/question/tests/behat/question_categories_idnumber.feature index f602a3e605c..e57c931683c 100644 --- a/question/tests/behat/question_categories_idnumber.feature +++ b/question/tests/behat/question_categories_idnumber.feature @@ -14,14 +14,17 @@ Feature: A teacher can put questions with idnumbers in categories in the questio And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And I log in as "teacher1" And I am on "Course 1" course homepage Scenario: A question can only have a unique idnumber within a category When the following "question categories" exist: - | contextlevel | reference | questioncategory | name | idnumber | - | Course | C1 | Top | top | | - | Course | C1 | top | Used category | c1used | + | contextlevel | reference | questioncategory | name | idnumber | + | Activity module | qbank1 | Top | top | | + | Activity module | qbank1 | top | Used category | c1used | And the following "questions" exist: | questioncategory | qtype | name | questiontext | idnumber | | Used category | essay | Test question 1 | Write about whatever you want | q1 | @@ -34,9 +37,9 @@ Feature: A teacher can put questions with idnumbers in categories in the questio Scenario: A question can be edited and saved without changing the idnumber When the following "question categories" exist: - | contextlevel | reference | questioncategory | name | idnumber | - | Course | C1 | Top | top | | - | Course | C1 | top | Used category | c1used | + | contextlevel | reference | questioncategory | name | idnumber | + | Activity module | qbank1 | Top | top | | + | Activity module | qbank1 | top | Used category | c1used | And the following "questions" exist: | questioncategory | qtype | name | questiontext | idnumber | | Used category | essay | Test question 1 | Write about whatever you want | q1 | @@ -46,10 +49,10 @@ Feature: A teacher can put questions with idnumbers in categories in the questio Scenario: Question idnumber conflicts found when saving to the same category. When the following "question categories" exist: - | contextlevel | reference | questioncategory | name | - | Course | C1 | Top | top | - | Course | C1 | top | Category 1 | - | Course | C1 | top | Category 2 | + | contextlevel | reference | questioncategory | name | + | Activity module | qbank1 | Top | top | + | Activity module | qbank1 | top | Category 1 | + | Activity module | qbank1 | top | Category 2 | And the following "questions" exist: | questioncategory | qtype | name | questiontext | idnumber | | Category 1 | essay | Question to edit | Write about whatever you want | q1 | @@ -62,10 +65,10 @@ Feature: A teacher can put questions with idnumbers in categories in the questio @javascript Scenario: Moving a question between categories can force a change to the idnumber And the following "question categories" exist: - | contextlevel | reference | questioncategory | name | idnumber | - | Course | C1 | Top | top | | - | Course | C1 | top | Subcategory | c1sub | - | Course | C1 | top | Used category | c1used | + | contextlevel | reference | questioncategory | name | idnumber | + | Activity module | qbank1 | Top | top | | + | Activity module | qbank1 | top | Subcategory | c1sub | + | Activity module | qbank1 | top | Used category | c1used | And the following "questions" exist: | questioncategory | qtype | name | questiontext | idnumber | | Used category | essay | Test question 1 | Write about whatever you want | q1 | @@ -79,8 +82,12 @@ Feature: A teacher can put questions with idnumbers in categories in the questio And I click on "Test question 3" "checkbox" in the "Test question 3" "table_row" And I click on "With selected" "button" And I click on question bulk action "move" - And I set the field "Question category" to "Subcategory" - And I press "Move to" + And I open the autocomplete suggestions list in the ".search-categories" "css_element" + And I click on "Used category" item in the autocomplete list + And I click on "Move questions" "button" + And I should see "Are you sure you want to move these questions?" + And I click on "Confirm" "button" + And I wait until the page is ready And I choose "Edit question" action for "Test question 3" in the question bank # The question just moved into this category needs to have a unique idnumber, so a number is appended. Then the field "ID number" matches value "q1_1" diff --git a/question/tests/behat/question_filter_pagination.feature b/question/tests/behat/question_filter_pagination.feature index 3af96c874eb..7bf60218558 100644 --- a/question/tests/behat/question_filter_pagination.feature +++ b/question/tests/behat/question_filter_pagination.feature @@ -14,9 +14,12 @@ Feature: A teacher can pagimate through question bank questions And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | questioncategory | name | - | Course | C1 | Top | Used category | + | contextlevel | reference | questioncategory | name | + | Activity module | qbank1 | Top | Used category | Given 100 "questions" exist with the following data: | questioncategory | Used category | | qtype | essay | @@ -27,8 +30,8 @@ Feature: A teacher can pagimate through question bank questions | Used category | essay | Not on first page | Write about whatever you want | Scenario: Questions can be paginated - When I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" - When I apply question bank filter "Category" with value "Course 1" + Given I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" + When I apply question bank filter "Category" with value "Top for Qbank 1" And I follow "Sort by Question name ascending" And I follow "Sort by Question name descending" And I should see "Tests question 1" diff --git a/question/tests/behat/question_highlight.feature b/question/tests/behat/question_highlight.feature index 6b2c74bc501..6a114e17534 100644 --- a/question/tests/behat/question_highlight.feature +++ b/question/tests/behat/question_highlight.feature @@ -14,9 +14,12 @@ Feature: A teacher can see highlighted questions in the question bank And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | course | idnumber | + | qbank | Qbank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And 101 "questions" exist with the following data: | questioncategory | Test questions | | qtype | essay | diff --git a/question/tests/behat/select_questions.feature b/question/tests/behat/select_questions.feature index 3cde91b564f..5cded600472 100644 --- a/question/tests/behat/select_questions.feature +++ b/question/tests/behat/select_questions.feature @@ -14,15 +14,18 @@ Feature: The questions in the question bank can be selected in various ways And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | user | questiontext | | Test questions | essay | A question 1 name | admin | Question 1 text | | Test questions | essay | B question 2 name | teacher1 | Question 2 text | | Test questions | numerical | C question 3 name | teacher1 | Question 3 text | - And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" @javascript Scenario: The question text can be chosen all in the list of questions diff --git a/question/tests/behat/sort_questions.feature b/question/tests/behat/sort_questions.feature index bab2f0ce6ca..03581e2f1ce 100644 --- a/question/tests/behat/sort_questions.feature +++ b/question/tests/behat/sort_questions.feature @@ -14,15 +14,18 @@ Feature: The questions in the question bank can be sorted in various ways And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | qbank | Qbank 1 | Question bank 1 | C1 | qbank1 | And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | + | contextlevel | reference | name | + | Activity module | qbank1 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | user | questiontext | idnumber | | Test questions | essay | A question 1 name | admin | Question 1 text | numidnum course question bank" page logged in as "teacher1" + And I am on the "Qbank 1" "core_question > question bank" page logged in as "teacher1" Scenario: The questions are sorted by type by default Then "A question 1 name" "checkbox" should appear before "C question 3 name" "checkbox" @@ -61,6 +64,7 @@ Feature: The questions in the question bank can be sorted in various ways @javascript Scenario: The question text can be shown in the list of questions + Given I change window size to "large" When I set the field "Show question text in the question list?" to "Yes" Then I should see "Question 1 text" And I should see "Question 2 text" diff --git a/question/tests/category_manager_test.php b/question/tests/category_manager_test.php index 6ad6362c624..644ebb94c6a 100644 --- a/question/tests/category_manager_test.php +++ b/question/tests/category_manager_test.php @@ -31,6 +31,17 @@ use core_question\local\bank\question_edit_contexts; * @covers \core_question\category_manager */ final class category_manager_test extends \advanced_testcase { + + /** + * Create a course and qbank module and return the module context for use in tests. + * + * @return \core\context\module|false + */ + private function create_course_and_get_qbank_context() { + $course = self::getDataGenerator()->create_course(); + $qbank = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + return context_module::instance($qbank->cmid); + } /** * Test creating a category. */ @@ -39,7 +50,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $topcat = question_get_top_category($context->id, true); $manager = new category_manager(); $id = $manager->add_category( @@ -63,7 +74,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $topcat = question_get_top_category($context->id, true); $manager = new category_manager(); $id = $manager->add_category( @@ -87,7 +98,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $topcat = question_get_top_category($context->id, true); $manager = new category_manager(); $manager->add_category( @@ -116,7 +127,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $topcat = question_get_top_category($context->id, true); $category = $questiongenerator->create_question_category([ @@ -154,7 +165,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $topcat = question_get_top_category($context->id, true); $category = $questiongenerator->create_question_category([ @@ -193,7 +204,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $topcat = question_get_top_category($context->id, true); $category = $questiongenerator->create_question_category([ @@ -231,7 +242,7 @@ final class category_manager_test extends \advanced_testcase { $this->setAdminUser(); $this->resetAfterTest(); - $context = context_course::instance(SITEID); + $context = $this->create_course_and_get_qbank_context(); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $topcat = question_get_top_category($context->id, true); $questiongenerator->create_question_category([ @@ -304,7 +315,7 @@ final class category_manager_test extends \advanced_testcase { $course = $generator->create_course(); $quiz = $generator->get_plugin_generator('mod_quiz')->create_instance(['course' => $course->id]); $contexts = new question_edit_contexts(context_module::instance($quiz->cmid)); - $defaultcat = question_make_default_categories([$contexts->lowest()]); + $defaultcat = question_get_default_category($contexts->lowest()->id, true); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); // Create the category. $category = $questiongenerator->create_question_category([ @@ -381,7 +392,7 @@ final class category_manager_test extends \advanced_testcase { $quiz = $generator->create_module('quiz', ['course' => $course->id]); $contexts = new question_edit_contexts(context_module::instance($quiz->cmid)); - $defaultcategory = question_make_default_categories([$contexts->lowest()]); + $defaultcategory = question_get_default_category($contexts->lowest()->id, true); $questiongerator = $generator->get_plugin_generator('core_question'); // Short answer question is made of one question. @@ -406,7 +417,7 @@ final class category_manager_test extends \advanced_testcase { $quiz = $generator->create_module('quiz', ['course' => $course->id]); $contexts = new question_edit_contexts(context_module::instance($quiz->cmid)); - $defaultcategory = question_make_default_categories([$contexts->lowest()]); + $defaultcategory = question_get_default_category($contexts->lowest()->id, true); $questiongerator = $generator->get_plugin_generator('core_question'); // Multi answer question is made of one parent and two child questions. @@ -435,7 +446,7 @@ final class category_manager_test extends \advanced_testcase { $quiz = $generator->create_module('quiz', ['course' => $course->id]); $contexts = new question_edit_contexts(context_module::instance($quiz->cmid)); - $defaultcategory = question_make_default_categories([$contexts->lowest()]); + $defaultcategory = question_get_default_category($contexts->lowest()->id, true); $questiongerator = $generator->get_plugin_generator('core_question'); // Create two versions of a multianswer question which will lead to @@ -467,7 +478,7 @@ final class category_manager_test extends \advanced_testcase { $quiz = $generator->create_module('quiz', ['course' => $course->id]); $contexts = new question_edit_contexts(context_module::instance($quiz->cmid)); - $defaultcategory = question_make_default_categories([$contexts->lowest()]); + $defaultcategory = question_get_default_category($contexts->lowest()->id, true); $questiongerator = $generator->get_plugin_generator('core_question'); // Multi answer question is made of one parent and two child questions. @@ -507,7 +518,7 @@ final class category_manager_test extends \advanced_testcase { $qcategory1 = $questiongenerator->create_question_category(['contextid' => $context->id]); // Try to delete a top category. - $categorytop = question_get_top_category($qcategory1->id, true)->id; + $categorytop = question_get_top_category($qcategory1->contextid, true)->id; $this->expectException('moodle_exception'); $this->expectExceptionMessage(get_string('cannotdeletetopcat', 'question')); $manager->require_can_delete_category($categorytop); diff --git a/question/tests/generator/lib.php b/question/tests/generator/lib.php index c9dd94b585c..e13906ba25a 100644 --- a/question/tests/generator/lib.php +++ b/question/tests/generator/lib.php @@ -74,7 +74,26 @@ class core_question_generator extends component_generator_base { $qbank = $this->datagenerator->create_module('qbank', ['course' => SITEID]); $record['contextid'] = context_module::instance($qbank->cmid)->id; } + } else { + // Any requests for a question category in a contextlevel that is no longer supported + // will have a qbank instance created on the associated context and then the category + // will be made for that context instead. + $context = context::instance_by_id($record['contextid']); + if ($context->contextlevel !== CONTEXT_MODULE) { + $course = match ($context->contextlevel) { + CONTEXT_COURSE => get_course($context->instanceid), + CONTEXT_SYSTEM => get_site(), + CONTEXT_COURSECAT => $this->datagenerator->create_course(['category' => $context->instanceid]), + default => throw new \Exception('Invalid context to infer a question bank from.'), + }; + $qbank = \core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true); + $bankcontext = context_module::instance($qbank->id); + } else { + $bankcontext = $context; + } + $record['contextid'] = $bankcontext->id; } + if (!isset($record['parent'])) { $record['parent'] = question_get_top_category($record['contextid'], true)->id; } diff --git a/question/tests/privacy/provider_test.php b/question/tests/privacy/provider_test.php index a0b3519bcea..15325a06015 100644 --- a/question/tests/privacy/provider_test.php +++ b/question/tests/privacy/provider_test.php @@ -289,17 +289,18 @@ class provider_test extends \core_privacy\tests\provider_testcase { $otheruser = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); - $context = \context_course::instance($course->id); - $othercourse = $this->getDataGenerator()->create_course(); - $othercontext = \context_course::instance($othercourse->id); + $qbank1 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbank1context = \context_module::instance($qbank1->cmid); + $qbank2 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbank2context = \context_module::instance($qbank2->cmid); // Create a couple of questions. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category([ - 'contextid' => $context->id, + 'contextid' => $qbank1context->id, ]); $othercat = $questiongenerator->create_question_category([ - 'contextid' => $othercontext->id, + 'contextid' => $qbank2context->id, ]); // Create questions: @@ -326,7 +327,7 @@ class provider_test extends \core_privacy\tests\provider_testcase { $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist( $user, 'core_question', - [$context->id] + [$qbank1context->id] ); // Find out how many questions are in the question bank to start with. @@ -370,17 +371,18 @@ class provider_test extends \core_privacy\tests\provider_testcase { $otheruser = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); - $context = \context_course::instance($course->id); - $othercourse = $this->getDataGenerator()->create_course(); - $othercontext = \context_course::instance($othercourse->id); + $qbank1 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbank1context = \context_module::instance($qbank1->cmid); + $qbank2 = self::getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $qbank2context = \context_module::instance($qbank2->cmid); // Create a couple of questions. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category([ - 'contextid' => $context->id, + 'contextid' => $qbank1context->id, ]); $othercat = $questiongenerator->create_question_category([ - 'contextid' => $othercontext->id, + 'contextid' => $qbank2context->id, ]); // Create questions: @@ -407,7 +409,7 @@ class provider_test extends \core_privacy\tests\provider_testcase { // Delete the data and check it is removed. $this->setUser(); - provider::delete_data_for_all_users_in_context($context); + provider::delete_data_for_all_users_in_context($qbank1context); $this->assertEquals($questioncount, $DB->count_records('question')); diff --git a/question/tests/question_bank_column_test.php b/question/tests/question_bank_column_test.php index e659a76d12a..d2d90dd5e62 100644 --- a/question/tests/question_bank_column_test.php +++ b/question/tests/question_bank_column_test.php @@ -42,10 +42,13 @@ class question_bank_column_test extends \advanced_testcase { public function test_column_header_multi_sort_no_tooltips(): void { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $cm = get_coursemodule_from_id('qbank', $qbank->cmid); $questionbank = new view( new question_edit_contexts(\context_course::instance($course->id)), new \moodle_url('/'), - $course + $course, + $cm ); $columnbase = new testable_core_question_column($questionbank); @@ -76,10 +79,13 @@ class question_bank_column_test extends \advanced_testcase { public function test_column_header_multi_sort_with_tooltips(): void { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); + $qbank = $this->getDataGenerator()->create_module('qbank', ['course' => $course->id]); + $cm = get_coursemodule_from_id('qbank', $qbank->cmid); $questionbank = new view( new question_edit_contexts(\context_course::instance($course->id)), new \moodle_url('/'), - $course + $course, + $cm ); $columnbase = new testable_core_question_column($questionbank); diff --git a/question/type/calculated/tests/question_type_test.php b/question/type/calculated/tests/question_type_test.php index e10a8fc91f8..4cc45367adf 100644 --- a/question/type/calculated/tests/question_type_test.php +++ b/question/type/calculated/tests/question_type_test.php @@ -105,7 +105,7 @@ class question_type_test extends \advanced_testcase { $this->assertEquals($question->createdby, $questiondata->createdby); $this->assertEquals($question->createdby, $questiondata->modifiedby); $this->assertEquals('', $questiondata->idnumber); - $this->assertEquals($syscontext->id, $questiondata->contextid); + $this->assertEquals($category->contextid, $questiondata->contextid); $this->assertEquals([], $questiondata->hints); // Options. diff --git a/question/type/essay/tests/restore_test.php b/question/type/essay/tests/restore_test.php index 630a9eaa145..377bdf0a95e 100644 --- a/question/type/essay/tests/restore_test.php +++ b/question/type/essay/tests/restore_test.php @@ -63,7 +63,7 @@ class restore_test extends \restore_date_testcase { $newqbank = reset($newqbanks); // Verify that the restored question has options. - $newcategory = question_make_default_category(\context_module::instance($newqbank->id)); + $newcategory = question_get_default_category(\context_module::instance($newqbank->id)->id, true); $newessay = $DB->get_record_sql('SELECT q.* FROM {question} q JOIN {question_versions} qv ON qv.questionid = q.id diff --git a/question/type/gapselect/tests/behat/import_test.feature b/question/type/gapselect/tests/behat/import_test.feature index 572ffc0e216..dfdcd5ecc47 100644 --- a/question/type/gapselect/tests/behat/import_test.feature +++ b/question/type/gapselect/tests/behat/import_test.feature @@ -31,6 +31,7 @@ Feature: Import and export select missing words questions # Now export again. And I am on the "Course 1" "core_question > course question export" page logged in as teacher And I set the field "id_format_xml" to "1" + And I set the field "Export category" to "Default for C1 (1)" And I press "Export questions to file" And following "click here" should download a file that: | Has mimetype | text/xml | diff --git a/question/type/multianswer/tests/question_type_test.php b/question/type/multianswer/tests/question_type_test.php index 6c25c8ea658..bdb6d134af9 100644 --- a/question/type/multianswer/tests/question_type_test.php +++ b/question/type/multianswer/tests/question_type_test.php @@ -192,7 +192,7 @@ class question_type_test extends \advanced_testcase { $this->assertEquals($question->createdby, $questiondata->createdby); $this->assertEquals($question->createdby, $questiondata->modifiedby); $this->assertEquals('', $questiondata->idnumber); - $this->assertEquals($syscontext->id, $questiondata->contextid); + $this->assertEquals($category->contextid, $questiondata->contextid); // Build the expected hint base. $hintbase = [ diff --git a/question/type/questiontypebase.php b/question/type/questiontypebase.php index 78f585d7f4d..1adbe0265ad 100644 --- a/question/type/questiontypebase.php +++ b/question/type/questiontypebase.php @@ -1396,7 +1396,9 @@ class question_type { * required to set up and save a question of any type for testing purposes. * Alternate DB table prefix may be used to facilitate data deletion. */ + #[\core\attribute\deprecated(replacement: null, since: '5.0', mdl: 'MDL-71378')] public function generate_test($name, $courseid=null) { + \core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]); $form = new stdClass(); $form->name = $name; $form->questiontextformat = 1; diff --git a/question/type/truefalse/tests/question_type_test.php b/question/type/truefalse/tests/question_type_test.php index 43e035ddeb8..ab4075df97d 100644 --- a/question/type/truefalse/tests/question_type_test.php +++ b/question/type/truefalse/tests/question_type_test.php @@ -100,7 +100,7 @@ class question_type_test extends \advanced_testcase { $this->assertEquals($question->createdby, $questiondata->createdby); $this->assertEquals($question->createdby, $questiondata->modifiedby); $this->assertEquals('', $questiondata->idnumber); - $this->assertEquals($syscontext->id, $questiondata->contextid); + $this->assertEquals($category->contextid, $questiondata->contextid); // Options. $this->assertEquals($questiondata->id, $questiondata->options->question); diff --git a/report/questioninstances/tests/behat/question_reports.feature b/report/questioninstances/tests/behat/question_reports.feature index 4c7e57e7724..bd249365680 100644 --- a/report/questioninstances/tests/behat/question_reports.feature +++ b/report/questioninstances/tests/behat/question_reports.feature @@ -11,12 +11,12 @@ Feature: A Teacher can generate question instance reports And the following "courses" exist: | fullname | shortname | format | | Course 1 | C1 | weeks | - And the following "question categories" exist: - | contextlevel | reference | name | - | Course | C1 | Test questions | And the following "activities" exist: | activity | course | name | | quiz | C1 | Test quiz Q001 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | Test quiz Q001 | Test questions | And the following "questions" exist: | questioncategory | qtype | name | | Test questions | truefalse | TF | @@ -36,9 +36,9 @@ Feature: A Teacher can generate question instance reports Given I am on the "C1" "Course" page logged in as "admin" And I navigate to "Reports > Question instances" in site administration When I press "Get the report" - Then "Course: Course 1" row "Total" column of "generaltable" table should contain "2" - And "Course: Course 1" row "Visible" column of "generaltable" table should contain "2" - And "Course: Course 1" row "Hidden" column of "generaltable" table should contain "0" + Then "Quiz: Test quiz Q001" row "Total" column of "generaltable" table should contain "2" + And "Quiz: Test quiz Q001" row "Visible" column of "generaltable" table should contain "2" + And "Quiz: Test quiz Q001" row "Hidden" column of "generaltable" table should contain "0" Scenario: Generate report displaying hidden questions Given I am on the "Test quiz Q001" "mod_quiz > question bank" page logged in as "admin" @@ -46,11 +46,11 @@ Feature: A Teacher can generate question instance reports And I press "Delete" And I navigate to "Reports > Question instances" in site administration When I press "Get the report" - Then "Course: Course 1" row "Total" column of "generaltable" table should contain "2" - And "Course: Course 1" row "Visible" column of "generaltable" table should contain "1" - And "Course: Course 1" row "Hidden" column of "generaltable" table should contain "1" + Then "Quiz: Test quiz Q001" row "Total" column of "generaltable" table should contain "2" + And "Quiz: Test quiz Q001" row "Visible" column of "generaltable" table should contain "1" + And "Quiz: Test quiz Q001" row "Hidden" column of "generaltable" table should contain "1" And I set the field "menuqtype" to "True/False" And I press "Get the report" - And "Course: Course 1" row "Total" column of "generaltable" table should contain "1" - And "Course: Course 1" row "Visible" column of "generaltable" table should contain "0" - And "Course: Course 1" row "Hidden" column of "generaltable" table should contain "1" + And "Quiz: Test quiz Q001" row "Total" column of "generaltable" table should contain "1" + And "Quiz: Test quiz Q001" row "Visible" column of "generaltable" table should contain "0" + And "Quiz: Test quiz Q001" row "Hidden" column of "generaltable" table should contain "1" diff --git a/theme/classic/tests/behat/behat_theme_classic_behat_core_question.php b/theme/classic/tests/behat/behat_theme_classic_behat_core_question.php index e0885ef989b..b933916904e 100644 --- a/theme/classic/tests/behat/behat_theme_classic_behat_core_question.php +++ b/theme/classic/tests/behat/behat_theme_classic_behat_core_question.php @@ -35,15 +35,10 @@ class behat_theme_classic_behat_core_question extends behat_core_question { * @param string $questiontypename The question type name * @param TableNode $questiondata The data to fill the question type form. */ + #[\core\attribute\deprecated('behat_core_question::i_add_a_question_filling_the_form_with()', since: '5.0', mdl: 'MDL-71378')] public function i_add_a_question_filling_the_form_with($questiontypename, TableNode $questiondata) { - // Go to question bank. - $this->execute("behat_general::click_link", get_string('questionbank', 'question')); - - // Click on create question. - $this->execute('behat_forms::press_button', get_string('createnewquestion', 'question')); - - // Add question. - $this->finish_adding_question($questiontypename, $questiondata); + \core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]); + parent::i_add_a_question_filling_the_form_with($questiontypename, $questiondata); } }