Merge branch 'wip-MDL-27177-master' of git://github.com/abgreeve/moodle

This commit is contained in:
Dan Poltawski 2015-08-25 09:54:40 +01:00
commit f1352da1ba
5 changed files with 224 additions and 23 deletions

View File

@ -1067,3 +1067,60 @@ function user_mygrades_url($userid = null, $courseid = SITEID) {
}
return $url;
}
/**
* Check if a user has the permission to viewdetails in a shared course's context.
*
* @param object $user The other user's details.
* @param object $course Use this course to see if we have permission to see this user's profile.
* @param context $usercontext The user context if available.
* @return bool true for ability to view this user, else false.
*/
function user_can_view_profile($user, $course = null, $usercontext = null) {
global $USER, $CFG;
if ($user->deleted) {
return false;
}
// If any of these four things, return true.
// Number 1.
if ($USER->id == $user->id) {
return true;
}
// Number 2.
if (empty($CFG->forceloginforprofiles)) {
return true;
}
if (empty($usercontext)) {
$usercontext = context_user::instance($user->id);
}
// Number 3.
if (has_capability('moodle/user:viewdetails', $usercontext)) {
return true;
}
// Number 4.
if (has_coursecontact_role($user->id)) {
return true;
}
if (isset($course)) {
$sharedcourses = array($course);
} else {
$sharedcourses = enrol_get_shared_courses($USER->id, $user->id, true);
}
foreach ($sharedcourses as $sharedcourse) {
$coursecontext = context_course::instance($sharedcourse->id);
if (has_capability('moodle/user:viewdetails', $coursecontext)) {
if (!groups_user_groups_visible($sharedcourse, $user->id)) {
// Not a member of the same group.
continue;
}
return true;
}
}
return false;
}

View File

@ -36,6 +36,7 @@ require_once(dirname(__FILE__) . '/../config.php');
require_once($CFG->dirroot . '/my/lib.php');
require_once($CFG->dirroot . '/tag/lib.php');
require_once($CFG->dirroot . '/user/profile/lib.php');
require_once($CFG->dirroot . '/user/lib.php');
require_once($CFG->libdir.'/filelib.php');
$userid = optional_param('id', 0, PARAM_INT);
@ -75,10 +76,7 @@ if ((!$user = $DB->get_record('user', array('id' => $userid))) || ($user->delete
$currentuser = ($user->id == $USER->id);
$context = $usercontext = context_user::instance($userid, MUST_EXIST);
if (!$currentuser &&
!empty($CFG->forceloginforprofiles) &&
!has_capability('moodle/user:viewdetails', $context) &&
!has_coursecontact_role($userid)) {
if (!user_can_view_profile($user, null, $context)) {
// Course managers can be browsed at site level. If not forceloginforprofiles, allow access (bug #4366).
$struser = get_string('user');

View File

@ -9,15 +9,18 @@ Feature: Access to full profiles of users
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student2@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
| Course 2 | C2 | topics |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| teacher1 | C1 | editingteacher |
| student3 | C2 | student |
Scenario: Viewing full profiles with default settings
When I log in as "student1"
@ -60,3 +63,69 @@ Feature: Access to full profiles of users
And I follow "Student 2"
And I follow "Full profile"
Then I should see "First access to site"
@javascript
Scenario: Viewing own full profile
Given I log in as "student1"
When I follow "Profile" in the user menu
Then I should see "First access to site"
@javascript
Scenario: Viewing full profiles of someone with the course contact role
Given I log in as "admin"
And I navigate to "Courses" node in "Site administration > Appearance"
And I set the following fields to these values:
| Course creator | 1 |
And I press "Save changes"
And I navigate to "Assign system roles" node in "Site administration > Users > Permissions"
And I follow "Course creator"
And I click on "//div[@class='userselector']/descendant::option[contains(., 'Student 3')]" "xpath_element"
And I press "Add"
And I log out
When I log in as "student1"
And I follow "Messages" in the user menu
And I set the following fields to these values:
| Search people and messages | Student 3 |
And I press "Search people and messages"
And I follow "Picture of Student 3"
Then I should see "First access to site"
@javascript
Scenario: View full profiles of someone in the same group in a course with separate groups.
Given I log in as "admin"
And I am on site homepage
And I follow "Course 1"
And I follow "Edit settings"
And I set the following fields to these values:
| Group mode | Separate groups |
| Force group mode | Yes |
And I press "Save and display"
And I log out
When I log in as "student1"
And I follow "Messages" in the user menu
And I set the following fields to these values:
| Search people and messages | Student 2 |
And I press "Search people and messages"
And I follow "Picture of Student 2"
And I should not see "First access to site"
And I should see "The details of this user are not available to you"
And I log out
And I log in as "admin"
And I am on site homepage
And I follow "Course 1"
And I expand "Users" node
And I follow "Groups"
And I press "Create group"
And I set the following fields to these values:
| Group name | Group 1 |
And I press "Save changes"
And I add "Student 1 (student1@example.com)" user to "Group 1" group members
And I add "Student 2 (student2@example.com)" user to "Group 1" group members
And I log out
And I log in as "student1"
And I follow "Messages" in the user menu
And I set the following fields to these values:
| Search people and messages | Student 2 |
And I press "Search people and messages"
And I follow "Picture of Student 2"
Then I should see "First access to site"

View File

@ -398,4 +398,94 @@ class core_userliblib_testcase extends advanced_testcase {
$this->assertEquals(intval($matches[2]), $testsize);
}
/**
* Test user_can_view_profile
*/
public function test_user_can_view_profile() {
global $DB, $CFG;
$this->resetAfterTest();
// Create five users.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
$user7 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Add the course creator role to the course contact and assign a user to that role.
$CFG->coursecontact = '2';
$coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
$this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
// Create two courses.
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course2->id);
// Prepare another course with separate groups and groupmodeforce set to true.
$record = new stdClass();
$record->groupmode = 1;
$record->groupmodeforce = 1;
$course3 = $this->getDataGenerator()->create_course($record);
// Enrol users 1 and 2 in first course.
$this->getDataGenerator()->enrol_user($user1->id, $course1->id);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
// Enrol users 2 and 3 in second course.
$this->getDataGenerator()->enrol_user($user2->id, $course2->id);
$this->getDataGenerator()->enrol_user($user3->id, $course2->id);
// Enrol users 1, 4, and 5 into course 3.
$this->getDataGenerator()->enrol_user($user1->id, $course3->id);
$this->getDataGenerator()->enrol_user($user4->id, $course3->id);
$this->getDataGenerator()->enrol_user($user5->id, $course3->id);
// Remove capability moodle/user:viewdetails in course 2.
assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
$coursecontext->mark_dirty();
// Set current user to user 1.
$this->setUser($user1);
// User 1 can see User 1's profile.
$this->assertTrue(user_can_view_profile($user1));
$tempcfg = $CFG->forceloginforprofiles;
$CFG->forceloginforprofiles = 0;
// Not forced to log in to view profiles, should be able to see all profiles besides user 6.
$users = array($user1, $user2, $user3, $user4, $user5, $user7);
foreach ($users as $user) {
$this->assertTrue(user_can_view_profile($user));
}
// Restore setting.
$CFG->forceloginforprofiles = $tempcfg;
// User 1 can not see user 6 as they have been deleted.
$this->assertFalse(user_can_view_profile($user6));
// User 1 can see User 7 as they are a course contact.
$this->assertTrue(user_can_view_profile($user7));
// User 1 is in a course with user 2 and has the right capability - return true.
$this->assertTrue(user_can_view_profile($user2));
// User 1 is not in a course with user 3 - return false.
$this->assertFalse(user_can_view_profile($user3));
// Set current user to user 2.
$this->setUser($user2);
// User 2 is in a course with user 3 but does not have the right capability - return false.
$this->assertFalse(user_can_view_profile($user3));
// Set user 1 in one group and users 4 and 5 in another group.
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
groups_add_member($group1->id, $user1->id);
groups_add_member($group2->id, $user4->id);
groups_add_member($group2->id, $user5->id);
$this->setUser($user1);
// Check that user 1 can not see user 4.
$this->assertFalse(user_can_view_profile($user4));
// Check that user 5 can see user 4.
$this->setUser($user5);
$this->assertTrue(user_can_view_profile($user4));
$CFG->coursecontact = null;
}
}

View File

@ -24,6 +24,7 @@
require_once("../config.php");
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/tag/lib.php');
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/badgeslib.php');
@ -125,9 +126,8 @@ if ($currentuser) {
$PAGE->set_title("$strpersonalprofile: ");
$PAGE->set_heading("$strpersonalprofile: ");
// Check course level capabilities.
if (!has_capability('moodle/user:viewdetails', $coursecontext) && // Normal enrolled user or mnager.
($user->deleted or !has_capability('moodle/user:viewdetails', $usercontext))) { // Usually parent.
// Check to see if the user can see this user's profile.
if (!user_can_view_profile($user, $course, $usercontext) && !$isparent) {
print_error('cannotviewprofile');
}
@ -152,22 +152,9 @@ if ($currentuser) {
exit;
}
// If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group.
// Except when we are a parent, in which case we would not be in any group.
if (groups_get_course_groupmode($course) == SEPARATEGROUPS
and $course->groupmodeforce
and !has_capability('moodle/site:accessallgroups', $coursecontext)
and !has_capability('moodle/site:accessallgroups', $coursecontext, $user->id)
and !$isparent) {
if (!isloggedin() or isguestuser()) {
// Do not use require_login() here because we might have already used require_login($course).
redirect(get_login_url());
}
$mygroups = array_keys(groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid, 'g.id, g.name'));
$usergroups = array_keys(groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name'));
if (!array_intersect($mygroups, $usergroups)) {
print_error("groupnotamember", '', "../course/view.php?id=$course->id");
}
if (!isloggedin() or isguestuser()) {
// Do not use require_login() here because we might have already used require_login($course).
redirect(get_login_url());
}
}