]> git.mjollnir.org Git - moodle.git/commitdiff
user selection: MDL-16993 Convert the assign roles page to use the new components.
authortjhunt <tjhunt>
Thu, 30 Oct 2008 10:49:15 +0000 (10:49 +0000)
committertjhunt <tjhunt>
Thu, 30 Oct 2008 10:49:15 +0000 (10:49 +0000)
At the same time, I took the opportunity to try to work on some of the usability issues on this page. Note that I have not quite finished! So don't comment until tomorrow.

In the course of doing this, I also did: half of

MDL-11529 Show the number of assignees of each role in the change role dropdown on this page.
MDL-17067 Make it clear in the UI that admins are not allowed to unassign themselves.

admin/roles/assign.html [deleted file]
admin/roles/assign.php
admin/roles/roles.js
group/members.php
lang/en_utf8/error.php
lang/en_utf8/role.php
lib/accesslib.php
theme/standard/styles_layout.css
user/selector/lib.php
user/selector/script.js
user/selector/search.php

diff --git a/admin/roles/assign.html b/admin/roles/assign.html
deleted file mode 100755 (executable)
index 099bb2d..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-<form id="assignform" method="post" action="">
-<div style="text-align:center;">
-<label for="extendperiod"><?php print_string('enrolperiod') ?></label> <?php choose_from_menu($periodmenu, "extendperiod", $defaultperiod, $unlimitedperiod); ?>
-<label for="extendbase"><?php print_string('startingfrom') ?></label> <?php choose_from_menu($basemenu, "extendbase", 3, ""); ?>
-
-<input type="hidden" name="userid" value="<?php p($userid) ?>" />
-<input type="hidden" name="courseid" value="<?php p($courseid) ?>" />
-<input type="hidden" name="sesskey" value="<?php p(sesskey()) ?>" />
-<input type="hidden" name="contextid" value="<?php p($contextid) ?>" />
-<input type="hidden" name="roleid" value="<?php p($roleid) ?>" />
-  <table summary="" style="margin-left:auto;margin-right:auto" border="0" cellpadding="5" cellspacing="0">
-    <tr>
-      <td valign="top">
-          <label for="removeselect"><?php print_string('existingusers', 'role', count($contextusers)); ?></label>
-          <br />
-          <select name="removeselect[]" size="20" id="removeselect" multiple="multiple"
-                  onfocus="getElementById('assignform').add.disabled=true;
-                           getElementById('assignform').remove.disabled=false;
-                           getElementById('assignform').addselect.selectedIndex=-1;">
-
-          <?php
-            $i = 0;
-            foreach ($contextusers as $contextuser) {
-                $fullname = fullname($contextuser, true);
-                if ($contextuser->hidden) {
-                    $hidden=' ('.get_string('hiddenassign').') ';
-                } else {
-                    $hidden="";
-                }
-                echo "<option value=\"$contextuser->id\">".$fullname.", ".$contextuser->email.$hidden."</option>\n";
-                $i++;    
-            }
-            if ($i==0) {
-                echo '<option/>'; // empty select breaks xhtml strict
-            }
-          ?>
-          
-          </select></td>
-      <td valign="top">
-        <br />
-        <label title="<?php print_string('hiddenassign') ?>">
-            <input type="checkbox" name="hidden" value="1" />
-            <img src="<?php echo $CFG->pixpath; ?>/t/hide.gif" alt="<?php print_string('hiddenassign') ?>" class="hide-show-image" />
-            <?php helpbutton('hiddenassign', get_string('hiddenassign')); ?>
-        </label>
-        <?php check_theme_arrows(); ?>
-        <p class="arrow_button">
-            <input name="add" id="add" type="submit" value="<?php echo $THEME->larrow.'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
-            <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$THEME->rarrow; ?>" title="<?php print_string('remove'); ?>" />
-        </p>
-      </td>
-      <td valign="top">
-          <label for="addselect"><?php print_string('potentialusers', 'role', $usercount); ?></label>
-          <br />
-          <select name="addselect[]" size="20" id="addselect" multiple="multiple"
-                  onfocus="getElementById('assignform').add.disabled=false;
-                           getElementById('assignform').remove.disabled=true;
-                           getElementById('assignform').removeselect.selectedIndex=-1;">
-          <?php
-            $i=0;
-            if (!empty($searchtext)) {
-                echo "<optgroup label=\"$strsearchresults (" . $usercount . ")\">\n";
-                foreach ($availableusers as $user) {
-                    $fullname = fullname($user, true);
-                    echo "<option value=\"$user->id\">".$fullname.", ".$user->email."</option>\n";
-                    $i++;
-                }
-                echo "</optgroup>\n";
-
-            } else {
-                if ($usercount > MAX_USERS_PER_PAGE) {
-                    echo '<optgroup label="'.get_string('toomanytoshow').'"><option></option></optgroup>'."\n"
-                          .'<optgroup label="'.get_string('trysearching').'"><option></option></optgroup>'."\n";
-                } else {
-                    foreach ($availableusers as $user) {
-                        $fullname = fullname($user, true);
-                        echo "<option value=\"$user->id\">".$fullname.", ".$user->email."</option>\n";
-                        $i++;
-                    }
-                }
-            }
-            if ($i==0) {
-                echo '<option/>'; // empty select breaks xhtml strict
-            }
-          ?>
-         </select>
-         <br />
-         <label for="searchtext" class="accesshide"><?php p($strsearch) ?></label>
-         <input type="text" name="searchtext" id="searchtext" size="30" value="<?php p($searchtext, true) ?>"
-                  onfocus ="getElementById('assignform').add.disabled=true;
-                            getElementById('assignform').remove.disabled=true;
-                            getElementById('assignform').removeselect.selectedIndex=-1;
-                            getElementById('assignform').addselect.selectedIndex=-1;"
-                  onkeydown = "var keyCode = event.which ? event.which : event.keyCode;
-                               if (keyCode == 13) {
-                                    getElementById('assignform').previoussearch.value=1;
-                                    getElementById('assignform').submit();
-                               } " />
-         <input name="search" id="search" type="submit" value="<?php p($strsearch) ?>" />
-         <?php
-              if (!empty($searchtext)) {
-                  echo '<input name="showall" id="showall" type="submit" value="'.$strshowall.'" />'."\n";
-              }
-         ?>
-       </td>
-    </tr>
-  </table>
-</div>
-</form>
-
-
index 8c78c30a9c9498ba82b8468f1c85a076de7ecfb8..a072fbf1ad64bd3f57c30231a221d6e4acce0d4b 100755 (executable)
@@ -1,28 +1,25 @@
 <?php // $Id$
       // Script to assign users to contexts
 
-    require_once('../../config.php');
-    require_once($CFG->dirroot.'/mod/forum/lib.php');
+    require_once(dirname(__FILE__) . '/../../config.php');
     require_once($CFG->libdir.'/adminlib.php');
+    require_once($CFG->dirroot.'/user/selector/lib.php');
+    require_js(array('yui_yahoo', 'yui_dom', 'yui_event'));
+    require_js($CFG->admin . '/roles/roles.js');
 
-    define("MAX_USERS_PER_PAGE", 5000);
     define("MAX_USERS_TO_LIST_PER_ROLE", 10);
 
-    $contextid      = required_param('contextid',PARAM_INT); // context id
-    $roleid         = optional_param('roleid', 0, PARAM_INT); // required role id
-    $add            = optional_param('add', 0, PARAM_BOOL);
-    $remove         = optional_param('remove', 0, PARAM_BOOL);
-    $showall        = optional_param('showall', 0, PARAM_BOOL);
-    $searchtext     = optional_param('searchtext', '', PARAM_RAW); // search string
+    $contextid      = required_param('contextid',PARAM_INT);
+    $roleid         = optional_param('roleid', 0, PARAM_INT);
+    $userid         = optional_param('userid', 0, PARAM_INT); // needed for user tabs
+    $courseid       = optional_param('courseid', 0, PARAM_INT); // needed for user tabs
     $hidden         = optional_param('hidden', 0, PARAM_BOOL); // whether this assignment is hidden
     $extendperiod   = optional_param('extendperiod', 0, PARAM_INT);
     $extendbase     = optional_param('extendbase', 0, PARAM_INT);
-    $userid         = optional_param('userid', 0, PARAM_INT); // needed for user tabs
-    $courseid       = optional_param('courseid', 0, PARAM_INT); // needed for user tabs
 
     $errors = array();
 
-    $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/role/assign.php?contextid=' . $contextid;
+    $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/assign.php?contextid=' . $contextid;
     if (!empty($userid)) {
         $baseurl .= '&amp;userid='.$userid;
     }
         $course = clone($SITE);
     }
 
+/// Check login and permissions.
     require_login($course);
-
     require_capability('moodle/role:assign', $context);
 
-/// needed for tabs.php
-
+/// These are needed early because of tabs.php
     $overridableroles = get_overridable_roles($context, ROLENAME_BOTH);
     list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true);
 
-/// Get some language strings
+/// Make sure this user can assign this role
+    if ($roleid && !isset($assignableroles[$roleid])) {
+        $a = stdClass;
+        $a->role = $roleid;
+        $a->context = $contextname;
+        print_error('cannotassignrolehere', '', get_context_url($context), $a);
+    }
 
+/// Get some language strings
     $strpotentialusers = get_string('potentialusers', 'role');
     $strexistingusers = get_string('existingusers', 'role');
     $straction = get_string('assignroles', 'role');
@@ -74,6 +77,7 @@
     $strparticipants = get_string('participants');
     $strsearchresults = get_string('searchresults');
 
+/// Build the list of options for the enrolment period dropdown.
     $unlimitedperiod = get_string('unlimited');
     $defaultperiod = $course->enrolperiod;
     for ($i=1; $i<=365; $i++) {
@@ -81,6 +85,7 @@
         $periodmenu[$seconds] = get_string('numdays', '', $i);
     }
 
+/// Build the list of options for the starting from dropdown.
     $timeformat = get_string('strftimedate');
     $today = time();
     $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
         }
     }
 
-/// Make sure this user can assign this role
-
-    if ($roleid) {
-        if (!isset($assignableroles[$roleid])) {
-            error ('you can not override this role in this context');
-        }
-    }
-
-    if ($userid) {
+/// Print the header and tabs
+    if ($context->contextlevel == CONTEXT_USER) {
         $user = $DB->get_record('user', array('id'=>$userid));
         $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
-    }
 
-
-/// Print the header and tabs
-
-    if ($context->contextlevel == CONTEXT_USER) {
         /// course header
         $navlinks = array();
         if ($courseid != SITEID) {
         $showroles = 1;
         $currenttab = 'assign';
         include_once($CFG->dirroot.'/user/tabs.php');
+
     } else if ($context->contextlevel == CONTEXT_SYSTEM) {
         admin_externalpage_setup('assignroles');
         admin_externalpage_print_header();
-    } else if ($context->contextlevel==CONTEXT_COURSE and $context->instanceid == SITEID) {
+
+    } else if ($context->contextlevel == CONTEXT_COURSE and $context->instanceid == SITEID) {
         admin_externalpage_setup('frontpageroles');
         admin_externalpage_print_header();
         $currenttab = 'assign';
         include_once('tabs.php');
+
     } else {
         $currenttab = 'assign';
         include_once('tabs.php');
     }
 
-
-/// Process incoming role assignment
-
-    if ($frm = data_submitted()) {
-
-        if ($add and !empty($frm->addselect) and confirm_sesskey()) {
-
-            foreach ($frm->addselect as $adduser) {
-                if (!$adduser = clean_param($adduser, PARAM_INT)) {
-                    continue;
-                }
-                $allow = true;
-                if ($inmeta) {
-                    if (has_capability('moodle/course:managemetacourse', $context, $adduser)) {
-                        //ok
-                    } else {
-                        $managerroles = get_roles_with_capability('moodle/course:managemetacourse', CAP_ALLOW, $context);
-                        if (!empty($managerroles) and !array_key_exists($roleid, $managerroles)) {
-                            $erruser = $DB->get_record('user', array('id'=>$adduser), 'id, firstname, lastname');
-                            $errors[] = get_string('metaassignerror', 'role', fullname($erruser));
-                            $allow = false;
-                        }
-                    }
-                }
-                if ($allow) {
-                    switch($extendbase) {
-                        case 0:
-                            $timestart = $course->startdate;
-                            break;
-                        case 3:
-                            $timestart = $today;
-                            break;
-                        case 4:
-                            $timestart = $course->enrolstartdate;
-                            break;
-                        case 5:
-                            $timestart = $course->enrolenddate;
-                            break;
-                    }
-
-                    if($extendperiod > 0) {
-                        $timeend = $timestart + $extendperiod;
-                    } else {
-                        $timeend = 0;
-                    }
-                    if (! role_assign($roleid, $adduser, 0, $context->id, $timestart, $timeend, $hidden)) {
-                        $errors[] = "Could not add user with id $adduser to this role!";
-                    }
-                }
-            }
-            
-            $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
-            add_to_log($course->id, 'role', 'assign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
-        } else if ($remove and !empty($frm->removeselect) and confirm_sesskey()) {
-
-            $sitecontext = get_context_instance(CONTEXT_SYSTEM);
-            $topleveladmin = false;
-
-            // we only worry about this if the role has doanything capability at site level
-            if ($context->id == $sitecontext->id && $adminroles = get_roles_with_capability('moodle/site:doanything', CAP_ALLOW, $sitecontext)) {
-                foreach ($adminroles as $adminrole) {
-                    if ($adminrole->id == $roleid) {
-                        $topleveladmin = true;
-                    }
-                }
-            }
-
-            foreach ($frm->removeselect as $removeuser) {
-                $removeuser = clean_param($removeuser, PARAM_INT);
-
-                if ($topleveladmin && ($removeuser == $USER->id)) {   // Prevent unassigning oneself from being admin
-                    continue;
-                }
-
-                if (! role_unassign($roleid, $removeuser, 0, $context->id)) {
-                    $errors[] = "Could not remove user with id $removeuser from this role!";
-                } else if ($inmeta) {
-                    sync_metacourse($courseid);
-                    $newroles = get_user_roles($context, $removeuser, false);
-                    if (!empty($newroles) and !array_key_exists($roleid, $newroles)) {
-                        $erruser = $DB->get_record('user', array('id'=>$removeuser), 'id, firstname, lastname');
-                        $errors[] = get_string('metaunassignerror', 'role', fullname($erruser));
-                        $allow = false;
-                    }
-                }
-            }
-            
-            $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
-            add_to_log($course->id, 'role', 'unassign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
-        } else if ($showall) {
-            $searchtext = '';
-        }
-    }
-
+/// Print heading.
     if ($isfrontpage) {
         print_heading_with_help(get_string('frontpageroles', 'admin'), 'assignroles');
     } else {
         print_heading_with_help(get_string('assignrolesin', 'role', $contextname), 'assignroles');
     }
 
-    if ($context->contextlevel==CONTEXT_SYSTEM) {
+/// Print a warning if we are assigning system roles.
+    if ($context->contextlevel == CONTEXT_SYSTEM) {
         print_box(get_string('globalroleswarning', 'role'));
     }
 
-    if ($roleid) {
+    if ($roleid) {  /// UI for assigning a particular role.
 
-    /// Get all existing participants in this context.
-        // Why is this not done with get_users???
-
-        if (!$contextusers = get_role_users($roleid, $context, false, 'u.id, u.firstname, u.lastname, u.email, ra.hidden')) {
-            $contextusers = array();
+    /// Create the user selector objects.
+        $options = array('context' => $context, 'roleid' => $roleid);
+        if ($context->contextlevel > CONTEXT_COURSE) {
+            $potentialuserselector = new potential_assignees_below_course('addselect', $options);
+        } else {
+            $potentialuserselector = new potential_assignees_course_and_above('addselect', $options);
+        }
+        if ($context->contextlevel == CONTEXT_SYSTEM && is_admin_role($roleid)) {
+            $currentuserselector = new existing_role_holders_site_admin('removeselect', $options);
+        } else {
+            $currentuserselector = new existing_role_holders('removeselect', $options);
         }
 
-        $select  = "username <> 'guest' AND deleted = 0 AND confirmed = 1";
-        $params = array();
+    /// Process incoming role assignments
+        if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
+            $userstoassign = $potentialuserselector->get_selected_users();
+            if (!empty($userstoassign)) {
+
+                foreach ($userstoassign as $adduser) {
+                    $allow = true;
+                    if ($inmeta) {
+                        if (has_capability('moodle/course:managemetacourse', $context, $adduser->id)) {
+                            //ok
+                        } else {
+                            $managerroles = get_roles_with_capability('moodle/course:managemetacourse', CAP_ALLOW, $context);
+                            if (!empty($managerroles) and !array_key_exists($roleid, $managerroles)) {
+                                $erruser = $DB->get_record('user', array('id'=>$adduser->id), 'id, firstname, lastname');
+                                $errors[] = get_string('metaassignerror', 'role', fullname($erruser));
+                                $allow = false;
+                            }
+                        }
+                    }
 
-        $usercount = $DB->count_records_select('user', $select, $params) - count($contextusers);
+                    if ($allow) {
+                        switch($extendbase) {
+                            case 0:
+                                $timestart = $course->startdate;
+                                break;
+                            case 3:
+                                $timestart = $today;
+                                break;
+                            case 4:
+                                $timestart = $course->enrolstartdate;
+                                break;
+                            case 5:
+                                $timestart = $course->enrolenddate;
+                                break;
+                        }
 
-        $searchtext = trim($searchtext);
+                        if($extendperiod > 0) {
+                            $timeend = $timestart + $extendperiod;
+                        } else {
+                            $timeend = 0;
+                        }
+                        if (! role_assign($roleid, $adduser->id, 0, $context->id, $timestart, $timeend, $hidden)) {
+                            $a = new stdClass;
+                            $a->role = $rolenames[$roleid];
+                            $a->user = fullname($adduser);
+                            $errors[] = get_string('assignerror', 'role', $a);
+                        }
+                    }
+                }
 
-        if ($searchtext !== '') {   // Search for a subset of remaining users
-            $LIKE      = $DB->sql_ilike();
-            $FULLNAME  = $DB->sql_fullname();
+                $potentialuserselector->invalidate_selected_users();
+                $currentuserselector->invalidate_selected_users();
 
-            $select .= " AND ($FULLNAME $LIKE :search1 OR email $LIKE :search2) ";
-            $params['search1'] = "%$searchtext%";
-            $params['search2'] = "%$searchtext%";
+                $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
+                add_to_log($course->id, 'role', 'assign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
+            }
         }
 
-        if ($context->contextlevel > CONTEXT_COURSE) { // mod or block (or group?)
-
-            /************************************************************************
-             *                                                                      *
-             * context level is above or equal course context level                 *
-             * in this case we pull out all users matching search criteria (if any) *
-             *                                                                      *
-             * MDL-11324                                                            *
-             * a mini get_users_by_capability() call here, this is done instead of  *
-             * get_users_by_capability() because                                    *
-             * 1) get_users_by_capability() does not deal with searching by name    *
-             * 2) exceptions array can be potentially large for large courses       *
-             * 3) $DB->get_recordset_sql() is more efficient                        *
-             *                                                                      *
-             ************************************************************************/
-
-            if ($possibleroles = get_roles_with_capability('moodle/course:view', CAP_ALLOW, $context)) {
-
-                $doanythingroles = get_roles_with_capability('moodle/site:doanything', CAP_ALLOW, get_context_instance(CONTEXT_SYSTEM));
-
-                $validroleids = array();
-                foreach ($possibleroles as $possiblerole) {
-                    if (isset($doanythingroles[$possiblerole->id])) {  // We don't want these included
-                            continue;
-                    }
-                    if ($caps = role_context_capabilities($possiblerole->id, $context, 'moodle/course:view')) { // resolved list
-                        if (isset($caps['moodle/course:view']) && $caps['moodle/course:view'] > 0) { // resolved capability > 0
-                            $validroleids[] = $possiblerole->id;
+    /// Process incoming role unassignments
+        if (optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) {
+            $userstounassign = $currentuserselector->get_selected_users();
+            if (!empty($userstounassign)) {
+
+                foreach ($userstounassign as $removeuser) {
+                    if (! role_unassign($roleid, $removeuser->id, 0, $context->id)) {
+                        $a = new stdClass;
+                        $a->role = $rolenames[$roleid];
+                        $a->user = fullname($removeuser);
+                        $errors[] = get_string('unassignerror', 'role', $a);
+                    } else if ($inmeta) {
+                        sync_metacourse($courseid);
+                        $newroles = get_user_roles($context, $removeuser->id, false);
+                        if (empty($newroles) || array_key_exists($roleid, $newroles)) {
+                            $errors[] = get_string('metaunassignerror', 'role', fullname($removeuser));
                         }
                     }
                 }
 
-                if ($validroleids) {
-                    $roleids =  '('.implode(',', $validroleids).')';
-
-                    $fields      = "SELECT u.id, u.firstname, u.lastname, u.email";
-                    $countfields = "SELECT COUNT('x')";
-
-                    $sql   = " FROM {user} u
-                               JOIN {role_assignments} ra ON ra.userid = u.id
-                               JOIN {role} r ON r.id = ra.roleid
-                              WHERE ra.contextid ".get_related_contexts_string($context)."
-                                    AND $select AND ra.roleid in $roleids
-                                    AND u.id NOT IN (
-                                       SELECT u.id
-                                         FROM {role_assignments} r, {user} u
-                                        WHERE r.contextid = :contextid
-                                              AND u.id = r.userid
-                                              AND r.roleid = :roleid)";
-                    $params['contextid'] = $contextid;
-                    $params['roleid']    = $roleid;
-
-                    $availableusers = $DB->get_recordset_sql("$fields $sql", $params);
-                    $usercount      = $DB->count_records_sql("$countfields $sql", $params);
-
-                } else {
-                    $availableusers = array();
-                    $usercount = 0;
-                }
-            }
-
-        } else {
+                $potentialuserselector->invalidate_selected_users();
+                $currentuserselector->invalidate_selected_users();
 
-            /************************************************************************
-             *                                                                      *
-             * context level is above or equal course context level                 *
-             * in this case we pull out all users matching search criteria (if any) *
-             *                                                                      *
-             ************************************************************************/
-
-            /// MDL-11111 do not include user already assigned this role in this context as available users
-            /// so that the number of available users is right and we save time looping later
-            $fields      = "SELECT id, firstname, lastname, email";
-            $countfields = "SELECT COUNT('x')";
-
-            $sql = " FROM {user}
-                    WHERE $select
-                          AND id NOT IN (
-                             SELECT u.id
-                               FROM {role_assignments} r, {user} u
-                              WHERE r.contextid = :contextid
-                                    AND u.id = r.userid
-                                    AND r.roleid = :roleid)";
-            $order = "ORDER BY lastname ASC, firstname ASC";
-
-            $params['contextid'] = $contextid;
-            $params['roleid']    = $roleid;
-
-            $availableusers = $DB->get_recordset_sql("$fields $sql $order", $params);
-            $usercount      = $DB->count_records_sql("$countfields $sql", $params);
+                $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
+                add_to_log($course->id, 'role', 'unassign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
+            }
         }
 
-        print_simple_box_start('center');
-        include('assign.html');
-        print_simple_box_end();
+    /// Print the form.
+        check_theme_arrows();
+?>
+<form id="assignform" method="post" action="<?php echo $baseurl . '&amp;roleid=' . $roleid ?>">
+<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
+
+  <table summary="" class="roleassigntable generaltable generalbox boxaligncenter" cellspacing="0">
+    <tr>
+      <td id="existingcell">
+          <p><label for="removeselect"><?php print_string('extusers', 'role'); ?></label></p>
+          <?php $currentuserselector->display() ?>
+      </td>
+      <td id="buttonscell">
+          <div id="addcontrols">
+              <input name="add" id="add" type="submit" value="<?php echo $THEME->larrow.'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
+
+              <p><input type="checkbox" name="hidden" id="hidden" value="1" <?php
+              if ($hidden) { echo 'checked="checked" '; } ?>/>
+              <label for="hidden" title="<?php print_string('createhiddenassign', 'role'); ?>">
+                  <?php print_string('hidden', 'role'); ?>
+                  <?php helpbutton('hiddenassign', get_string('createhiddenassign', 'role')); ?>
+              </label></p>
+
+              <p><label for="extendperiod"><?php print_string('enrolperiod') ?></label><br />
+              <?php choose_from_menu($periodmenu, "extendperiod", $defaultperiod, $unlimitedperiod); ?></p>
+
+              <p><label for="extendbase"><?php print_string('startingfrom') ?></label><br />
+              <?php choose_from_menu($basemenu, "extendbase", 3, ""); ?></p>
+          </div>
+
+          <div id="removecontrols">
+              <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$THEME->rarrow; ?>" title="<?php print_string('remove'); ?>" />
+          </div>
+      </td>
+      <td id="potentialcell">
+          <p><label for="addselect"><?php print_string('potusers', 'role'); ?></label></p>
+          <?php $potentialuserselector->display() ?>
+      </td>
+    </tr>
+  </table>
+</form>
+
+<?php
+        print_js_call('init_add_assign_page');
 
         if (!empty($errors)) {
             $msg = '<p>';
         }
 
         // Get the names of role holders for roles with between 1 and MAX_USERS_TO_LIST_PER_ROLE users,
-        // and so determine whether to show the extra column. 
+        // and so determine whether to show the extra column.
         $rolehodlernames = array();
         $strmorethanmax = get_string('morethan', 'role', MAX_USERS_TO_LIST_PER_ROLE);
         $showroleholders = false;
index 2f85336f0e22dfda5fc5f1b210c8ec85367191bc..c1588116c977fb8e5fba9ceddad4e2b9b7b1e68d 100644 (file)
@@ -111,3 +111,17 @@ cap_table_filter = {
         }
     }
 };
+
+function init_add_assign_page() {
+    var addselect = user_selector.get('addselect');
+    document.getElementById('add').disabled = addselect.is_selection_empty();
+    addselect.subscribe('selectionchanged', function(isempty) {
+        document.getElementById('add').disabled = isempty;
+    });
+
+    var removeselect = user_selector.get('removeselect');
+    document.getElementById('remove').disabled = removeselect.is_selection_empty();
+    removeselect.subscribe('selectionchanged', function(isempty) {
+        document.getElementById('remove').disabled = isempty;
+    });
+}
\ No newline at end of file
index 14f5daa4df64213f748db42b4bf740230bd748b8..daba4070b615d6d6b0841fd4a33ff7ee6c70be26 100644 (file)
@@ -94,7 +94,7 @@ check_theme_arrows();
 
     <table class="generaltable generalbox groupmanagementtable boxaligncenter" summary="">
     <tr>
-      <td id='memberscell'>
+      <td id='existingcell'>
           <p>
             <label for="removeselect"><?php print_string('groupmembers', 'group'); ?></label>
           </p>
@@ -106,7 +106,7 @@ check_theme_arrows();
             <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$THEME->rarrow; ?>" title="<?php print_string('remove'); ?>" />
         </p>
       </td>
-      <td id='nonmemberscell'>
+      <td id='potentialcell'>
           <p>
             <label for="addselect"><?php print_string('potentialmembs', 'group'); ?></label>
           </p>
index dbaf14e9335b472baadad4639a33360ab4aa6cf9..cf47e555cf45b9e4e0e8e3b4ad16dd36ca4b0d43 100644 (file)
@@ -22,6 +22,7 @@ $string['cannotaddrss'] = 'You do not have permission to add rss feeds';
 $string['cannotaddmodule'] = '$a module could not be added to the module list!';
 $string['cannotaddnewmodule'] = 'Could not add a new module of $a';
 $string['cannotaddnewinstance'] = 'Could not add a new instance of $a';
+$string['cannotassignrolehere'] = 'You are not allowed to assign this role (id = $a->roleid) in this context ($a->context)';
 $string['cannotsaveconfig'] = 'Problem saving config \"$a->name\" as \"$a->value\" for plugin \"$a->plugin\"';
 $string['cannotsavecomment'] = 'Cannot save comment';
 $string['cannotsavefile'] = 'Cannot save the file \"$a\"!';
index 15747e542848d155c8a9fe4ec3293238cff92dad..b6177efd22fc5b4b5036b058683133d723d3ce63 100644 (file)
@@ -8,6 +8,7 @@ $string['allowassign'] = 'Allow role assignments';
 $string['allowoverride'] = 'Allow role overrides';
 $string['allsiteusers'] = 'All site users';
 $string['assignanotherrole'] = 'Assign another role';
+$string['assignerror'] = 'Error while assigning the role $a->role to user $a->user.';
 $string['assignroles'] = 'Assign roles';
 $string['assignrolesin'] = 'Assign roles in $a';
 $string['assignglobalroles'] = 'Assign system roles';
@@ -55,6 +56,7 @@ $string['course:viewhiddenuserfields'] = 'View hidden user fields';
 $string['course:viewparticipants'] = 'View participants';
 $string['course:viewscales'] = 'View scales';
 $string['course:visibility'] = 'Hide/show courses';
+$string['createhiddenassign'] = 'Create hidden role assignments';
 $string['deletecourseoverrides'] = 'Delete all overrides in course';
 $string['deletelocalroles'] = 'Delete all local role assignments';
 $string['grade:edit'] = 'Edit grades';
@@ -87,8 +89,11 @@ $string['explainpermissions'] = 'Explain permissions';
 $string['explainpermissionsdetails'] = 'For capability $a->capability in context $a->context when logged in as $a->fullname.';
 $string['explainpermissionsinfo'] = '<p>To use this table:</p><ol><li>First look to see if there are any Prohibits. If there are, has_capability will return false.</li><li>Otherwise, read across the rows, left-to-right, top-to-bottom, and find the first cell where the number of Prevents and Allows are different. If there are more Allows than Prevents in that cell, then has_capability will return true, otherwise it will return false.</li><li>If no cell has different numbers of Prevents and Allows, then has_capability will return false.</li></ol>';
 $string['explainpermissionsdoanything'] = 'Note that this user has the moodle/site:doanything capability, so even though the table above shows that has_capability will return false, this user will actually be deemed to have the capability $a in most circumstances.';
+$string['extusers'] = 'Existing users';
+$string['extusersmatching'] = 'Existing users matching \'$a\'';
 $string['globalrole'] = 'System role';
 $string['globalroleswarning'] = 'WARNING! Any roles you assign from this page will apply to the assigned users throughout the entire system, including the front page and all the courses.';
+$string['hidden'] = 'Hidden';
 $string['inherit'] = 'Inherit';
 $string['legacy:admin'] = 'LEGACY ROLE: Administrator';
 $string['legacy:coursecreator'] = 'LEGACY ROLE: Course Creator';
@@ -116,6 +121,8 @@ $string['overriderolesin'] = 'Override roles in $a';
 $string['overrides'] = 'Overrides';
 $string['permissions'] = 'Permissions';
 $string['potentialusers'] = '$a potential users';
+$string['potusers'] = 'Potential users';
+$string['potusersmatching'] = 'Potential users matching \'$a\'';
 $string['portfolio:export'] = 'Export to portfolios';
 $string['prevent'] = 'Prevent';
 $string['prohibit'] = 'Prohibit';
@@ -172,6 +179,7 @@ $string['tag:manage'] = 'Manage all tags';
 $string['tag:create'] = 'Create new tags';
 $string['tag:edit'] = 'Edit existing tags';
 $string['tag:editblocks'] = 'Edit blocks in tags pages';
+$string['unassignerror'] = 'Error while unassigning the role $a->role from user $a->user.';
 $string['user:changeownpassword'] = 'Change own password';
 $string['user:create'] = 'Create users';
 $string['user:delete'] = 'Delete users';
index 0122461bb41ab653100c99bad9d81bfd6a79456e..05647862688d12cd994083b11a0f62e723b0da13 100755 (executable)
@@ -525,6 +525,26 @@ function is_siteadmin($userid) {
     return $DB->record_exists_sql($sql, $params);
 }
 
+/**
+ * @param integer $roleid a role id.
+ * @return boolean, whether this role is an admin role.
+ */
+function is_admin_role($roleid) {
+    global $CFG, $DB;
+
+    $sql = "SELECT 1
+              FROM {role_capabilities} rc
+              JOIN {context} ctx ON ctx.id = rc.contextid
+             WHERE ctx.contextlevel = 10
+                   AND rc.roleid = ?
+                   AND rc.capability IN (?, ?, ?)
+          GROUP BY rc.capability
+            HAVING SUM(rc.permission) > 0";
+    $params = array($roleid, 'moodle/site:config', 'moodle/legacy:admin', 'moodle/site:doanything');
+
+    return $DB->record_exists_sql($sql, $params);
+}
+
 function get_course_from_path ($path) {
     // assume that nothing is more than 1 course deep
     if (preg_match('!^(/.+)/\d+$!', $path, $matches)) {
@@ -4886,7 +4906,9 @@ function sort_by_roleassignment_authority($users, $context, $roles=array(), $sor
  * @param bool gethidden - whether to fetch hidden enrolments too
  * @return array()
  */
-function get_role_users($roleid, $context, $parent=false, $fields='', $sort='u.lastname ASC', $gethidden=true, $group='', $limitfrom='', $limitnum='') {
+function get_role_users($roleid, $context, $parent=false, $fields='',
+        $sort='u.lastname, u.firstname', $gethidden=true, $group='',
+        $limitfrom='', $limitnum='', $extrawheretest='', $whereparams=array()) {
     global $DB;
 
     if (empty($fields)) {
@@ -4927,6 +4949,11 @@ function get_role_users($roleid, $context, $parent=false, $fields='', $sort='u.l
 
     array_unshift($params, $context->id);
 
+    if ($extrawheretest) {
+        $extrawheretest = ' AND ' . $extrawheretest;
+        $params = array_merge($params, $whereparams);
+    }
+
     $sql = "SELECT $fields, ra.roleid
               FROM {role_assignments} ra
               JOIN {user} u ON u.id = ra.userid
@@ -4936,6 +4963,7 @@ function get_role_users($roleid, $context, $parent=false, $fields='', $sort='u.l
                    $roleselect
                    $groupselect
                    $hiddensql
+                   $extrawheretest
           ORDER BY $sort";                  // join now so that we can just use fullname() later
 
     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
index 1229060051e98a9a1fae3b54b537f38a066101a9..4d084c36a37cf1ee7f92c45b2a1381f6cd58b13e 100644 (file)
@@ -436,37 +436,40 @@ form.popupform label {
 .groupmanagementtable {
   width: 90%;
 }
-
 .groupmanagementtable td {
   vertical-align: middle;
 }
-
 .groupmanagementtable p {
-  text-align: center;
+  text-align: left;
+  margin-bottom: 0.2em;
 }
-
-.groupmanagementtable #memberscell,
-.groupmanagementtable #nonmemberscell {
+.groupmanagementtable #existingcell,
+.groupmanagementtable #potentialcell {
   width: 42%;
 }
-.groupmanagementtable #memberscell label,
-.groupmanagementtable #nonmemberscell label {
+.groupmanagementtable #existingcell label,
+.groupmanagementtable #potentialcell label {
   font-weight: bold;
 }
 .groupmanagementtable #buttonscell {
   width: 16%;
 }
+.groupmanagementtable #buttonscell p {
+  text-align: center;
+}
 .groupmanagementtable #buttonscell input {
     width: 80%;
     padding: 1em 0;
-    margin: 2em 0;
+}
+.groupmanagementtable #buttonscell #remove {
+    margin: 7em 0;
 }
 .groupmanagementtable #backcell {
     padding-top: 2em;
     text-align: center;
 }
-#removeselect_wrapper,
-#addselect_wrapper {
+.groupmanagementtable #removeselect_wrapper,
+.groupmanagementtable #addselect_wrapper {
     width: 100%;
 }
 .groupmanagementtable #removeselect_wrapper label,
@@ -549,6 +552,9 @@ div.hide {
 .userselector select {
   width: 100%;
 }
+.userselector div {
+  margin-top: 0.2em;
+}
 .userselector div label {
   margin-right: 0.3em;
 }
@@ -1027,11 +1033,47 @@ body#admin-modules table.generaltable td.c0
   padding: 5px;
 }
 
-#admin-roles-manage .selector,
-#admin-roles-assign .selector,
-#admin-roles-override .selector {
-  text-align:center;
-  margin-bottom:1em;
+.roleassigntable {
+    width: 100%;
+}
+.roleassigntable td {
+    vertical-align: middle;
+    padding: 0 0.3em 1em;
+}
+.roleassigntable p {
+    text-align: left;
+    margin-bottom: 0.2em;
+}
+.roleassigntable #existingcell,
+.roleassigntable #potentialcell {
+    width: 38%;
+}
+.roleassigntable #existingcell label,
+.roleassigntable #potentialcell label {
+    font-weight: bold;
+}
+.roleassigntable #buttonscell {
+    width: 24%;
+}
+.roleassigntable #buttonscell #add,
+.roleassigntable #buttonscell #remove {
+    width: 100%;
+    margin: 0.3em 0;
+    padding: 0.5em 0;
+}
+.roleassigntable #buttonscell p {
+    margin: 0.3em 0;
+}
+.roleassigntable #buttonscell #remove {
+    margin-top: 5em;
+}
+.roleassigntable #removeselect_wrapper,
+.roleassigntable #addselect_wrapper {
+    width: 100%;
+}
+.roleassigntable #removeselect_wrapper label,
+.roleassigntable #addselect_wrapper label {
+    font-weight: normal;
 }
 
 #admin-roles-manage table.roledesc,
index 84a43b7f55feb3ec4b5b1a62a70dfe078142bb26..2a6cd8014e09e7a6ebee8529badf30d2cb33f952 100644 (file)
@@ -71,10 +71,12 @@ abstract class user_selector_base {
     public function __construct($name, $options = array()) {
         global $CFG;
         $this->name = $name;
-        if (empty($CFG->extrauserselectorfields)) {
-            $this->extrafields = array();
-        } else {
+        if (isset($options['extrafields'])) {
+            $this->extrafields = $options['extrafields'];
+        } else if (!empty($CFG->extrauserselectorfields)) {
             $this->extrafields = explode(',', $CFG->extrauserselectorfields);
+        } else {
+            $this->extrafields = array();
         }
         if (isset($options['exclude']) && is_array($options['exclude'])) {
             $this->exclude = $options['exclude'];
@@ -107,7 +109,7 @@ abstract class user_selector_base {
     }
 
     /**
-     * @return array the userids that were selected. This is a more sophisticated version
+     * @return array of user objects. The users that were selected. This is a more sophisticated version
      * of optional_param($this->name, array(), PARAM_INTEGER) that validates the
      * returned list of ids against the rules for this user selector.
      */
@@ -233,7 +235,9 @@ abstract class user_selector_base {
      *      array should be the string names of optgroups. The keys of the inner
      *      arrays should be userids, and the values should be user objects
      *      containing at least the list of fields returned by the method
-     *      required_fields_sql().
+     *      required_fields_sql(). If a user object has a ->disabled property
+     *      that is true, then that option will be displayed greyed out, and
+     *      will not be returned by get_selected_users.
      */
     public abstract function find_users($search);
 
@@ -245,6 +249,7 @@ abstract class user_selector_base {
             'class' => get_class($this),
             'name' => $this->name,
             'exclude' => $this->exclude,
+            'extrafields' => $this->extrafields
         );
     }
 
@@ -274,7 +279,11 @@ abstract class user_selector_base {
         // Aggregate the resulting list back into a single one.
         $users = array();
         foreach ($groupedusers as $group) {
-            $users += $group;
+            foreach ($group as $user) {
+                if (!isset($users[$user->id]) && empty($user->disabled)) {
+                    $users[$user->id] = $user;
+                }
+            }
         }
 
         // If we are only supposed to be selecting a single user, make sure we do.
@@ -317,14 +326,18 @@ abstract class user_selector_base {
         $params = array();
         $tests = array();
 
+        if ($u) {
+            $u .= '.';
+        }
+
         // If we have a $search string, put a field LIKE '$search%' condition on each field.
         if ($search) {
             $conditions = array(
-                $DB->sql_fullname($u . '.firstname', $u . '.lastname'),
-                $conditions[] = $u . '.lastname'
+                $DB->sql_fullname($u . 'firstname', $u . 'lastname'),
+                $conditions[] = $u . 'lastname'
             );
             foreach ($this->extrafields as $field) {
-                $conditions[] = $u . '.' . $field;
+                $conditions[] = $u . $field;
             }
             $ilike = ' ' . $DB->sql_ilike() . ' ?';
             foreach ($conditions as &$condition) {
@@ -334,17 +347,22 @@ abstract class user_selector_base {
             $tests[] = '(' . implode(' OR ', $conditions) . ')';
         }
 
+        // Add some additional sensible conditions
+        $tests[] = $u . "username <> 'guest'";
+        $tests[] = $u . 'deleted = 0';
+        $tests[] = $u . 'confirmed = 1';
+
         // If we are being asked to exclude any users, do that.
         if (!empty($this->exclude)) {
             list($usertest, $userparams) = $DB->get_in_or_equal($this->exclude, SQL_PARAMS_QM, '', false);
-            $tests[] = $u . '.id ' . $usertest;
+            $tests[] = $u . 'id ' . $usertest;
             $params = array_merge($params, $userparams);
         }
 
         // If we are validating a set list of userids, add an id IN (...) test.
         if (!empty($this->validatinguserids)) {
             list($usertest, $userparams) = $DB->get_in_or_equal($this->validatinguserids);
-            $tests[] = $u . '.id ' . $usertest;
+            $tests[] = $u . 'id ' . $usertest;
             $params = array_merge($params, $userparams);
         }
 
@@ -411,13 +429,14 @@ abstract class user_selector_base {
         if (!empty($users)) {
             $output = '  <optgroup label="' . s($groupname) . ' (' . count($users) . ')">' . "\n";
             foreach ($users as $user) {
-                if ($select || isset($this->selected[$user->id])) {
-                    $selectattr = ' selected="selected"';
-                } else {
-                    $selectattr = '';
+                $attributes = '';
+                if (!empty($user->disabled)) {
+                    $attributes .= ' disabled="disabled"';
+                } else if ($select || isset($this->selected[$user->id])) {
+                    $attributes .= ' selected="selected"';
                 }
                 unset($this->selected[$user->id]);
-                $output .= '    <option' . $selectattr . ' value="' . $user->id . '">' .
+                $output .= '    <option' . $attributes . ' value="' . $user->id . '">' .
                         $this->output_user($user) . "</option>\n";
             }
         } else {
@@ -434,7 +453,7 @@ abstract class user_selector_base {
      * @param object $user the user to display.
      * @return string a string representation of the user.
      */
-    protected function output_user($user) {
+    public function output_user($user) {
         $bits = array(
             fullname($user)
         );
@@ -477,13 +496,183 @@ abstract class user_selector_base {
     }
 }
 
+// User selectors for managing role assignments ================================
+
+/**
+ * Base class to avoid duplicating code.
+ */
+abstract class role_assign_user_selector_base extends user_selector_base {
+    const MAX_USERS_PER_PAGE = 100;
+
+    protected $roleid;
+    protected $context;
+
+    /**
+     * @param string $name control name
+     * @param array $options should have two elements with keys groupid and courseid.
+     */
+    public function __construct($name, $options) {
+        global $CFG;
+        parent::__construct($name, $options);
+        $this->roleid = $options['roleid'];
+        if (isset($options['context'])) {
+            $this->context = $options['context'];
+        } else {
+            $this->context = get_context_instance_by_id($options['contextid']);
+        }
+        require_once($CFG->dirroot . '/group/lib.php');
+    }
+
+    protected function get_options() {
+        $options = parent::get_options();
+        $options['roleid'] = $this->roleid;
+        $options['contextid'] = $this->context->id;
+        return $options;
+    }
+}
+
 /**
- * User selector subclass  for the list of potential users on the assign roles page.
+ * User selector subclass for the list of potential users on the assign roles page,
+ * when we are assigning in a context below the course level. (CONTEXT_MODULE and
+ * CONTEXT_BLOCK).
  *
+ * In this case we replicate part of get_users_by_capability() get the users
+ * with moodle/course:view (or moodle/site:doanything). We can't use
+ * get_users_by_capability() becuase 
+ *   1) get_users_by_capability() does not deal with searching by name
+ *   2) exceptions array can be potentially large for large courses
  */
-class role_assign_potential_user_selector extends user_selector_base {
+class potential_assignees_below_course extends role_assign_user_selector_base {
     public function find_users($search) {
-        return array(); // TODO
+        global $DB;
+
+        // Get roles with some assignement to the 'moodle/course:view' capability.
+        $possibleroles = get_roles_with_capability('moodle/course:view', CAP_ALLOW, $this->context);
+        if (empty($possibleroles)) {
+            // If there aren't any, we are done.
+            return array();
+        }
+
+        // Now exclude the admin roles, and check the actual permission on
+        // 'moodle/course:view' to make sure it is allow.
+        $doanythingroles = get_roles_with_capability('moodle/site:doanything',
+                CAP_ALLOW, get_context_instance(CONTEXT_SYSTEM));
+        $validroleids = array();
+
+        foreach ($possibleroles as $possiblerole) {
+            if (isset($doanythingroles[$possiblerole->id])) {
+                    continue;
+            }
+
+            if ($caps = role_context_capabilities($possiblerole->id, $this->context, 'moodle/course:view')) { // resolved list
+                if (isset($caps['moodle/course:view']) && $caps['moodle/course:view'] > 0) { // resolved capability > 0
+                    $validroleids[] = $possiblerole->id;
+                }
+            }
+        }
+
+        // If there are no valid roles, we are done.
+        if (!$validroleids) {
+            return array();
+        }
+
+        // Now we have to go to the database.
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+        if ($wherecondition) {
+            $wherecondition = ' AND ' . $wherecondition;
+        }
+        $roleids =  '('.implode(',', $validroleids).')';
+
+        $fields      = 'SELECT ' . $this->required_fields_sql('u');
+        $countfields = 'SELECT COUNT(1)';
+
+        $sql   = " FROM {user} u
+                   JOIN {role_assignments} ra ON ra.userid = u.id
+                   JOIN {role} r ON r.id = ra.roleid
+                  WHERE ra.contextid " . get_related_contexts_string($this->context)."
+                        $wherecondition
+                        AND ra.roleid IN $roleids
+                        AND u.id NOT IN (
+                           SELECT u.id
+                             FROM {role_assignments} r, {user} u
+                            WHERE r.contextid = ?
+                                  AND u.id = r.userid
+                                  AND r.roleid = ?)";
+        $order = ' ORDER BY lastname ASC, firstname ASC';
+
+        $params[] = $this->context->id;
+        $params[]    = $this->roleid;
+
+        // Check to see if there are too many to show sensibly.
+        $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
+        if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
+            return array(get_string('toomanytoshow') => array(),
+                    get_string('trysearching') => array());
+        }
+
+        // If not, show them.
+        $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
+
+        if (empty($availableusers)) {
+            return array();
+        }
+
+        if ($search) {
+            $groupname = get_string('potusersmatching', 'role', $search);
+        } else {
+            $groupname = get_string('potusers', 'role');
+        }
+
+        return array($groupname => $availableusers);
+    }
+}
+
+/**
+ * User selector subclass for the list of potential users on the assign roles page,
+ * when we are assigning in a context at or above the course level. In this case we
+ * show all the users in the system who do not already have the role.
+ */
+class potential_assignees_course_and_above extends role_assign_user_selector_base {
+    public function find_users($search) {
+        global $DB;
+
+        list($wherecondition, $params) = $this->search_sql($search, '');
+
+        $fields      = 'SELECT ' . $this->required_fields_sql('');
+        $countfields = 'SELECT COUNT(1)';
+
+        $sql = " FROM {user} 
+                WHERE $wherecondition
+                      AND id NOT IN (
+                         SELECT u.id
+                           FROM {role_assignments} r, {user} u
+                          WHERE r.contextid = ?
+                                AND u.id = r.userid
+                                AND r.roleid = ?)";
+        $order = ' ORDER BY lastname ASC, firstname ASC';
+
+        $params[] = $this->context->id;
+        $params[] = $this->roleid;
+
+        $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
+        if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
+            return array(get_string('toomanytoshow') => array(),
+                    get_string('trysearching') => array());
+        }
+
+        $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
+
+        if (empty($availableusers)) {
+            return array();
+        }
+
+        if ($search) {
+            $groupname = get_string('potusersmatching', 'role', $search);
+        } else {
+            $groupname = get_string('potusers', 'role');
+        }
+
+        return array($groupname => $availableusers);
     }
 }
 
@@ -491,12 +680,67 @@ class role_assign_potential_user_selector extends user_selector_base {
  * User selector subclass for the list of users who already have the role in
  * question on the assign roles page.
  */
-class role_assign_current_user_selector extends user_selector_base {
+class existing_role_holders extends role_assign_user_selector_base {
+    protected $strhidden;
+
+    public function __construct($name, $options) {
+        parent::__construct($name, $options);
+        $this->strhidden = get_string('hiddenassign');
+    }
+
+    public function find_users($search) {
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+        $contextusers = get_role_users($this->roleid, $this->context, false,
+                $this->required_fields_sql('u') . ', ra.hidden', 'u.lastname, u.firstname',
+                true, '', '', '', $wherecondition, $params);
+
+        if (empty($contextusers)) {
+            return array();
+        }
+
+        if ($search) {
+            $groupname = get_string('extusersmatching', 'role', $search);
+        } else {
+            $groupname = get_string('extusers', 'role');
+        }
+
+        return array($groupname => $contextusers);
+    }
+
+    // Override to add (hidden) to hidden role assignments.
+    public function output_user($user) {
+        $output = parent::output_user($user);
+        if ($user->hidden) {
+            $output .= ' (' . $this->strhidden . ')';
+        }
+        return $output;
+    }
+}
+
+/**
+ * A special subclass to use when unassigning admins at site level. Disables
+ * the option for admins to unassign themselves.
+ */
+class existing_role_holders_site_admin extends existing_role_holders {
     public function find_users($search) {
-        return array(); // TODO
+        global $USER;
+        $groupeduses = parent::find_users($search);
+        foreach ($groupeduses as $group) {
+            foreach ($group as &$user) {
+                if ($user->id == $USER->id) {
+                    $user->disabled = true;
+                }
+            }
+        }
+        return $groupeduses;
     }
 }
 
+// User selectors for managing group memebers ==================================
+
+/**
+ * Base class to avoid duplicating code.
+ */
 abstract class groups_user_selector_base extends user_selector_base {
     protected $groupid;
     protected $courseid;
@@ -521,8 +765,6 @@ abstract class groups_user_selector_base extends user_selector_base {
     }
 
     /**
-     * Enter description here...
-     *
      * @param array $roles array in the format returned by groups_calculate_role_people.
      * @return array array in the format find_users is supposed to return.
      */
@@ -571,7 +813,7 @@ class group_members_selector extends groups_user_selector_base {
 class group_non_members_selector extends groups_user_selector_base {
     const MAX_USERS_PER_PAGE = 100;
 
-    protected function output_user($user) {
+    public function output_user($user) {
         return parent::output_user($user) . ' (' . $user->numgroups . ')';
     }
 
index 07e33b93b87e43d5084a413101f7a6238abffa81..c954a197ad18519660605a8c6fe9b2e9e48ebe40 100644 (file)
@@ -249,7 +249,7 @@ user_selector.prototype.handle_failure = function() {
     // If we are in developer debug mode, output a link to help debug the failure.
     if (moodle_cfg.developerdebug) {
         var link = document.createElement('a');
-        link.href = this.searchurl + this.get_search_text();
+        link.href = this.searchurl + this.get_search_text() + '&debug=1';
         link.appendChild(document.createTextNode('Ajax call failed. Click here to try the search call directly.'))
         this.searchfield.parentNode.appendChild(link);
     }
@@ -302,7 +302,7 @@ user_selector.prototype.output_options = function(data) {
             var option = options[0];
             if (option.selected) {
                 var optiontext = option.innerText || option.textContent
-                this.selected[option.value] = { id: option.value, formatted: optiontext };
+                this.selected[option.value] = { id: option.value, name: optiontext, disabled: option.disabled };
             }
             optgroup.removeChild(option);
         }
@@ -360,8 +360,10 @@ user_selector.prototype.output_group = function(groupname, users, select) {
         var user = users[userid];
         var option = document.createElement('option');
         option.value = user.id;
-        option.appendChild(document.createTextNode(this.output_user(user)));
-        if (select || this.selected[user.id]) {
+        option.appendChild(document.createTextNode(user.name));
+        if (user.disabled) {
+            option.disabled = 'disabled';
+        } else if (select || this.selected[user.id]) {
             option.selected = 'selected';
         }
         delete this.selected[user.id];
@@ -384,22 +386,5 @@ user_selector.prototype.output_group = function(groupname, users, select) {
     this.listbox.appendChild(optgroup);
 }
 
-/**
- * Convert a user object to a string suitable for displaying as an option in the list box.
- *
- * @param Object user the user to display.
- * @return string a string representation of the user.
- */
-user_selector.prototype.output_user = function(user) {
-    if (user.formatted) {
-        return user.formatted;
-    }
-    var output = user.fullname;
-    for (var i = 0; i < this.extrafields.length; i++) {
-        output += ', ' + user[this.extrafields[i]];
-    }
-    return output;
-}
-
 // Say that we want to be a source of custom events.
 YAHOO.lang.augmentProto(user_selector, YAHOO.util.EventProvider);
\ No newline at end of file
index 3a269727ac33ed1f073270db4c4ffeb0858f03f4..00912a34c3ff4d7b6fb61d5109efd943e7beb2e7 100644 (file)
 require_once(dirname(__FILE__) . '/../../config.php');
 require_once($CFG->dirroot . '/user/selector/lib.php');
 
+// In developer debug mode, when there is a debug=1 in the URL send as plain text
+// for easier debugging.
+if (debugging('', DEBUG_DEVELOPER) && optional_param('debug', false, PARAM_BOOL)) {
+    header('Content-type: text/plain; charset=UTF-8');
+    $debugmode = true;
+} else {
+    header('Content-type: application/json');
+    $debugmode = false;
+}
+
 // Check access.
 if (!isloggedin()) {;
     print_error('mustbeloggedin');
@@ -50,8 +60,17 @@ if (!isset($USER->userselectors[$selectorhash])) {
     print_error('unknownuserselector');
 }
 
-// Create the appropriate userselector.
+// Get the options.
 $options = $USER->userselectors[$selectorhash];
+
+if ($debugmode) {
+    echo 'Search string: ', $search, "\n";
+    echo 'Options: ';
+    print_r($options);
+    echo "\n";
+}
+
+// Create the appropriate userselector.
 $classname = $options['class'];
 unset($options['class']);
 $name = $options['name'];
@@ -65,12 +84,16 @@ $userselector = new $classname($name, $options);
 // Do the search and output the results.
 $users = $userselector->find_users($search);
 foreach ($users as &$group) {
-    foreach ($group as &$user) {
-        $user->fullname = fullname($user);
+    foreach ($group as $user) {
+        $output = new stdClass;
+        $output->id = $user->id;
+        $output->name = $userselector->output_user($user);
+        if (!empty($user->disabled)) {
+            $output->disabled = true;
+        }
+        $group[$user->id] = $output;
     }
 }
 
-
-header('Content-type: application/json');
 echo json_encode(array('results' => $users));
 ?>
\ No newline at end of file