From 01403b2f0d54c7c6081d8f54628a55761fe58724 Mon Sep 17 00:00:00 2001 From: Mahmoud Kassaei Date: Mon, 22 Feb 2021 08:25:11 +0000 Subject: [PATCH] MDL-70895 Questions: Default options when creating a question --- question/question.php | 4 ++ question/type/ddwtos/tests/behat/add.feature | 33 +++++++----- question/type/edit_question_form.php | 24 ++++++--- question/type/gapselect/edit_form_base.php | 2 +- question/type/gapselect/questiontypebase.php | 5 ++ .../type/gapselect/tests/behat/add.feature | 41 +++++++++++++++ .../type/gapselect/tests/edit_form_test.php | 4 ++ .../multichoice/edit_multichoice_form.php | 11 ++-- question/type/multichoice/questiontype.php | 8 +++ .../type/multichoice/tests/behat/add.feature | 51 ++++++++++++------- question/type/questiontypebase.php | 39 ++++++++++++++ .../shortanswer/edit_shortanswer_form.php | 5 +- question/type/shortanswer/questiontype.php | 5 ++ .../type/shortanswer/tests/behat/add.feature | 13 +++-- question/type/upgrade.txt | 19 +++++++ 15 files changed, 218 insertions(+), 46 deletions(-) create mode 100644 question/type/gapselect/tests/behat/add.feature diff --git a/question/question.php b/question/question.php index ca4cba7d057..1fe9b06ae9b 100644 --- a/question/question.php +++ b/question/question.php @@ -271,6 +271,10 @@ if ($mform->is_cancelled()) { } } + // If this is a new question, save defaults for user in user_preferences table. + if (empty($question->id)) { + $qtypeobj->save_defaults_for_new_questions($fromform); + } $question = $qtypeobj->save_question($question, $fromform); if (isset($fromform->tags)) { // If we have any question context level tags then set those tags now. diff --git a/question/type/ddwtos/tests/behat/add.feature b/question/type/ddwtos/tests/behat/add.feature index 46a92386db4..a712f027bbd 100644 --- a/question/type/ddwtos/tests/behat/add.feature +++ b/question/type/ddwtos/tests/behat/add.feature @@ -20,15 +20,24 @@ Feature: Test creating a drag and drop into text question @javascript Scenario: Create a drag and drop into text question - When I add a "Drag and drop into text" question filling the form with: - | Question name | Drag and drop into text 001 | - | Question text | The [[1]] [[2]] on the [[3]]. | - | General feedback | The cat sat on the mat. | - | id_choices_0_answer | cat | - | id_choices_1_answer | sat | - | id_choices_2_answer | mat | - | id_choices_3_answer | dog | - | id_choices_4_answer | table | - | Hint 1 | First hint | - | Hint 2 | Second hint | - Then I should see "Drag and drop into text 001" + Given I add a "Drag and drop into text" question filling the form with: + | Question name | Drag and drop into text 001 | + | Question text | The [[1]] [[2]] on the [[3]]. | + | General feedback | The cat sat on the mat. | + | id_shuffleanswers | 1 | + | id_choices_0_answer | cat | + | id_choices_1_answer | sat | + | id_choices_2_answer | mat | + | id_choices_3_answer | dog | + | id_choices_4_answer | table | + | Penalty for each incorrect try | 20% | + | Hint 1 | First hint | + | Hint 2 | Second hint | + And I should see "Drag and drop into text 001" + # Checking that the next new question form displays user preferences settings. + When I press "Create a new question ..." + And I set the field "item_qtype_ddwtos" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + Then the following fields match these values: + | id_shuffleanswers | 1 | + | Penalty for each incorrect try | 20% | diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 3bf44cbadd5..02d7d02b7f6 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -113,6 +113,21 @@ abstract class question_edit_form extends question_wizard_form { parent::__construct($submiturl, null, 'post', '', ['data-qtype' => $this->qtype()], $formeditable); } + /** + * Return default value for a given form element either from user_preferences table or $default. + * + * To make use of user_preferences in your qtype default settings, you need to replace + * $mform->setDefault({elementname}, {defaultvalue}); in edit_{qtypename}_form.php with + * $mform->setDefault({elementname}, $this->get_default_value({elementname}, {defaultvalue})); + * + * @param string $name the name of the form field. + * @param mixed $default default value. + * @return string|null default value for a given form element. + */ + protected function get_default_value(string $name, $default): ?string { + return question_bank::get_qtype($this->qtype())->get_default_value($name, $default); + } + /** * Build the form definition. * @@ -121,10 +136,7 @@ abstract class question_edit_form extends question_wizard_form { * override this method and remove the ones you don't want with $mform->removeElement(). */ protected function definition() { - global $COURSE, $CFG, $DB, $PAGE; - - $qtype = $this->qtype(); - $langfile = "qtype_{$qtype}"; + global $DB, $PAGE; $mform = $this->_form; @@ -189,7 +201,7 @@ abstract class question_edit_form extends question_wizard_form { $mform->addElement('float', 'defaultmark', get_string('defaultmark', 'question'), array('size' => 7)); - $mform->setDefault('defaultmark', 1); + $mform->setDefault('defaultmark', $this->get_default_value('defaultmark', 1)); $mform->addRule('defaultmark', null, 'required', null, 'client'); $mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'question'), @@ -497,7 +509,7 @@ abstract class question_edit_form extends question_wizard_form { $mform->addElement('select', 'penalty', get_string('penaltyforeachincorrecttry', 'question'), $penaltyoptions); $mform->addHelpButton('penalty', 'penaltyforeachincorrecttry', 'question'); - $mform->setDefault('penalty', 0.3333333); + $mform->setDefault('penalty', $this->get_default_value('penalty', 0.3333333)); if (isset($this->question->hints)) { $counthints = count($this->question->hints); diff --git a/question/type/gapselect/edit_form_base.php b/question/type/gapselect/edit_form_base.php index 72b460061a0..1ae7d121d1e 100644 --- a/question/type/gapselect/edit_form_base.php +++ b/question/type/gapselect/edit_form_base.php @@ -136,7 +136,7 @@ class qtype_gapselect_edit_form_base extends question_edit_form { $mform->setExpanded('choicehdr', 1); $mform->addElement('checkbox', 'shuffleanswers', get_string('shuffle', 'qtype_gapselect')); - $mform->setDefault('shuffleanswers', 0); + $mform->setDefault('shuffleanswers', $this->get_default_value('shuffleanswers', 0)); $textboxgroup = array(); $textboxgroup[] = $mform->createElement('group', 'choices', diff --git a/question/type/gapselect/questiontypebase.php b/question/type/gapselect/questiontypebase.php index 86f4a3d5c14..0d89c0d8a2c 100644 --- a/question/type/gapselect/questiontypebase.php +++ b/question/type/gapselect/questiontypebase.php @@ -319,6 +319,11 @@ abstract class qtype_gapselect_base extends question_type { return $goupofanswers; } + public function save_defaults_for_new_questions(stdClass $fromform): void { + parent::save_defaults_for_new_questions($fromform); + $this->set_default_value('shuffleanswers', $fromform->shuffleanswers ?? 0); + } + public function get_possible_responses($questiondata) { $question = $this->make_question($questiondata); diff --git a/question/type/gapselect/tests/behat/add.feature b/question/type/gapselect/tests/behat/add.feature new file mode 100644 index 00000000000..0b6d50a5a8a --- /dev/null +++ b/question/type/gapselect/tests/behat/add.feature @@ -0,0 +1,41 @@ +@qtype @qtype_gapselect +Feature: Test creating a Select missing words question + As a teacher + In order to test my students + I need to be able to create Select missing words questions + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | T1 | Teacher1 | teacher1@moodle.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I navigate to "Question bank" in current page administration + + @javascript + Scenario: Create a Select missing words question + Given I add a "Select missing words" question filling the form with: + | Question name | Select missing words 001 | + | Question text | The [[1]] [[2]] on the [[3]]. | + | General feedback | The cat sat on the mat. | + | id_shuffleanswers | 1 | + | id_choices_0_answer | cat | + | id_choices_1_answer | sat | + | id_choices_2_answer | mat | + | id_choices_3_answer | dog | + | id_choices_4_answer | table | + | Hint 1 | First hint | + | Hint 2 | Second hint | + And I should see "Select missing words 001" + # Checking that the next new question form displays user preferences settings. + When I press "Create a new question ..." + And I set the field "item_qtype_gapselect" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + Then the following fields match these values: + | id_shuffleanswers | 1 | diff --git a/question/type/gapselect/tests/edit_form_test.php b/question/type/gapselect/tests/edit_form_test.php index dee9bd51043..53b962a0f1c 100644 --- a/question/type/gapselect/tests/edit_form_test.php +++ b/question/type/gapselect/tests/edit_form_test.php @@ -49,6 +49,10 @@ class qtype_gapselect_edit_form_base_testable extends qtype_gapselect_edit_form_ public function set_allowed_tags(array $allowed) { $this->allowedhtmltags = $allowed; } + + public function qtype() { + return 'gapselect'; + } } diff --git a/question/type/multichoice/edit_multichoice_form.php b/question/type/multichoice/edit_multichoice_form.php index 0624c3ef292..c88857f241c 100644 --- a/question/type/multichoice/edit_multichoice_form.php +++ b/question/type/multichoice/edit_multichoice_form.php @@ -46,22 +46,25 @@ class qtype_multichoice_edit_form extends question_edit_form { ); $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu); - $mform->setDefault('single', get_config('qtype_multichoice', 'answerhowmany')); + $mform->setDefault('single', $this->get_default_value('single', + get_config('qtype_multichoice', 'answerhowmany'))); $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0, 1)); $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice'); - $mform->setDefault('shuffleanswers', get_config('qtype_multichoice', 'shuffleanswers')); + $mform->setDefault('shuffleanswers', $this->get_default_value('shuffleanswers', + get_config('qtype_multichoice', 'shuffleanswers'))); $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), qtype_multichoice::get_numbering_styles()); - $mform->setDefault('answernumbering', get_config('qtype_multichoice', 'answernumbering')); + $mform->setDefault('answernumbering', $this->get_default_value('answernumbering', + get_config('qtype_multichoice', 'answernumbering'))); $mform->addElement('selectyesno', 'showstandardinstruction', get_string('showstandardinstruction', 'qtype_multichoice'), null, null, [0, 1]); $mform->addHelpButton('showstandardinstruction', 'showstandardinstruction', 'qtype_multichoice'); - $mform->setDefault('showstandardinstruction', 0); + $mform->setDefault('showstandardinstruction', $this->get_default_value('showstandardinstruction', 0)); $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'), question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START)); diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index 241456296b8..c9366b28977 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -85,6 +85,14 @@ class qtype_multichoice extends question_type { return $options; } + public function save_defaults_for_new_questions(stdClass $fromform): void { + parent::save_defaults_for_new_questions($fromform); + $this->set_default_value('single', $fromform->single); + $this->set_default_value('shuffleanswers', $fromform->shuffleanswers); + $this->set_default_value('answernumbering', $fromform->answernumbering); + $this->set_default_value('showstandardinstruction', $fromform->showstandardinstruction); + } + public function save_question_options($question) { global $DB; $context = $question->context; diff --git a/question/type/multichoice/tests/behat/add.feature b/question/type/multichoice/tests/behat/add.feature index 6aa59eeaa51..ebd8467eff2 100644 --- a/question/type/multichoice/tests/behat/add.feature +++ b/question/type/multichoice/tests/behat/add.feature @@ -38,22 +38,37 @@ Feature: Test creating a Multiple choice question | Hint 2 | Second hint | Then I should see "Multi-choice-001" + @javascript Scenario: Create a Multiple choice question with single response - When I add a "Multiple choice" question filling the form with: - | Question name | Multi-choice-002 | - | Question text | Find the capital city of England. | - | General feedback | London is the capital city of England. | - | One or multiple answers? | One answer only | - | Choice 1 | Manchester | - | Choice 2 | Buckingham | - | Choice 3 | London | - | Choice 4 | Barcelona | - | Choice 5 | Paris | - | id_fraction_0 | None | - | id_fraction_1 | None | - | id_fraction_2 | 100% | - | id_fraction_3 | None | - | id_fraction_4 | None | - | Hint 1 | First hint | - | Hint 2 | Second hint | - Then I should see "Multi-choice-002" + Given I add a "Multiple choice" question filling the form with: + | Question name | Multi-choice-002 | + | Question text | Find the capital city of England. | + | General feedback | London is the capital city of England. | + | Default mark | 5 | + | One or multiple answers? | One answer only | + | Shuffle the choices? | 0 | + | Number the choices? | 1., 2., 3., ... | + | Show standard instructions | Yes | + | Choice 1 | Manchester | + | Choice 2 | Buckingham | + | Choice 3 | London | + | Choice 4 | Barcelona | + | Choice 5 | Paris | + | id_fraction_0 | None | + | id_fraction_1 | None | + | id_fraction_2 | 100% | + | id_fraction_3 | None | + | id_fraction_4 | None | + | Hint 1 | First hint | + | Hint 2 | Second hint | + And I should see "Multi-choice-002" + # Checking that the next new question form displays user preferences settings. + When I press "Create a new question ..." + And I set the field "Multiple choice" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + Then the following fields match these values: + | Default mark | 5 | + | One or multiple answers? | One answer only | + | Shuffle the choices? | 0 | + | Number the choices? | 1., 2., 3., ... | + | Show standard instructions | Yes | diff --git a/question/type/questiontypebase.php b/question/type/questiontypebase.php index 21c00ecc812..dad811cc97d 100644 --- a/question/type/questiontypebase.php +++ b/question/type/questiontypebase.php @@ -295,6 +295,45 @@ class question_type { public function set_default_options($questiondata) { } + /** + * Return default value for a given form element either from user_preferences table or $default. + * + * @param string $name the name of the form element. + * @param mixed $default default value. + * @return string|null default value for a given form element. + */ + public function get_default_value(string $name, $default): ?string { + return get_user_preferences($this->plugin_name() . '_' . $name, $default ?? '0'); + } + + /** + * Save the default value for a given form element in user_preferences table. + * + * @param string $name the name of the value to set. + * @param string $value the setting value. + */ + public function set_default_value(string $name, string $value): void { + set_user_preference($this->plugin_name() . '_' . $name, $value); + } + + /** + * Save question defaults when creating new questions. + * + * @param stdClass $fromform data from the form. + */ + public function save_defaults_for_new_questions(stdClass $fromform): void { + // Some question types may not make use of the certain form elements, so + // we need to do a check on the following generic form elements. For instance, + // 'defaultmark' is not use in qtype_multianswer and 'penalty' in not used in + // qtype_essay and qtype_recordrtc. + if (isset($fromform->defaultmark)) { + $this->set_default_value('defaultmark', $fromform->defaultmark); + } + if (isset($fromform->penalty)) { + $this->set_default_value('penalty', $fromform->penalty); + } + } + /** * Saves (creates or updates) a question. * diff --git a/question/type/shortanswer/edit_shortanswer_form.php b/question/type/shortanswer/edit_shortanswer_form.php index 50971ec7227..43eebc38e0c 100644 --- a/question/type/shortanswer/edit_shortanswer_form.php +++ b/question/type/shortanswer/edit_shortanswer_form.php @@ -36,12 +36,13 @@ defined('MOODLE_INTERNAL') || die(); class qtype_shortanswer_edit_form extends question_edit_form { protected function definition_inner($mform) { - $menu = array( + $menu = [ get_string('caseno', 'qtype_shortanswer'), get_string('caseyes', 'qtype_shortanswer') - ); + ]; $mform->addElement('select', 'usecase', get_string('casesensitive', 'qtype_shortanswer'), $menu); + $mform->setDefault('usecase', $this->get_default_value('usecase', $menu[0])); $mform->addElement('static', 'answersinstruct', get_string('correctanswers', 'qtype_shortanswer'), diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index c98ec743e95..3039f9d93bc 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -54,6 +54,11 @@ class qtype_shortanswer extends question_type { $this->delete_files_in_hints($questionid, $contextid); } + public function save_defaults_for_new_questions(stdClass $fromform): void { + parent::save_defaults_for_new_questions($fromform); + $this->set_default_value('usecase', $fromform->usecase); + } + public function save_question_options($question) { global $DB; $result = new stdClass(); diff --git a/question/type/shortanswer/tests/behat/add.feature b/question/type/shortanswer/tests/behat/add.feature index a84b3aab4e7..100ae4425e8 100644 --- a/question/type/shortanswer/tests/behat/add.feature +++ b/question/type/shortanswer/tests/behat/add.feature @@ -18,17 +18,24 @@ Feature: Test creating a Short answer question And I am on "Course 1" course homepage And I navigate to "Question bank" in current page administration + @javascript Scenario: Create a Short answer question - When I add a "Short answer" question filling the form with: + Given I add a "Short answer" question filling the form with: | Question name | shortanswer-001 | | Question text | What is the national langauge in France? | | General feedback | The national langauge in France is French | | Default mark | 1 | - | Case sensitivity | No, case is unimportant | + | Case sensitivity | Yes, case must match | | id_answer_0 | French | | id_fraction_0 | 100% | | id_feedback_0 | Well done. French is correct. | | id_answer_1 | * | | id_fraction_1 | None | | id_feedback_1 | Your answer is incorrect. | - Then I should see "shortanswer-001" + And I should see "shortanswer-001" + # Checking that the next new question form displays user preferences settings. + When I press "Create a new question ..." + And I set the field "Short answer" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + Then the following fields match these values: + | Case sensitivity | Yes, case must match | diff --git a/question/type/upgrade.txt b/question/type/upgrade.txt index 48cd5e8cd23..080c7a80d43 100644 --- a/question/type/upgrade.txt +++ b/question/type/upgrade.txt @@ -1,5 +1,24 @@ This files describes API changes for question type plugins. + +=== 3.11 === +* Introducing the following \question_type base class methods to save/fetch the last form values + that were used when creating questions as the new defaults when creating new questions: + - \question_type::get_default_value() + - Fetches the default value for a given question field from the user preference. + Question type plugins can use this in edit_{qtypename}_form.php when using $mform->setDefault(). + - \question_type::set_default_value() + - Saves the default value for a given question form field in the user preferences. + - \question_type::save_defaults_for_new_questions() + - Saves the question type plugin's defined form defaults into the user preferences. + It calls \question_type::set_default_value() to save each form field default value + into the user preferences. + - Question type plugins using \question_type::get_default_value() for their form fields must implement + this in order to save the values from these form fields as defaults for new questions. + + This will help teachers who repeatedly create questions and use the same values for the fields + (e.g. Default mark, Penalty for each incorrect try, etc.) in the question edit form. + === 3.8 === * There is a new method for question types get_extra_question_bank_actions.