diff --git a/group/clientlib.js b/group/clientlib.js index 5a5528e51de52..6f74d1cf80232 100644 --- a/group/clientlib.js +++ b/group/clientlib.js @@ -21,7 +21,7 @@ function UpdatableGroupsCombo(wwwRoot, courseId) { var membersComboEl = document.getElementById("members"); if (membersComboEl) { - // Clear the members combo box. + // Clear the members list box. while (membersComboEl.firstChild) { membersComboEl.removeChild(membersComboEl.firstChild); } @@ -30,7 +30,7 @@ function UpdatableGroupsCombo(wwwRoot, courseId) { if (groupsComboEl && o.responseText) { var groups = eval("("+o.responseText+")"); - // Populate the groups combo box. + // Populate the groups list box. for (var i=0; iid = $member->id; - $user->name = fullname($member, true); - $member_names[] = $user; + $roles = array(); + if ($groupmemberroles = groups_get_members_by_role($groupid,$courseid,'u.id,u.firstname,u.lastname')) { + foreach($groupmemberroles as $roleid=>$roledata) { + $shortroledata=new StdClass; + $shortroledata->name=$roledata->name; + $shortroledata->users=array(); + foreach($roledata->users as $member) { + $shortmember=new StdClass; + $shortmember->id=$member->id; + $shortmember->name=fullname($member, true); + $shortroledata->users[]=$shortmember; + } + $roles[]=$shortroledata; } - $json = new Services_JSON(); - echo $json->encode($member_names); } + $json = new Services_JSON(); + echo $json->encode($roles); die; // Client side JavaScript takes it from here. case 'deletegroup': @@ -194,21 +199,22 @@ $member_names = array(); +$atleastonemember = false; if ($sel_groupid) { - if ($members = groups_get_members($groupid)) { - foreach($members as $member) { - $member_names[$member->id] = fullname($member, true); + + if ($groupmemberroles = groups_get_members_by_role($groupid,$courseid,'u.id,u.firstname,u.lastname')) { + foreach($groupmemberroles as $roleid=>$roledata) { + echo ''; + foreach($roledata->users as $member) { + echo ''; + $atleastonemember = true; + } + echo ''; } - } + } } - -if ($member_names) { - // Put the groupings into a hash and sort them - foreach ($member_names as $userid=>$username) { - echo "\n"; - } - -} else { + +if (!$atleastonemember) { // Print an empty option to avoid the XHTML error of having an empty select element echo ''; } diff --git a/group/lib.php b/group/lib.php index 0f9a495fdaeea..a1ac96994cd5e 100644 --- a/group/lib.php +++ b/group/lib.php @@ -300,20 +300,19 @@ function groups_delete_groupings($courseid, $showfeedback=false) { /* =================================== */ /** - * Gets the users for a course who are not in a specified group + * Gets the users for a course who are not in a specified group, and returns + * them in an array organised by role. For the array format, see + * groups_get_members_by_role. * @param int $groupid The id of the group * @param string searchtext similar to searchtext in role assign, search - * @return array An array of the userids of the non-group members, or false if - * an error occurred. - * This function was changed to get_users_by_capability style - * mostly because of the searchtext requirement + * @return array An array of role id or '*' => information about that role + * including a list of users */ -function groups_get_users_not_in_group($courseid, $groupid, $searchtext='', $sort = 'u.lastname ASC') { +function groups_get_users_not_in_group_by_role($courseid, $groupid, $searchtext='', $sort = 'u.lastname ASC') { global $CFG; - $context = get_context_instance(CONTEXT_COURSE, $courseid); - + if ($searchtext !== '') { // Search for a subset of remaining users $LIKE = sql_ilike(); $FULLNAME = sql_fullname(); @@ -322,6 +321,41 @@ function groups_get_users_not_in_group($courseid, $groupid, $searchtext='', $sor $wheresearch = ''; } +/// Get list of allowed roles + if(!($validroleids=groups_get_possible_roles($context))) { + return; + } + $roleids = '('.implode(',', $validroleids).')'; + +/// Construct the main SQL + $select = " SELECT r.id AS roleid,r.shortname AS roleshortname,r.name AS rolename, + u.id AS userid, u.firstname, u.lastname"; + $from = " FROM {$CFG->prefix}user u + INNER JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id + INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid"; + + $where = " WHERE ra.contextid ".get_related_contexts_string($context)." + AND u.deleted = 0 + AND ra.roleid in $roleids + AND u.id NOT IN (SELECT userid + FROM {$CFG->prefix}groups_members + WHERE groupid = $groupid) + $wheresearch"; + $orderby = " ORDER BY $sort"; + + return groups_calculate_role_people(get_recordset_sql( + $select.$from.$where.$orderby)); +} + + +/** + * Obtains a list of the possible roles that group members might come from, + * on a course. Generally this includes all the roles who would have + * course:view on that course, except the doanything roles. + * @param object $context Context of course + * @return Array of role ID integers, or false if error/none. + */ +function groups_get_possible_roles($context) { $capability = 'moodle/course:view'; $doanything = false; @@ -350,28 +384,10 @@ function groups_get_users_not_in_group($courseid, $groupid, $searchtext='', $sor if (empty($validroleids)) { return false; } - $roleids = '('.implode(',', $validroleids).')'; + return $validroleids; } else { return false; // No need to continue, since no roles have this capability set - } - -/// Construct the main SQL - $select = " SELECT u.id, u.firstname, u.lastname"; - $from = " FROM {$CFG->prefix}user u - INNER JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id - INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid"; - - $where = " WHERE ra.contextid ".get_related_contexts_string($context)." - AND u.deleted = 0 - AND ra.roleid in $roleids - AND u.id NOT IN (SELECT userid - FROM {$CFG->prefix}groups_members - WHERE groupid = $groupid) - $wheresearch"; - $groupby = " GROUP BY u.id, u.firstname, u.lastname "; - $orderby = " ORDER BY $sort"; - - return get_records_sql($select.$from.$where.$groupby.$orderby); + } } @@ -491,4 +507,127 @@ function groups_unassign_grouping($groupingid, $groupid) { return delete_records('groupings_groups', 'groupingid', $groupingid, 'groupid', $groupid); } +/** + * Lists users in a group based on their role on the course. + * Returns false if there's an error or there are no users in the group. + * Otherwise returns an array of role ID => role data, where role data includes: + * (role) $id, $shortname, $name + * $users: array of objects for each user which include the specified fields + * Users who do not have a role are stored in the returned array with key '-' + * and pseudo-role details (including a name, 'No role'). Users with multiple + * roles, same deal with key '*' and name 'Multiple roles'. You can find out + * which roles each has by looking in the $roles array of the user object. + * @param int $groupid + * @param int $courseid Course ID (should match the group's course) + * @param string $fields List of fields from user table prefixed with u, default 'u.*' + * @param string $sort SQL ORDER BY clause, default 'u.lastname ASC' + * @return array Complex array as described above + */ +function groups_get_members_by_role($groupid, $courseid, $fields='u.*', $sort='u.lastname ASC') { + global $CFG; + + // Retrieve information about all users and their roles on the course or + // parent ('related') contexts + $context=get_context_instance(CONTEXT_COURSE,$courseid); + $rs=get_recordset_sql($crap="SELECT r.id AS roleid,r.shortname AS roleshortname,r.name AS rolename, + u.id AS userid,$fields + FROM {$CFG->prefix}groups_members gm + INNER JOIN {$CFG->prefix}user u ON u.id = gm.userid + INNER JOIN {$CFG->prefix}role_assignments ra + ON ra.userid = u.id + INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid + WHERE gm.groupid='$groupid' + AND ra.contextid ".get_related_contexts_string($context)." + ORDER BY r.sortorder,$sort"); + + return groups_calculate_role_people($rs); +} + +/** + * Internal function used by groups_get_members_by_role to handle the + * results of a database query that includes a list of users and possible + * roles on a course. + * + * @param object $rs The record set (may be false) + * @return array As described in groups_get_members_by_role + */ +function groups_calculate_role_people($rs) { + global $CFG; + if(!$rs) { + return false; + } + + // Array of all involved roles + $roles=array(); + // Array of all retrieved users + $users=array(); + // Fill arrays + while($rec=rs_fetch_next_record($rs)) { + // Create information about user if this is a new one + if(!array_key_exists($rec->userid,$users)) { + // User data includes all the optional fields, but not any of the + // stuff we added to get the role details + $userdata=clone($rec); + unset($userdata->roleid); + unset($userdata->roleshortname); + unset($userdata->rolename); + unset($userdata->userid); + $userdata->id=$rec->userid; + + // Make an array to hold the list of roles for this user + $userdata->roles=array(); + $users[$rec->userid]=$userdata; + } + // If user has a role... + if(!is_null($rec->roleid)) { + // Create information about role if this is a new one + if(!array_key_exists($rec->roleid,$roles)) { + $roledata=new StdClass; + $roledata->id=$rec->roleid; + $roledata->shortname=$rec->roleshortname; + $roledata->name=$rec->rolename; + $roledata->users=array(); + $roles[$roledata->id]=$roledata; + } + // Record that user has role + $users[$rec->userid]->roles[] = $roles[$rec->roleid]; + } + } + rs_close($rs); + + // Return false if there weren't any users + if(count($users)==0) { + return false; + } + + // Add pseudo-role for multiple roles + $roledata=new StdClass; + $roledata->name=get_string('multipleroles','role'); + $roledata->users=array(); + $roles['*']=$roledata; + + // Now we rearrange the data to store users by role + foreach($users as $userid=>$userdata) { + $rolecount=count($userdata->roles); + if($rolecount==0) { + debugging("Unexpected: user $userid is missing roles"); + } else if($rolecount>1) { + $roleid='*'; + } else { + $roleid=$userdata->roles[0]->id; + } + $roles[$roleid]->users[$userid]=$userdata; + } + + // Delete roles not used + foreach($roles as $key=>$roledata) { + if(count($roledata->users)===0) { + unset($roles[$key]); + } + } + + // Return list of roles containing their users + return $roles; +} + ?> diff --git a/group/members.php b/group/members.php index 51981eb136746..eb88e74b78f35 100644 --- a/group/members.php +++ b/group/members.php @@ -77,12 +77,16 @@ $groupmembersoptions = ''; $groupmemberscount = 0; -if ($groupmembers = groups_get_members($groupid)) { - foreach($groupmembers as $member) { - $groupmembersoptions .= ''; - $groupmemberscount ++; +// Get members, organised by role, and display +if ($groupmemberroles = groups_get_members_by_role($groupid,$courseid,'u.id,u.firstname,u.lastname')) { + foreach($groupmemberroles as $roleid=>$roledata) { + $groupmembersoptions .= ''; + foreach($roledata->users as $member) { + $groupmembersoptions .= ''; + $groupmemberscount ++; + } + $groupmembersoptions .= ''; } - } else { $groupmembersoptions .= ''; } @@ -91,35 +95,42 @@ $potentialmembersoptions = ''; $potentialmemberscount = 0; -$potentialmembers = groups_get_users_not_in_group($courseid, $groupid, $searchtext); -if (!empty($potentialmembers)) { - $potentialmemberscount = count($potentialmembers); -} else { - $potentialmemberscount = 0; +// Get potential members, organised by role, and count them +$potentialmembersbyrole = groups_get_users_not_in_group_by_role($courseid, $groupid, $searchtext); +$potentialmemberscount=0; +$potentialmembersids=array(); +if (!empty($potentialmembersbyrole)) { + foreach($potentialmembersbyrole as $roledata) { + $potentialmemberscount+=count($roledata->users); + $potentialmembersids=array_merge($potentialmembersids,array_keys($roledata->users)); + } } + if ($potentialmemberscount <= MAX_USERS_PER_PAGE) { - if ($potentialmembers != false) { + if ($potentialmemberscount != 0) { // Get other groups user already belongs to $sql = "SELECT u.id AS userid, g.* FROM {$CFG->prefix}user u " . "INNER JOIN {$CFG->prefix}groups_members gm ON u.id = gm.userid " . "INNER JOIN {$CFG->prefix}groups g ON gm.groupid = g.id " . - "WHERE u.id IN (".implode(',',array_keys($potentialmembers)).") AND g.courseid = {$course->id} "; + "WHERE u.id IN (".implode(',',$potentialmembersids).") AND g.courseid = {$course->id} "; $rs = get_recordset_sql($sql); $groups = array(); $usergroups = array(); while ($usergroup = rs_fetch_next_record($rs)) { $usergroups[$usergroup->userid][$usergroup->id] = $usergroup; } - - // Put the groupings into a hash and sorts them - foreach ($potentialmembers as $userid => $user) { - $nonmembers[$userid] = fullname($user)." (".@count($usergroups[$userid]).")"; - } - - // Print out the HTML - foreach($nonmembers as $id => $name) { - $potentialmembersoptions .= "\n"; + rs_close($rs); + + foreach($potentialmembersbyrole as $roleid=>$roledata) { + $potentialmembersoptions .= ''; + foreach($roledata->users as $member) { + $name=htmlspecialchars(fullname($member, true)); + $potentialmembersoptions .= ''; + } + $potentialmembersoptions .= ''; } } else { $potentialmembersoptions .= ''; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index e056b025bf970..ad296ca3d5d74 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -90,6 +90,7 @@ $string['manageroles'] = 'Manage roles'; $string['metaassignerror'] = 'Can not assign this role to user \"$a\" because Manage metacourse capability is needed.'; $string['metaunassignerror'] = 'Role of user \"$a\" was automatically reassigned, please unassign the role in child courses instead.'; +$string['multipleroles'] = 'Multiple roles'; $string['overridepermissions'] = 'Override permissions'; $string['overridepermissionsin'] = 'Override permissions in $a'; $string['morethan'] = 'More than $a';