]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-12991 Updated groups UI so that it categorises members and potential members...
authorsam_marshall <sam_marshall>
Thu, 17 Jan 2008 11:28:54 +0000 (11:28 +0000)
committersam_marshall <sam_marshall>
Thu, 17 Jan 2008 11:28:54 +0000 (11:28 +0000)
group/clientlib.js
group/index.php
group/lib.php
group/members.php
lang/en_utf8/role.php

index 5a5528e51de52319776c9ac795f005c433b12e31..6f74d1cf8023229c96e786d81f67d261c96a872a 100644 (file)
@@ -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; i<groups.length; i++) {
                         var optionEl = document.createElement("option");
                         optionEl.setAttribute("value", groups[i].id);
@@ -51,7 +51,7 @@ function UpdatableGroupsCombo(wwwRoot, courseId) {
 
     };
 
-    // Add onchange event to groups combo box.
+    // Add onchange event to groups list box.
     // Okay, this is not working in IE. The onchange is never fired...
     // I'm hard coding the onchange in ../index.php. Not ideal, but it works
     // then. vyshane AT moodle DOT com.
@@ -80,21 +80,27 @@ function UpdatableMembersCombo(wwwRoot, courseId) {
                if (o.responseText !== undefined) {
                 var selectEl = document.getElementById("members");
                    if (selectEl && o.responseText) {
-                       var members = eval("("+o.responseText+")");
+                    var roles = eval("("+o.responseText+")");
 
-                    // Clear the members combo box.
+                    // Clear the members list box.
                     if (selectEl) {
                         while (selectEl.firstChild) {
                             selectEl.removeChild(selectEl.firstChild);
                         }
                     }
-                       // Populate the members combo box.
-                    for (var i=0; i<members.length; i++) {
-                        var optionEl = document.createElement("option");
-                        optionEl.setAttribute("value", members[i].id);
-                        optionEl.title = members[i].name;
-                        optionEl.innerHTML = members[i].name;
-                        selectEl.appendChild(optionEl);
+                    // Populate the members list box.
+                    for (var i=0; i<roles.length; i++) {
+                        var optgroupEl = document.createElement("optgroup");
+                        optgroupEl.setAttribute("label",roles[i].name);
+
+                        for(var j=0; j<roles[i].users.length; j++) {
+                            var optionEl = document.createElement("option");
+                            optionEl.setAttribute("value", roles[i].users[j].id);
+                            optionEl.title = roles[i].users[j].name;
+                            optionEl.innerHTML = roles[i].users[j].name;
+                            optgroupEl.appendChild(optionEl);
+                        }
+                        selectEl.appendChild(optgroupEl);
                     }
                 }
                }
@@ -128,7 +134,7 @@ UpdatableMembersCombo.prototype.refreshMembers = function (groupId) {
         spanEl.innerHTML = selectEl.options[selectEl.selectedIndex].title;
     }
 
-    // Clear the members combo box.
+    // Clear the members list box.
     selectEl = document.getElementById("members");
     if (selectEl) {
         while (selectEl.firstChild) {
index ea316c881fd8918db8c8ca421e73c21872ff6d77..d70ebf2e1286a42bc55e7e3bd4b03c319eec4f9e 100644 (file)
@@ -46,18 +46,23 @@ switch ($action) {
         break;
 
     case 'ajax_getmembersingroup':
-        $members = array();
-        if ($members = groups_get_members($groupid)) {
-            $member_names = array();
-            foreach($members as $member) {
-                $user = new object();
-                $user->id   = $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 @@ echo ' onclick="window.status=this.options[this.selectedIndex].title;" onmouseou
 
 $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 '<optgroup label="'.htmlspecialchars($roledata->name).'">';
+            foreach($roledata->users as $member) {
+                echo '<option value="'.$member->id.'">'.fullname($member, true).'</option>';
+                $atleastonemember = true;
+            }
+            echo '</optgroup>';        
         }
-    }
+    } 
 }
-
-if ($member_names) {
-    // Put the groupings into a hash and sort them
-    foreach ($member_names as $userid=>$username) {
-        echo "<option value=\"{$userid}\" title=\"{$username}\">{$username}</option>\n";
-    }
-
-} else {
+    
+if (!$atleastonemember) {
     // Print an empty option to avoid the XHTML error of having an empty select element
     echo '<option>&nbsp;</option>';
 }
index 0f9a495fdaeea91cb5b00c54749f354188ff6243..a1ac96994cd5e559bfccd4b86065b72c0b0470bc 100644 (file)
@@ -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;
+}
+
 ?>
index 51981eb136746c5c0f08e478190d35d13d44fa7f..eb88e74b78f350238fedc6443b2e8a9c9be52fe0 100644 (file)
@@ -77,12 +77,16 @@ if ($frm = data_submitted() and confirm_sesskey()) {
 $groupmembersoptions = '';
 $groupmemberscount = 0;
 
-if ($groupmembers = groups_get_members($groupid)) {
-    foreach($groupmembers as $member) {
-        $groupmembersoptions .= '<option value="'.$member->id.'">'.fullname($member, true).'</option>';
-        $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 .= '<optgroup label="'.htmlspecialchars($roledata->name).'">';
+        foreach($roledata->users as $member) {
+            $groupmembersoptions .= '<option value="'.$member->id.'">'.fullname($member, true).'</option>';
+            $groupmemberscount ++;
+        }
+        $groupmembersoptions .= '</optgroup>';
     }
-
 } else {
     $groupmembersoptions .= '<option>&nbsp;</option>';
 }
@@ -91,35 +95,42 @@ $potentialmembers = array();
 $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 .= "<option value=\"$id\">$name</option>\n";
+        rs_close($rs);
+
+        foreach($potentialmembersbyrole as $roleid=>$roledata) {
+            $potentialmembersoptions .= '<optgroup label="'.htmlspecialchars($roledata->name).'">';
+            foreach($roledata->users as $member) {
+                $name=htmlspecialchars(fullname($member, true));
+                $potentialmembersoptions .= '<option value="'.$member->id.
+                    '" title="'.$name.'">'.$name.
+                    ' ('.@count($usergroups[$member->id]).')</option>';
+            }
+            $potentialmembersoptions .= '</optgroup>';
         }
     } else {
         $potentialmembersoptions .= '<option>&nbsp;</option>';
index e056b025bf9709942768f53db877dae9a3746c1f..ad296ca3d5d7491b5f19573b8138fa979d3f964d 100644 (file)
@@ -90,6 +90,7 @@ $string['localroles'] = 'Locally assigned roles';
 $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';