MDL-34411 mod_quiz: quiz overrides to observe group membership

This commit is contained in:
Shamim Rezaie 2019-05-30 16:40:35 +10:00 committed by Jenkins
parent 043b38d580
commit ce1fd00203
5 changed files with 249 additions and 54 deletions

View File

@ -405,7 +405,7 @@ $string['gradingdetailszeropenalty'] = 'You were not penalized for this submissi
$string['gradingmethod'] = 'Grading method: {$a}';
$string['groupoverrides'] = 'Group overrides';
$string['groupoverridesdeleted'] = 'Group overrides deleted';
$string['groupsnone'] = 'There are no groups in this course';
$string['groupsnone'] = 'No groups you can access.';
$string['guestsno'] = 'Sorry, guests cannot see or attempt quizzes';
$string['hidebreaks'] = 'Hide page breaks';
$string['hidereordertool'] = 'Hide the reordering tool';

View File

@ -78,13 +78,16 @@ class quiz_override_form extends moodleform {
}
protected function definition() {
global $CFG, $DB;
global $DB;
$cm = $this->cm;
$mform = $this->_form;
$mform->addElement('header', 'override', get_string('override', 'quiz'));
$quizgroupmode = groups_get_activity_groupmode($cm);
$accessallgroups = ($quizgroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $this->context);
if ($this->groupmode) {
// Group override.
if ($this->groupid) {
@ -96,7 +99,8 @@ class quiz_override_form extends moodleform {
$mform->freeze('groupid');
} else {
// Prepare the list of groups.
$groups = groups_get_all_groups($cm->course);
// Only include the groups the current can access.
$groups = $accessallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
if (empty($groups)) {
// Generate an error.
$link = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$cm->id));
@ -136,9 +140,17 @@ class quiz_override_form extends moodleform {
'This is unexpected, and a problem because there is no way to pass these ' .
'parameters to get_users_by_capability. See MDL-34657.');
}
$users = get_users_by_capability($this->context, 'mod/quiz:attempt',
'u.id, u.email, ' . get_all_user_name_fields(true, 'u'),
$sort, '', '', '', '', false, true);
// Get the list of appropriate users, depending on whether and how groups are used.
if ($accessallgroups) {
$users = get_users_by_capability($this->context, 'mod/quiz:attempt',
'u.id, u.email, ' . get_all_user_name_fields(true, 'u'),
$sort);
} else if ($groups = groups_get_activity_allowed_groups($cm)) {
$users = get_users_by_capability($this->context, 'mod/quiz:attempt',
'u.id, u.email, ' . get_all_user_name_fields(true, 'u'),
$sort, '', '', array_keys($groups));
}
// Filter users based on any fixed restrictions (groups, profile).
$info = new \core_availability\info_module($cm);
@ -220,7 +232,6 @@ class quiz_override_form extends moodleform {
}
public function validation($data, $files) {
global $COURSE, $DB;
$errors = parent::validation($data, $files);
$mform =& $this->_form;

View File

@ -35,11 +35,18 @@ $mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', de
list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'quiz');
$quiz = $DB->get_record('quiz', array('id' => $cm->instance), '*', MUST_EXIST);
// Get the course groups.
$groups = groups_get_all_groups($cm->course);
if ($groups === false) {
$groups = array();
}
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);
$quizgroupmode = groups_get_activity_groupmode($cm);
$accessallgroups = ($quizgroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $context);
// Get the course groups that the current user can access.
$groups = $accessallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
// Default mode is "group", unless there are no groups.
if ($mode != "user" and $mode != "group") {
@ -55,13 +62,6 @@ $url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$cm->id, 'mode'=>
$PAGE->set_url($url);
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);
// Display a list of overrides.
$PAGE->set_pagelayout('admin');
$PAGE->set_title(get_string('overrides', 'quiz'));
@ -71,38 +71,63 @@ echo $OUTPUT->heading(format_string($quiz->name, true, array('context' => $conte
// Delete orphaned group overrides.
$sql = 'SELECT o.id
FROM {quiz_overrides} o LEFT JOIN {groups} g
ON o.groupid = g.id
WHERE o.groupid IS NOT NULL
AND g.id IS NULL
AND o.quiz = ?';
FROM {quiz_overrides} o
LEFT JOIN {groups} g ON o.groupid = g.id
WHERE o.groupid IS NOT NULL
AND g.id IS NULL
AND o.quiz = ?';
$params = array($quiz->id);
$orphaned = $DB->get_records_sql($sql, $params);
if (!empty($orphaned)) {
$DB->delete_records_list('quiz_overrides', 'id', array_keys($orphaned));
}
$overrides = [];
// Fetch all overrides.
if ($groupmode) {
$colname = get_string('group');
$sql = 'SELECT o.*, g.name
FROM {quiz_overrides} o
JOIN {groups} g ON o.groupid = g.id
WHERE o.quiz = :quizid
ORDER BY g.name';
$params = array('quizid' => $quiz->id);
// To filter the result by the list of groups that the current user has access to.
if ($groups) {
$params = ['quizid' => $quiz->id];
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
$params += $inparams;
$sql = "SELECT o.*, g.name
FROM {quiz_overrides} o
JOIN {groups} g ON o.groupid = g.id
WHERE o.quiz = :quizid AND g.id $insql
ORDER BY g.name";
$overrides = $DB->get_records_sql($sql, $params);
}
} else {
$colname = get_string('user');
list($sort, $params) = users_order_by_sql('u');
$sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
FROM {quiz_overrides} o
JOIN {user} u ON o.userid = u.id
WHERE o.quiz = :quizid
ORDER BY ' . $sort;
$params['quizid'] = $quiz->id;
}
$overrides = $DB->get_records_sql($sql, $params);
if ($accessallgroups) {
$sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
FROM {quiz_overrides} o
JOIN {user} u ON o.userid = u.id
WHERE o.quiz = :quizid
ORDER BY ' . $sort;
$overrides = $DB->get_records_sql($sql, $params);
} else if ($groups) {
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
$params += $inparams;
$sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
FROM {quiz_overrides} o
JOIN {user} u ON o.userid = u.id
JOIN {groups_members} gm ON u.id = gm.userid
WHERE o.quiz = :quizid AND gm.groupid ' . $insql . '
ORDER BY ' . $sort;
$overrides = $DB->get_records_sql($sql, $params);
}
}
// Initialise table.
$table = new html_table();
@ -256,13 +281,21 @@ if ($groupmode) {
} else {
$users = array();
// See if there are any students in the quiz.
$users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id');
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(get_string('usersnone', 'quiz'), 'error');
echo $OUTPUT->notification($nousermessage, 'error');
$options['disabled'] = true;
}
echo $OUTPUT->single_button($overrideediturl->out(true,

View File

@ -0,0 +1,113 @@
@mod @mod_quiz
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.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Terry 1 | Teacher 1 | teacher1@example.com |
| student1 | Sam 1 | Student 1 | student1@example.com |
| teacher2 | Terry 2 | Teacher 2 | teacher2@example.com |
| student2 | Sam 2 | Student 2 | student2@example.com |
| teacher3 | Terry 3 | Teacher 3 | teacher3@example.com |
| student3 | Sam 3 | Student 3 | student3@example.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 |
| student1 | C1 | student |
| teacher2 | C1 | editingteacher |
| student2 | C1 | student |
| teacher3 | C1 | editingteacher |
| student3 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
| Group 3 | C1 | G3 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| teacher1 | G1 |
| teacher1 | G3 |
| student2 | G2 |
| teacher2 | G2 |
| teacher2 | G3 |
| student3 | G3 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | groupmode |
| quiz | Test quiz | Test quiz description | C1 | quiz1 | 1 |
Scenario: Override Group 1 as teacher of Group 1
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
And I press "Add group override"
Then the "Override group" select box should contain "Group 1"
And the "Override group" select box should not contain "Group 2"
Scenario: Override Group 1 as teacher in no group
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
When I log in as "teacher3"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
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
Given I log in as "admin"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
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
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
Then I should see "Group 1" in the ".generaltable" "css_element"
And I should see "Group 2" in the ".generaltable" "css_element"
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 |
And I log in as "admin"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
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
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test quiz"
And I navigate to "Group overrides" in current page administration
Then I should see "Group 1" in the ".generaltable" "css_element"
And I should not see "Group 2" in the ".generaltable" "css_element"

View File

@ -1,4 +1,4 @@
@mod @mod_quiz @javascript
@mod @mod_quiz
Feature: Quiz user override
In order to grant a student special access to a quiz
As a teacher
@ -18,24 +18,14 @@ Feature: Quiz user override
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "activities" exist:
| activity | name | intro | course | idnumber |
| quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | truefalse | TF1 | First question |
| Test questions | truefalse | TF2 | Second question |
And quiz "Quiz 1" contains the following questions:
| question | page | maxmark |
| TF1 | 1 | |
| TF2 | 1 | 3.0 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
@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"
And I navigate to "User overrides" in current page administration
And I press "Add user override"
@ -58,8 +48,11 @@ Feature: Quiz user override
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 follow "Quiz 1"
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"
@ -71,3 +64,48 @@ Feature: Quiz user override
| Attempts allowed | 1 |
And I press "Save"
Then "Edit" "icon" should exist in the "Student One" "table_row"
Scenario: A teacher without accessallgroups permission should only be able to add user override for users that he/she shares groups with,
when the activity's group mode is to "separate groups"
Given the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| teacher1 | 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
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"
Scenario: Override user in an activity with group mode set to "separate groups" as a teacher who is not a member in any group, and does not have accessallgroups permission
Given the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
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
Then I should see "No groups you can access."
And the "Add user override" "button" should be disabled