Merge branch 'MDL-62487' of https://github.com/timhunt/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Mon, 18 May 2020 04:10:02 +0000 (12:10 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 18 May 2020 04:10:02 +0000 (12:10 +0800)
mod/quiz/report/grading/classes/privacy/provider.php
mod/quiz/report/grading/gradingsettings_form.php
mod/quiz/report/grading/lang/en/quiz_grading.php
mod/quiz/report/grading/report.php
mod/quiz/report/grading/tests/behat/grading.feature
mod/quiz/report/grading/tests/privacy_provider_test.php [new file with mode: 0644]
mod/quiz/tests/behat/behat_mod_quiz.php

index bc5cae8..96594c0 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Privacy Subsystem implementation for quiz_grading.
+ * Privacy subsystem implementation for quiz_grading.
  *
- * @package    quiz_grading
- * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   quiz_grading
+ * @copyright 2020 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 namespace quiz_grading\privacy;
 
+use core_privacy\local\metadata\collection;
+use core_privacy\local\request\writer;
+
 defined('MOODLE_INTERNAL') || die();
 
 /**
- * Privacy Subsystem for quiz_grading implementing null_provider.
- *
- * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * Privacy subsystem for quiz_grading.
  */
-class provider implements \core_privacy\local\metadata\null_provider {
+class provider implements
+        \core_privacy\local\metadata\provider,
+        \core_privacy\local\request\user_preference_provider {
+
+    /**
+     * Returns meta data about this system.
+     *
+     * @param   collection     $collection The initialised collection to add items to.
+     * @return  collection     A listing of user data stored through this system.
+     */
+    public static function get_metadata(collection $collection) : collection {
+        $collection->add_user_preference('quiz_grading_pagesize', 'privacy:preference:pagesize');
+        $collection->add_user_preference('quiz_grading_order', 'privacy:preference:order');
+
+        return $collection;
+    }
 
     /**
-     * Get the language string identifier with the component's language
-     * file to explain why this plugin stores no data.
+     * Export all user preferences for the plugin.
      *
-     * @return  string
+     * @param   int         $userid The userid of the user whose data is to be exported.
      */
-    public static function get_reason() : string {
-        return 'privacy:metadata';
+    public static function export_user_preferences(int $userid) {
+
+        // Page size.
+        $pagesize = get_user_preferences("quiz_grading_pagesize", null, $userid);
+        if ($pagesize !== null) {
+            writer::export_user_preference('quiz_grading', 'pagesize', $pagesize,
+                    get_string('privacy:preference:pagesize', 'quiz_grading'));
+        }
+
+        // Attempt order.
+        $order = get_user_preferences("quiz_grading_order", null, $userid);
+        if ($order !== null) {
+            switch ($order) {
+                case 'random':
+                    $order = get_string('randomly', 'quiz_grading');
+                    break;
+                case 'date':
+                    $order = get_string('bydate', 'quiz_grading');
+                    break;
+                case 'studentfirstname':
+                    $order = get_string('studentfirstname', 'quiz_grading');
+                    break;
+                case 'studentlastname':
+                    $order = get_string('studentlastname', 'quiz_grading');
+                    break;
+                case 'idnumber':
+                    $order = get_string('bystudentidnumber', 'quiz_grading');
+                    break;
+            }
+            writer::export_user_preference('quiz_grading', 'order', $order,
+                    get_string('privacy:preference:order', 'quiz_grading'));
+        }
     }
 }
index 84983ee..3bb8314 100644 (file)
@@ -48,7 +48,7 @@ class quiz_grading_settings_form extends moodleform {
         $this->counts = $counts;
         $this->shownames = $shownames;
         $this->showidnumbers = $showidnumbers;
-        parent::__construct($CFG->wwwroot . '/mod/quiz/report.php', null, 'get');
+        parent::__construct($CFG->wwwroot . '/mod/quiz/report.php');
     }
 
     protected function definition() {
index cd939b0..aa1224f 100644 (file)
@@ -67,7 +67,8 @@ $string['nothingfound'] = 'Nothing to display';
 $string['options'] = 'Options';
 $string['orderattempts'] = 'Order attempts';
 $string['pluginname'] = 'Manual grading';
-$string['privacy:metadata'] = 'The Quiz Manual grading plugin does not store any personal data. It provides an interface for users to store data without storing any data itself.';
+$string['privacy:preference:order'] = 'What order to show the attempts that need grading.';
+$string['privacy:preference:pagesize'] = 'How many attempts to show on each page of the grading interface.';
 $string['qno'] = 'Q #';
 $string['questionname'] = 'Question name';
 $string['questionsperpage'] = 'Questions per page';
index 39c0c40..88ef37e 100644 (file)
@@ -42,14 +42,32 @@ class quiz_grading_report extends quiz_default_report {
     const DEFAULT_PAGE_SIZE = 5;
     const DEFAULT_ORDER = 'random';
 
-    protected $viewoptions = array();
+    /** @var array URL parameters for what is being displayed when grading. */
+    protected $viewoptions = [];
+
+    /** @var int the current group, 0 if none, or NO_GROUPS_ALLOWED. */
+    protected $currentgroup;
+
+    /** @var array from quiz_report_get_significant_questions. */
     protected $questions;
+
+    /** @var stdClass the course settings. */
+    protected $course;
+
+    /** @var stdClass the course_module settings. */
     protected $cm;
+
+    /** @var stdClass the quiz settings. */
     protected $quiz;
+
+    /** @var context the quiz context. */
     protected $context;
 
-    /** @var renderer_base Renderer of Quiz Grading. */
-    private $renderer;
+    /** @var quiz_grading_renderer Renderer of Quiz Grading. */
+    protected $renderer;
+
+    /** @var string fragment of SQL code to restrict to the relevant users. */
+    protected $userssql;
 
     public function display($quiz, $cm, $course) {
 
@@ -66,11 +84,15 @@ class quiz_grading_report extends quiz_default_report {
         if (!in_array($grade, array('all', 'needsgrading', 'autograded', 'manuallygraded'))) {
             $grade = null;
         }
-        $pagesize = optional_param('pagesize', self::DEFAULT_PAGE_SIZE, PARAM_INT);
+        $pagesize = optional_param('pagesize',
+                get_user_preferences('quiz_grading_pagesize', self::DEFAULT_PAGE_SIZE),
+                PARAM_INT);
         $page = optional_param('page', 0, PARAM_INT);
-        $order = optional_param('order', self::DEFAULT_ORDER, PARAM_ALPHA);
+        $order = optional_param('order',
+                get_user_preferences('quiz_grading_order', self::DEFAULT_ORDER),
+                PARAM_ALPHA);
 
-        // Assemble the options requried to reload this page.
+        // Assemble the options required to reload this page.
         $optparams = array('includeauto', 'page');
         foreach ($optparams as $param) {
             if ($$param) {
@@ -85,7 +107,7 @@ class quiz_grading_report extends quiz_default_report {
         }
 
         // Check permissions.
-        $this->context = context_module::instance($cm->id);
+        $this->context = context_module::instance($this->cm->id);
         require_capability('mod/quiz:grade', $this->context);
         $shownames = has_capability('quiz/grading:viewstudentnames', $this->context);
         $showidnumbers = has_capability('quiz/grading:viewidnumber', $this->context);
@@ -124,40 +146,45 @@ class quiz_grading_report extends quiz_default_report {
                     array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), $this->currentgroup);
         }
 
-        $hasquestions = quiz_has_questions($quiz->id);
-        $counts = null;
-        if ($slot && $hasquestions) {
-            // Make sure there is something to do.
-            $statecounts = $this->get_question_state_summary(array($slot));
-            foreach ($statecounts as $record) {
-                if ($record->questionid == $questionid) {
-                    $counts = $record;
-                    break;
-                }
-            }
-            // If not, redirect back to the list.
-            if (!$counts || $counts->$grade == 0) {
-                redirect($this->list_questions_url(), get_string('alldoneredirecting', 'quiz_grading'));
-            }
+        $hasquestions = quiz_has_questions($this->quiz->id);
+        if (!$hasquestions) {
+            $this->print_header_and_tabs($cm, $course, $quiz, 'grading');
+            echo $this->renderer->render_quiz_no_question_notification($quiz, $cm, $this->context);
+            return true;
         }
 
-        // Start output.
-        $this->print_header_and_tabs($cm, $course, $quiz, 'grading');
+        if (!$slot) {
+            $this->display_index($includeauto);
+            return true;
+        }
 
-        // What sort of page to display?
-        if (!$hasquestions) {
-            echo $this->renderer->render_quiz_no_question_notification($quiz, $cm, $this->context);
+        // Display the grading UI for one question.
 
-        } else if (!$slot) {
-            echo $this->display_index($includeauto);
+        // Make sure there is something to do.
+        $counts = null;
+        $statecounts = $this->get_question_state_summary([$slot]);
+        foreach ($statecounts as $record) {
+            if ($record->questionid == $questionid) {
+                $counts = $record;
+                break;
+            }
+        }
 
-        } else {
-            echo $this->display_grading_interface($slot, $questionid, $grade,
-                    $pagesize, $page, $shownames, $showidnumbers, $order, $counts);
+        // If not, redirect back to the list.
+        if (!$counts || $counts->$grade == 0) {
+            redirect($this->list_questions_url(), get_string('alldoneredirecting', 'quiz_grading'));
         }
+
+        $this->display_grading_interface($slot, $questionid, $grade,
+                $pagesize, $page, $shownames, $showidnumbers, $order, $counts);
         return true;
     }
 
+    /**
+     * Get the JOIN conditions needed so we only show attempts by relevant users.
+     *
+     * @return qubaid_join
+     */
     protected function get_qubaids_condition() {
 
         $where = "quiza.quiz = :mangrquizid AND
@@ -183,6 +210,12 @@ class quiz_grading_report extends quiz_default_report {
         return new qubaid_join("{quiz_attempts} quiza $usersjoin ", 'quiza.uniqueid', $where, $params);
     }
 
+    /**
+     * Load the quiz_attempts rows corresponding to a list of question_usage ids.
+     *
+     * @param int[] $qubaids the question_usage ids of the quiz_attempts to load.
+     * @return array quiz attempts, with added user name fields.
+     */
     protected function load_attempts_by_usage_ids($qubaids) {
         global $DB;
 
@@ -208,20 +241,20 @@ class quiz_grading_report extends quiz_default_report {
 
     /**
      * Get the URL of the front page of the report that lists all the questions.
-     * @param $includeauto if not given, use the current setting, otherwise,
-     *      force a paricular value of includeauto in the URL.
-     * @return string the URL.
+     *
+     * @return moodle_url the URL.
      */
     protected function base_url() {
         return new moodle_url('/mod/quiz/report.php',
-                array('id' => $this->cm->id, 'mode' => 'grading'));
+                ['id' => $this->cm->id, 'mode' => 'grading']);
     }
 
     /**
      * Get the URL of the front page of the report that lists all the questions.
-     * @param $includeauto if not given, use the current setting, otherwise,
-     *      force a paricular value of includeauto in the URL.
-     * @return string the URL.
+     *
+     * @param bool $includeauto if not given, use the current setting, otherwise,
+     *      force a particular value of includeauto in the URL.
+     * @return moodle_url the URL.
      */
     protected function list_questions_url($includeauto = null) {
         $url = $this->base_url();
@@ -236,18 +269,20 @@ class quiz_grading_report extends quiz_default_report {
     }
 
     /**
+     * Get the URL to grade a batch of question attempts.
+     *
      * @param int $slot
      * @param int $questionid
      * @param string $grade
-     * @param mixed $page = true, link to current page. false = omit page.
+     * @param int|bool $page = true, link to current page. false = omit page.
      *      number = link to specific page.
+     * @return moodle_url
      */
     protected function grade_question_url($slot, $questionid, $grade, $page = true) {
         $url = $this->base_url();
-        $url->params(array('slot' => $slot, 'qid' => $questionid, 'grade' => $grade));
+        $url->params(['slot' => $slot, 'qid' => $questionid, 'grade' => $grade]);
         $url->params($this->viewoptions);
 
-        $options = $this->viewoptions;
         if (!$page) {
             $url->remove_params('page');
         } else if (is_integer($page)) {
@@ -257,6 +292,14 @@ class quiz_grading_report extends quiz_default_report {
         return $url;
     }
 
+    /**
+     * Renders the contents of one cell of the table on the index view.
+     *
+     * @param stdClass $counts counts of different types of attempt for this slot.
+     * @param string $type the type of count to format.
+     * @param string $gradestring get_string identifier for the grading link text, if required.
+     * @return string HTML.
+     */
     protected function format_count_for_table($counts, $type, $gradestring) {
         $result = $counts->$type;
         if ($counts->$type > 0) {
@@ -266,9 +309,15 @@ class quiz_grading_report extends quiz_default_report {
         return $result;
     }
 
+    /**
+     * Display the report front page which summarises the number of attempts to grade.
+     *
+     * @param bool $includeauto whether to show automatically-graded questions.
+     */
     protected function display_index($includeauto) {
         global $PAGE;
-        $output = '';
+
+        $this->print_header_and_tabs($this->cm, $this->course, $this->quiz, 'grading');
 
         if ($groupmode = groups_get_activity_groupmode($this->cm)) {
             // Groups is being used.
@@ -280,8 +329,8 @@ class quiz_grading_report extends quiz_default_report {
         } else {
             $linktext = get_string('alsoshowautomaticallygraded', 'quiz_grading');
         }
-        $output .= $this->renderer->render_display_index_heading($linktext, $this->list_questions_url(!$includeauto));
-        $data = array();
+        echo $this->renderer->render_display_index_heading($linktext, $this->list_questions_url(!$includeauto));
+        $data = [];
         $header = [];
 
         $header[] = get_string('qno', 'quiz_grading');
@@ -302,7 +351,7 @@ class quiz_grading_report extends quiz_default_report {
                 continue;
             }
 
-            $row = array();
+            $row = [];
 
             $row[] = $this->questions[$counts->slot]->number;
 
@@ -322,30 +371,37 @@ class quiz_grading_report extends quiz_default_report {
 
             $data[] = $row;
         }
-        $output .= $this->renderer->render_questions_table($includeauto, $data, $header);
-        return $output;
+        echo $this->renderer->render_questions_table($includeauto, $data, $header);
     }
 
+    /**
+     * Display the UI for grading attempts at one question.
+     *
+     * @param int $slot identifies which question to grade.
+     * @param int $questionid identifies which question to grade.
+     * @param string $grade type of attempts to grade.
+     * @param int $pagesize number of questions to show per page.
+     * @param int $page current page number.
+     * @param bool $shownames whether student names should be shown.
+     * @param bool $showidnumbers wither student idnumbers should be shown.
+     * @param string $order preferred order of attempts.
+     * @param stdClass $counts object that stores the number of each type of attempt.
+     */
     protected function display_grading_interface($slot, $questionid, $grade,
             $pagesize, $page, $shownames, $showidnumbers, $order, $counts) {
-        $output = '';
 
         if ($pagesize * $page >= $counts->$grade) {
             $page = 0;
         }
 
-        list($qubaids, $count) = $this->get_usage_ids_where_question_in_state(
-                $grade, $slot, $questionid, $order, $page, $pagesize);
-        $attempts = $this->load_attempts_by_usage_ids($qubaids);
-
-        // Prepare the form.
-        $hidden = array(
+        // Prepare the options form.
+        $hidden = [
             'id' => $this->cm->id,
             'mode' => 'grading',
             'slot' => $slot,
             'qid' => $questionid,
             'page' => $page,
-        );
+        ];
         if (array_key_exists('includeauto', $this->viewoptions)) {
             $hidden['includeauto'] = $this->viewoptions['includeauto'];
         }
@@ -358,6 +414,18 @@ class quiz_grading_report extends quiz_default_report {
         $settings->order = $order;
         $mform->set_data($settings);
 
+        // If the form was submitted, save the user preferences, and
+        // redirect to a cleaned-up GET URL.
+        if ($mform->get_data()) {
+            set_user_preference('quiz_grading_pagesize', $pagesize);
+            set_user_preference('quiz_grading_order', $order);
+            redirect($this->grade_question_url($slot, $questionid, $grade, $page));
+        }
+
+        list($qubaids, $count) = $this->get_usage_ids_where_question_in_state(
+                $grade, $slot, $questionid, $order, $page, $pagesize);
+        $attempts = $this->load_attempts_by_usage_ids($qubaids);
+
         // Question info.
         $questioninfo = new stdClass();
         $questioninfo->number = $this->questions[$slot]->number;
@@ -370,6 +438,8 @@ class quiz_grading_report extends quiz_default_report {
         $paginginfo->of = $count;
         $qubaidlist = implode(',', $qubaids);
 
+        $this->print_header_and_tabs($this->cm, $this->course, $this->quiz, 'grading');
+
         $gradequestioncontent = '';
         foreach ($qubaids as $qubaid) {
             $attempt = $attempts[$qubaid];
@@ -403,7 +473,7 @@ class quiz_grading_report extends quiz_default_report {
                 'sesskey' => sesskey()
         ];
 
-        $output .= $this->renderer->render_grading_interface(
+        echo $this->renderer->render_grading_interface(
                 $questioninfo,
                 $this->list_questions_url(),
                 $mform,
@@ -413,9 +483,13 @@ class quiz_grading_report extends quiz_default_report {
                 $hiddeninputs,
                 $gradequestioncontent
         );
-        return $output;
     }
 
+    /**
+     * When saving a grading page, are all the submitted marks valid?
+     *
+     * @return bool true if all valid, else false.
+     */
     protected function validate_submitted_marks() {
 
         $qubaids = optional_param('qubaids', null, PARAM_SEQUENCE);
@@ -426,7 +500,7 @@ class quiz_grading_report extends quiz_default_report {
 
         $slots = optional_param('slots', '', PARAM_SEQUENCE);
         if (!$slots) {
-            $slots = array();
+            $slots = [];
         } else {
             $slots = explode(',', $slots);
         }
@@ -442,6 +516,9 @@ class quiz_grading_report extends quiz_default_report {
         return true;
     }
 
+    /**
+     * Save all submitted marks to the database.
+     */
     protected function process_submitted_data() {
         global $DB;
 
@@ -454,7 +531,7 @@ class quiz_grading_report extends quiz_default_report {
 
         $qubaids = clean_param_array(explode(',', $qubaids), PARAM_INT);
         $attempts = $this->load_attempts_by_usage_ids($qubaids);
-        $events = array();
+        $events = [];
 
         $transaction = $DB->start_delegated_transaction();
         foreach ($qubaids as $qubaid) {
@@ -463,16 +540,16 @@ class quiz_grading_report extends quiz_default_report {
             $attemptobj->process_submitted_actions(time());
 
             // Add the event we will trigger later.
-            $params = array(
+            $params = [
                 'objectid' => $attemptobj->get_question_attempt($assumedslotforevents)->get_question()->id,
                 'courseid' => $attemptobj->get_courseid(),
                 'context' => context_module::instance($attemptobj->get_cmid()),
-                'other' => array(
+                'other' => [
                     'quizid' => $attemptobj->get_quizid(),
                     'attemptid' => $attemptobj->get_attemptid(),
-                    'slot' => $assumedslotforevents
-                )
-            );
+                    'slot' => $assumedslotforevents,
+                ],
+            ];
             $events[] = \mod_quiz\event\question_manually_graded::create($params);
         }
         $transaction->allow_commit();
@@ -516,10 +593,10 @@ class quiz_grading_report extends quiz_default_report {
      * @param int $page implements paging of the results.
      *      Ignored if $orderby = random or $pagesize is null.
      * @param int $pagesize implements paging of the results. null = all.
+     * @return array with two elements, an array of usage ids, and a count of the total number.
      */
     protected function get_usage_ids_where_question_in_state($summarystate, $slot,
             $questionid = null, $orderby = 'random', $page = 0, $pagesize = null) {
-        global $CFG, $DB;
         $dm = new question_engine_data_mapper();
 
         if ($pagesize && $orderby != 'random') {
@@ -530,7 +607,7 @@ class quiz_grading_report extends quiz_default_report {
 
         $qubaids = $this->get_qubaids_condition();
 
-        $params = array();
+        $params = [];
         if ($orderby == 'date') {
             list($statetest, $params) = $dm->in_summary_state_test(
                     'manuallygraded', false, 'mangrstate');
@@ -543,7 +620,7 @@ class quiz_grading_report extends quiz_default_report {
         } else if ($orderby == 'studentfirstname' || $orderby == 'studentlastname' || $orderby == 'idnumber') {
             $qubaids->from .= " JOIN {user} u ON quiza.userid = u.id ";
             // For name sorting, map orderby form value to
-            // actual column names; 'idnumber' maps naturally
+            // actual column names; 'idnumber' maps naturally.
             switch ($orderby) {
                 case "studentlastname":
                     $orderby = "u.lastname, u.firstname";
index 253f70a..2a308a1 100644 (file)
@@ -4,8 +4,7 @@ Feature: Basic use of the Manual grading report
   As a teacher
   I need to use the manual grading report
 
-  @javascript
-  Scenario: Use the Manual grading report
+  Background:
     Given the following "users" exist:
       | username | firstname | lastname | email                | idnumber |
       | teacher1 | T1        | Teacher1 | teacher1@example.com | T1000    |
@@ -30,10 +29,10 @@ Feature: Basic use of the Manual grading report
       | question          | page |
       | Short answer 001  | 1    |
 
+  Scenario: Use the Manual grading report
+
     # Check report shows nothing when there are no attempts.
-    When I log in as "teacher1"
-    And I am on "Course 1" course homepage
-    And I follow "Quiz 1"
+    When I am on the "Quiz 1" "mod_quiz > View" page logged in as "teacher1"
     And I navigate to "Results > Manual grading" in current page administration
     Then I should see "Manual grading"
     And I should see "Quiz 1"
@@ -44,7 +43,7 @@ Feature: Basic use of the Manual grading report
     # Use the manual grading report.
     And user "student1" has attempted "Quiz 1" with responses:
       | slot | response |
-      |   1  | Paris    |
+      | 1    | Paris    |
     And I reload the page
     And I should see "Short answer 001"
     And "Short answer 001" row "To grade" column of "questionstograde" table should contain "0"
@@ -65,3 +64,22 @@ Feature: Basic use of the Manual grading report
     And I should see "All selected attempts have been graded. Returning to the list of questions."
     And "Short answer 001" row "To grade" column of "questionstograde" table should contain "0"
     And "Short answer 001" row "Already graded" column of "questionstograde" table should contain "1"
+
+  Scenario: Manual grading settings are remembered as user preferences
+    Given user "student1" has attempted "Quiz 1" with responses:
+      | slot | response |
+      | 1    | Paris    |
+    When I am on the "Quiz 1" "mod_quiz > Manual grading report" page logged in as "teacher1"
+    And I follow "Also show questions that have been graded automatically"
+    And I click on "update grades" "link" in the "Short answer 001" "table_row"
+    And I set the following fields to these values:
+      | Questions per page | 42      |
+      | Order attempts     | By date |
+    And I press "Change options"
+    And I log out
+    And I am on the "Quiz 1" "mod_quiz > Manual grading report" page logged in as "teacher1"
+    And I follow "Also show questions that have been graded automatically"
+    And I click on "update grades" "link" in the "Short answer 001" "table_row"
+    Then the following fields match these values:
+      | Questions per page | 42      |
+      | Order attempts     | By date |
diff --git a/mod/quiz/report/grading/tests/privacy_provider_test.php b/mod/quiz/report/grading/tests/privacy_provider_test.php
new file mode 100644 (file)
index 0000000..96c3f5e
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy provider tests.
+ *
+ * @package   quiz_grading
+ * @copyright 2020 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use core_privacy\local\metadata\collection;
+use quiz_grading\privacy\provider;
+use core_privacy\local\request\writer;
+use core_privacy\local\request\transform;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/question/engine/questionattempt.php');
+
+/**
+ * Privacy provider tests class.
+ */
+class quiz_grading_privacy_provider_testcase extends \core_privacy\tests\provider_testcase {
+    /**
+     * When no preference exists, there should be no export.
+     */
+    public function test_preference_unset() {
+        global $USER;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        provider::export_user_preferences($USER->id);
+
+        $this->assertFalse(writer::with_context(\context_system::instance())->has_any_data());
+    }
+
+    /**
+     * Preference does exist.
+     */
+    public function test_preference_bool_true() {
+        global $USER;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        set_user_preference('quiz_grading_pagesize', 42);
+        set_user_preference('quiz_grading_order', 'random');
+
+        provider::export_user_preferences($USER->id);
+
+        $writer = writer::with_context(\context_system::instance());
+        $this->assertTrue($writer->has_any_data());
+
+        $preferences = $writer->get_user_preferences('quiz_grading');
+
+        $this->assertNotEmpty($preferences->pagesize);
+        $this->assertEquals(42, $preferences->pagesize->value);
+
+        $this->assertNotEmpty($preferences->order);
+        $this->assertEquals('Randomly', $preferences->order->value);
+    }
+}
index 563ffbe..5db5ba7 100644 (file)
@@ -108,6 +108,10 @@ class behat_mod_quiz extends behat_question_base {
                 return new moodle_url('/mod/quiz/report.php',
                     ['id' => $this->get_cm_by_quiz_name($identifier)->id, 'mode' => 'statistics']);
 
+            case 'Manual grading report':
+                return new moodle_url('/mod/quiz/report.php',
+                        ['id' => $this->get_cm_by_quiz_name($identifier)->id, 'mode' => 'grading']);
+
             case 'Attempt review':
                 if (substr_count($identifier, ' > ') !== 2) {
                     throw new coding_exception('For "attempt review", name must be ' .