Skip to content

Commit 93b4771

Browse files
committed
MDL-59977 core: do not directly check 'viewparticipant' capability
1 parent 32f9550 commit 93b4771

File tree

17 files changed

+261
-48
lines changed

17 files changed

+261
-48
lines changed

blocks/activity_results/block_activity_results.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
defined('MOODLE_INTERNAL') || die();
2727

2828
require_once($CFG->dirroot . '/lib/grade/constants.php');
29+
require_once($CFG->dirroot . '/course/lib.php');
2930

3031
define('B_ACTIVITYRESULTS_NAME_FORMAT_FULL', 1);
3132
define('B_ACTIVITYRESULTS_NAME_FORMAT_ID', 2);
@@ -328,7 +329,7 @@ public function get_content() {
328329
if ($nameformat == B_ACTIVITYRESULTS_NAME_FORMAT_FULL) {
329330
if (has_capability('moodle/course:managegroups', $context)) {
330331
$grouplink = $CFG->wwwroot.'/group/overview.php?id='.$courseid.'&group=';
331-
} else if (has_capability('moodle/course:viewparticipants', $context)) {
332+
} else if (course_can_view_participants($context)) {
332333
$grouplink = $CFG->wwwroot.'/user/index.php?id='.$courseid.'&group=';
333334
} else {
334335
$grouplink = '';

blocks/participants/block_participants.php

+13-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@
2222
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2323
*/
2424

25+
defined('MOODLE_INTERNAL') || die();
26+
27+
require_once($CFG->dirroot . '/course/lib.php');
28+
29+
/**
30+
* Participants block
31+
*
32+
* @package block_participants
33+
* @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
34+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35+
*/
2536
class block_participants extends block_list {
2637
function init() {
2738
$this->title = get_string('pluginname', 'block_participants');
@@ -48,12 +59,12 @@ function get_content() {
4859
$this->content = '';
4960
return $this->content;
5061
} else if ($this->page->course->id == SITEID) {
51-
if (!has_capability('moodle/site:viewparticipants', context_system::instance())) {
62+
if (!course_can_view_participants(context_system::instance())) {
5263
$this->content = '';
5364
return $this->content;
5465
}
5566
} else {
56-
if (!has_capability('moodle/course:viewparticipants', $currentcontext)) {
67+
if (!course_can_view_participants($currentcontext)) {
5768
$this->content = '';
5869
return $this->content;
5970
}

course/lib.php

+33-4
Original file line numberDiff line numberDiff line change
@@ -3894,16 +3894,14 @@ function course_get_user_navigation_options($context, $course = null) {
38943894
// Frontpage settings?
38953895
if ($isfrontpage) {
38963896
// We are on the front page, so make sure we use the proper capability (site:viewparticipants).
3897-
$options->participants = has_capability('moodle/site:viewparticipants', $sitecontext) ||
3898-
has_capability('moodle/course:enrolreview', $sitecontext);
3897+
$options->participants = course_can_view_participants($sitecontext);
38993898
$options->badges = !empty($CFG->enablebadges) && has_capability('moodle/badges:viewbadges', $sitecontext);
39003899
$options->tags = !empty($CFG->usetags) && $isloggedin;
39013900
$options->search = !empty($CFG->enableglobalsearch) && has_capability('moodle/search:query', $sitecontext);
39023901
$options->calendar = $isloggedin;
39033902
} else {
39043903
// We are in a course, so make sure we use the proper capability (course:viewparticipants).
3905-
$options->participants = has_capability('moodle/course:viewparticipants', $context) ||
3906-
has_capability('moodle/course:enrolreview', $context);
3904+
$options->participants = course_can_view_participants($context);
39073905
$options->badges = !empty($CFG->enablebadges) && !empty($CFG->badges_allowcoursebadges) &&
39083906
has_capability('moodle/badges:viewbadges', $context);
39093907
// Add view grade report is permitted.
@@ -4237,3 +4235,34 @@ function course_check_module_updates_since($cm, $from, $fileareas = array(), $fi
42374235

42384236
return $updates;
42394237
}
4238+
4239+
/**
4240+
* Returns true if the user can view the participant page, false otherwise,
4241+
*
4242+
* @param context $context The context we are checking.
4243+
* @return bool
4244+
*/
4245+
function course_can_view_participants($context) {
4246+
$viewparticipantscap = 'moodle/course:viewparticipants';
4247+
if ($context->contextlevel == CONTEXT_SYSTEM) {
4248+
$viewparticipantscap = 'moodle/site:viewparticipants';
4249+
}
4250+
4251+
return has_any_capability([$viewparticipantscap, 'moodle/course:enrolreview'], $context);
4252+
}
4253+
4254+
/**
4255+
* Checks if a user can view the participant page, if not throws an exception.
4256+
*
4257+
* @param context $context The context we are checking.
4258+
* @throws required_capability_exception
4259+
*/
4260+
function course_require_view_participants($context) {
4261+
if (!course_can_view_participants($context)) {
4262+
$viewparticipantscap = 'moodle/course:viewparticipants';
4263+
if ($context->contextlevel == CONTEXT_SYSTEM) {
4264+
$viewparticipantscap = 'moodle/site:viewparticipants';
4265+
}
4266+
throw new required_capability_exception($context, $viewparticipantscap, 'nopermissions', '');
4267+
}
4268+
}

course/recent_form.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
2828
}
2929

30+
require_once($CFG->dirroot . '/course/lib.php');
3031
require_once($CFG->libdir.'/formslib.php');
3132

3233
class recent_form extends moodleform {
@@ -64,9 +65,9 @@ function definition() {
6465
}
6566

6667
if ($COURSE->id == SITEID) {
67-
$viewparticipants = has_capability('moodle/site:viewparticipants', context_system::instance());
68+
$viewparticipants = course_can_view_participants(context_system::instance());
6869
} else {
69-
$viewparticipants = has_capability('moodle/course:viewparticipants', $context);
70+
$viewparticipants = course_can_view_participants($context);
7071
}
7172

7273
if ($viewparticipants) {

course/tests/courselib_test.php

+170
Original file line numberDiff line numberDiff line change
@@ -3904,4 +3904,174 @@ public function test_course_module_bulk_update_calendar_events() {
39043904
// Update the assign instances for this course.
39053905
$this->assertTrue(course_module_bulk_update_calendar_events('assign', $course->id));
39063906
}
3907+
3908+
/**
3909+
* Test that a student can view participants in a course they are enrolled in.
3910+
*/
3911+
public function test_course_can_view_participants_as_student() {
3912+
$this->resetAfterTest();
3913+
3914+
$course = $this->getDataGenerator()->create_course();
3915+
$coursecontext = context_course::instance($course->id);
3916+
3917+
$user = $this->getDataGenerator()->create_user();
3918+
$this->getDataGenerator()->enrol_user($user->id, $course->id);
3919+
3920+
$this->setUser($user);
3921+
3922+
$this->assertTrue(course_can_view_participants($coursecontext));
3923+
}
3924+
3925+
/**
3926+
* Test that a student in a course can not view participants on the site.
3927+
*/
3928+
public function test_course_can_view_participants_as_student_on_site() {
3929+
$this->resetAfterTest();
3930+
3931+
$course = $this->getDataGenerator()->create_course();
3932+
3933+
$user = $this->getDataGenerator()->create_user();
3934+
$this->getDataGenerator()->enrol_user($user->id, $course->id);
3935+
3936+
$this->setUser($user);
3937+
3938+
$this->assertFalse(course_can_view_participants(context_system::instance()));
3939+
}
3940+
3941+
/**
3942+
* Test that an admin can view participants on the site.
3943+
*/
3944+
public function test_course_can_view_participants_as_admin_on_site() {
3945+
$this->resetAfterTest();
3946+
3947+
$this->setAdminUser();
3948+
3949+
$this->assertTrue(course_can_view_participants(context_system::instance()));
3950+
}
3951+
3952+
/**
3953+
* Test teachers can view participants in a course they are enrolled in.
3954+
*/
3955+
public function test_course_can_view_participants_as_teacher() {
3956+
global $DB;
3957+
3958+
$this->resetAfterTest();
3959+
3960+
$course = $this->getDataGenerator()->create_course();
3961+
$coursecontext = context_course::instance($course->id);
3962+
3963+
$user = $this->getDataGenerator()->create_user();
3964+
$roleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
3965+
$this->getDataGenerator()->enrol_user($user->id, $course->id, $roleid);
3966+
3967+
$this->setUser($user);
3968+
3969+
$this->assertTrue(course_can_view_participants($coursecontext));
3970+
}
3971+
3972+
/**
3973+
* Check the teacher can still view the participants page without the 'viewparticipants' cap.
3974+
*/
3975+
public function test_course_can_view_participants_as_teacher_without_view_participants_cap() {
3976+
global $DB;
3977+
3978+
$this->resetAfterTest();
3979+
3980+
$course = $this->getDataGenerator()->create_course();
3981+
$coursecontext = context_course::instance($course->id);
3982+
3983+
$user = $this->getDataGenerator()->create_user();
3984+
$roleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
3985+
$this->getDataGenerator()->enrol_user($user->id, $course->id, $roleid);
3986+
3987+
$this->setUser($user);
3988+
3989+
// Disable one of the capabilties.
3990+
assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT, $roleid, $coursecontext);
3991+
3992+
// Should still be able to view the page as they have the 'moodle/course:enrolreview' cap.
3993+
$this->assertTrue(course_can_view_participants($coursecontext));
3994+
}
3995+
3996+
/**
3997+
* Check the teacher can still view the participants page without the 'moodle/course:enrolreview' cap.
3998+
*/
3999+
public function test_course_can_view_participants_as_teacher_without_enrol_review_cap() {
4000+
global $DB;
4001+
4002+
$this->resetAfterTest();
4003+
4004+
$course = $this->getDataGenerator()->create_course();
4005+
$coursecontext = context_course::instance($course->id);
4006+
4007+
$user = $this->getDataGenerator()->create_user();
4008+
$roleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
4009+
$this->getDataGenerator()->enrol_user($user->id, $course->id, $roleid);
4010+
4011+
$this->setUser($user);
4012+
4013+
// Disable one of the capabilties.
4014+
assign_capability('moodle/course:enrolreview', CAP_PROHIBIT, $roleid, $coursecontext);
4015+
4016+
// Should still be able to view the page as they have the 'moodle/course:viewparticipants' cap.
4017+
$this->assertTrue(course_can_view_participants($coursecontext));
4018+
}
4019+
4020+
/**
4021+
* Check the teacher can not view the participants page without the required caps.
4022+
*/
4023+
public function test_course_can_view_participants_as_teacher_without_required_caps() {
4024+
global $DB;
4025+
4026+
$this->resetAfterTest();
4027+
4028+
$course = $this->getDataGenerator()->create_course();
4029+
$coursecontext = context_course::instance($course->id);
4030+
4031+
$user = $this->getDataGenerator()->create_user();
4032+
$roleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
4033+
$this->getDataGenerator()->enrol_user($user->id, $course->id, $roleid);
4034+
4035+
$this->setUser($user);
4036+
4037+
// Disable the capabilities.
4038+
assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT, $roleid, $coursecontext);
4039+
assign_capability('moodle/course:enrolreview', CAP_PROHIBIT, $roleid, $coursecontext);
4040+
4041+
$this->assertFalse(course_can_view_participants($coursecontext));
4042+
}
4043+
4044+
/**
4045+
* Check that an exception is not thrown if we can view the participants page.
4046+
*/
4047+
public function test_course_require_view_participants() {
4048+
$this->resetAfterTest();
4049+
4050+
$course = $this->getDataGenerator()->create_course();
4051+
$coursecontext = context_course::instance($course->id);
4052+
4053+
$user = $this->getDataGenerator()->create_user();
4054+
$this->getDataGenerator()->enrol_user($user->id, $course->id);
4055+
4056+
$this->setUser($user);
4057+
4058+
course_require_view_participants($coursecontext);
4059+
}
4060+
4061+
/**
4062+
* Check that an exception is thrown if we can't view the participants page.
4063+
*/
4064+
public function test_course_require_view_participants_as_student_on_site() {
4065+
$this->resetAfterTest();
4066+
4067+
$course = $this->getDataGenerator()->create_course();
4068+
4069+
$user = $this->getDataGenerator()->create_user();
4070+
$this->getDataGenerator()->enrol_user($user->id, $course->id);
4071+
4072+
$this->setUser($user);
4073+
4074+
$this->expectException('required_capability_exception');
4075+
course_require_view_participants(context_system::instance());
4076+
}
39074077
}

enrol/externallib.php

+12-12
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public static function get_enrolled_users_with_capability_parameters() {
8686
*/
8787
public static function get_enrolled_users_with_capability($coursecapabilities, $options) {
8888
global $CFG, $DB;
89+
90+
require_once($CFG->dirroot . '/course/lib.php');
8991
require_once($CFG->dirroot . "/user/lib.php");
9092

9193
if (empty($coursecapabilities)) {
@@ -145,11 +147,8 @@ public static function get_enrolled_users_with_capability($coursecapabilities, $
145147
throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
146148
}
147149

148-
if ($courseid == SITEID) {
149-
require_capability('moodle/site:viewparticipants', $context);
150-
} else {
151-
require_capability('moodle/course:viewparticipants', $context);
152-
}
150+
course_require_view_participants($context);
151+
153152
// The accessallgroups capability is needed to use this option.
154153
if (!empty($groupid) && groups_is_member($groupid)) {
155154
require_capability('moodle/site:accessallgroups', $coursecontext);
@@ -293,7 +292,9 @@ public static function get_users_courses_parameters() {
293292
* @return array of courses
294293
*/
295294
public static function get_users_courses($userid) {
296-
global $USER, $DB;
295+
global $CFG, $USER, $DB;
296+
297+
require_once($CFG->dirroot . '/course/lib.php');
297298

298299
// Do basic automatic PARAM checks on incoming data, using params description
299300
// If any problems are found then exceptions are thrown with helpful error messages
@@ -312,7 +313,7 @@ public static function get_users_courses($userid) {
312313
continue;
313314
}
314315

315-
if ($userid != $USER->id and !has_capability('moodle/course:viewparticipants', $context)) {
316+
if ($userid != $USER->id and !course_can_view_participants($context)) {
316317
// we need capability to view participants
317318
continue;
318319
}
@@ -520,6 +521,8 @@ public static function get_enrolled_users_parameters() {
520521
*/
521522
public static function get_enrolled_users($courseid, $options = array()) {
522523
global $CFG, $USER, $DB;
524+
525+
require_once($CFG->dirroot . '/course/lib.php');
523526
require_once($CFG->dirroot . "/user/lib.php");
524527

525528
$params = self::validate_parameters(
@@ -600,11 +603,8 @@ public static function get_enrolled_users($courseid, $options = array()) {
600603
throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
601604
}
602605

603-
if ($courseid == SITEID) {
604-
require_capability('moodle/site:viewparticipants', $context);
605-
} else {
606-
require_capability('moodle/course:viewparticipants', $context);
607-
}
606+
course_require_view_participants($context);
607+
608608
// to overwrite this parameter, you need role:review capability
609609
if ($withcapability) {
610610
require_capability('moodle/role:review', $coursecontext);

lib/classes/analytics/target/no_teaching.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public static function get_name() : \lang_string {
6363
* @return \core_analytics\prediction_action[]
6464
*/
6565
public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false) {
66+
global $CFG;
67+
68+
require_once($CFG->dirroot . '/course/lib.php');
6669

6770
// No need to call the parent as the parent's action is view details and this target only have 1 feature.
6871
$actions = array();
@@ -75,7 +78,7 @@ public function prediction_actions(\core_analytics\prediction $prediction, $incl
7578
$actions['viewcourse'] = new \core_analytics\prediction_action('viewcourse', $prediction,
7679
$url, $pix, get_string('view'));
7780

78-
if (has_any_capability(['moodle/course:viewparticipants', 'moodle/course:enrolreview'], $sampledata['context'])) {
81+
if (course_can_view_participants($sampledata['context'])) {
7982
$url = new \moodle_url('/user/index.php', array('id' => $course->id));
8083
$pix = new \pix_icon('i/cohort', get_string('participants'));
8184
$actions['viewparticipants'] = new \core_analytics\prediction_action('viewparticipants', $prediction,

0 commit comments

Comments
 (0)