diff --git a/question/type/ordering/classes/privacy/provider.php b/question/type/ordering/classes/privacy/provider.php index f95d77022d7..1e0e1a55eef 100644 --- a/question/type/ordering/classes/privacy/provider.php +++ b/question/type/ordering/classes/privacy/provider.php @@ -16,6 +16,9 @@ namespace qtype_ordering\privacy; +use core_privacy\local\metadata\collection; +use core_privacy\local\request\writer; + /** * Privacy Subsystem for qtype_numerical implementing null_provider. * @@ -23,10 +26,89 @@ namespace qtype_ordering\privacy; * @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com) * @author rdebleu@eWallah.net * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @codeCoverageIgnore A null provider so no special handling for us. */ -class provider implements \core_privacy\local\metadata\null_provider { - public static function get_reason(): string { - return 'privacy:metadata'; +class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\user_preference_provider { + /** + * Returns metadata about this question. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this question. + */ + public static function get_metadata(collection $collection): collection { + $collection->add_user_preference('qtype_ordering_layouttype', 'privacy:preference:layouttype'); + $collection->add_user_preference('qtype_ordering_selecttype', 'privacy:preference:selecttype'); + $collection->add_user_preference('qtype_ordering_selectcount', 'privacy:preference:selectcount'); + $collection->add_user_preference('qtype_ordering_gradingtype', 'privacy:preference:gradingtype'); + $collection->add_user_preference('qtype_ordering_showgrading', 'privacy:preference:showgrading'); + $collection->add_user_preference('qtype_ordering_numberingstyle', 'privacy:preference:numberingstyle'); + return $collection; + } + + /** + * Export all user preferences for the question. + * + * @param int $userid The userid of the user whose data is to be exported. + * @return void + */ + public static function export_user_preferences(int $userid): void { + $preference = get_user_preferences('qtype_ordering_layouttype', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'layouttype', + $preference, + get_string('privacy:preference:layouttype', 'qtype_ordering') + ); + } + + $preference = get_user_preferences('qtype_ordering_selecttype', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'selecttype', + $preference, + get_string('privacy:preference:selecttype', 'qtype_ordering') + ); + } + + $preference = get_user_preferences('qtype_ordering_selectcount', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'selectcount', + $preference, + get_string('privacy:preference:selectcount', 'qtype_ordering') + ); + } + + $preference = get_user_preferences('qtype_ordering_gradingtype', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'gradingtype', + $preference, + get_string('privacy:preference:gradingtype', 'qtype_ordering') + ); + } + + $preference = get_user_preferences('qtype_ordering_showgrading', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'showgrading', + $preference, + get_string('privacy:preference:showgrading', 'qtype_ordering') + ); + } + + $preference = get_user_preferences('qtype_ordering_numberingstyle', null, $userid); + if (null !== $preference) { + writer::export_user_preference( + 'qtype_ordering', + 'numberingstyle', + $preference, + get_string('privacy:preference:numberingstyle', 'qtype_ordering') + ); + } } } diff --git a/question/type/ordering/edit_ordering_form.php b/question/type/ordering/edit_ordering_form.php index 6a9c94b6b36..a469cc0a523 100644 --- a/question/type/ordering/edit_ordering_form.php +++ b/question/type/ordering/edit_ordering_form.php @@ -58,77 +58,66 @@ class qtype_ordering_edit_form extends question_edit_form { } public function definition_inner($mform): void { - - // Cache this plugins name. - $plugin = 'qtype_ordering'; - // Field for layouttype. - $name = 'layouttype'; - $label = get_string($name, $plugin); $options = qtype_ordering_question::get_layout_types(); - $mform->addElement('select', $name, $label, $options); - $mform->addHelpButton($name, $name, $plugin); - $mform->setDefault($name, $this->get_default_value($name, qtype_ordering_question::LAYOUT_VERTICAL)); + $mform->addElement('select', 'layouttype', get_string('layouttype', 'qtype_ordering'), $options); + $mform->addHelpButton('layouttype', 'layouttype', 'qtype_ordering'); + $mform->setDefault('layouttype', $this->get_default_value('layouttype', qtype_ordering_question::LAYOUT_VERTICAL)); // Field for selecttype. - $name = 'selecttype'; - $label = get_string($name, $plugin); $options = qtype_ordering_question::get_select_types(); - $mform->addElement('select', $name, $label, $options); - $mform->addHelpButton($name, $name, $plugin); - $mform->setDefault($name, $this->get_default_value($name, qtype_ordering_question::SELECT_ALL)); + $mform->addElement('select', 'selecttype', get_string('selecttype', 'qtype_ordering'), $options); + $mform->addHelpButton('selecttype', 'selecttype', 'qtype_ordering'); + $mform->setDefault('selecttype', $this->get_default_value('selecttype', qtype_ordering_question::SELECT_ALL)); // Field for selectcount. - $name = 'selectcount'; - $label = get_string($name, $plugin); - $mform->addElement('text', $name, $label, ['size' => 2]); - $mform->setDefault($name, qtype_ordering_question::MIN_SUBSET_ITEMS); - $mform->setType($name, PARAM_INT); + $mform->addElement('text', 'selectcount', get_string('selectcount', 'qtype_ordering'), ['size' => 2]); + $mform->setDefault('selectcount', qtype_ordering_question::MIN_SUBSET_ITEMS); + $mform->setType('selectcount', PARAM_INT); // Hide the field if 'Item selection type' is set to select all items. - $mform->hideIf($name, 'selecttype', 'eq', qtype_ordering_question::SELECT_ALL); - $mform->addHelpButton($name, $name, $plugin); - $mform->addRule($name, null, 'numeric', null, 'client'); + $mform->hideIf('selectcount', 'selecttype', 'eq', qtype_ordering_question::SELECT_ALL); + $mform->addHelpButton('selectcount', 'selectcount', 'qtype_ordering'); + $mform->addRule('selectcount', null, 'numeric', null, 'client'); // Field for gradingtype. - $name = 'gradingtype'; - $label = get_string($name, $plugin); $options = qtype_ordering_question::get_grading_types(); - $mform->addElement('select', $name, $label, $options); - $mform->addHelpButton($name, $name, $plugin); - $mform->setDefault($name, $this->get_default_value($name, qtype_ordering_question::GRADING_ABSOLUTE_POSITION)); + $mform->addElement('select', 'gradingtype', get_string('gradingtype', 'qtype_ordering'), $options); + $mform->addHelpButton('gradingtype', 'gradingtype', 'qtype_ordering'); + $mform->setDefault( + 'gradingtype', + $this->get_default_value('gradingtype', qtype_ordering_question::GRADING_ABSOLUTE_POSITION) + ); // Field for showgrading. - $name = 'showgrading'; - $label = get_string($name, $plugin); $options = [0 => get_string('hide'), 1 => get_string('show')]; - $mform->addElement('select', $name, $label, $options); - $mform->addHelpButton($name, $name, $plugin); - $mform->setDefault($name, $this->get_default_value($name, 1)); + $mform->addElement('select', 'showgrading', get_string('showgrading', 'qtype_ordering'), $options); + $mform->addHelpButton('showgrading', 'showgrading', 'qtype_ordering'); + $mform->setDefault('showgrading', $this->get_default_value('showgrading', 1)); // Field for numberingstyle. - $name = 'numberingstyle'; - $label = get_string($name, $plugin); $options = qtype_ordering_question::get_numbering_styles(); - $mform->addElement('select', $name, $label, $options); - $mform->addHelpButton($name, $name, $plugin); - $mform->setDefault($name, $this->get_default_value($name, qtype_ordering_question::NUMBERING_STYLE_DEFAULT)); + $mform->addElement('select', 'numberingstyle', get_string('numberingstyle', 'qtype_ordering'), $options); + $mform->addHelpButton('numberingstyle', 'numberingstyle', 'qtype_ordering'); + $mform->setDefault( + 'numberingstyle', + $this->get_default_value('numberingstyle', qtype_ordering_question::NUMBERING_STYLE_DEFAULT) + ); - $mform->addElement('header', 'answersheader', get_string('draggableitems', $plugin)); + $mform->addElement('header', 'answersheader', get_string('draggableitems', 'qtype_ordering')); $mform->setExpanded('answersheader', true); // Field for the answers. $elements = []; $options = []; - $name = 'answer'; - $elements[] = $mform->createElement('editor', $name, get_string('draggableitemno', $plugin), + $elements[] = $mform->createElement('editor', 'answer', get_string('draggableitemno', 'qtype_ordering'), $this->get_editor_attributes(), $this->get_editor_options()); - $elements[] = $mform->createElement('submit', $name . 'removeeditor', get_string('removeeditor', $plugin), + $elements[] = $mform->createElement('submit', 'answer' . 'removeeditor', get_string('removeeditor', 'qtype_ordering'), ['onclick' => 'skipClientValidation = true;']); - $options[$name] = ['type' => PARAM_RAW]; - $this->add_repeat_elements($mform, $name, $elements, $options); + $options['answer'] = ['type' => PARAM_RAW]; + $this->add_repeat_elements($mform, 'answer', $elements, $options); // Adjust HTML editor and removal buttons. - $this->adjust_html_editors($mform, $name); + $this->adjust_html_editors($mform, 'answer'); // Adding feedback fields (=Combined feedback). $this->add_combined_feedback_fields(true); @@ -370,12 +359,11 @@ class qtype_ordering_edit_form extends question_edit_form { public function validation($data, $files): array { $errors = []; - $plugin = 'qtype_ordering'; $minsubsetitems = qtype_ordering_question::MIN_SUBSET_ITEMS; // Make sure the entered size of the subset is no less than the defined minimum. if ($data['selecttype'] != qtype_ordering_question::SELECT_ALL && $data['selectcount'] < $minsubsetitems) { - $errors['selectcount'] = get_string('notenoughsubsetitems', $plugin, $minsubsetitems); + $errors['selectcount'] = get_string('notenoughsubsetitems', 'qtype_ordering', $minsubsetitems); } // Identify duplicates and report as an error. @@ -388,11 +376,11 @@ class qtype_ordering_edit_form extends question_edit_form { if ($answer = trim($answer)) { if (in_array($answer, $answers)) { $i = array_search($answer, $answers); - $item = get_string('draggableitemno', $plugin); + $item = get_string('draggableitemno', 'qtype_ordering'); $item = str_replace('{no}', $i + 1, $item); $item = html_writer::link("#id_answer_$i", $item); $a = (object) ['text' => $answer, 'item' => $item]; - $errors["answer[$answercount]"] = get_string('duplicatesnotallowed', $plugin, $a); + $errors["answer[$answercount]"] = get_string('duplicatesnotallowed', 'qtype_ordering', $a); } else { $answers[] = $answer; } @@ -403,10 +391,10 @@ class qtype_ordering_edit_form extends question_edit_form { // If there are no answers provided, show error message under first 2 answer boxes // If only 1 answer provided, show error message under second answer box. if ($answercount < 2) { - $errors['answer[1]'] = get_string('notenoughanswers', $plugin, 2); + $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_ordering', 2); if ($answercount == 0) { - $errors['answer[0]'] = get_string('notenoughanswers', $plugin, 2); + $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_ordering', 2); } } @@ -434,17 +422,13 @@ class qtype_ordering_edit_form extends question_edit_form { * @return array (type => description) */ protected function get_addcount_options(string $type, int $max = 10): array { - - // Cache plugin name. - $plugin = $this->plugin_name(); - // Generate options. $options = []; for ($i = 1; $i <= $max; $i++) { if ($i == 1) { - $options[$i] = get_string('addsingle'.$type, $plugin); + $options[$i] = get_string('addsingle'.$type, 'qtype_ordering'); } else { - $options[$i] = get_string('addmultiple'.$type.'s', $plugin, $i); + $options[$i] = get_string('addmultiple'.$type.'s', 'qtype_ordering', $i); } } return $options; @@ -461,9 +445,6 @@ class qtype_ordering_edit_form extends question_edit_form { */ protected function add_repeat_elements(MoodleQuickForm $mform, string $type, array $elements, array $options): void { - // Cache plugin name. - $plugin = $this->plugin_name(); - // Cache element names. $types = $type.'s'; $addtypes = 'add'.$types; @@ -476,7 +457,7 @@ class qtype_ordering_edit_form extends question_edit_form { $count = optional_param($addtypescount, self::NUM_ITEMS_ADD, PARAM_INT); $label = ($count == 1 ? 'addsingle'.$type : 'addmultiple'.$types); - $label = get_string($label, $plugin, $count); + $label = get_string($label, 'qtype_ordering', $count); $this->repeat_elements($elements, $repeats, $options, $counttypes, $addtypes, $count, $label, true); diff --git a/question/type/ordering/lang/en/qtype_ordering.php b/question/type/ordering/lang/en/qtype_ordering.php index b7ed6806547..4a401a06825 100644 --- a/question/type/ordering/lang/en/qtype_ordering.php +++ b/question/type/ordering/lang/en/qtype_ordering.php @@ -99,7 +99,12 @@ $string['pluginname_link'] = 'question/type/ordering'; $string['pluginnameadding'] = 'Adding an Ordering question'; $string['pluginnameediting'] = 'Editing an Ordering question'; $string['pluginnamesummary'] = 'Put jumbled items into a meaningful order.'; -$string['privacy:metadata'] = 'The ordering question type plugin does not store any personal data.'; +$string['privacy:preference:layouttype'] = 'layouttype.'; +$string['privacy:preference:selecttype'] = 'selecttype.'; +$string['privacy:preference:selectcount'] = 'selectcount.'; +$string['privacy:preference:gradingtype'] = 'gradingtype.'; +$string['privacy:preference:showgrading'] = 'showgrading.'; +$string['privacy:preference:numberingstyle'] = 'numberingstyle.'; $string['regradeissuenumitemschanged'] = 'The number of draggable items has changed.'; $string['relativeallpreviousandnext'] = 'Relative to ALL the previous and next items'; diff --git a/question/type/ordering/questiontype.php b/question/type/ordering/questiontype.php index 51838cac778..6777d4d50dc 100644 --- a/question/type/ordering/questiontype.php +++ b/question/type/ordering/questiontype.php @@ -28,12 +28,6 @@ class qtype_ordering extends question_type { /** @var int Number of hints default. */ const DEFAULT_NUM_HINTS = 2; - /** @var array Combined feedback fields */ - public array $feedbackfields = ['correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback']; - - /** - * @CodeCoverageIgnore - */ public function has_html_answers(): bool { return true; } @@ -59,6 +53,16 @@ class qtype_ordering extends question_type { $this->initialise_combined_feedback($question, $questiondata, true); } + public function save_defaults_for_new_questions(stdClass $fromform): void { + parent::save_defaults_for_new_questions($fromform); + $this->set_default_value('layouttype', $fromform->layouttype); + $this->set_default_value('selecttype', $fromform->selecttype); + $this->set_default_value('selectcount', $fromform->selectcount); + $this->set_default_value('gradingtype', $fromform->gradingtype); + $this->set_default_value('showgrading', $fromform->showgrading); + $this->set_default_value('numberingstyle', $fromform->numberingstyle); + } + public function save_question_options($question): bool|stdClass { global $DB; @@ -440,25 +444,9 @@ class qtype_ordering extends question_type { $question->feedbackformat[$i] = FORMAT_MOODLE; } - // Check that the required feedback fields exist. - $this->check_ordering_combined_feedback($question); - return $question; } - /** - * Check that the required feedback fields exist - * - * @param stdClass $question - */ - protected function check_ordering_combined_feedback(stdClass $question): void { - foreach ($this->feedbackfields as $field) { - if (empty($question->$field)) { - $question->$field = ['text' => '', 'format' => FORMAT_MOODLE, 'itemid' => 0, 'files' => null]; - } - } - } - /** * Given question object, returns array with array layouttype, selecttype, selectcount, gradingtype, showgrading * where layouttype, selecttype, gradingtype and showgrading are string representations. @@ -637,8 +625,6 @@ class qtype_ordering extends question_type { $format->import_combined_feedback($newquestion, $data); $newquestion->shownumcorrect = $format->getpath($data, ['#', 'shownumcorrect', 0, '#'], null); - // Check that the required feedback fields exist. - $this->check_ordering_combined_feedback($newquestion); $format->import_hints($newquestion, $data, true, true); diff --git a/question/type/ordering/tests/privacy/provider_test.php b/question/type/ordering/tests/privacy/provider_test.php new file mode 100644 index 00000000000..38f67c1682a --- /dev/null +++ b/question/type/ordering/tests/privacy/provider_test.php @@ -0,0 +1,112 @@ +. + +namespace qtype_ordering\privacy; + +use core_privacy\local\request\user_preference_provider; +use core_privacy\local\request\writer; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/question/type/ordering/classes/privacy/provider.php'); + +/** + * Privacy provider tests. + * + * @package qtype_ordering + * @copyright 2024 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\privacy\provider + */ +final class provider_test extends \core_privacy\tests\provider_testcase { + public function test_get_metadata(): void { + $collection = new \core_privacy\local\metadata\collection('qtype_ordering'); + $actual = \qtype_ordering\privacy\provider::get_metadata($collection); + $this->assertEquals($collection, $actual); + } + + public function test_export_user_preferences_no_pref(): void { + $this->resetAfterTest(); + + $user = $this->getDataGenerator()->create_user(); + provider::export_user_preferences($user->id); + $writer = writer::with_context(\context_system::instance()); + $this->assertFalse($writer->has_any_data()); + } + + /** + * Test the export_user_preferences given different inputs + * @dataProvider user_preference_provider + + * @param string $name The name of the user preference to get/set + * @param string $value The value stored in the database + * @param string $expected The expected transformed value + */ + public function test_export_user_preferences($name, $value, $expected): void { + $this->resetAfterTest(); + $user = $this->getDataGenerator()->create_user(); + set_user_preference("qtype_ordering_$name", $value, $user); + provider::export_user_preferences($user->id); + $writer = writer::with_context(\context_system::instance()); + $this->assertTrue($writer->has_any_data()); + $preferences = $writer->get_user_preferences('qtype_ordering'); + foreach ($preferences as $key => $pref) { + $preference = get_user_preferences("qtype_ordering_$key", null, $user->id); + if ($preference === null) { + continue; + } + $desc = get_string("privacy:preference:{$key}", 'qtype_ordering'); + $this->assertEquals($expected, $pref->value); + $this->assertEquals($desc, $pref->description); + } + } + + /** + * Create an array of valid user preferences for the ordering question type. + * + * @return array Array of valid user preferences. + */ + public static function user_preference_provider(): array { + return [ + 'layouttype H' => ['layouttype', 0, 0], + 'layouttype V' => ['layouttype', 1, 1], + 'selecttype ALL' => ['selecttype', 0, 0], + 'selecttype RAND' => ['selecttype', 1, 1], + 'selecttype CONT' => ['selecttype', 2, 2], + 'selectcount 1' => ['selectcount', 1, 1], + 'selectcount 3' => ['selectcount', 3, 3], + 'selectcount 5' => ['selectcount', 5, 5], + 'gradingtype ALL' => ['gradingtype', -1, -1], + 'gradingtype ABS' => ['gradingtype', 0, 0], + 'gradingtype REL' => ['gradingtype', 7, 7], + 'gradingtype REL NEXT EXC' => ['gradingtype', 1, 1], + 'gradingtype REL NEXT INC' => ['gradingtype', 2, 2], + 'gradingtype REL BOTH' => ['gradingtype', 3, 3], + 'gradingtype REL ALL' => ['gradingtype', 4, 4], + 'gradingtype LONG ORDER' => ['gradingtype', 5, 5], + 'gradingtype LONG CONT' => ['gradingtype', 6, 6], + 'showgrading F' => ['showgrading', 0, 0], + 'showgrading T' => ['showgrading', 1, 1], + 'numberingstyle NONE' => ['numberingstyle', 'none', 'none'], + 'numberingstyle abc' => ['numberingstyle', 'abc', 'abc'], + 'numberingstyle ABC' => ['numberingstyle', 'ABC', 'ABC'], + 'numberingstyle 123' => ['numberingstyle', '123', '123'], + 'numberingstyle iii' => ['numberingstyle', 'iii', 'iii'], + 'numberingstyle IIII' => ['numberingstyle', 'IIII', 'IIII'], + ]; + } +}