Merge branch 'MDL-69735' of https://github.com/timhunt/moodle
authorJun Pataleta <jun@moodle.com>
Wed, 16 Dec 2020 15:01:28 +0000 (23:01 +0800)
committerJun Pataleta <jun@moodle.com>
Wed, 16 Dec 2020 15:01:28 +0000 (23:01 +0800)
lib/classes/output/notification.php
lib/outputrenderers.php
mod/quiz/db/access.php
mod/quiz/lang/en/deprecated.txt
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/overrides.php
mod/quiz/tests/behat/quiz_group_override.feature
mod/quiz/tests/behat/quiz_user_override.feature
mod/quiz/version.php

index 1db678b..023193d 100644 (file)
@@ -84,9 +84,10 @@ class notification implements \renderable, \templatable {
      * Notification constructor.
      *
      * @param string $message the message to print out
-     * @param string $messagetype one of the NOTIFY_* constants..
+     * @param ?string $messagetype one of the NOTIFY_* constants..
+     * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
      */
-    public function __construct($message, $messagetype = null) {
+    public function __construct($message, $messagetype = null, $closebutton = true) {
         $this->message = $message;
 
         if (empty($messagetype)) {
@@ -94,6 +95,8 @@ class notification implements \renderable, \templatable {
         }
 
         $this->messagetype = $messagetype;
+
+        $this->closebutton = $closebutton;
     }
 
     /**
index 6a64c28..6b61cab 100644 (file)
@@ -2875,10 +2875,11 @@ EOD;
      * Note: \core\notification::add() may be more suitable for your usage.
      *
      * @param string $message The message to print out.
-     * @param string $type    The type of notification. See constants on \core\output\notification.
+     * @param ?string $type   The type of notification. See constants on \core\output\notification.
+     * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
      * @return string the HTML to output.
      */
-    public function notification($message, $type = null) {
+    public function notification($message, $type = null, $closebutton = true) {
         $typemappings = [
             // Valid types.
             'success'           => \core\output\notification::NOTIFY_SUCCESS,
@@ -2922,7 +2923,7 @@ EOD;
             }
         }
 
-        $notification = new \core\output\notification($message, $type);
+        $notification = new \core\output\notification($message, $type, $closebutton);
         if (count($extraclasses)) {
             $notification->set_extra_classes($extraclasses);
         }
@@ -4886,9 +4887,10 @@ class core_renderer_cli extends core_renderer {
      *
      * @param string $message The message to print out.
      * @param string $type    The type of notification. See constants on \core\output\notification.
+     * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
      * @return string A template fragment for a notification
      */
-    public function notification($message, $type = null) {
+    public function notification($message, $type = null, $closebutton = true) {
         $message = clean_text($message);
         if ($type === 'notifysuccess' || $type === 'success') {
             return "++ $message ++\n";
@@ -4972,8 +4974,10 @@ class core_renderer_ajax extends core_renderer {
      *
      * @param string $message The message to print out.
      * @param string $type    The type of notification. See constants on \core\output\notification.
+     * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
      */
-    public function notification($message, $type = null) {}
+    public function notification($message, $type = null, $closebutton = true) {
+    }
 
     /**
      * Used to display a redirection message.
index ae31ddb..246641f 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$capabilities = array(
+$capabilities = [
 
     // Ability to see that the quiz exists, and the basic information
     // about it, for example the start date and time limit.
-    'mod/quiz:view' => array(
+    'mod/quiz:view' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'guest' => CAP_ALLOW,
             'student' => CAP_ALLOW,
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Ability to add a new quiz to the course.
-    'mod/quiz:addinstance' => array(
+    'mod/quiz:addinstance' => [
         'riskbitmask' => RISK_XSS,
 
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
-        'archetypes' => array(
+        'archetypes' => [
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        ),
+        ],
         'clonepermissionsfrom' => 'moodle/course:manageactivities'
-    ),
+    ],
 
     // Ability to do the quiz as a 'student'.
-    'mod/quiz:attempt' => array(
+    'mod/quiz:attempt' => [
         'riskbitmask' => RISK_SPAM,
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'student' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Ability for a 'Student' to review their previous attempts. Review by
     // 'Teachers' is controlled by mod/quiz:viewreports.
-    'mod/quiz:reviewmyattempts' => array(
+    'mod/quiz:reviewmyattempts' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'student' => CAP_ALLOW
-        ),
+        ],
         'clonepermissionsfrom' => 'moodle/quiz:attempt'
-    ),
+    ],
 
     // Edit the quiz settings, add and remove questions.
-    'mod/quiz:manage' => array(
+    'mod/quiz:manage' => [
         'riskbitmask' => RISK_SPAM,
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Edit the quiz overrides.
-    'mod/quiz:manageoverrides' => array(
+    'mod/quiz:manageoverrides' => [
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
+
+    // View the quiz overrides (only checked for users who don't have mod/quiz:manageoverrides.
+    'mod/quiz:viewoverrides' => [
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => [
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        ]
+    ],
 
     // Preview the quiz.
-    'mod/quiz:preview' => array(
+    'mod/quiz:preview' => [
         'captype' => 'write', // Only just a write.
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Manually grade and comment on student attempts at a question.
-    'mod/quiz:grade' => array(
+    'mod/quiz:grade' => [
         'riskbitmask' => RISK_SPAM | RISK_XSS,
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Regrade quizzes.
-    'mod/quiz:regrade' => array(
+    'mod/quiz:regrade' => [
         'riskbitmask' => RISK_SPAM,
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        ),
+        ],
         'clonepermissionsfrom' =>  'mod/quiz:grade'
-    ),
+    ],
 
     // View the quiz reports.
-    'mod/quiz:viewreports' => array(
+    'mod/quiz:viewreports' => [
         'riskbitmask' => RISK_PERSONAL,
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Delete attempts using the overview report.
-    'mod/quiz:deleteattempts' => array(
+    'mod/quiz:deleteattempts' => [
         'riskbitmask' => RISK_DATALOSS,
         'captype' => 'write',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array(
+        'archetypes' => [
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
-        )
-    ),
+        ]
+    ],
 
     // Do not have the time limit imposed. Used for accessibility legislation compliance.
-    'mod/quiz:ignoretimelimits' => array(
+    'mod/quiz:ignoretimelimits' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array()
-    ),
+        'archetypes' => []
+    ],
 
     // Receive a confirmation message of own quiz submission.
-    'mod/quiz:emailconfirmsubmission' => array(
+    'mod/quiz:emailconfirmsubmission' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array()
-    ),
+        'archetypes' => []
+    ],
 
     // Receive a notification message of other peoples' quiz submissions.
-    'mod/quiz:emailnotifysubmission' => array(
+    'mod/quiz:emailnotifysubmission' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array()
-    ),
+        'archetypes' => []
+    ],
 
     // Receive a notification message when a quiz attempt becomes overdue.
-    'mod/quiz:emailwarnoverdue' => array(
+    'mod/quiz:emailwarnoverdue' => [
         'captype' => 'read',
         'contextlevel' => CONTEXT_MODULE,
-        'archetypes' => array()
-    ),
-);
+        'archetypes' => []
+    ],
+];
 
index 29a19bc..ecadc82 100644 (file)
@@ -1,3 +1,4 @@
 numattemptsmade,mod_quiz
 reviewofattempt,mod_quiz
-reviewofpreview,mod_quiz
\ No newline at end of file
+reviewofpreview,mod_quiz
+settingsoverrides,mod_quiz
index b268444..b59e404 100644 (file)
@@ -613,6 +613,9 @@ $string['overridedeleteusersure'] = 'Are you sure you want to delete the overrid
 $string['overridegroup'] = 'Override group';
 $string['overridegroupeventname'] = '{$a->quiz} - {$a->group}';
 $string['overrides'] = 'Overrides';
+$string['overridesforquiz'] = 'Settings overrides: {$a}';
+$string['overridesnoneforgroups'] = 'No group settings overrides have been created for this quiz.';
+$string['overridesnoneforusers'] = 'No user settings overrides have been created for this quiz.';
 $string['overrideuser'] = 'Override user';
 $string['overrideusereventname'] = '{$a->quiz} - Override';
 $string['pageshort'] = 'P';
@@ -724,7 +727,8 @@ $string['quizisopen'] = 'This quiz is open';
 $string['quizisclosedwillopen'] = 'Quiz closed (opens {$a})';
 $string['quizisopenwillclose'] = 'Quiz open (closes {$a})';
 $string['quiz:manage'] = 'Manage quizzes';
-$string['quiz:manageoverrides'] = 'Manage quiz overrides';
+$string['quiz:manageoverrides'] = 'Manage quiz settings overrides';
+$string['quiz:viewoverrides'] = 'View quiz settings overrides';
 $string['quiznavigation'] = 'Quiz navigation';
 $string['quizopen'] = 'Open the quiz';
 $string['quizeventopens'] = '{$a} opens';
@@ -886,7 +890,6 @@ $string['serveridentifier'] = 'Identifier';
 $string['serverinfo'] = 'Server information';
 $string['servers'] = 'Servers';
 $string['serverurl'] = 'Server URL';
-$string['settingsoverrides'] = 'Settings overrides';
 $string['shortanswer'] = 'Short answer';
 $string['show'] = 'Show';
 $string['showall'] = 'Show all questions on one page';
@@ -1008,3 +1011,4 @@ $string['yourfinalgradeis'] = 'Your final grade for this quiz is {$a}.';
 $string['numattemptsmade'] = '{$a} attempts made on this quiz';
 $string['reviewofattempt'] = 'Review of attempt {$a}';
 $string['reviewofpreview'] = 'Review of preview';
+$string['settingsoverrides'] = 'Settings overrides';
index eb9b090..8f377d1 100644 (file)
@@ -1716,7 +1716,7 @@ function quiz_extend_settings_navigation($settings, $quiznode) {
         $beforekey = $keys[$i + 1];
     }
 
-    if (has_capability('mod/quiz:manageoverrides', $PAGE->cm->context)) {
+    if (has_any_capability(['mod/quiz:manageoverrides', 'mod/quiz:viewoverrides'], $PAGE->cm->context)) {
         $url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$PAGE->cm->id));
         $node = navigation_node::create(get_string('groupoverrides', 'quiz'),
                 new moodle_url($url, array('mode'=>'group')),
index a42c3d6..6e0e0db 100644 (file)
@@ -22,7 +22,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-
 require_once(__DIR__ . '/../../config.php');
 require_once($CFG->dirroot.'/mod/quiz/lib.php');
 require_once($CFG->dirroot.'/mod/quiz/locallib.php');
@@ -33,14 +32,17 @@ $cmid = required_param('cmid', PARAM_INT);
 $mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', default is 'group'.
 
 list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'quiz');
-$quiz = $DB->get_record('quiz', array('id' => $cm->instance), '*', MUST_EXIST);
+$quiz = $DB->get_record('quiz', ['id' => $cm->instance], '*', MUST_EXIST);
 
 require_login($course, false, $cm);
 
 $context = context_module::instance($cm->id);
 
 // Check the user has the required capabilities to list overrides.
-require_capability('mod/quiz:manageoverrides', $context);
+$canedit = has_capability('mod/quiz:manageoverrides', $context);
+if (!$canedit) {
+    require_capability('mod/quiz:viewoverrides', $context);
+}
 
 $quizgroupmode = groups_get_activity_groupmode($cm);
 $accessallgroups = ($quizgroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $context);
@@ -58,16 +60,14 @@ if ($mode != "user" and $mode != "group") {
 }
 $groupmode = ($mode == "group");
 
-$url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$cm->id, 'mode'=>$mode));
+$url = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $cm->id, 'mode' => $mode]);
 
+$title = get_string('overridesforquiz', 'quiz',
+        format_string($quiz->name, true, ['context' => $context]));
 $PAGE->set_url($url);
-
-// Display a list of overrides.
 $PAGE->set_pagelayout('admin');
-$PAGE->set_title(get_string('overrides', 'quiz'));
+$PAGE->set_title($title);
 $PAGE->set_heading($course->fullname);
-echo $OUTPUT->header();
-echo $OUTPUT->heading(format_string($quiz->name, true, array('context' => $context)));
 
 // Delete orphaned group overrides.
 $sql = 'SELECT o.id
@@ -76,7 +76,7 @@ $sql = 'SELECT o.id
          WHERE o.groupid IS NOT NULL
                AND g.id IS NULL
                AND o.quiz = ?';
-$params = array($quiz->id);
+$params = [$quiz->id];
 $orphaned = $DB->get_records_sql($sql, $params);
 if (!empty($orphaned)) {
     $DB->delete_records_list('quiz_overrides', 'id', array_keys($orphaned));
@@ -131,16 +131,18 @@ if ($groupmode) {
 
 // Initialise table.
 $table = new html_table();
-$table->headspan = array(1, 2, 1);
-$table->colclasses = array('colname', 'colsetting', 'colvalue', 'colaction');
-$table->head = array(
-        $colname,
-        get_string('overrides', 'quiz'),
-        get_string('action'),
-);
+$table->headspan = [1, 2, 1];
+$table->colclasses = ['colname', 'colsetting', 'colvalue', 'colaction'];
+$table->head = [
+    $colname,
+    get_string('overrides', 'quiz'),
+];
+if ($canedit) {
+    $table->head[] = get_string('action');
+}
 
-$userurl = new moodle_url('/user/view.php', array());
-$groupurl = new moodle_url('/group/overview.php', array('id' => $cm->course));
+$userurl = new moodle_url('/user/view.php', []);
+$groupurl = new moodle_url('/group/overview.php', ['id' => $cm->course]);
 
 $overridedeleteurl = new moodle_url('/mod/quiz/overridedelete.php');
 $overrideediturl = new moodle_url('/mod/quiz/overrideedit.php');
@@ -149,11 +151,8 @@ $hasinactive = false; // Whether there are any inactive overrides.
 
 foreach ($overrides as $override) {
 
-    $fields = array();
-    $values = array();
+    // Check if this override is active.
     $active = true;
-
-    // Check for inactive overrides.
     if (!$groupmode) {
         if (!has_capability('mod/quiz:attempt', $context, $override->userid)) {
             // User not allowed to take the quiz.
@@ -163,6 +162,13 @@ foreach ($overrides as $override) {
             $active = false;
         }
     }
+    if (!$active) {
+        $hasinactive = true;
+    }
+
+    // Prepare the information about which settings are overridden.
+    $fields = [];
+    $values = [];
 
     // Format timeopen.
     if (isset($override->timeopen)) {
@@ -170,28 +176,24 @@ foreach ($overrides as $override) {
         $values[] = $override->timeopen > 0 ?
                 userdate($override->timeopen) : get_string('noopen', 'quiz');
     }
-
     // Format timeclose.
     if (isset($override->timeclose)) {
         $fields[] = get_string('quizcloses', 'quiz');
         $values[] = $override->timeclose > 0 ?
                 userdate($override->timeclose) : get_string('noclose', 'quiz');
     }
-
     // Format timelimit.
     if (isset($override->timelimit)) {
         $fields[] = get_string('timelimit', 'quiz');
         $values[] = $override->timelimit > 0 ?
                 format_time($override->timelimit) : get_string('none', 'quiz');
     }
-
     // Format number of attempts.
     if (isset($override->attempts)) {
         $fields[] = get_string('attempts', 'quiz');
         $values[] = $override->attempts > 0 ?
                 $override->attempts : get_string('unlimited');
     }
-
     // Format password.
     if (isset($override->password)) {
         $fields[] = get_string('requirepassword', 'quiz');
@@ -199,110 +201,130 @@ foreach ($overrides as $override) {
                 get_string('enabled', 'quiz') : get_string('none', 'quiz');
     }
 
-    // Icons.
-    $iconstr = '';
-
-    // Edit.
-    $editurlstr = $overrideediturl->out(true, array('id' => $override->id));
-    $iconstr = '<a title="' . get_string('edit') . '" href="'. $editurlstr . '">' .
-            $OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a> ';
-    // Duplicate.
-    $copyurlstr = $overrideediturl->out(true,
-            array('id' => $override->id, 'action' => 'duplicate'));
-    $iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
-            $OUTPUT->pix_icon('t/copy', get_string('copy')) . '</a> ';
-    // Delete.
-    $deleteurlstr = $overridedeleteurl->out(true,
-            array('id' => $override->id, 'sesskey' => sesskey()));
-    $iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
-                $OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a> ';
-
+    // Prepare the information about who this override applies to.
     if ($groupmode) {
         $usergroupstr = '<a href="' . $groupurl->out(true,
-                array('group' => $override->groupid)) . '" >' . $override->name . '</a>';
+                        ['group' => $override->groupid]) . '" >' . $override->name . '</a>';
     } else {
         $usergroupstr = '<a href="' . $userurl->out(true,
-                array('id' => $override->userid)) . '" >' . fullname($override) . '</a>';
+                        ['id' => $override->userid]) . '" >' . fullname($override) . '</a>';
     }
-
-    $class = '';
     if (!$active) {
-        $class = "dimmed_text";
         $usergroupstr .= '*';
-        $hasinactive = true;
     }
-
     $usergroupcell = new html_table_cell();
     $usergroupcell->rowspan = count($fields);
     $usergroupcell->text = $usergroupstr;
-    $actioncell = new html_table_cell();
-    $actioncell->rowspan = count($fields);
-    $actioncell->text = $iconstr;
 
+    // Prepare the actions.
+    if ($canedit) {
+        // Icons.
+        $iconstr = '';
+
+        // Edit.
+        $editurlstr = $overrideediturl->out(true, ['id' => $override->id]);
+        $iconstr = '<a title="' . get_string('edit') . '" href="' . $editurlstr . '">' .
+                $OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a> ';
+        // Duplicate.
+        $copyurlstr = $overrideediturl->out(true,
+                ['id' => $override->id, 'action' => 'duplicate']);
+        $iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
+                $OUTPUT->pix_icon('t/copy', get_string('copy')) . '</a> ';
+        // Delete.
+        $deleteurlstr = $overridedeleteurl->out(true,
+                ['id' => $override->id, 'sesskey' => sesskey()]);
+        $iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
+                $OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a> ';
+
+        $actioncell = new html_table_cell();
+        $actioncell->rowspan = count($fields);
+        $actioncell->text = $iconstr;
+    }
+
+    // Add the data to the table.
     for ($i = 0; $i < count($fields); ++$i) {
         $row = new html_table_row();
-        $row->attributes['class'] = $class;
+        if (!$active) {
+            $row->attributes['class'] = 'dimmed_text';
+        }
+
         if ($i == 0) {
             $row->cells[] = $usergroupcell;
         }
-        $cell1 = new html_table_cell();
-        $cell1->text = $fields[$i];
-        $row->cells[] = $cell1;
-        $cell2 = new html_table_cell();
-        $cell2->text = $values[$i];
-        $row->cells[] = $cell2;
-        if ($i == 0) {
+
+        $labelcell = new html_table_cell();
+        $labelcell->text = $fields[$i];
+        $row->cells[] = $labelcell;
+        $valuecell = new html_table_cell();
+        $valuecell->text = $values[$i];
+        $row->cells[] = $valuecell;
+
+        if ($canedit && $i == 0) {
             $row->cells[] = $actioncell;
         }
+
         $table->data[] = $row;
     }
 }
 
+// Display a list of overrides.
+echo $OUTPUT->header();
+echo $OUTPUT->heading($title);
+
 // Output the table and button.
-echo html_writer::start_tag('div', array('id' => 'quizoverrides'));
+echo html_writer::start_tag('div', ['id' => 'quizoverrides']);
 if (count($table->data)) {
     echo html_writer::table($table);
+} else {
+    if ($groupmode) {
+        echo $OUTPUT->notification(get_string('overridesnoneforgroups', 'quiz'), 'info', false);
+    } else {
+        echo $OUTPUT->notification(get_string('overridesnoneforusers', 'quiz'), 'info', false);
+    }
 }
 if ($hasinactive) {
-    echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'quiz'), 'dimmed_text');
+    echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'quiz'), 'info', false);
 }
 
-echo html_writer::start_tag('div', array('class' => 'buttons'));
-$options = array();
-if ($groupmode) {
-    if (empty($groups)) {
-        // There are no groups.
-        echo $OUTPUT->notification(get_string('groupsnone', 'quiz'), 'error');
-        $options['disabled'] = true;
-    }
-    echo $OUTPUT->single_button($overrideediturl->out(true,
-            array('action' => 'addgroup', 'cmid' => $cm->id)),
-            get_string('addnewgroupoverride', 'quiz'), 'post', $options);
-} else {
-    $users = array();
-    // See if there are any students in the quiz.
-    if ($accessallgroups) {
-        $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id');
-        $nousermessage = get_string('usersnone', 'quiz');
-    } else if ($groups) {
-        $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id', '', '', '', array_keys($groups));
-        $nousermessage = get_string('usersnone', 'quiz');
+if ($canedit) {
+    echo html_writer::start_tag('div', ['class' => 'buttons']);
+    $options = [];
+    if ($groupmode) {
+        if (empty($groups)) {
+            // There are no groups.
+            echo $OUTPUT->notification(get_string('groupsnone', 'quiz'), 'error');
+            $options['disabled'] = true;
+        }
+        echo $OUTPUT->single_button($overrideediturl->out(true,
+                ['action' => 'addgroup', 'cmid' => $cm->id]),
+                get_string('addnewgroupoverride', 'quiz'), 'post', $options);
     } else {
-        $nousermessage = get_string('groupsnone', 'quiz');
-    }
-    $info = new \core_availability\info_module($cm);
-    $users = $info->filter_user_list($users);
+        $users = [];
+        // See if there are any students in the quiz.
+        if ($accessallgroups) {
+            $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id');
+            $nousermessage = get_string('usersnone', 'quiz');
+        } else if ($groups) {
+            $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id', '', '', '', array_keys($groups));
+            $nousermessage = get_string('usersnone', 'quiz');
+        } else {
+            $nousermessage = get_string('groupsnone', 'quiz');
+        }
+        $info = new \core_availability\info_module($cm);
+        $users = $info->filter_user_list($users);
 
-    if (empty($users)) {
-        // There are no students.
-        echo $OUTPUT->notification($nousermessage, 'error');
-        $options['disabled'] = true;
+        if (empty($users)) {
+            // There are no students.
+            echo $OUTPUT->notification($nousermessage, 'error');
+            $options['disabled'] = true;
+        }
+        echo $OUTPUT->single_button($overrideediturl->out(true,
+                ['action' => 'adduser', 'cmid' => $cm->id]),
+                get_string('addnewuseroverride', 'quiz'), 'get', $options);
     }
-    echo $OUTPUT->single_button($overrideediturl->out(true,
-            array('action' => 'adduser', 'cmid' => $cm->id)),
-            get_string('addnewuseroverride', 'quiz'), 'get', $options);
+    echo html_writer::end_tag('div');
 }
-echo html_writer::end_tag('div');
+
 echo html_writer::end_tag('div');
 
 // Finish the page.
index c20ac9f..04edfc4 100644 (file)
@@ -2,7 +2,7 @@
 Feature: Quiz group override
   In order to grant a group special access to a quiz
   As a teacher
-  I need to create an override for thta group.
+  I need to create an override for that group.
 
   Background:
     Given the following "users" exist:
@@ -13,6 +13,7 @@ Feature: Quiz group override
       | student2 | Sam 2      | Student 2 | student2@example.com |
       | teacher3 | Terry 3    | Teacher 3 | teacher3@example.com |
       | student3 | Sam 3      | Student 3 | student3@example.com |
+      | helper   | Exam       | Helper    | helper@example.com   |
     And the following "courses" exist:
       | fullname | shortname | category |
       | Course 1 | C1        | 0        |
@@ -24,6 +25,7 @@ Feature: Quiz group override
       | student2 | C1     | student        |
       | teacher3 | C1     | editingteacher |
       | student3 | C1     | student        |
+      | helper   | C1     | teacher        |
     And the following "groups" exist:
       | name    | course | idnumber |
       | Group 1 | C1     | G1       |
@@ -38,6 +40,9 @@ Feature: Quiz group override
       | teacher2 | G2    |
       | teacher2 | G3    |
       | student3 | G3    |
+      | helper   | G1    |
+      | helper   | G2    |
+      | helper   | G3    |
     And the following "activities" exist:
       | activity | name      | intro                 | course | idnumber | groupmode |
       | quiz     | Test quiz | Test quiz description | C1     | quiz1    | 1         |
@@ -59,37 +64,49 @@ Feature: Quiz group override
     Then I should see "No groups you can access."
     And the "Add group override" "button" should be disabled
 
-  Scenario: A teacher with accessallgroups permission should see all group overrides
-    When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "admin"
+  Scenario: A teacher can create an override
+    When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
     And I press "Add group override"
     And I set the following fields to these values:
-      | Override group | Group 1 |
-      | Attempts allowed | 2 |
+      | Override group   | Group 1 |
+      | Attempts allowed | 2       |
     And I press "Save and enter another override"
     And I set the following fields to these values:
-      | Override group   | Group 2 |
+      | Override group   | Group 3 |
       | Attempts allowed | 2       |
     And I press "Save"
-    And I log out
-    And I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
-    Then I should see "Group 1" in the ".generaltable" "css_element"
-    And I should see "Group 2" in the ".generaltable" "css_element"
+    Then "Group 1" "table_row" should exist
+
+  Scenario: A teacher with accessallgroups permission should see all group overrides
+    Given the following "mod_quiz > group overrides" exist:
+      | quiz      | group | attempts |
+      | Test quiz | G1    | 2        |
+      | Test quiz | G2    | 2        |
+    When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
+    Then "Group 1" "table_row" should exist
+    And "Group 2" "table_row" should exist
 
   Scenario: A teacher without accessallgroups permission should only see the group overrides within his/her groups, when the activity's group mode is "separate groups"
     Given the following "permission overrides" exist:
       | capability                  | permission | role           | contextlevel | reference |
       | moodle/site:accessallgroups | Prevent    | editingteacher | Course       | C1        |
-    When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "admin"
-    And I press "Add group override"
-    And I set the following fields to these values:
-      | Override group | Group 1 |
-      | Attempts allowed | 2 |
-    And I press "Save and enter another override"
-    And I set the following fields to these values:
-      | Override group | Group 2 |
-      | Attempts allowed | 2 |
-    And I press "Save"
-    And I log out
+    And the following "mod_quiz > group overrides" exist:
+      | quiz      | group | attempts |
+      | Test quiz | G1    | 2        |
+      | Test quiz | G2    | 2        |
     When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
-    Then I should see "Group 1" in the ".generaltable" "css_element"
-    And I should not see "Group 2" in the ".generaltable" "css_element"
+    Then "Group 1" "table_row" should exist
+    And "Group 2" "table_row" should not exist
+
+  Scenario: A non-editing teacher can see the overrides, but not change them
+    Given the following "mod_quiz > group overrides" exist:
+      | quiz      | group | attempts |
+      | Test quiz | G1    | 2        |
+      | Test quiz | G2    | 2        |
+    When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "helper"
+    Then "Group 1" "table_row" should exist
+    And "Group 2" "table_row" should exist
+    And "Add group override" "button" should not exist
+    And "Edit" "link" should not exist in the "Group 1" "table_row"
+    And "Copy" "link" should not exist in the "Group 1" "table_row"
+    And "Delete" "link" should not exist in the "Group 1" "table_row"
index 408345d..c9e7d90 100644 (file)
@@ -7,7 +7,8 @@ Feature: Quiz user override
   Background:
     Given the following "users" exist:
       | username | firstname | lastname | email                |
-      | teacher1 | Teacher   | One      | teacher1@example.com |
+      | teacher  | Teacher   | One      | teacher@example.com  |
+      | helper   | Exam      | Helper   | helper@example.com   |
       | student1 | Student   | One      | student1@example.com |
       | student2 | Student   | Two      | student2@example.com |
     And the following "courses" exist:
@@ -15,18 +16,17 @@ Feature: Quiz user override
       | Course 1 | C1        | 0        |
     And the following "course enrolments" exist:
       | user     | course | role           |
-      | teacher1 | C1     | editingteacher |
+      | teacher  | C1     | editingteacher |
+      | helper   | C1     | teacher        |
       | student1 | C1     | student        |
       | student2 | C1     | student        |
-    And the following "activities" exist:
-      | activity   | name   | intro              | course | idnumber |
-      | quiz       | Quiz 1 | Quiz 1 description | C1     | quiz1    |
 
   @javascript
   Scenario: Add, modify then delete a user override
-    Given I log in as "teacher1"
-    And I am on "Course 1" course homepage
-    When I follow "Quiz 1"
+    Given the following "activities" exist:
+      | activity   | name      | course | idnumber |
+      | quiz       | Test quiz | C1     | quiz1    |
+    And I am on the "Test quiz" "mod_quiz > View" page logged in as "teacher"
     And I navigate to "User overrides" in current page administration
     And I press "Add user override"
     And I set the following fields to these values:
@@ -38,26 +38,24 @@ Feature: Quiz user override
       | timeclose[hour]      | 08       |
       | timeclose[minute]    | 00       |
     And I press "Save"
-    And I should see "Wednesday, 1 January 2020, 8:00"
-    Then I click on "Edit" "link" in the "Student One" "table_row"
+    Then I should see "Wednesday, 1 January 2020, 8:00"
+
+    And I click on "Edit" "link" in the "Student One" "table_row"
     And I set the following fields to these values:
       | timeclose[year] | 2030 |
     And I press "Save"
     And I should see "Tuesday, 1 January 2030, 8:00"
+
     And I click on "Delete" "link"
     And I press "Continue"
     And I should not see "Student One"
 
   @javascript
-  Scenario: Being able to modify a user override when the quiz is not available to the student
-    Given I log in as "teacher1"
-    And I am on "Course 1" course homepage
-    And I follow "Quiz 1"
-    And I navigate to "Edit settings" in current page administration
-    And I expand all fieldsets
-    And I set the field "Availability" to "Hide from students"
-    And I click on "Save and display" "button"
-    When I navigate to "User overrides" in current page administration
+  Scenario: Can add a user override when the quiz is not available to the student
+    Given the following "activities" exist:
+      | activity   | name      | course | idnumber | visible |
+      | quiz       | Test quiz | C1     | quiz1    | 0       |
+    When I am on the "Test quiz" "mod_quiz > User overrides" page logged in as "teacher"
     And I press "Add user override"
     And I set the following fields to these values:
       | Override user    | Student1 |
@@ -77,18 +75,15 @@ Feature: Quiz user override
     And the following "group members" exist:
       | user     | group |
       | student1 | G1    |
-      | teacher1 | G1    |
+      | teacher  | G1    |
       | student2 | G2    |
     And the following "permission overrides" exist:
       | capability                  | permission | role           | contextlevel | reference |
       | moodle/site:accessallgroups | Prevent    | editingteacher | Course       | C1        |
     And the following "activities" exist:
-      | activity | name   | intro              | course | idnumber | groupmode |
-      | quiz     | Quiz 2 | Quiz 2 description | C1     | quiz2    | 1         |
-    When I log in as "teacher1"
-    And I am on "Course 1" course homepage
-    And I follow "Quiz 2"
-    And I navigate to "User overrides" in current page administration
+      | activity | name      | course | idnumber | groupmode |
+      | quiz     | Test quiz | C1     | quiz1    | 1         |
+    When I am on the "Test quiz" "mod_quiz > User overrides" page logged in as "teacher"
     And I press "Add user override"
     Then the "Override user" select box should contain "Student One, student1@example.com"
     And the "Override user" select box should not contain "Student Two, student2@example.com"
@@ -104,11 +99,25 @@ Feature: Quiz user override
       | capability                  | permission | role           | contextlevel | reference |
       | moodle/site:accessallgroups | Prevent    | editingteacher | Course       | C1        |
     And the following "activities" exist:
-      | activity | name   | intro              | course | idnumber | groupmode |
-      | quiz     | Quiz 2 | Quiz 2 description | C1     | quiz2    | 1         |
-    When I log in as "teacher1"
-    And I am on "Course 1" course homepage
-    And I follow "Quiz 2"
-    And I navigate to "User overrides" in current page administration
+      | activity | name      | course | idnumber | groupmode |
+      | quiz     | Test quiz | C1     | quiz1    | 1         |
+    When I am on the "Test quiz" "mod_quiz > User overrides" page logged in as "teacher"
     Then I should see "No groups you can access."
     And the "Add user override" "button" should be disabled
+
+  Scenario: A non-editing teacher can see the overrides, but not change them
+    Given the following "activities" exist:
+      | activity   | name      | course | idnumber |
+      | quiz       | Test quiz | C1     | quiz1    |
+    And the following "mod_quiz > user overrides" exist:
+      | quiz      | user     | attempts |
+      | Test quiz | student1 | 2        |
+      | Test quiz | student2 | 2        |
+    And I am on the "Test quiz" "mod_quiz > View" page logged in as "helper"
+    When I navigate to "User overrides" in current page administration
+    Then "Student One" "table_row" should exist
+    And "Student Two" "table_row" should exist
+    And "Add user override" "button" should not exist
+    And "Edit" "link" should not exist in the "Student One" "table_row"
+    And "Copy" "link" should not exist in the "Student One" "table_row"
+    And "Delete" "link" should not exist in the "Student One" "table_row"
index ca5d4f5..4ec5a89 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2021052500;
+$plugin->version   = 2021052501;
 $plugin->requires  = 2021052500;
 $plugin->component = 'mod_quiz';