# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: moodle/enrol/ajax.php
--- moodle/enrol/ajax.php No Base Revision
+++ moodle/enrol/ajax.php Locally New
@@ -0,0 +1,176 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file processes AJAX enrollment actions and returns JSON
+ *
+ * @package moodlecore
+ * @copyright  2010 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('AJAX_SCRIPT', true);
+
+require('../config.php');
+require_once("$CFG->dirroot/enrol/locallib.php");
+require_once("$CFG->dirroot/enrol/renderer.php");
+require_once("$CFG->dirroot/group/lib.php");
+
+// Must have the sesskey
+require_sesskey();
+$id      = required_param('id', PARAM_INT); // course id
+$action  = optional_param('action', '', PARAM_ACTION);
+
+$url = new moodle_url('/enrol/ajax.php', array('id'=>$id));
+if ($action !== '') {
+    $url->param('action', $action);
+}
+$PAGE->set_url($url);
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+if ($course->id == SITEID) {
+    redirect(new moodle_url('/'));
+}
+
+require_login($course);
+require_capability('moodle/course:enrolreview', $context);
+
+$manager = new course_enrolment_manager($course);
+
+$outcome = new stdClass;
+$outcome->success = false;
+$outcome->response = new stdClass;
+$outcome->error = '';
+
+if (!confirm_sesskey()) {
+    $outcome->error = 'invalidsesskey';
+    echo json_encode($outcome);
+    die();
+}
+
+switch ($action) {
+    case 'unenrol':
+
+        $ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
+        if ($manager->unenrol_user($ue)) {
+            $outcome->success = true;
+        } else {
+            $outcome->error = 'unabletounenrol';
+        }
+        break;
+    case 'unassign':
+        $role = required_param('role', PARAM_INT);
+        $user = required_param('user', PARAM_INT);
+        if ($manager->unassign_role_from_user($user, $role)) {
+            $outcome->success = true;
+        } else {
+            $outcome->error = 'unabletounassign';
+        }
+        break;
+
+    case 'assign':
+
+        $user = required_param('user', PARAM_INT);
+        $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+        $roleid = required_param('roleid', PARAM_INT);
+
+        if (!is_enrolled($context, $user)) {
+            $outcome->error = 'mustbeenrolled';
+            break; // no roles without enrolments here in this script
+        }
+
+        if ($manager->assign_role_to_user($roleid, $user->id)) {
+            $outcome->success = true;
+            $outcome->response->roleid = $roleid;
+        } else {
+            $outcome->error = 'unabletoassign';
+        }
+        break;
+
+    case 'getassignable':
+        $outcome->success = true;
+        $outcome->response = $manager->get_assignable_roles();
+        break;
+
+    case 'searchusers':
+        $enrolid = required_param('enrolid', PARAM_INT);
+        $search  = optional_param('search', '', PARAM_CLEAN);
+        $page = optional_param('page', 0, PARAM_INT);
+        $outcome->response = $manager->get_potential_users($enrolid, $search, false, $page);
+        foreach ($outcome->response['users'] as &$user) {
+            $user->picture = $OUTPUT->user_picture($user);
+            $user->fullname = fullname($user);
+        }
+        $outcome->success = true;
+
+        break;
+
+    case 'enrol':
+        $enrolid = required_param('enrolid', PARAM_INT);
+        $userid = required_param('userid', PARAM_INT);
+
+        $roleid = optional_param('role', null, PARAM_INT);
+        $duration = optional_param('duration', 0, PARAM_INT);
+        $startdate = optional_param('startdate', 0, PARAM_INT);
+        if (empty($roleid)) {
+            $roleid = null;
+        }
+
+        switch($startdate) {
+            case 2:
+                $timestart = $course->startdate;
+                break;
+            case 3:
+            default:
+                $today = time();
+                $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
+                $timestart = $today;
+                break;
+        }
+        if ($duration <= 0) {
+            $timestart = 0;
+            $timeend = 0;
+        } else {
+            $timeend = $timestart + ($duration*24*60*60);
+        }
+
+        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+        $instances = $manager->get_enrolment_instances();
+        $plugins = $manager->get_enrolment_plugins();
+        if (!array_key_exists($enrolid, $instances)) {
+            $outcome->error = 'invalidinstance';
+            break;
+        }
+        $instance = $instances[$enrolid];
+        $plugin = $plugins[$instance->enrol];
+        try {
+            $plugin->enrol_user($instance, $user->id, $roleid, $timestart, $timeend);
+        } catch (Exception $e) {
+            $outcome->error = 'unabletoenrol';
+            break;
+        }
+        $outcome->success = true;
+        break;
+
+    default:
+        $outcome->error = 'unknownaction';
+        break;
+}
+
+echo json_encode($outcome);
+die();
\ No newline at end of file
Index: moodle/enrol/locallib.php
--- moodle/enrol/locallib.php No Base Revision
+++ moodle/enrol/locallib.php Locally New
@@ -0,0 +1,759 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the course_enrolment_manager class which is used to interface
+ * with the functions that exist in enrollib.php in relation to a single course.
+ *
+ * @package   moodlecore
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * This class provides a targeted tied together means of interfacing the enrolment
+ * tasks together with a course.
+ *
+ * It is provided as a convenience more than anything else.
+ *
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_enrolment_manager {
+
+    /**
+     * The course context
+     * @var stdClass
+     */
+    protected $context;
+    /**
+     * The course we are managing enrolments for
+     * @var stdClass
+     */
+    protected $course = null;
+    /**
+     * Limits the focus of the manager to one enrolment plugin instance
+     * @var string
+     */
+    protected $instancefilter = null;
+
+    /**
+     * The total number of users enrolled in the course
+     * Populated by course_enrolment_manager::get_total_users
+     * @var int
+     */
+    protected $totalusers = null;
+    /**
+     * An array of users currently enrolled in the course
+     * Populated by course_enrolment_manager::get_users
+     * @var array
+     */
+    protected $users = array();
+
+    /**
+     * Constructs the course enrolment manager
+     *
+     * @param stdClass $course
+     * @param string $instancefilter
+     */
+    public function __construct($course, $instancefilter = null) {
+        $this->context = get_context_instance(CONTEXT_COURSE, $course->id);
+        $this->course = $course;
+        $this->instancefilter = $instancefilter;
+    }
+
+    /**
+     * Returns the total number of enrolled users in the course.
+     *
+     * If a filter was specificed this will be the total number of users enrolled
+     * in this course by means of that instance.
+     *
+     * @global moodle_database $DB
+     * @return int
+     */
+    public function get_total_users() {
+        global $DB;
+        if ($this->totalusers === null) {
+            list($instancessql, $params, $filter) = $this->get_instance_sql();
+            $sqltotal = "SELECT COUNT(DISTINCT u.id)
+                           FROM {user} u
+                           JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
+                           JOIN {enrol} e ON (e.id = ue.enrolid)";
+            $this->totalusers = (int)$DB->count_records_sql($sqltotal, $params);
+        }
+        return $this->totalusers;
+    }
+
+    /**
+     * Gets all of the users enrolled in this course.
+     *
+     * If a filter was specificed this will be the users who were enrolled
+     * in this course by means of that instance.
+     *
+     * @global moodle_database $DB
+     * @param string $sort
+     * @param string $direction ASC or DESC
+     * @param int $page First page should be 0
+     * @param int $perpage Defaults to 25
+     * @return array
+     */
+    public function get_users($sort, $direction='ASC', $page=0, $perpage=25) {
+        global $DB;
+        if ($direction !== 'ASC') {
+            $direction = 'DESC';
+        }
+        $key = md5("$sort-$direction-$page-$perpage");
+        if (!array_key_exists($key, $this->users)) {
+            list($instancessql, $params, $filter) = $this->get_instance_sql();
+            $sql = "SELECT DISTINCT u.*, ul.timeaccess AS lastseen
+                      FROM {user} u
+                      JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
+                      JOIN {enrol} e ON (e.id = ue.enrolid)
+                 LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)";
+            if ($sort === 'firstname') {
+                $sql .= " ORDER BY u.firstname $direction, u.lastname $direction";
+            } else if ($sort === 'lastname') {
+                $sql .= " ORDER BY u.lastname $direction, u.firstname $direction";
+            } else if ($sort === 'email') {
+                $sql .= " ORDER BY u.email $direction, u.lastname $direction, u.firstname $direction";
+            } else if ($sort === 'lastseen') {
+                $sql .= " ORDER BY ul.timeaccess $direction, u.lastname $direction, u.firstname $direction";
+            }
+            $this->users[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
+        }
+        return $this->users[$key];
+    }
+
+    /**
+     * Gets an array of the users that can be enrolled in this course.
+     *
+     * @global moodle_database $DB
+     * @param int $enrolid
+     * @param string $search
+     * @param bool $searchanywhere
+     * @param int $page Defaults to 0
+     * @param int $perpage Defaults to 25
+     * @return array Array(totalusers => int, users => array)
+     */
+    public function get_potential_users($enrolid, $search='', $searchanywhere=false, $page=0, $perpage=25) {
+        global $DB;
+
+        // Add some additional sensible conditions
+        $tests = array("u.username <> 'guest'", 'u.deleted = 0', 'u.confirmed = 1');
+        $params = array();
+        if (!empty($search)) {
+            $conditions = array('u.firstname','u.lastname');
+            $ilike = ' ' . $DB->sql_ilike();
+            if ($searchanywhere) {
+                $searchparam = '%' . $search . '%';
+            } else {
+                $searchparam = $search . '%';
+            }
+            $i = 0;
+            foreach ($conditions as &$condition) {
+                $condition .= "$ilike :con{$i}00";
+                $params["con{$i}00"] = $searchparam;
+                $i++;
+            }
+            $tests[] = '(' . implode(' OR ', $conditions) . ')';
+        }
+        $wherecondition = implode(' AND ', $tests);
+
+        $ufields = user_picture::fields('u');
+
+        $fields      = 'SELECT u.id, u.firstname, u.lastname, u.username, u.email, u.lastaccess, u.picture, u.imagealt, '.$ufields;
+        $countfields = 'SELECT COUNT(1)';
+        $sql = " FROM {user} u
+                WHERE $wherecondition
+                      AND u.id NOT IN (SELECT ue.userid
+                                         FROM {user_enrolments} ue
+                                         JOIN {enrol} e ON (e.id = ue.enrolid AND e.id = :enrolid))";
+        $order = ' ORDER BY u.lastname ASC, u.firstname ASC';
+        $params['enrolid'] = $enrolid;
+        $totalusers = $DB->count_records_sql($countfields . $sql, $params);
+        $availableusers = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
+        return array('totalusers'=>$totalusers, 'users'=>$availableusers);
+    }
+
+    /**
+     * Gets an array containing some SQL to user for when selecting, params for
+     * that SQL, and the filter that was used in constructing the sql.
+     *
+     * This function caches its result in a static var
+     *
+     * @global moodle_database $DB
+     * @staticvar string $instancessql
+     * @return string
+     */
+    protected function get_instance_sql() {
+        global $DB;
+        static $instancessql = null;
+        if ($instancessql === null) {
+            $instances = $this->get_enrolment_instances();
+            $filter = $this->get_enrolment_filter();
+            if ($filter && array_key_exists($filter, $instances)) {
+                $sql = " = :ifilter";
+                $params = array('ifilter'=>$filter);
+            } else {
+                $filter = 0;
+                if ($instances) {
+                    list($sql, $params) = $DB->get_in_or_equal(array_keys($this->get_enrolment_instances()), SQL_PARAMS_NAMED);
+                } else {
+                    // no enabled instances, oops, we should probably say something
+                    $sql = "= :never";
+                    $params = array('never'=>-1);
+                }
+            }
+            $this->instancefilter = $filter;
+            $instancessql = array($sql, $params, $filter);
+        }
+        return $instancessql;
+    }
+
+    /**
+     * Returns all of the enrolment instances for this course.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $instances
+     * @return array
+     */
+    public function get_enrolment_instances() {
+        static $instances = null;
+        if ($instances === null) {
+            $instances = enrol_get_instances($this->course->id, true);
+        }
+        return $instances;
+    }
+
+    /**
+     * Returns the names for all of the enrolment instances for this course.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $inames
+     * @return array
+     */
+    public function get_enrolment_instance_names() {
+        static $inames = null;
+        if ($inames === null) {
+            $instances = $this->get_enrolment_instances();
+            $plugins = $this->get_enrolment_plugins();
+            foreach ($instances as $key=>$instance) {
+                if (!isset($plugins[$instance->enrol])) {
+                    // weird, some broken stuff in plugin
+                    unset($instances[$key]);
+                    continue;
+                }
+                $inames[$key] = $plugins[$instance->enrol]->get_instance_name($instance);
+            }
+        }
+        return $inames;
+    }
+
+    /**
+     * Gets all of the enrolment plugins that are active for this course.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $plugins
+     * @return array
+     */
+    public function get_enrolment_plugins() {
+        static $plugins = null;
+        if ($plugins === null) {
+            $plugins = enrol_get_plugins(true);
+        }
+        return $plugins;
+    }
+
+    /**
+     * Gets all of the roles this course can contain.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $roles
+     * @return array
+     */
+    public function get_all_roles() {
+        static $roles = null;
+        if ($roles === null) {
+            $roles = role_fix_names(get_all_roles(), $this->context);
+        }
+        return $roles;
+    }
+
+    /**
+     * Gets all of the assignable roles for this course.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $roles
+     * @return array
+     */
+    public function get_assignable_roles() {
+        static $roles = null;
+        if ($roles === null) {
+            $roles = get_assignable_roles($this->context, ROLENAME_ALIAS, false); // verifies unassign access control too
+        }
+        return $roles;
+    }
+
+    /**
+     * Gets all of the groups for this course.
+     *
+     * This function caches its result in a static var
+     *
+     * @staticvar array $groups
+     * @return array
+     */
+    public function get_all_groups() {
+        static $groups = null;
+        if ($groups === null) {
+            $groups = groups_get_all_groups($this->course->id);
+            foreach ($groups as $gid=>$group) {
+                $groups[$gid]->name = format_string($group->name);
+            }
+        }
+        return $groups;
+    }
+
+    /**
+     * Unenroles a user from the course given the users ue entry
+     *
+     * @global moodle_database $DB
+     * @param stdClass $ue
+     * @return bool
+     */
+    public function unenrol_user($ue) {
+        global $DB;
+
+        $instances = $this->get_enrolment_instances();
+        $plugins = $this->get_enrolment_plugins();
+
+        $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+
+        if (!isset($instances[$ue->enrolid])) {
+            return false;
+        }
+        $instance = $instances[$ue->enrolid];
+        $plugin = $plugins[$instance->enrol];
+        if (!$plugin->allow_unenrol($instance) || !has_capability("enrol/$instance->enrol:unenrol", $this->context)) {
+            return false;
+        }
+        $plugin->unenrol_user($instance, $ue->userid);
+        return true;
+    }
+
+    /**
+     * Removes an assigned role from a user.
+     *
+     * @global moodle_database $DB
+     * @param int $userid
+     * @param int $roleid
+     * @return bool
+     */
+    public function unassign_role_from_user($userid, $roleid) {
+        global $DB;
+        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+        try {
+            role_unassign($roleid, $user->id, $this->context->id, '', NULL);
+        } catch (Exception $e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Assigns a role to a user.
+     *
+     * @param int $roleid
+     * @param int $userid
+     * @return int|false
+     */
+    public function assign_role_to_user($roleid, $userid) {
+        return role_assign($roleid, $userid, $this->context->id, '', NULL);
+    }
+
+    /**
+     * Adds a user to a group
+     *
+     * @param stdClass $user
+     * @param int $groupid
+     * @return bool
+     */
+    public function add_user_to_group($user, $groupid) {
+        require_capability('moodle/course:managegroups', $this->context);
+        $group = $this->get_group($groupid);
+        if (!$group) {
+            return false;
+        }
+        return groups_add_member($group->id, $user->id);
+    }
+
+    /**
+     * Removes a user from a group
+     *
+     * @global moodle_database $DB
+     * @param StdClass $user
+     * @param int $groupid
+     * @return bool
+     */
+    public function remove_user_from_group($user, $groupid) {
+        global $DB;
+        require_capability('moodle/course:managegroups', $this->context);
+        $group = $this->get_group($groupid);
+        if (!$group) {
+            return false;
+        }
+        return groups_remove_member($group, $user);
+    }
+
+    /**
+     * Gets the requested group
+     *
+     * @param int $groupid
+     * @return stdClass|int
+     */
+    public function get_group($groupid) {
+        $groups = $this->get_all_groups();
+        if (!array_key_exists($groupid, $groups)) {
+            return false;
+        }
+        return $groups[$groupid];
+    }
+
+    /**
+     * Edits an enrolment
+     *
+     * @param stdClass $userenrolment
+     * @param stdClass $data
+     * @return bool
+     */
+    public function edit_enrolment($userenrolment, $data) {
+        $instances = $this->get_enrolment_instances();
+        if (!array_key_exists($userenrolment->enrolid, $instances)) {
+            return false;
+        }
+        $instance = $instances[$userenrolment->enrolid];
+
+        $plugins = $this->get_enrolment_plugins();
+        $plugin = $plugins[$instance->enrol];
+        if (!$plugin->allow_unenrol($instance) || !has_capability("enrol/$instance->enrol:unenrol", $this->context)) {
+            return false;
+        }
+
+        if (!isset($data->status)) {
+            $data->status = $userenrolment->status;
+        }
+
+        $plugin->update_user_enrol($instance, $userenrolment->userid, $data->status, $data->timestart, $data->timeend);
+        return true;
+    }
+
+    /**
+     * Returns the current enrolment filter that is being applied by this class
+     * @return string
+     */
+    public function get_enrolment_filter() {
+        return $this->instancefilter;
+    }
+
+    /**
+     * Gets the roles assigned to this user that are applicable for this course.
+     *
+     * @param int $userid
+     * @return array
+     */
+    public function get_user_roles($userid) {
+        $roles = array();
+        $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
+        foreach ($ras as $ra) {
+            if ($ra->contextid != $this->context->id) {
+                if (!array_key_exists($ra->roleid, $roles)) {
+                    $roles[$ra->roleid] = null;
+                }
+                // higher ras, course always takes precedence
+                continue;
+            }
+            if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
+                continue;
+            }
+            $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
+        }
+        return $roles;
+    }
+
+    /**
+     * Gets the enrolments this user has in the course
+     *
+     * @global moodle_database $DB
+     * @param int $userid
+     * @return array
+     */
+    public function get_user_enrolments($userid) {
+        global $DB;
+        list($instancessql, $params, $filter) = $this->get_instance_sql();
+        $params['userid'] = $userid;
+        $userenrolments = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
+        $instances = $this->get_enrolment_instances();
+        $plugins = $this->get_enrolment_plugins();
+        $inames = $this->get_enrolment_instance_names();
+        foreach ($userenrolments as &$ue) {
+            $ue->enrolmentinstance     = $instances[$ue->enrolid];
+            $ue->enrolmentplugin       = $plugins[$ue->enrolmentinstance->enrol];
+            $ue->enrolmentinstancename = $inames[$ue->enrolmentinstance->id];
+        }
+        return $userenrolments;
+    }
+
+    /**
+     * Gets the groups this user belongs to
+     *
+     * @param int $userid
+     * @return array
+     */
+    public function get_user_groups($userid) {
+        return groups_get_all_groups($this->course->id, $userid, 0, 'g.id');
+    }
+
+    /**
+     * Retursn an array of params that would go into the URL to return to this
+     * exact page.
+     *
+     * @return array
+     */
+    public function get_url_params() {
+        $args = array(
+            'id' => $this->course->id
+        );
+        if (!empty($this->instancefilter)) {
+            $args['ifilter'] = $this->instancefilter;
+        }
+        return $args;
+    }
+
+    /**
+     * Returns the course this object is managing enrolments for
+     *
+     * @return stdClass
+     */
+    public function get_course() {
+        return $this->course;
+    }
+
+    /**
+     * Returns the course context
+     *
+     * @return stdClass
+     */
+    public function get_context() {
+        return $this->context;
+    }
+
+    /**
+     * Gets an array of users for display, this includes minimal user information
+     * as well as minimal information on the users roles, groups, and enrolments.
+     *
+     * @param core_enrol_renderer $renderer
+     * @param moodle_url $pageurl
+     * @param int $sort
+     * @param string $direction ASC or DESC
+     * @param int $page
+     * @param int $perpage
+     * @return array
+     */
+    public function get_users_for_display(core_enrol_renderer $renderer, moodle_url $pageurl, $sort, $direction, $page, $perpage) {
+        $users = $this->get_users($sort, $direction, $page, $perpage);
+
+        $now = time();
+        $strnever = get_string('never');
+        $straddgroup = get_string('addgroup', 'group');
+        $strunenrol = get_string('unenrol', 'enrol');
+        $stredit = get_string('edit');
+
+        $iconedit        = $renderer->pix_url('t/edit');
+        $iconenroladd    = $renderer->pix_url('t/enroladd');
+        $iconenrolremove = $renderer->pix_url('t/delete');
+
+        $allroles   = $this->get_all_roles();
+        $assignable = $this->get_assignable_roles();
+        $allgroups  = $this->get_all_groups();
+        $courseid   = $this->get_course()->id;
+        $context    = $this->get_context();
+        $canmanagegroups = has_capability('moodle/course:managegroups', $context);
+
+        $url = new moodle_url($pageurl, $this->get_url_params());
+
+        $userdetails = array();
+        foreach ($users as $user) {
+            $details = array(
+                'userid'     => $user->id,
+                'courseid'   => $courseid,
+                'picture'    => new user_picture($user),
+                'firstname'  => fullname($user, true),
+                'email'      => $user->email,
+                'lastseen'   => $strnever,
+                'roles'      => array(),
+                'groups'     => array(),
+                'enrolments' => array()
+            );
+
+            if ($user->lastseen) {
+                $details['lastseen'] = format_time($user->lastaccess);
+            }
+
+            // Roles
+            foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
+                $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>(!$rassignable || !isset($assignable[$rid])));
+            }
+
+            // Users
+            $usergroups = $this->get_user_groups($user->id);
+            foreach($usergroups as $gid=>$unused) {
+                $details['groups'][$gid] = $allgroups[$gid]->name;
+            }
+
+            // Enrolments
+            foreach ($this->get_user_enrolments($user->id) as $ue) {
+                if ($ue->timestart and $ue->timeend) {
+                    $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
+                    $periodoutside = ($ue->timestart && $ue->timeend && $now < $ue->timestart && $now > $ue->timeend);
+                } else if ($ue->timestart) {
+                    $period = get_string('periodstart', 'enrol', userdate($ue->timestart));
+                    $periodoutside = ($ue->timestart && $now < $ue->timestart);
+                } else if ($ue->timeend) {
+                    $period = get_string('periodend', 'enrol', userdate($ue->timeend));
+                    $periodoutside = ($ue->timeend && $now > $ue->timeend);
+                } else {
+                    $period = '';
+                    $periodoutside = false;
+                }
+                $details['enrolments'][$ue->id] = array(
+                    'text' => $ue->enrolmentinstancename,
+                    'period' => $period,
+                    'dimmed' =>  ($periodoutside || $ue->status != ENROL_USER_ACTIVE),
+                    'canunenrol' => ($ue->enrolmentplugin->allow_unenrol($ue->enrolmentinstance) && has_capability("enrol/".$ue->enrolmentinstance->enrol.":unenrol", $context)),
+                    'canmanage' => ($ue->enrolmentplugin->allow_manage($ue->enrolmentinstance) && has_capability("enrol/".$ue->enrolmentinstance->enrol.":manage", $context))
+                );
+            }
+            $userdetails[$user->id] = $details;
+        }
+        return $userdetails;
+
+        if (1==2){
+            // get list of roles
+            $roles = $this->get_user_roles($user->id);
+            foreach ($roles as $rid=>$unassignable) {
+                if ($unassignable && isset($assignable[$rid])) {
+                    $icon = html_writer::empty_tag('img', array('alt'=>get_string('unassignarole', 'role', $allroles[$rid]->localname), 'src'=>$iconenrolremove));
+                    $url = new moodle_url($url, array('action'=>'unassign', 'role'=>$rid, 'user'=>$user->id));
+                    $roles[$rid] = html_writer::tag('div', $allroles[$rid]->localname . html_writer::link($url, $icon, array('class'=>'unassignrolelink', 'rel'=>$rid)), array('class'=>'role role_'.$rid));
+                } else {
+                    $roles[$rid] = html_writer::tag('div', $allroles[$rid]->localname, array('class'=>'role unchangeable', 'rel'=>$rid));
+                }
+            }
+            $addrole = '';
+            if ($assignable) {
+                foreach ($assignable as $rid=>$unused) {
+                    if (!isset($roles[$rid])) {
+                        //candidate for role assignment
+                        $url = new moodle_url($url, array('action'=>'assign', 'user'=>$user->id));
+                        $icon = html_writer::empty_tag('img', array('alt'=>get_string('assignroles', 'role', $allroles[$rid]->localname), 'src'=>$iconenroladd));
+                        $addrole .= html_writer::link($url, $icon, array('class'=>'assignrolelink'));
+                        break;
+                    }
+                }
+            }
+            $roles = html_writer::tag('div', implode('', $roles), array('class'=>'roles'));
+            if ($addrole) {
+                $roles = html_writer::tag('div', $addrole, array('class'=>'addrole')).$roles;
+            }
+
+            // Get list of groups
+            $usergroups = $this->get_user_groups($user->id);
+            $groups = array();
+            foreach($usergroups as $gid=>$unused) {
+                $group = $allgroups[$gid];
+                if ($canmanagegroups) {
+                    $icon = html_writer::empty_tag('img', array('alt'=>get_string('removefromgroup', 'group', $group->name), 'src'=>$iconenrolremove));
+                    $url = new moodle_url($url, array('action'=>'removemember', 'group'=>$gid, 'user'=>$user->id));
+                    $groups[] = $group->name . html_writer::link($url, $icon);
+                } else {
+                    $groups[] = $group->name;
+                }
+            }
+            $groups = implode(', ', $groups);
+            if ($canmanagegroups and (count($usergroups) < count($allgroups))) {
+                $icon = html_writer::empty_tag('img', array('alt'=>$straddgroup, 'src'=>$iconenroladd));
+                $url = new moodle_url($url, array('action'=>'addmember', 'user'=>$user->id));
+                $groups .= '<div>'.html_writer::link($url, $icon).'</div>';
+            }
+
+
+            // get list of enrol instances
+            $ues = $this->get_user_enrolments($user->id);
+            $edits = array();
+            foreach ($ues as $ue) {
+                $edit       = $ue->enrolmentinstancename;
+
+                $dimmed = false;
+                if ($ue->timestart and $ue->timeend) {
+                    $edit .= '&nbsp;('.get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend))).')';
+                    $dimmed = ($now < $ue->timestart and $now > $ue->timeend);
+                } else if ($ue->timestart) {
+                    $edit .= '&nbsp;('.get_string('periodstart', 'enrol', userdate($ue->timestart)).')';
+                    $dimmed = ($now < $ue->timestart);
+                } else if ($ue->timeend) {
+                    $edit .= '&nbsp;('.get_string('periodend', 'enrol', userdate($ue->timeend)).')';
+                    $dimmed = ($now > $ue->timeend);
+                }
+
+                if ($dimmed or $ue->status != ENROL_USER_ACTIVE) {
+                    $edit = html_writer::tag('span', $edit, array('class'=>'dimmed_text'));
+                }
+
+                if ($ue->enrolmentplugin->allow_unenrol($ue->enrolmentinstance) && has_capability("enrol/".$ue->enrolmentinstance->enrol.":unenrol", $context)) {
+                    $icon = html_writer::empty_tag('img', array('alt'=>$strunenrol, 'src'=>$iconenrolremove));
+                    $url = new moodle_url($url, array('action'=>'unenrol', 'ue'=>$ue->id));
+                    $edit .= html_writer::link($url, $icon, array('class'=>'unenrollink', 'rel'=>$ue->id));
+                }
+
+                if ($ue->enrolmentplugin->allow_manage($ue->enrolmentinstance) && has_capability("enrol/".$ue->enrolmentinstance->enrol.":manage", $context)) {
+                    $icon = html_writer::empty_tag('img', array('alt'=>$stredit, 'src'=>$iconedit));
+                    $url = new moodle_url($url, array('action'=>'edit', 'ue'=>$ue->id));
+                    $edit .= html_writer::link($url, $icon, array('class'=>'editenrollink', 'rel'=>$ue->id));
+                }
+
+                $edits[] = html_writer::tag('div', $edit, array('class'=>'enrolment'));
+            }
+            $edits = implode('', $edits);
+
+
+            $userdetails[$user->id] = array(
+                'picture'   => $renderer->user_picture($user, array('courseid'=>$courseid)),
+                'firstname' => html_writer::link(new moodle_url('/user/view.php', array('id'=>$user->id, 'course'=>$courseid)), fullname($user, true)),
+                'email'     => $user->email,
+                'lastseen'  => $strlastaccess,
+                'role'      => $roles,
+                'group'     => $groups,
+                'enrol'     => $edits
+            );
+        }
+        return $userdetails;
+    }
+}
\ No newline at end of file
Index: moodle/enrol/renderer.php
--- moodle/enrol/renderer.php No Base Revision
+++ moodle/enrol/renderer.php Locally New
@@ -0,0 +1,592 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This is the main renderer for the enrol section.
+ *
+ * @package   moodlecore
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * This is the core renderer
+ *
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_enrol_renderer extends plugin_renderer_base {
+
+    /**
+     * Renders a course enrolment table
+     *
+     * @param course_enrolment_table $table
+     * @return string
+     */
+    protected function render_course_enrolment_table(course_enrolment_table $table) {
+        $content = '';
+        $enrolmentselector = $table->get_enrolment_selector($this->page);
+        if ($enrolmentselector) {
+            $content .= $this->output->render($enrolmentselector);
+        }
+        $content  .= $this->output->render($table->get_enrolment_type_filter());
+        $content .= $this->output->render($table->get_paging_bar());
+        $content .= html_writer::table($table);
+        $content .= $this->output->render($table->get_paging_bar());
+        $enrolmentselector = $table->get_enrolment_selector($this->page);
+        if ($enrolmentselector) {
+            $content .= $this->output->render($enrolmentselector);
+        }
+        return $content;
+    }
+
+    /**
+     * Generates HTML to display the users roles and any available actions
+     *
+     * @param int $userid
+     * @param array $roles
+     * @param array $assignableroles
+     * @param moodle_url $pageurl
+     * @return string
+     */
+    public function user_roles_and_actions($userid, $roles, $assignableroles, $pageurl) {
+        $iconenroladd    = $this->output->pix_url('t/enroladd');
+        $iconenrolremove = $this->output->pix_url('t/delete');
+
+        // get list of roles
+        $rolesoutput = '';
+        foreach ($roles as $roleid=>$role) {
+            if (!$role['unchangeable']) {
+                $strunassign = get_string('unassignarole', 'role', $role['text']);
+                $icon = html_writer::empty_tag('img', array('alt'=>$strunassign, 'src'=>$iconenrolremove));
+                $url = new moodle_url($pageurl, array('action'=>'unassign', 'role'=>$roleid, 'user'=>$userid));
+                $rolesoutput .= html_writer::tag('div', $role['text'] . html_writer::link($url, $icon, array('class'=>'unassignrolelink', 'rel'=>$roleid, 'title'=>$strunassign)), array('class'=>'role role_'.$roleid));
+            } else {
+                $rolesoutput .= html_writer::tag('div', $role['text'], array('class'=>'role unchangeable', 'rel'=>$roleid));
+            }
+        }
+        $output = '';
+        if (!empty($assignableroles)) {
+            $roleids = array_keys($roles);
+            $hasallroles = true;
+            foreach (array_keys($assignableroles) as $key) {
+                if (!in_array($key, $roleids)) {
+                    $hasallroles = false;
+                    break;
+                }
+            }
+            if (!$hasallroles) {
+                $url = new moodle_url($pageurl, array('action'=>'assign', 'user'=>$userid));
+                $icon = html_writer::empty_tag('img', array('alt'=>get_string('assignroles', 'role'), 'src'=>$iconenroladd));
+                $output = html_writer::tag('div', html_writer::link($url, $icon, array('class'=>'assignrolelink', 'title'=>get_string('assignroles', 'role'))), array('class'=>'addrole'));
+            }
+        }
+        $output .= html_writer::tag('div', $rolesoutput, array('class'=>'roles'));
+        return $output;
+    }
+
+    /**
+     * Generates the HTML to view the users groups and available group actions
+     *
+     * @param int $userid
+     * @param array $groups
+     * @param array $allgroups
+     * @param bool $canmanagegroups
+     * @param moodle_url $pageurl
+     * @return string
+     */
+    public function user_groups_and_actions($userid, $groups, $allgroups, $canmanagegroups, $pageurl) {
+        $iconenroladd    = $this->output->pix_url('t/enroladd');
+        $iconenrolremove = $this->output->pix_url('t/delete');
+        $straddgroup = get_string('addgroup', 'group');
+
+        $groupoutput = '';
+        foreach($groups as $groupid=>$name) {
+            if ($canmanagegroups) {
+                $icon = html_writer::empty_tag('img', array('alt'=>get_string('removefromgroup', 'group', $name), 'src'=>$iconenrolremove));
+                $url = new moodle_url($pageurl, array('action'=>'removemember', 'group'=>$groupid, 'user'=>$userid));
+                $groupoutput .= html_writer::tag('div', $name . html_writer::link($url, $icon), array('class'=>'group', 'rel'=>$groupid));
+            } else {
+                $groupoutput .= html_writer::tag('div', $name, array('class'=>'group', 'rel'=>$groupid));
+            }
+        }
+        $groupoutput = html_writer::tag('div', $groupoutput, array('class'=>'groups'));
+        if ($canmanagegroups && (count($groups) < count($allgroups))) {
+            $icon = html_writer::empty_tag('img', array('alt'=>$straddgroup, 'src'=>$iconenroladd));
+            $url = new moodle_url($pageurl, array('action'=>'addmember', 'user'=>$userid));
+            $groupoutput .= html_writer::tag('div', html_writer::link($url, $icon), array('class'=>'addgroup'));
+        }
+        return $groupoutput;
+    }
+
+    /**
+     * Generates the HTML for the given enrolments + available actions
+     *
+     * @param int $userid
+     * @param array $enrolments
+     * @param moodle_url $pageurl
+     * @return string
+     */
+    public function user_enrolments_and_actions($userid, $enrolments, $pageurl) {
+        $iconedit        = $this->output->pix_url('t/edit');
+        $iconenrolremove = $this->output->pix_url('t/delete');
+        $strunenrol = get_string('unenrol', 'enrol');
+        $stredit = get_string('edit');
+
+        $output = '';
+        foreach ($enrolments as $ueid=>$enrolment) {
+            $enrolmentoutput = $enrolment['text'].' '.$enrolment['period'];
+            if ($enrolment['dimmed']) {
+                $enrolmentoutput = html_writer::tag('span', $enrolmentoutput, array('class'=>'dimmed_text'));
+            }
+            if ($enrolment['canunenrol']) {
+                $icon = html_writer::empty_tag('img', array('alt'=>$strunenrol, 'src'=>$iconenrolremove));
+                $url = new moodle_url($pageurl, array('action'=>'unenrol', 'ue'=>$ueid));
+                $enrolmentoutput .= html_writer::link($url, $icon, array('class'=>'unenrollink', 'rel'=>$ueid));
+            }
+            if ($enrolment['canmanage']) {
+                $icon = html_writer::empty_tag('img', array('alt'=>$stredit, 'src'=>$iconedit));
+                $url = new moodle_url($url, array('action'=>'edit', 'ue'=>$ueid));
+                $enrolmentoutput .= html_writer::link($url, $icon, array('class'=>'editenrollink', 'rel'=>$ueid));
+            }
+            $output .= html_writer::tag('div', $enrolmentoutput, array('class'=>'enrolment'));
+        }
+        return $output;
+    }
+
+}
+
+/**
+ * Main course enrolment table
+ *
+ * This table is used to display the enrolment information for a course.
+ * It requires that a course enrolment manager be provided during constuct with
+ * provides all of the information for the table.
+ * The control then produces the table, the paging, and the associated JS actions
+ * for the page.
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_enrolment_table extends html_table implements renderable {
+
+    /**
+     * The get/post variable that is used to identify the page.
+     * Default: page
+     */
+    const PAGEVAR = 'page';
+
+    /**
+     * The get/post variable to is used to identify the number of items to display
+     * per page.
+     * Default: perpage
+     */
+    const PERPAGEVAR = 'perpage';
+
+    /**
+     * The get/post variable that is used to identify the sort field for the table.
+     * Default: sort
+     */
+    const SORTVAR = 'sort';
+
+    /**
+     * The get/post variable that is used to identify the sort direction for the table.
+     * Default: dir
+     */
+    const SORTDIRECTIONVAR = 'dir';
+
+    /**
+     * The default number of items per page.
+     * Default: 20
+     */
+    const DEFAULTPERPAGE = 20;
+
+    /**
+     * The default sort, options are course_enrolment_table::$sortablefields
+     * Default: lastname
+     */
+    const DEFAULTSORT = 'lastname';
+
+    /**
+     * The default direction
+     * Default: ASC
+     */
+    const DEFAULTSORTDIRECTION = 'ASC';
+
+    /**
+     * The current page, starting from 0
+     * @var int
+     */
+    public $page = 0;
+
+    /**
+     * The total number of pages
+     * @var int
+     */
+    public $pages = 0;
+
+    /**
+     * The number of items to display per page
+     * @var int
+     */
+    public $perpage = 0;
+
+    /**
+     * The URL of the page for this table
+     * @var moodle_url
+     */
+    public $pageurl;
+
+    /**
+     * The sort field for this table, should be one of course_enrolment_table::$sortablefields
+     * @var string
+     */
+    public $sort;
+
+    /**
+     * The sort direction, either ASC or DESC
+     * @var string
+     */
+    public $sortdirection;
+
+    /**
+     * The course manager this table is displaying for
+     * @var course_enrolment_manager
+     */
+    protected $manager;
+
+    /**
+     * The paging bar that controls the paging for this table
+     * @var paging_bar
+     */
+    protected $pagingbar = null;
+
+    /**
+     * The total number of users enrolled in the course
+     * @var int
+     */
+    protected $totalusers = null;
+
+    /**
+     * The users enrolled in this course
+     * @var array
+     */
+    protected $users = null;
+
+    /**
+     * The fields for this table
+     * @var array
+     */
+    protected $fields = array();
+
+    protected static $sortablefields = array('firstname', 'lastname', 'email', 'lastaccess');
+
+    /**
+     * Constructs the table
+     *
+     * @param course_enrolment_manager $manager
+     */
+    public function __construct(course_enrolment_manager $manager, moodle_url $pageurl) {
+
+        $this->manager = $manager;
+        $this->pageurl = $pageurl;
+        
+        $this->page =           optional_param(self::PAGEVAR, 0, PARAM_INT);
+        $this->perpage =        optional_param(self::PERPAGEVAR, self::DEFAULTPERPAGE, PARAM_INT);
+        $this->sort =           optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHA);
+        $this->sortdirection  = optional_param(self::SORTDIRECTIONVAR, self::DEFAULTSORTDIRECTION, PARAM_ALPHA);
+
+        $this->attributes = array('class'=>'userenrolment');
+        if (!in_array($this->sort, self::$sortablefields)) {
+            $this->sort = self::DEFAULTSORT;
+        }
+        if ($this->page < 0) {
+            $this->page = 0;
+        }
+        if ($this->sortdirection !== 'ASC' && $this->sortdirection !== 'DESC') {
+            $this->sortdirection = self::DEFAULTSORTDIRECTION;
+        }
+
+        $this->id = html_writer::random_id();
+        $this->set_total_users($manager->get_total_users());
+    }
+
+    /**
+     * Gets the sort direction for a given field
+     *
+     * @param string $field
+     * @return string ASC or DESC
+     */
+    public function get_field_sort_direction($field) {
+        if ($field == $this->sort) {
+            return ($this->sortdirection == 'ASC')?'DESC':'ASC';
+        }
+        return self::DEFAULTSORTDIRECTION;
+    }
+
+    /**
+     * Sets the fields for this table. These get added to the tables head as well.
+     *
+     * You can also use a multi dimensional array for this to have multiple fields
+     * in a single column
+     *
+     * @param array $fields An array of fields to set
+     * @param string $output
+     */
+    public function set_fields($fields, $output=null) {
+        global $OUTPUT;
+        if ($output === null) {
+            $output = $OUTPUT;
+        }
+        $this->fields = $fields;
+        $this->head = array();
+        $this->colclasses = array();
+        $this->align = array();
+        $url = new moodle_url($this->pageurl, $this->get_url_params()+$this->manager->get_url_params());
+        foreach ($fields as $name => $label) {
+            $newlabel = '';
+            if (is_array($label)) {
+                $bits = array();
+                foreach ($label as $n => $l) {
+                    if ($l === false) {
+                        continue;
+                    }
+                    if (!in_array($n, self::$sortablefields)) {
+                        $bits[] = $l;
+                    } else {
+                        $link = html_writer::link(new moodle_url($url, array(self::SORTVAR=>$n)), $fields[$name][$n]);
+                        if ($this->sort == $n) {
+                            $link .= ' '.html_writer::link(new moodle_url($url, array(self::SORTVAR=>$n, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($n))), $this->get_direction_icon($output, $n));
+                        }
+                        $bits[] = html_writer::tag('span', $link, array('class'=>'subheading_'.$n));
+
+                    }
+                }
+                $newlabel = join(' / ', $bits);
+            } else {
+                if (!in_array($name, self::$sortablefields)) {
+                    $newlabel = $label;
+                } else {
+                    $newlabel  = html_writer::link(new moodle_url($url, array(self::SORTVAR=>$name)), $fields[$name]);
+                    if ($this->sort == $name) {
+                        $newlabel .= ' '.html_writer::link(new moodle_url($url, array(self::SORTVAR=>$name, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($name))), $this->get_direction_icon($output, $name));
+                    }
+                }
+            }
+            $this->head[] = $newlabel;
+            $this->colclasses[] = 'field col_'.$name;
+        }
+    }
+    /**
+     * Sets the total number of users
+     *
+     * @param int $totalusers
+     */
+    public function set_total_users($totalusers) {
+        $this->totalusers = $totalusers;
+        $this->pages = ceil($this->totalusers / $this->perpage);
+        if ($this->page > $this->pages) {
+            $this->page = $this->pages;
+        }
+    }
+    /**
+     
+     */
+    /**
+     * Sets the users for this table
+     *
+     * @param array $users
+     * @param moodle_page $page
+     */
+    public function set_users(array $users, moodle_page $page=null) {
+        global $PAGE;
+        if ($page === null) {
+            $page = $PAGE;
+        }
+        foreach ($users as $userid=>$user) {
+            $user = (array)$user;
+            $row = new html_table_row();
+            $row->attributes = array('class' => 'userinforow');
+            $row->id = 'user_'.$userid;
+            $row->cells = array();
+            foreach ($this->fields as $field => $label) {
+                if (is_array($label)) {
+                    $bits = array();
+                    foreach (array_keys($label) as $subfield) {
+                        if (array_key_exists($subfield, $user)) {
+                            $bits[] = html_writer::tag('div', $user[$subfield], array('class'=>'subfield subfield_'.$subfield));
+                        }
+                    }
+                    if (empty($bits)) {
+                        $bits[] = '&nbsp;';
+                    }
+                    $row->cells[] = new html_table_cell(join(' ', $bits));
+                } else {
+                    if (!array_key_exists($field, $user)) {
+                        $user[$field] = '&nbsp;';
+                    }
+                    $row->cells[] = new html_table_cell($user[$field]);
+                }
+            }
+            $this->data[] = $row;
+        }
+
+        $arguments = array(array('containerId'=>$this->id, 'userIds'=>array_keys($users), 'courseId'=>$this->manager->get_course()->id));
+        $page->requires->yui_module(array('moodle-enrol-rolemanager', 'moodle-enrol-rolemanager-skin'), 'M.enrol.rolemanager.init', $arguments);
+    }
+    
+    /**
+     * Gets the paging bar instance for this table
+     *
+     * @return paging_bar
+     */
+    public function get_paging_bar() {
+        if ($this->pagingbar == null) {
+            $this->pagingbar = new paging_bar($this->totalusers, $this->page, $this->perpage, $this->pageurl, self::PAGEVAR);
+        }
+        return $this->pagingbar;
+    }
+
+    /**
+     * Gets the direction icon for the sortable field within this table
+     *
+     * @param core_renderer $output
+     * @param string $field
+     * @return string
+     */
+    protected function get_direction_icon($output, $field) {
+        $direction = self::DEFAULTSORTDIRECTION;
+        if ($this->sort == $field) {
+            $direction = $this->sortdirection;
+        }
+        if ($direction === 'ASC') {
+            return html_writer::empty_tag('img', array('alt'=>'', 'src'=>$output->pix_url('t/down')));
+        } else {
+            return html_writer::empty_tag('img', array('alt'=>'', 'src'=>$output->pix_url('t/up')));
+        }
+    }
+
+    /**
+     * Gets the params that will need to be added to the url in order to return to this page.
+     *
+     * @return array
+     */
+    public function get_url_params() {
+        return array(
+            self::PAGEVAR => $this->page,
+            self::PERPAGEVAR => $this->perpage,
+            self::SORTVAR => $this->sort,
+            self::SORTDIRECTIONVAR => $this->sortdirection
+        );
+    }
+
+    /**
+     * Gets the enrolment type filter control for this table
+     *
+     * @return single_select
+     */
+    public function get_enrolment_type_filter() {
+        $url = new moodle_url($this->pageurl, $this->manager->get_url_params()+$this->get_url_params());
+        $selector = new single_select($url, 'ifilter', array(0=>get_string('all')) + $this->manager->get_enrolment_instance_names(), $this->manager->get_enrolment_filter(), array());
+        $selector->set_label( get_string('enrolmentinstances', 'enrol'));
+        return $selector;
+    }
+
+    /**
+     * Gets the enrolment selector control for this table and initialises its
+     * JavaScript
+     *
+     * @return single_button|url_select
+     */
+    public function get_enrolment_selector(moodle_page $page) {
+        static $count = 0;
+
+        $instances  = $this->manager->get_enrolment_instances();
+        $plugins    = $this->manager->get_enrolment_plugins();
+        // print enrol link or selection
+        $links = array();
+        foreach($instances as $instance) {
+            $plugin = $plugins[$instance->enrol];
+            if ($link = $plugin->get_manual_enrol_link($instance)) {
+                $links[$instance->id] = $link;
+            }
+        }
+        if (!empty($links)) {
+            $arguments = array();
+            $count ++;
+            if (count($links) == 1) {
+                $control = new single_button(reset($links), get_string('enrolusers', 'enrol_manual'), 'get');
+                $control->class = 'singlebutton enrolusersbutton instance'.$count;
+                $control->formid = 'manuallyenrol_single_'+$count;
+                $arguments[] = array('id'=>key($links), 'name'=>$plugins[$instances[key($links)]->enrol]->get_instance_name($instances[key($links)]));
+            } else if (count($links) > 1) {
+                $inames     = $this->manager->get_enrolment_instance_names();
+                $options = array();
+                foreach ($links as $i=>$link) {
+                    $options[$link->out(false)] = $inames[$i];
+                    $arguments[] = array('id'=>$i, 'name'=>$plugins[$instances[$i]->enrol]->get_instance_name($instances[$i]));
+                }
+                $control = new url_select($options, '', array(''=>get_string('enrolusers', 'enrol_manual').'...'));
+                $control->class = 'singlebutton enrolusersbutton instance'.$count;
+                $control->formid = 'manuallyenrol_select_'+$count;
+            }
+            $course = $this->manager->get_course();
+            $url = new moodle_url($this->pageurl, $this->manager->get_url_params()+$this->get_url_params());
+            $timeformat = get_string('strftimedatefullshort');
+            $today = time();
+            $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
+            $startdateoptions = array();
+            if ($course->startdate > 0) {
+                $startdateoptions[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $timeformat) . ')';
+            }
+            $startdateoptions[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
+
+            if ($count == 1) {
+                $page->requires->strings_for_js(array(
+                    'ajaxoneuserfound',
+                    'ajaxxusersfound',
+                    'ajaxnext25',
+                    'enrol',
+                    'enrolmentoptions',
+                    'enrolusers',
+                    'errajaxfailedenrol',
+                    'errajaxsearch',
+                    'none',
+                    'usersearch',
+                    'unlimitedduration',
+                    'startdatetoday',
+                    'durationdays',
+                    'enrolperiod'), 'enrol');
+                $page->requires->string_for_js('assignroles', 'role');
+                $page->requires->string_for_js('startingfrom', 'moodle');
+
+
+                $arguments = array(array(
+                    'instances'=>$arguments,
+                    'courseid'=>$course->id,
+                    'ajaxurl'=>'/enrol/ajax.php',
+                    'url'=>$url->out(false),
+                    'optionsStartDate'=>$startdateoptions));
+                $page->requires->yui_module(array('moodle-enrol-enrolmentmanager', 'moodle-enrol-enrolmentmanager-skin'), 'M.enrol.enrolmentmanager.init', $arguments);
+            }
+            return $control;
+        }
+        return null;
+    }
+}
\ No newline at end of file
Index: moodle/enrol/users.php
--- moodle/enrol/users.php Base (1.4)
+++ moodle/enrol/users.php Locally Modified (Based On 1.4)
@@ -17,138 +17,80 @@
 /**
  * Main course enrolment management UI, this is not compatible with frontpage course.
  *
- * @package    core
- * @subpackage enrol
+ * @package    moodlecore
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 require('../config.php');
+require_once("$CFG->dirroot/enrol/locallib.php");
 require_once("$CFG->dirroot/enrol/users_forms.php");
+require_once("$CFG->dirroot/enrol/renderer.php");
 require_once("$CFG->dirroot/group/lib.php");
 
 $id      = required_param('id', PARAM_INT); // course id
 $action  = optional_param('action', '', PARAM_ACTION);
-$confirm = optional_param('confirm', 0, PARAM_BOOL);
+$filter  = optional_param('ifilter', 0, PARAM_INT);
 
-$ifilter = optional_param('ifilter', 0, PARAM_INT); // only one instance
-$page    = optional_param('page', 0, PARAM_INT);
-$perpage = optional_param('perpage', 20, PARAM_INT);
-$sort    = optional_param('sort', 'lastname', PARAM_ALPHA);
-$dir     = optional_param('dir', 'ASC', PARAM_ALPHA);
+$PAGE->set_url(new moodle_url('/enrol/users.php', array('id'=>$id)));
 
-
 $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
 $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
 
-require_login($course);
-require_capability('moodle/course:enrolreview', $context);
-
 if ($course->id == SITEID) {
-    redirect("$CFG->wwwroot/");
+    redirect(new moodle_url('/'));
 }
 
-$managegroups = has_capability('moodle/course:managegroups', $context);
-$instances = enrol_get_instances($course->id, true);
-$plugins   = enrol_get_plugins(true);
-$inames    = array();
-foreach ($instances as $k=>$i) {
-    if (!isset($plugins[$i->enrol])) {
-        // weird, some broken stuff in plugin
-        unset($instances[$k]);
-        continue;
-    }
-    $inames[$k] = $plugins[$i->enrol]->get_instance_name($i);
-}
-
-// validate paging params
-if ($ifilter != 0 and !isset($instances[$ifilter])) {
-    $ifilter = 0;
-}
-if ($perpage < 3) {
-    $perpage = 3;
-}
-if ($page < 0) {
-    $page = 0;
-}
-if (!in_array($dir, array('ASC', 'DESC'))) {
-    $dir = 'ASC';
-}
-if (!in_array($sort, array('firstname', 'lastname', 'email', 'lastseen'))) {
-    $dir = 'lastname';
-}
-
-$PAGE->set_url('/enrol/users.php', array('id'=>$course->id, 'page'=>$page, 'sort'=>$sort, 'dir'=>$dir, 'perpage'=>$perpage, 'ifilter'=>$ifilter));
+require_login($course);
+require_capability('moodle/course:enrolreview', $context);
 $PAGE->set_pagelayout('admin');
 
-//lalala- nav hack
-navigation_node::override_active_url(new moodle_url('/enrol/users.php', array('id'=>$course->id)));
+$manager = new course_enrolment_manager($course, $filter);
+$table = new course_enrolment_table($manager, $PAGE->url);
+$pageurl = new moodle_url($PAGE->url, $manager->get_url_params()+$table->get_url_params());
 
+// Check if there is an action to take
+if ($action) {
 
-$allroles   = get_all_roles();
-$allroles   = role_fix_names($allroles, $context);
-$assignable = get_assignable_roles($context, ROLENAME_ALIAS, false); // verifies unassign access control too
-$allgroups  = groups_get_all_groups($course->id);
-foreach ($allgroups as $gid=>$group) {
-    $allgroups[$gid]->name = format_string($group->name);
-}
+    // Check if the page is confirmed (and sesskey is correct)
+    $confirm = optional_param('confirm', false, PARAM_BOOL) && confirm_sesskey();
 
-if ($action) {
+    $actiontaken = false;
+    $pagetitle = '';
+    $pageheading = '';
+    $mform = null;
+    $pagecontent = null;
+
     switch ($action) {
         case 'unenrol':
-            $ue = required_param('ue', PARAM_INT);
-            if (!$ue = $DB->get_record('user_enrolments', array('id'=>$ue))) {
-                break;
-            }
-            $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
-            if (!isset($instances[$ue->enrolid])) {
-                break;
-            }
-            $instance = $instances[$ue->enrolid];
-            $plugin = $plugins[$instance->enrol];
-            if (!$plugin->allow_unenrol($instance) or !has_capability("enrol/$instance->enrol:unenrol", $context)) {
-                break;
-            }
-
-            if ($confirm and confirm_sesskey()) {
-                $plugin->unenrol_user($instance, $ue->userid);
-                redirect($PAGE->url);
-
+            $ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
+            if ($confirm && $manager->unenrol_user($ue)) {
+                redirect($pageurl);
             } else {
-                $yesurl = new moodle_url($PAGE->url, array('action'=>'unenrol', 'ue'=>$ue->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+                $yesurl = new moodle_url($pageurl, array('action'=>'unenrol', 'ue'=>$ue->id, 'confirm'=>1, 'sesskey'=>sesskey()));
                 $message = get_string('unenrolconfirm', 'enrol', array('user'=>fullname($user, true), 'course'=>format_string($course->fullname)));
-                $PAGE->set_title(get_string('unenrol', 'enrol'));
-                echo $OUTPUT->header();
-                echo $OUTPUT->heading(get_string('unenrol', 'enrol'));
-                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
-                echo $OUTPUT->footer();
-                die;
+                $pagetitle = get_string('unenrol', 'enrol');
+                $pagecontent = $OUTPUT->confirm($message, $yesurl, $pageurl);
             }
+            $actiontaken = true;
             break;
 
         case 'unassign':
             $role = required_param('role', PARAM_INT);
             $user = required_param('user', PARAM_INT);
-            if (!isset($assignable[$role])) {
-                break;
-            }
-            $role = $allroles[$role];
-            $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
-
-            if ($confirm and confirm_sesskey()) {
-                role_unassign($role->id, $user->id, $context->id, '', NULL);
-                redirect($PAGE->url);
-
+            if ($confirm && $manager->unassign_role_from_user($user, $role)) {
+                redirect($pageurl);
             } else {
-                $yesurl = new moodle_url($PAGE->url, array('action'=>'unassign', 'role'=>$role->id, 'user'=>$user->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+                $allroles = $manager->get_all_roles();
+                $role = $allroles[$role];
+                $yesurl = new moodle_url($pageurl, array('action'=>'unassign', 'role'=>$role->id, 'user'=>$user->id, 'confirm'=>1, 'sesskey'=>sesskey()));
                 $message = get_string('unassignconfirm', 'role', array('user'=>fullname($user, true), 'role'=>$role->localname));
-                $PAGE->set_title(get_string('unassignarole', 'role', $role->localname));
-                echo $OUTPUT->header();
-                echo $OUTPUT->heading(get_string('unassignarole', 'role', $role->localname));
-                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
-                echo $OUTPUT->footer();
-                die;
+                $pagetitle = get_string('unassignarole', 'role', $role->localname);
+                $pagecontent = $OUTPUT->confirm($message, $yesurl, $pageurl);
             }
+            $actiontaken = true;
             break;
 
         case 'assign':
@@ -159,363 +101,118 @@
                 break; // no roles without enrolments here in this script
             }
 
-            $mform = new enrol_users_assign_form(NULL, array('user'=>$user, 'course'=>$course, 'assignable'=>$assignable));
-
-            if ($mform->is_cancelled()) {
-                redirect($PAGE->url);
-
-            } else if ($data = $mform->get_data()) {
-                if ($data->roleid) {
-                    role_assign($data->roleid, $user->id, $context->id, '', NULL);
+            $mform = new enrol_users_assign_form(NULL, array('user'=>$user, 'course'=>$course, 'assignable'=>$manager->get_assignable_roles()));
+            $mform->set_data($pageurl->params());
+            $data = $mform->get_data();
+            if ($mform->is_cancelled() || ($data && $manager->assign_role_to_user($data->roleid, $user->id))) {
+                redirect($pageurl);
+            } else {
+                $pagetitle = get_string('assignroles', 'role');
                 }
-                redirect($PAGE->url);
-            }
+            $actiontaken = true;
+            break;
 
-            $PAGE->set_title(get_string('assignroles', 'role'));
-            echo $OUTPUT->header();
-            echo $OUTPUT->heading(get_string('assignroles', 'role'));
-            $mform->display();
-            echo $OUTPUT->footer();
-            die;
-
         case 'removemember':
-            $group = required_param('group', PARAM_INT);
-            $user  = required_param('user', PARAM_INT);
-            if (!$managegroups) {
+            $groupid = required_param('group', PARAM_INT);
+            $userid  = required_param('user', PARAM_INT);
+            $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+            if ($confirm && $manager->remove_user_from_group($user, $groupid)) {
+                redirect($pageurl);
+            } else {
+                $group = $manager->get_group($groupid);
+                if (!$group) {
                 break;
             }
-            if (!isset($allgroups[$group])) {
-                break;
-            }
-            $group = $allgroups[$group];
-            $user  = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
-
-            if ($confirm and confirm_sesskey()) {
-                groups_remove_member($group, $user);
-                redirect($PAGE->url);
-
-            } else {
-                $yesurl = new moodle_url($PAGE->url, array('action'=>'removemember', 'group'=>$group->id, 'user'=>$user->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $yesurl = new moodle_url($pageurl, array('action'=>'removemember', 'group'=>$groupid, 'user'=>$userid, 'confirm'=>1, 'sesskey'=>sesskey()));
                 $message = get_string('removefromgroupconfirm', 'group', array('user'=>fullname($user, true), 'group'=>$group->name));
-                $PAGE->set_title(get_string('removefromgroup', 'group', $group->name));
-                echo $OUTPUT->header();
-                echo $OUTPUT->heading(get_string('removefromgroup', 'group', $group->name));
-                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
-                echo $OUTPUT->footer();
-                die;
+                $pagetitle = get_string('removefromgroup', 'group', $group->name);
+                $pagecontent = $OUTPUT->confirm($message, $yesurl, $pageurl);
             }
+            $actiontaken = true;
             break;
 
         case 'addmember':
-            $user = required_param('user', PARAM_INT);
-            $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+            $userid = required_param('user', PARAM_INT);
+            $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
 
-            if (!$managegroups) {
-                break;
+            $mform = new enrol_users_addmember_form(NULL, array('user'=>$user, 'course'=>$course, 'allgroups'=>$manager->get_all_groups()));
+            $mform->set_data($pageurl->params());
+            $data = $mform->get_data();
+            if ($mform->is_cancelled() || ($data && $manager->add_user_to_group($user, $data->groupid))) {
+                redirect($pageurl);
+            } else {
+                $pagetitle = get_string('addgroup', 'group');
             }
-            if (!is_enrolled($context, $user)) {
-                break; // no roles without enrolments here in this script
-            }
+            $actiontaken = true;
+            break;
 
-            $mform = new enrol_users_addmember_form(NULL, array('user'=>$user, 'course'=>$course, 'allgroups'=>$allgroups));
-
-            if ($mform->is_cancelled()) {
-                redirect($PAGE->url);
-
-            } else if ($data = $mform->get_data()) {
-                if ($data->groupid) {
-                    groups_add_member($data->groupid, $user->id);
-                }
-                redirect($PAGE->url);
-            }
-
-            $PAGE->set_title(get_string('addgroup', 'group'));
-            echo $OUTPUT->header();
-            echo $OUTPUT->heading(get_string('addgroup', 'group'));
-            $mform->display();
-            echo $OUTPUT->footer();
-            die;
-
         case 'edit':
             $ue = required_param('ue', PARAM_INT);
             if (!$ue = $DB->get_record('user_enrolments', array('id'=>$ue))) {
+                $actiontaken = false;
                 break;
             }
             $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
-            if (!isset($instances[$ue->enrolid])) {
-                break;
-            }
-            $instance = $instances[$ue->enrolid];
-            $plugin = $plugins[$instance->enrol];
-            if (!$plugin->allow_unenrol($instance) or !has_capability("enrol/$instance->enrol:unenrol", $context)) {
-                break;
-            }
 
             $mform = new enrol_users_edit_form(NULL, array('user'=>$user, 'course'=>$course, 'ue'=>$ue));
-
-            if ($mform->is_cancelled()) {
-                redirect($PAGE->url);
-
-            } else if ($data = $mform->get_data()) {
-                if (!isset($data->status)) {
-                    $status = $ue->status;
+            $mform->set_data($pageurl->params());
+            $data = $mform->get_data();
+            if ($mform->is_cancelled() || ($data && $manager->edit_enrolment($ue, $data))) {
+                redirect($pageurl);
+            } else {
+                $pagetitle = fullname($user);
                 }
-                $plugin->update_user_enrol($instance, $ue->userid, $data->status, $data->timestart, $data->timeend);
-                redirect($PAGE->url);
+            $actiontaken = true;
+            break;
             }
 
-            $PAGE->set_title(fullname($user));
+    if ($actiontaken) {
+        if (empty($pageheading)) {
+            $pageheading = $pagetitle;
+        }
+        $PAGE->set_title($pagetitle);
+        $PAGE->set_heading($pageheading);
             echo $OUTPUT->header();
             echo $OUTPUT->heading(fullname($user));
+        if (!is_null($mform)) {
             $mform->display();
-            echo $OUTPUT->footer();
-            die;
-    }
-}
-
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('enrolledusers', 'enrol'));
-$PAGE->set_title(get_string('enrolledusers', 'enrol'));
-
-notify('NOTICE TO TESTERS: This interface will shortly be replaced with a new one.');   // TODO FIXME REMOVE THIS
-
-if ($ifilter) {
-    $instancessql = " = :ifilter";
-    $params = array('ifilter'=>$ifilter);
 } else {
-    if ($instances) {
-        list($instancessql, $params) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED);
-    } else {
-        // no enabled instances, oops, we should probably say something
-        $instancessql = "= :never";
-        $params = array('never'=>-1);
+            echo $pagecontent;
     }
+        echo $OUTPUT->footer();
+        exit;
 }
-
-$sqltotal = "SELECT COUNT(DISTINCT u.id)
-               FROM {user} u
-               JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
-               JOIN {enrol} e ON (e.id = ue.enrolid)";
-$totalusers = $DB->count_records_sql($sqltotal, $params);
-
-$ufields = user_picture::fields('u');
-$sql = "SELECT DISTINCT $ufields, ul.timeaccess AS lastseen
-          FROM {user} u
-          JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
-          JOIN {enrol} e ON (e.id = ue.enrolid)
-     LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)";
-if ($sort === 'firstname') {
-    $sql .= " ORDER BY u.firstname $dir, u.lastname $dir";
-} else if ($sort === 'lastname') {
-    $sql .= " ORDER BY u.lastname $dir, u.firstname $dir";
-} else if ($sort === 'email') {
-    $sql .= " ORDER BY u.email $dir, u.lastname $dir, u.firstname $dir";
-} else if ($sort === 'lastseen') {
-    $sql .= " ORDER BY ul.timeaccess $dir, u.lastname $dir, u.firstname $dir";
 }
 
-$pagingbar = new paging_bar($totalusers, $page, $perpage, $PAGE->url, 'page');
 
-$users = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
+$fields = array(
+    'userdetails' => array (
+        'picture' => false,
+        'firstname' => get_string('firstname'),
+        'lastname' => get_string('lastname'),
+        'email' => get_string('email')
+    ),
+    'lastseen' => get_string('lastaccess'),
+    'role' => get_string('roles', 'role'),
+    'group' => get_string('groups', 'group'),
+    'enrol' => get_string('enrolmentinstances', 'enrol')
+);
+$table->set_fields($fields);
 
-$strfirstname = get_string('firstname');
-$strlastname  = get_string('lastname');
-$stremail     = get_string('email');
-$strlastseen  = get_string('lastaccess');
-
-if ($dir === 'ASC') {
-    $diricon = html_writer::empty_tag('img', array('alt'=>'', 'src'=>$OUTPUT->pix_url('t/down')));
-    $newdir = 'DESC';
-} else {
-    $diricon  = html_writer::empty_tag('img', array('alt'=>'', 'src'=>$OUTPUT->pix_url('t/up')));
-    $newdir = 'ASC';
+$renderer = $PAGE->get_renderer('core_enrol');
+$users = $manager->get_users_for_display($renderer, $pageurl, $table->sort, $table->sortdirection, $table->page, $table->perpage);
+foreach ($users as $userid=>&$user) {
+    $user['picture'] = $OUTPUT->render($user['picture']);
+    $user['role'] = $renderer->user_roles_and_actions($userid, $user['roles'], $manager->get_assignable_roles(), $pageurl);
+    $user['group'] = $renderer->user_groups_and_actions($userid, $user['groups'], $manager->get_all_groups(), has_capability('moodle/course:managegroups', $manager->get_context()), $pageurl);
+    $user['enrol'] = $renderer->user_enrolments_and_actions($userid, $user['enrolments'], $pageurl);
 }
+$table->set_users($users);
 
-$table = new html_table();
-$table->head = array();
-if ($sort === 'firstname') {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strfirstname);
-    $h .= " $diricon / ";
-    $h .= html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastname')), $strlastname);
-} else if ($sort === 'lastname') {
-    $newdir = ($dir === 'ASC') ? 'DESC' : 'ASC';
-    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'firstname')), $strfirstname);
-    $h .= " / ";
-    $h .= html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strlastname);
-    $h .= " $diricon";
-} else {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'firstname')), $strfirstname);
-    $h .= " / ";
-    $h .= html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastname')), $strlastname);
-}
-$table->head[] = $h;
-if ($sort === 'email') {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $stremail);
-    $h .= " $diricon";
-} else {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'email')), $stremail);
-}
-$table->head[] = $h;
-if ($sort === 'lastseen') {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strlastseen);
-    $h .= " $diricon";
-} else {
-    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastseen')), $strlastseen);
-}
-$table->head[] = $h;
-$table->head[] = get_string('roles', 'role');
-$table->head[] = get_string('groups', 'group');
-$table->head[] = get_string('enrolmentinstances', 'enrol');
+$PAGE->set_title(get_string('enrolledusers', 'enrol'));
+$PAGE->set_heading($PAGE->course->fullname);
 
-$table->align = array('left', 'left', 'left', 'left', 'left', 'left');
-$table->width = "95%";
-foreach ($users as $user) {
-    $picture = $OUTPUT->user_picture($user, array('courseid'=>$course->id));
-
-    if ($user->lastseen) {
-        $strlastaccess = userdate($user->lastseen);
-    } else {
-        $strlastaccess = get_string('never');
-    }
-    $fullname = fullname($user, true);
-
-    // get list of roles
-    $roles = array();
-    $ras = get_user_roles($context, $user->id, true, 'c.contextlevel DESC, r.sortorder ASC');
-    foreach ($ras as $ra) {
-        if ($ra->contextid != $context->id) {
-            if (!isset($roles[$ra->roleid])) {
-                $roles[$ra->roleid] = null;
-            }
-            // higher ras, course always takes precedence
-            continue;
-        }
-        if (isset($roles[$ra->roleid]) and $roles[$ra->roleid] === false) {
-            continue;
-        }
-        $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
-    }
-    foreach ($roles as $rid=>$unassignable) {
-        if ($unassignable and isset($assignable[$rid])) {
-            $icon = html_writer::empty_tag('img', array('alt'=>get_string('unassignarole', 'role', $allroles[$rid]->localname), 'src'=>$OUTPUT->pix_url('t/delete')));
-            $url = new moodle_url($PAGE->url, array('action'=>'unassign', 'role'=>$rid, 'user'=>$user->id));
-            $roles[$rid] = $allroles[$rid]->localname . html_writer::link($url, $icon);
-        } else {
-            $roles[$rid] = $allroles[$rid]->localname;
-        }
-    }
-    $addrole = '';
-    if ($assignable) {
-        foreach ($assignable as $rid=>$unused) {
-            if (!isset($roles[$rid])) {
-                //candidate for role assignment
-                $icon = html_writer::empty_tag('img', array('alt'=>get_string('assignroles', 'role'), 'src'=>$OUTPUT->pix_url('t/add')));
-                $url = new moodle_url($PAGE->url, array('action'=>'assign', 'user'=>$user->id));
-                $addrole .= html_writer::link($url, $icon);
-                break;
-            }
-        }
-    }
-    $roles = implode(', ', $roles);
-    if ($addrole) {
-        $roles = $roles . '<div>'.$addrole.'</div>';
-    }
-
-    $groups = array();
-    $usergroups = groups_get_all_groups($course->id, $user->id, 0, 'g.id');
-    foreach($usergroups as $gid=>$unused) {
-        $group = $allgroups[$gid];
-        if ($managegroups) {
-            $icon = html_writer::empty_tag('img', array('alt'=>get_string('removefromgroup', 'group', $group->name), 'src'=>$OUTPUT->pix_url('t/delete')));
-            $url = new moodle_url($PAGE->url, array('action'=>'removemember', 'group'=>$gid, 'user'=>$user->id));
-            $groups[] = $group->name . html_writer::link($url, $icon);
-        } else {
-            $groups[] = $group->name;
-        }
-    }
-    $groups = implode(', ', $groups);
-    if ($managegroups and (count($usergroups) < count($allgroups))) {
-        $icon = html_writer::empty_tag('img', array('alt'=>get_string('addgroup', 'group'), 'src'=>$OUTPUT->pix_url('t/add')));
-        $url = new moodle_url($PAGE->url, array('action'=>'addmember', 'user'=>$user->id));
-        $groups .= '<div>'.html_writer::link($url, $icon).'</div>';
-    }
-
-
-    // get list of enrol instances
-    $now = time();
-    $edits = array();
-    $params['userid'] = $user->id;
-    $ues = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
-    foreach ($ues as $ue) {
-        $instance = $instances[$ue->enrolid];
-        $plugin   = $plugins[$instance->enrol];
-
-        $edit = $inames[$instance->id];
-
-        $dimmed = false;
-        if ($ue->timestart and $ue->timeend) {
-            $edit .= '&nbsp;('.get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend))).')';
-            $dimmed = ($now < $ue->timestart and $now > $ue->timeend);
-        } else if ($ue->timestart) {
-            $edit .= '&nbsp;('.get_string('periodstart', 'enrol', userdate($ue->timestart)).')';
-            $dimmed = ($now < $ue->timestart);
-        } else if ($ue->timeend) {
-            $edit .= '&nbsp;('.get_string('periodend', 'enrol', userdate($ue->timeend)).')';
-            $dimmed = ($now > $ue->timeend);
-        }
-
-        if ($dimmed or $ue->status != ENROL_USER_ACTIVE) {
-            $edit = html_writer::tag('span', $edit, array('class'=>'dimmed_text'));
-        }
-
-        if ($plugin->allow_unenrol($instance) and has_capability("enrol/$instance->enrol:unenrol", $context)) {
-            $icon = html_writer::empty_tag('img', array('alt'=>get_string('unenrol', 'enrol'), 'src'=>$OUTPUT->pix_url('t/delete')));
-            $url = new moodle_url($PAGE->url, array('action'=>'unenrol', 'ue'=>$ue->id));
-            $edit .= html_writer::link($url, $icon);
-        }
-
-        if ($plugin->allow_manage($instance) and has_capability("enrol/$instance->enrol:manage", $context)) {
-            $icon = html_writer::empty_tag('img', array('alt'=>get_string('edit'), 'src'=>$OUTPUT->pix_url('t/edit')));
-            $url = new moodle_url($PAGE->url, array('action'=>'edit', 'ue'=>$ue->id));
-            $edit .= html_writer::link($url, $icon);
-        }
-
-        $edits[] = $edit;
-    }
-    $edits = implode('<br />', $edits);
-
-    $table->data[] = array("$picture <a href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>", $user->email, $strlastaccess, $roles, $groups, $edits);
-}
-
-$ifilters = new single_select($PAGE->url, 'ifilter', array(0=>get_string('all')) + $inames, $ifilter, array());
-$ifilters->set_label(get_string('enrolmentinstances', 'enrol'));
-
-echo $OUTPUT->render($ifilters);
-echo $OUTPUT->render($pagingbar);
-echo html_writer::table($table);
-echo $OUTPUT->render($pagingbar);
-
-// print enrol link or selection
-$links = array();
-foreach($instances as $instance) {
-    $plugin = $plugins[$instance->enrol];
-    if ($link = $plugin->get_manual_enrol_link($instance)) {
-        $links[$instance->id] = $link;
-    }
-}
-if (count($links) == 1) {
-    $link = reset($links);
-    echo $OUTPUT->single_button($link, get_string('enrolusers', 'enrol_manual'), 'get');
-
-} else if (count($links) > 1) {
-    $options = array();
-    foreach ($links as $i=>$link) {
-        $options[$link->out(false)] = $inames[$i];
-    }
-    echo $OUTPUT->url_select($options, '', array(''=>get_string('enrolusers', 'enrol_manual').'...'));
-}
-
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('enrolledusers', 'enrol'));
+echo $renderer->render($table);
 echo $OUTPUT->footer();
-
-
Index: moodle/enrol/users_forms.php
--- moodle/enrol/users_forms.php Base (1.1)
+++ moodle/enrol/users_forms.php Locally Modified (Based On 1.1)
@@ -30,7 +30,7 @@
 
 class enrol_users_assign_form extends moodleform {
     function definition() {
-        global $CFG, $DB, $PAGE;
+        global $CFG, $DB;
 
         $mform = $this->_form;
 
@@ -75,14 +75,13 @@
 
         $this->add_action_buttons();
 
-        $this->set_data($PAGE->url->params());
         $this->set_data(array('action'=>'assign', 'user'=>$user->id));
     }
 }
 
 class enrol_users_addmember_form extends moodleform {
     function definition() {
-        global $CFG, $DB, $PAGE;
+        global $CFG, $DB;
 
         $mform = $this->_form;
 
@@ -130,14 +129,13 @@
 
         $this->add_action_buttons();
 
-        $this->set_data($PAGE->url->params());
         $this->set_data(array('action'=>'addmember', 'user'=>$user->id));
     }
 }
 
 class enrol_users_edit_form extends moodleform {
     function definition() {
-        global $CFG, $DB, $PAGE;
+        global $CFG, $DB;
 
         $mform = $this->_form;
 
@@ -184,7 +182,6 @@
 
         $this->add_action_buttons();
 
-        $this->set_data($PAGE->url->params());
         $this->set_data(array('action'=>'edit', 'ue'=>$ue->id, 'status'=>$ue->status, 'timestart'=>$ue->timestart, 'timeend'=>$ue->timeend));
     }
 
Index: moodle/enrol/yui/enrolmentmanager/assets/skins/sam/enrolmentmanager.css
--- moodle/enrol/yui/enrolmentmanager/assets/skins/sam/enrolmentmanager.css No Base Revision
+++ moodle/enrol/yui/enrolmentmanager/assets/skins/sam/enrolmentmanager.css Locally New
@@ -0,0 +1,71 @@
+/**************************************
+
+Structure of the user enroller panel
+
+.user-enroller-panel(.visible)
+    .uep-wrap
+        .uep-header
+        .uep-content
+            .uep-ajax-content
+                .uep-search-results
+                    .totalusers
+                    .users
+                        .user.clearfix(.odd|.even)(.enrolled)
+                            .count
+                            .picture
+                            .details
+                                .fullname
+                                .email
+                            .options
+                                .enrol
+                    .uep-more-results
+            .uep-loading-lightbox(.hidden)
+                .loading-icon
+        .uep-footer
+            .uep-search
+                input
+            .uep-searchoptions
+                .collapsibleheading
+                .collapsiblearea(.hidden)
+                    .uep-enrolment-option
+                        .role
+                        .startdate
+                        .duration
+
+**************************************/
+
+.user-enroller-panel {width:400px;background-color:#666;position:absolute;top:10%;left:10%;border:1px solid #666;border-width:0 5px 5px 0;}
+.user-enroller-panel.hidden {display:none;}
+.user-enroller-panel .uep-wrap {margin-top:-5px;margin-left:-5px;background-color:#FFF;border:1px solid #999;height:inherit;}
+
+.user-enroller-panel .uep-header {background-color:#eee;padding:1px;}
+.user-enroller-panel .uep-header h2 {margin:3px 1em 0.5em 1em;font-size:1em;}
+.user-enroller-panel .uep-header .close {width:25px;height:15px;position:absolute;top:2px;right:1em;cursor:pointer;background:url("sprite.png") no-repeat scroll 0 0 transparent;}
+
+.user-enroller-panel .uep-content {text-align:center;position:relative;width:100%;border-top:1px solid #999;border-bottom:1px solid #999;}
+.user-enroller-panel .uep-ajax-content {height:375px;overflow:auto;}
+.user-enroller-panel .uep-search-results .totalusers {background-color:#eee;padding:5px;border-bottom:1px solid #BBB;font-size:7pt;font-weight: bold;}
+.user-enroller-panel .uep-search-results .user {width:100%;text-align:left;font-size:9pt;border-bottom:1px solid #ddd;border-top:1px solid #eee;}
+.user-enroller-panel .uep-search-results .user.odd {border-bottom:1px solid #ddd;border-top:1px solid #eee;background-color:#f9f9f9;}
+.user-enroller-panel .uep-search-results .user .count {width:20px;float:left;font-size:7pt;line-height:41px;border-right:1px solid #ddd;background-color:#EEE;text-align:right;padding:2px;}
+.user-enroller-panel .uep-search-results .user .picture {width:45px;float:left;margin:3px;}
+.user-enroller-panel .uep-search-results .user .details {width:250px;float:left;margin:3px;}
+.user-enroller-panel .uep-search-results .user .options {padding-right:7px;font-size:8pt;margin:3px;}
+.user-enroller-panel .uep-search-results .user .options .enrol {margin:3px;float:right;border:1px outset #ddd;background-color:#EEE;padding:3px;-moz-border-radius:3px;cursor:pointer;}
+.user-enroller-panel .uep-search-results .user.enrolled .count {width:40px;color:#eee;}
+.user-enroller-panel .uep-search-results .uep-more-results {background-color:#eee;padding:5px;border-top:1px solid #BBB;}
+
+.user-enroller-panel .uep-loading-lightbox {position:absolute;width:100%;height:100%;top:0;left:0;background-color:#FFF;min-width:50px;min-height:50px;}
+.user-enroller-panel .uep-loading-lightbox.hidden {display:none;}
+.user-enroller-panel .uep-loading-lightbox .loading-icon {margin:auto;vertical-align:middle;margin-top:125px;}
+
+.user-enroller-panel .uep-footer {padding:3px;background-color:#ddd;}
+.user-enroller-panel .uep-search {margin:3px;}
+.user-enroller-panel .uep-search label {padding-right:8px;}
+.user-enroller-panel .uep-search input {width:70%;}
+.user-enroller-panel .uep-searchoptions {margin:3px;cursor:pointer;}
+.user-enroller-panel .uep-searchoptions select {margin-left:1em;}
+.user-enroller-panel .collapsibleheading img {margin-right:8px;}
+.user-enroller-panel .collapsiblearea {border:1px solid #bbb;background-color:#f6f6f6;}
+.user-enroller-panel .collapsiblearea.hidden {display:none;}
+.user-enroller-panel .collapsiblearea .uep-enrolment-option {margin:5px 1em;}
Index: moodle/enrol/yui/enrolmentmanager/assets/skins/sam/sprite.png
MIME: application/octet-stream; encoding: Base64; length: 1002
iVBORw0KGgoAAAANSUhEUgAAABkAAAAPCAMAAAAmuJTXAAAAAXNSR0IArs4c
6QAAAwBQTFRF19jawcHB/v7+bW1t/Pz8+/v79fX1+vr69/f34+Pl5+jo7/Dw
Qm/Z5vH/3+Dg7Ozs4eHi8vLy9vb24OHi9fX2AAAA6+vs4eLj5/H/8fHy5fD/
9/f5QW7X8vT06+zsy+L/3+3/6Orq4O3/3Oz/4u7/0eb/+fn6M1u8x9//1ef/
xN3/P2rRhYWFz+P/1+j/3+Dh4/D/7e/v+vr75/L/2un/3t/g5ebm+fn5f39/
4ODh4uPjIiIi5ufoLz6s6urr2trb3Nzd0NHS5+jq19fYutj/7OztvsHC8fLy
9PT0xMXG3d3fs7W36Ojq+fr6MFa1LlKw9PX1QGzV1NXWQG3WOWHEuLu9ycrL
9fb2N1/CrK+wPWfOO2XLOWPI7O3t7+/vQGvU8PDx8PDw8PHx8fHx8vL0KEym
K06r7e3v5eXm9vb3zM7Q9jQ04XcbnZ2d7Ykb8dJYKDGTpcv/4XoSsbGx+by8
6Ks7lqrDys7p56Q96sAp6r036bol17Mo+AsLc3Nz68I1+ei26LI6boqs9UdH
lpaW6bc4+BUV88486Kcb+CQk81JHEw5L5pw/1AgBSUlJ5WgEoXMOLjec9ctM
5rIe4efv+GFe6JJC5poV534X5ubm1TEB5trB2qMNJCqRampq8dRGzHwK8Jo0
O0Ob9Y+PM0Os8bOHxJYWubrX8dVn9HV1+tCaRFmzGRtq+9XVLStr9uBT3E0E
+ufK5IoO14YG+Ne+gZm3ICSB+39/XH7K6XkU6HQOJSuH6Ojo91VV9mpqtJNI
9Mii4FkCzrFMhIfB98SA4mEN9bJVfn+srbHEUmO67o8prYYiHyR39eCMPFG2
/HF3nqPR5cpNpKvb++rf5nYu4e7/38l1/e/i61ZX7Ysh86M78JYs1jwg2caL
9np64XQRd3ST5M526goLTWjDzrZu+euo+cXF8vn/c4bO+9dQz7eH8ahh++1s
/v3028BN6pFh9tp9pKSlOk2y7S4vUW/GZmZmJkeg5ubn29zdv9r/pair2Nja
3N3f6PL/////////FVh3eAAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAA
CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2gcBBgsElflP/AAAAFZJREFU
GNNjYGDEDhgYGP9hB4zEyBSzAQm2YkyZYmYdtn9sOszFGDJAQR1+HZA0hmlA
qTqEBIoL+OuY6/ixuQCnHtz25ELdloupJ5cNThIdBhgyOMMaAFIZ83j7F2ns
AAAAAElFTkSuQmCC
Index: moodle/enrol/yui/enrolmentmanager/enrolmentmanager.js
--- moodle/enrol/yui/enrolmentmanager/enrolmentmanager.js No Base Revision
+++ moodle/enrol/yui/enrolmentmanager/enrolmentmanager.js Locally New
@@ -0,0 +1,485 @@
+YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
+
+    var UEP = {
+        NAME : 'Enrolment Manager',
+        /** Properties **/
+        BASE : 'base',
+        SEARCH : 'search',
+        PARAMS : 'params',
+        URL : 'url',
+        AJAXURL : 'ajaxurl',
+        MULTIPLE : 'multiple',
+        PAGE : 'page',
+        COURSEID : 'courseid',
+        USERS : 'users',
+        USERCOUNT : 'userCount',
+        REQUIREREFRESH : 'requiresRefresh',
+        LASTSEARCH : 'lastPreSearchValue',
+        INSTANCES : 'instances',
+        OPTIONSTARTDATE : 'optionsStartDate',
+        DEFAULTROLE : 'defaultRole',
+        DEFAULTSTARTDATE : 'defaultStartDate',
+        DEFAULTDURATION : 'defaultDuration',
+        ASSIGNABLEROLES : 'assignableRoles'
+    },
+    /** CSS classes for nodes in structure **/
+    CSS = {
+        PANEL : 'user-enroller-panel',
+        WRAP : 'uep-wrap',
+        HEADER : 'uep-header',
+        CONTENT : 'uep-content',
+        AJAXCONTENT : 'uep-ajax-content',
+        SEARCHRESULTS : 'uep-search-results',
+        TOTALUSERS : 'totalusers',
+        USERS : 'users',
+        USER : 'user',
+        MORERESULTS : 'uep-more-results',
+        LIGHTBOX : 'uep-loading-lightbox',
+        LOADINGICON : 'loading-icon',
+        FOOTER : 'uep-footer',
+        ENROL : 'enrol',
+        ENROLLED : 'enrolled',
+        COUNT : 'count',
+        PICTURE : 'picture',
+        DETAILS : 'details',
+        FULLNAME : 'fullname',
+        EMAIL : 'email',
+        OPTIONS : 'options',
+        ODD  : 'odd',
+        EVEN : 'even',
+        HIDDEN : 'hidden',
+        SEARCHOPTIONS : 'uep-searchoptions',
+        COLLAPSIBLEHEADING : 'collapsibleheading',
+        COLLAPSIBLEAREA : 'collapsiblearea',
+        SEARCHOPTION : 'uep-enrolment-option',
+        ROLE : 'role',
+        STARTDATE : 'startdate',
+        DURATION : 'duration',
+        ACTIVE : 'active',
+        SEARCH : 'uep-search',
+        CLOSE : 'close'
+    };
+
+    var USERENROLLER = function(config) {
+        USERENROLLER.superclass.constructor.apply(this, arguments);
+    };
+    Y.extend(USERENROLLER, Y.Base, {
+        _searchTimeout : null,
+        _loadingNode : null,
+        initializer : function(config) {
+            this.set(UEP.BASE, Y.Node.create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
+                .append(Y.Node.create('<div class="'+CSS.WRAP+'"></div>')
+                    .append(Y.Node.create('<div class="'+CSS.HEADER+' header"></div>')
+                        .append(Y.Node.create('<div class="'+CSS.CLOSE+'"></div>'))
+                        .append(Y.Node.create('<h2>'+M.str.enrol.enrolusers+'</h2>')))
+                    .append(Y.Node.create('<div class="'+CSS.CONTENT+'"></div>')
+                        .append(Y.Node.create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
+                        .append(Y.Node.create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
+                            .append(Y.Node.create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
+                                .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
+                            .setStyle('opacity', 0.5)))
+                    .append(Y.Node.create('<div class="'+CSS.FOOTER+'"></div>')
+                        .append(Y.Node.create('<div class="'+CSS.SEARCH+'"><label>'+M.str.enrol.usersearch+'</label></div>')
+                            .append(Y.Node.create('<input type="text" id="enrolusersearch" value="" />'))
+                        )
+                        .append(Y.Node.create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
+                            .append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.str.enrol.enrolmentoptions+'</div>'))
+                            .append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
+                                .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.ROLE+'">'+M.str.role.assignroles+'</div>')
+                                    .append(Y.Node.create('<select><option value="">'+M.str.enrol.none+'</option></select>')))
+                                .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.STARTDATE+'">'+M.str.moodle.startingfrom+'</div>')
+                                    .append(Y.Node.create('<select></select>')))
+                                .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.DURATION+'">'+M.str.enrol.enrolperiod+'</div>')
+                                    .append(Y.Node.create('<select><option value="0" selected="selected">'+M.str.enrol.unlimitedduration+'</option></select>')))
+                            )
+                        )
+                    )
+                )
+            );
+
+            this.set(UEP.SEARCH, this.get(UEP.BASE).one('#enrolusersearch'));
+            Y.all('.enrolusersbutton input').each(function(node){
+                if (node.getAttribute('type', 'submit')) {
+                    node.on('click', this.show, this);
+                }
+            }, this);
+            this.get(UEP.BASE).one('.'+CSS.HEADER+' .'+CSS.CLOSE).on('click', this.hide, this);
+            this._loadingNode = this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.LIGHTBOX);
+            var params = this.get(UEP.PARAMS);
+            params['id'] = this.get(UEP.COURSEID);
+            this.set(UEP.PARAMS, params);
+
+            Y.on('key', this.preSearch, this.get(UEP.SEARCH), 'down:13', this);
+            
+            Y.one(document.body).append(this.get(UEP.BASE));
+
+            var base = this.get(UEP.BASE);
+            base.plug(Y.Plugin.Drag);
+            base.dd.addHandle('.'+CSS.HEADER+' h2');
+            base.one('.'+CSS.HEADER+' h2').setStyle('cursor', 'move');
+
+
+            this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/collapsed', 'moodle'));
+            this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
+                this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
+                this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
+                if (this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).hasClass(CSS.HIDDEN)) {
+                    this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/collapsed', 'moodle'));
+                } else {
+                    this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/expanded', 'moodle'));
+                }
+            }, this);
+
+            this.populateAssignableRoles();
+            this.populateStartDates();
+            this.populateDuration();
+        },
+        populateAssignableRoles : function() {
+            this.on('assignablerolesloaded', function(){
+                var roles = this.get(UEP.ASSIGNABLEROLES);
+                var s = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.ROLE+' select');
+                var v = this.get(UEP.DEFAULTROLE);
+                var index = 0, count = 0;
+                for (var i in roles) {
+                    count++;
+                    var option = Y.Node.create('<option value="'+i+'">'+roles[i]+'</option>');
+                    if (i == v) {
+                        index = count;
+                    }
+                    s.append(option);
+                }
+                s.set('selectedIndex', index);
+            }, this);
+            this.getAssignableRoles();
+        },
+        populateStartDates : function() {
+            var select = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.STARTDATE+' select');
+            var defaultvalue = this.get(UEP.DEFAULTSTARTDATE);
+            var options = this.get(UEP.OPTIONSTARTDATE);
+            var index = 0, count = 0;
+            for (var i in options) {
+                count++;
+                var option = Y.Node.create('<option value="'+i+'">'+options[i]+'</option>');
+                if (i == defaultvalue) {
+                    index = count;
+                }
+                select.append(option);
+            }
+            select.set('selectedIndex', index);
+        },
+        populateDuration : function() {
+            var select = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.DURATION+' select');
+            var defaultvalue = this.get(UEP.DEFAULTDURATION);
+            var index = 0, count = 0;
+            for (var i = 1; i <= 365; i++) {
+                count++;
+                var option = Y.Node.create('<option value="'+i+'">'+M.str.enrol.durationdays.replace(/\%d/, i)+'</option>');
+                if (i == defaultvalue) {
+                    index = count;
+                }
+                select.append(option);
+            }
+            select.set('selectedIndex', index);
+        },
+        getAssignableRoles : function(){
+            Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
+                method:'POST',
+                data:'id='+this.get(UEP.COURSEID)+'&action=getassignable&sesskey='+M.cfg.sesskey,
+                on: {
+                    complete: function(tid, outcome, args) {
+                        try {
+                            var roles = Y.JSON.parse(outcome.responseText);
+                            this.set(UEP.ASSIGNABLEROLES, roles.response);
+                        } catch (e) {
+                            Y.fail(UEP.NAME+': Failed to load assignable roles');
+                        }
+                        this.getAssignableRoles = function() {
+                            this.fire('assignablerolesloaded');
+                        }
+                        this.getAssignableRoles();
+                    }
+                },
+                context:this
+            });
+        },
+        preSearch : function(e) {
+            this.search(null, false);
+            /*
+            var value = this.get(UEP.SEARCH).get('value');
+            if (value.length < 3 || value == this.get(UEP.LASTSEARCH)) {
+                return;
+            }
+            this.set(UEP.LASTSEARCH, value);
+            if (this._searchTimeout) {
+                clearTimeout(this._searchTimeout);
+                this._searchTimeout = null;
+            }
+            var self = this;
+            this._searchTimeout = setTimeout(function(){
+                self._searchTimeout = null;
+                self.search(null, false);
+            }, 300);
+            */
+        },
+        show : function(e) {
+            e.preventDefault();
+            e.halt();
+
+            var base = this.get(UEP.BASE);
+            base.removeClass(CSS.HIDDEN);
+            var x = (base.get('winWidth') - 400)/2;
+            var y = (parseInt(base.get('winHeight'))-base.get('offsetHeight'))/2 + parseInt(base.get('docScrollY'));
+            if (y < parseInt(base.get('winHeight'))*0.1) {
+                y = parseInt(base.get('winHeight'))*0.1;
+            }
+            base.setXY([x,y]);
+            
+            if (this.get(UEP.USERS)===null) {
+                this.search(e, false);
+            }
+        },
+        hide : function(e) {
+            this.get(UEP.BASE).addClass(CSS.HIDDEN);
+            if (this.get(UEP.REQUIREREFRESH)) {
+                window.location = this.get(UEP.URL);
+            }
+        },
+        search : function(e, append) {
+            if (e) {
+                e.halt();
+                e.preventDefault();
+            }
+            var on, params;
+            if (append) {
+                this.set(UEP.PAGE, this.get(UEP.PAGE)+1);
+            } else {
+                this.set(UEP.USERCOUNT, 0);
+            }
+            params = this.get(UEP.PARAMS);
+            params['sesskey'] = M.cfg.sesskey;
+            params['action'] = 'searchusers';
+            params['search'] = this.get(UEP.SEARCH).get('value');
+            params['page'] = this.get(UEP.PAGE);
+            if (this.get(UEP.MULTIPLE)) {
+                alert('oh no there are multiple');
+            } else {
+                var instance = this.get(UEP.INSTANCES)[0];
+                params['enrolid'] = instance.id;
+            }
+            Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
+                method:'POST',
+                data:build_querystring(params),
+                on : {
+                    start : this.displayLoading,
+                    complete: this.processSearchResults,
+                    end : this.removeLoading
+                },
+                context:this,
+                arguments:{
+                    append:append,
+                    enrolid:params['enrolid']
+                }
+            });
+        },
+        displayLoading : function() {
+            this._loadingNode.removeClass(CSS.HIDDEN);
+        },
+        removeLoading : function() {
+            this._loadingNode.addClass(CSS.HIDDEN);
+        },
+        processSearchResults : function(tid, outcome, args) {
+            try {
+                var result = Y.JSON.parse(outcome.responseText);
+            } catch (e) {
+                Y.fail(UEP.NAME+': Failed to parse user search response  ['+e.linenum+':'+e.message+']');
+            }
+            if (!result.success) {
+                this.setContent = M.str.enrol.errajaxsearch;
+            }
+            var users;
+            if (!args.append) {
+                users = Y.Node.create('<div class="'+CSS.USERS+'"></div>');
+            } else {
+                users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
+            }
+            var count = this.get(UEP.USERCOUNT);
+            for (var i in result.response.users) {
+                count++;
+                var user = result.response.users[i];
+                users.append(Y.Node.create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
+                    .addClass((i%2)?CSS.ODD:CSS.EVEN)
+                    .append(Y.Node.create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
+                    .append(Y.Node.create('<div class="'+CSS.PICTURE+'"></div>')
+                        .append(Y.Node.create(user.picture)))
+                    .append(Y.Node.create('<div class="'+CSS.DETAILS+'"></div>')
+                        .append(Y.Node.create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
+                        .append(Y.Node.create('<div class="'+CSS.EMAIL+'">'+user.email+'</div>')))
+                    .append(Y.Node.create('<div class="'+CSS.OPTIONS+'"></div>')
+                        .append(Y.Node.create('<div class="'+CSS.ENROL+'">'+M.str.enrol.enrol+'</div>')))
+                );
+            }
+            this.set(UEP.USERCOUNT, count);
+            if (!args.append) {
+                var usersstr = (result.response.totalusers == '1')?M.str.enrol.ajaxoneuserfound:M.str.enrol.ajaxxusersfound.replace(/\[users\]/, result.response.totalusers);
+                var content = Y.Node.create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
+                    .append(Y.Node.create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
+                    .append(users);
+                if (result.response.totalusers > (this.get(UEP.PAGE)+1)*25) {
+                    var fetchmore = Y.Node.create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol.ajaxnext25+'</a></div>');
+                    fetchmore.on('click', this.search, this, true);
+                    content.append(fetchmore)
+                }
+                this.setContent(content);
+                Y.delegate("click", this.enrolUser, users, '.'+CSS.USER+' .'+CSS.ENROL, this, args);
+            } else {
+                if (result.response.totalusers <= (this.get(UEP.PAGE)+1)*25) {
+                    this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
+                }
+            }
+        },
+        enrolUser : function(e, args) {
+            var user = e.currentTarget.ancestor('.'+CSS.USER);
+            var params = [];
+            params['id'] = this.get(UEP.COURSEID);
+            params['userid'] = user.getAttribute("rel");
+            params['enrolid'] = args.enrolid;
+            params['sesskey'] = M.cfg.sesskey;
+            params['action'] = 'enrol';
+            params['role'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.ROLE+' select').get('value');
+            params['startdate'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.STARTDATE+' select').get('value');
+            params['duration'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.DURATION+' select').get('value');
+            Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
+                method:'POST',
+                data:build_querystring(params),
+                on: {
+                    start : this.displayLoading,
+                    complete : function(tid, outcome, args) {
+                        try {
+                            var result = Y.JSON.parse(outcome.responseText);
+                        } catch (e) {
+                            Y.fail(UEP.NAME+': Failed to parse user search response  ['+e.linenum+':'+e.message+']');
+                        }
+                        if (result.success) {
+                            args.userNode.addClass(CSS.ENROLLED);
+                            args.userNode.one('.'+CSS.ENROL).remove();
+                            this.set(UEP.REQUIREREFRESH, true);
+                        } else {
+                            alert(M.str.enrol.errajaxfailedenrol);
+                        }
+                    },
+                    end : this.removeLoading
+                },
+                context:this,
+                arguments:{
+                    params : params,
+                    userNode : user
+                }
+            });
+
+        },
+        setContent: function(content) {
+            this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.AJAXCONTENT).setContent(content);
+        }
+    }, {
+        NAME : UEP.NAME,
+        ATTRS : {
+            url : {
+                validator : Y.Lang.isString
+            },
+            ajaxurl : {
+                validator : Y.Lang.isString
+            },
+            base : {
+                setter : function(node) {
+                    var n = Y.one(node);
+                    if (!n) {
+                        Y.fail(UEP.NAME+': invalid base node set');
+                    }
+                    return n;
+                }
+            },
+            users : {
+                validator : Y.Lang.isArray,
+                value : null
+            },
+            courseid : {
+                value : null
+            },
+            params : {
+                validator : Y.Lang.isArray,
+                value : []
+            },
+            instances : {
+                validator : Y.Lang.isArray,
+                setter : function(instances) {
+                    var i,ia = [], count=0;
+                    for (i in instances) {
+                        ia.push(instances[i]);
+                        count++;
+                    }
+                    this.set(UEP.MULTIPLE, (count>1));
+                }
+            },
+            multiple : {
+                validator : Y.Lang.isBool,
+                value : false
+            },
+            page : {
+                validator : Y.Lang.isNumber,
+                value : 0
+            },
+            userCount : {
+                value : 0,
+                validator : Y.Lang.isNumber
+            },
+            requiresRefresh : {
+                value : false,
+                validator : Y.Lang.isBool
+            },
+            search : {
+                setter : function(node) {
+                    var n = Y.one(node);
+                    if (!n) {
+                        Y.fail(UEP.NAME+': invalid search node set');
+                    }
+                    return n;
+                }
+            },
+            lastPreSearchValue : {
+                value : '',
+                validator : Y.Lang.isString
+            },
+            strings  : {
+                value : {},
+                validator : Y.Lang.isObject
+            },
+            defaultRole : {
+                value : 0,
+                validator : Y.Lang.isNumber
+            },
+            defaultStartDate : {
+                value : 2,
+                validator : Y.Lang.isNumber
+            },
+            defaultDuration : {
+                value : ''
+            },
+            assignableRoles : {
+                value : []
+            },
+            optionsStartDate : {
+                value : []
+            }
+        }
+    });
+    Y.augment(USERENROLLER, Y.EventTarget);
+
+
+    M.enrol = M.enrol || {};
+    M.enrol.enrolmentmanager = {
+        init : function(cfg) {
+            new USERENROLLER(cfg);
+        }
+    }
+
+}, '@VERSION@', {requires:['base','node', 'overlay', 'io', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key']});
\ No newline at end of file
Index: moodle/enrol/yui/rolemanager/assets/skins/sam/rolemanager.css
--- moodle/enrol/yui/rolemanager/assets/skins/sam/rolemanager.css No Base Revision
+++ moodle/enrol/yui/rolemanager/assets/skins/sam/rolemanager.css Locally New
@@ -0,0 +1,8 @@
+.enrolpanel {display:none;position:absolute;background-color:#666;padding:0 5px;}
+.enrolpanel.visible {display:block;}
+.enrolpanel .container {position:relative;background-color:#fff;border:1px solid #999;top:-5px;}
+.enrolpanel .container .header {border-bottom:1px solid #999;}
+.enrolpanel .container .header h2 {font-size:90%;text-align:center;margin:5px;}
+.enrolpanel .container .header .close {width:25px;height:15px;position:absolute;top:5px;right:1em;cursor:pointer;background:url("sprite.png") no-repeat scroll 0 0 transparent;}
+.enrolpanel .container .content {}
+.enrolpanel .container .content input {margin:5px;font-size:10px;}
\ No newline at end of file
Index: moodle/enrol/yui/rolemanager/assets/skins/sam/sprite.png
MIME: application/octet-stream; encoding: Base64; length: 1002
iVBORw0KGgoAAAANSUhEUgAAABkAAAAPCAMAAAAmuJTXAAAAAXNSR0IArs4c
6QAAAwBQTFRF19jawcHB/v7+bW1t/Pz8+/v79fX1+vr69/f34+Pl5+jo7/Dw
Qm/Z5vH/3+Dg7Ozs4eHi8vLy9vb24OHi9fX2AAAA6+vs4eLj5/H/8fHy5fD/
9/f5QW7X8vT06+zsy+L/3+3/6Orq4O3/3Oz/4u7/0eb/+fn6M1u8x9//1ef/
xN3/P2rRhYWFz+P/1+j/3+Dh4/D/7e/v+vr75/L/2un/3t/g5ebm+fn5f39/
4ODh4uPjIiIi5ufoLz6s6urr2trb3Nzd0NHS5+jq19fYutj/7OztvsHC8fLy
9PT0xMXG3d3fs7W36Ojq+fr6MFa1LlKw9PX1QGzV1NXWQG3WOWHEuLu9ycrL
9fb2N1/CrK+wPWfOO2XLOWPI7O3t7+/vQGvU8PDx8PDw8PHx8fHx8vL0KEym
K06r7e3v5eXm9vb3zM7Q9jQ04XcbnZ2d7Ykb8dJYKDGTpcv/4XoSsbGx+by8
6Ks7lqrDys7p56Q96sAp6r036bol17Mo+AsLc3Nz68I1+ei26LI6boqs9UdH
lpaW6bc4+BUV88486Kcb+CQk81JHEw5L5pw/1AgBSUlJ5WgEoXMOLjec9ctM
5rIe4efv+GFe6JJC5poV534X5ubm1TEB5trB2qMNJCqRampq8dRGzHwK8Jo0
O0Ob9Y+PM0Os8bOHxJYWubrX8dVn9HV1+tCaRFmzGRtq+9XVLStr9uBT3E0E
+ufK5IoO14YG+Ne+gZm3ICSB+39/XH7K6XkU6HQOJSuH6Ojo91VV9mpqtJNI
9Mii4FkCzrFMhIfB98SA4mEN9bJVfn+srbHEUmO67o8prYYiHyR39eCMPFG2
/HF3nqPR5cpNpKvb++rf5nYu4e7/38l1/e/i61ZX7Ysh86M78JYs1jwg2caL
9np64XQRd3ST5M526goLTWjDzrZu+euo+cXF8vn/c4bO+9dQz7eH8ahh++1s
/v3028BN6pFh9tp9pKSlOk2y7S4vUW/GZmZmJkeg5ubn29zdv9r/pair2Nja
3N3f6PL/////////FVh3eAAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAA
CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2gcBBgsElflP/AAAAFZJREFU
GNNjYGDEDhgYGP9hB4zEyBSzAQm2YkyZYmYdtn9sOszFGDJAQR1+HZA0hmlA
qTqEBIoL+OuY6/ixuQCnHtz25ELdloupJ5cNThIdBhgyOMMaAFIZ83j7F2ns
AAAAAElFTkSuQmCC
Index: moodle/enrol/yui/rolemanager/rolemanager.js
--- moodle/enrol/yui/rolemanager/rolemanager.js No Base Revision
+++ moodle/enrol/yui/rolemanager/rolemanager.js Locally New
@@ -0,0 +1,383 @@
+YUI.add('moodle-enrol-rolemanager', function(Y) {
+
+    var MOD_NAME                    = 'Moodle role manager',
+        MOD_USER                    = 'Moodle role user',
+        MOD_PANEL                   = 'Moodle role assignment panel',
+        USERIDS                     = 'userIds',
+        COURSEID                    = 'courseId',
+        USERID                      = 'userId',
+        CONTAINER                   = 'container',
+        CONTAINERID                 = 'containerId',
+        ASSIGNABLEROLES             = 'assignableRoles',
+        ASSIGNROLELINK              = 'assignRoleLink',
+        ASSIGNROLELINKSELECTOR      = 'assignRoleLinkSelector',
+        UNASSIGNROLELINKS           = 'unassignRoleLinks',
+        UNASSIGNROLELINKSSELECTOR   = 'unassignRoleLinksSelector',
+        MANIPULATOR                 = 'manipulator',
+        CURRENTROLES                = 'currentroles';
+
+    var ROLE = function(config) {
+        ROLE.superclass.constructor.apply(this, arguments);
+    }
+    ROLE.NAME = MOD_NAME;
+    ROLE.ATTRS = {
+        containerId : {
+            validator: Y.Lang.isString
+        },
+        container : {
+            setter : function(node) {
+                var n = Y.one(node);
+                if (!n) {
+                    Y.fail(MOD_NAME+': invalid container set');
+                }
+                return n;
+            }
+        },
+        courseId : {
+            value: 0,
+            setter : function(courseId) {
+                if (!(/^\d+$/.test(courseId))) {
+                    Y.fail(MOD_NAME+': Invalid course id specified');
+                }
+                return courseId;
+            }
+        },
+        userIds : {
+            validator: Y.Lang.isArray
+        },
+        assignableRoles : {
+            value : []
+        }
+    }
+    Y.extend(ROLE, Y.Base, {
+        users : [],
+        roleAssignmentPanel : null,
+        panelsubmitevent : null,
+        rolesLoadedEvent : null,
+        initializer : function(config) {
+            var i;
+            var container = Y.one('#'+this.get(CONTAINERID));
+            container.addClass('ajaxactive');
+            this.set(CONTAINER, container);
+            
+            var userids = this.get(USERIDS);
+            for (i in userids) {
+                this.users[userids[i]] = new ROLEUSER({userId:userids[i],manipulator:this}).wire();
+            }
+        },
+        addRole : function(e, user) {
+            e.halt();
+            this.rolesLoadedEvent = this.on('assignablerolesloaded', function(){
+                this.rolesLoadedEvent.detach();
+                var panel = this._getRoleAssignmentPanel();
+                this.panelsubmitevent = panel.on('submit', this.addRoleCallback, this);
+                panel.hide().display(user);
+            }, this);
+            this._loadAssignableRoles();
+        },
+        addRoleCallback : function(e, roleid, userid) {
+            this.panelsubmitevent.detach();
+            var panel = this._getRoleAssignmentPanel();
+            Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
+                method:'POST',
+                data:'id='+this.get(COURSEID)+'&action=assign&sesskey='+M.cfg.sesskey+'&roleid='+roleid+'&user='+userid,
+                on: {
+                    complete: function(tid, outcome, args) {
+                        try {
+                            var o = Y.JSON.parse(outcome.responseText);
+                            if (o.success) {
+                                panel.user.addRoleToDisplay(args.roleid, this.get(ASSIGNABLEROLES)[args.roleid]);
+                            }
+                        } catch (e) {
+                            Y.fail(MOD_PANEL+': Failed to parse role assignment response  ['+e.linenum+':'+e.message+']');
+                        }
+                        panel.hide();
+                    }
+                },
+                context:this,
+                arguments:{
+                    roleid : roleid
+                }
+            });
+        },
+        removeRole : function(e, user, roleid) {
+            e.halt();
+            if (confirm('Are you sure you wish to remove this role from this user?')) {
+                this.removeRoleCallback(e, user.get(USERID), roleid);
+            }
+        },
+        removeRoleCallback : function(e, userid, roleid) {
+            Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
+                method:'POST',
+                data:'id='+this.get(COURSEID)+'&action=unassign&sesskey='+M.cfg.sesskey+'&role='+roleid+'&user='+userid,
+                on: {
+                    complete: function(tid, outcome, args) {
+                        try {
+                            var o = Y.JSON.parse(outcome.responseText);
+                            if (o.success) {
+                                this.users[userid].removeRoleFromDisplay(args.roleid);
+                            }
+                        } catch (e) {
+                            Y.fail(MOD_PANEL+': Failed to parse role assignment response ['+e.linenum+':'+e.message+']');
+                        }
+                    }
+                },
+                context:this,
+                arguments:{
+                    roleid : roleid
+                }
+            });
+        },
+        _loadAssignableRoles : function() {
+            var c = this.get(COURSEID);
+            Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
+                method:'POST',
+                data:'id='+this.get(COURSEID)+'&action=getassignable&sesskey='+M.cfg.sesskey,
+                on: {
+                    complete: function(tid, outcome, args) {
+                        try {
+                            var roles = Y.JSON.parse(outcome.responseText);
+                            this.set(ASSIGNABLEROLES, roles.response);
+                        } catch (e) {
+                            Y.fail(MOD_NAME+': Failed to load assignable roles');
+                        }
+                        this._loadAssignableRoles = function() {
+                            this.fire('assignablerolesloaded');
+                        }
+                        this._loadAssignableRoles();
+                    }
+                },
+                context:this
+            });
+        },
+        _getRoleAssignmentPanel : function() {
+            if (this.roleAssignmentPanel === null) {
+                this.roleAssignmentPanel = new ROLEPANEL({manipulator:this});
+            }
+            return this.roleAssignmentPanel;
+        }
+    });
+    Y.augment(ROLE, Y.EventTarget);
+
+    var ROLEUSER = function(config) {
+        ROLEUSER.superclass.constructor.apply(this, arguments);
+    }
+    ROLEUSER.NAME = MOD_USER;
+    ROLEUSER.ATTRS = {
+        userId  : {
+            validator: Y.Lang.isNumber
+        },
+        manipulator : {
+            validator: Y.Lang.isObject
+        },
+        container : {
+            setter : function(node) {
+                var n = Y.one(node);
+                if (!n) {
+                    Y.fail(MOD_USER+': invalid container set '+node);
+                }
+                return n;
+            }
+        },
+        assignableroles : {
+            value : []
+        },
+        currentroles : {
+            value : [],
+            validator: Y.Lang.isArray
+        },
+        assignRoleLink : {
+            setter : function(node) {
+                if (node===false) {
+                    return node;
+                }
+                var n = Y.one(node);
+                if (!n) {
+                    Y.fail(MOD_NAME+': invalid assign role link given '+node);
+                }
+                return n;
+            },
+            value : false
+        },
+        assignRoleLinkSelector : {
+            value : '.assignrolelink',
+            validator : Y.Lang.isString
+        },
+        unassignRoleLinks : {
+        },
+        unassignRoleLinksSelector : {
+            value : '.unassignrolelink',
+            validator : Y.Lang.isString
+        }
+    }
+    Y.extend(ROLEUSER, Y.Base, {
+        initializer : function() {
+            var container = this.get(MANIPULATOR).get(CONTAINER).one('#user_'+this.get(USERID));
+            this.set(CONTAINER,        container);
+            var assignrole = container.one(this.get(ASSIGNROLELINKSELECTOR));
+            if (assignrole) {
+                this.set(ASSIGNROLELINK, assignrole.ancestor());
+            }
+            this.set(UNASSIGNROLELINKS , container.all(this.get(UNASSIGNROLELINKSSELECTOR)));
+        },
+        wire : function() {
+            var container = this.get(MANIPULATOR).get(CONTAINER).one('#user_'+this.get(USERID));
+            var arl = this.get(ASSIGNROLELINK);
+            var uarls = this.get(UNASSIGNROLELINKS);
+            var m = this.get(MANIPULATOR);
+            if (arl) {
+                arl.ancestor().on('click', m.addRole, m, this);
+            }
+            var currentroles = [];
+            if (uarls.size() > 0) {
+                uarls.each(function(link){
+                    link.roleId = link.getAttribute('rel');
+                    link.on('click', m.removeRole, m, this, link.roleId);
+                    currentroles[link.roleId] = true;
+                }, this);
+            }
+            container.all('.role.unchangeable').each(function(node){
+                currentroles[node.getAttribute('rel')] = true;
+            }, this);
+            
+            this.set(CURRENTROLES, currentroles);
+            return this;
+        },
+        _checkIfHasAllRoles : function() {
+            var roles = this.get(MANIPULATOR).get(ASSIGNABLEROLES);
+            var current = this.get(CURRENTROLES);
+            var allroles = true, i = 0;
+            for (i in roles) {
+                if (!current[i]) {
+                    allroles = false;
+                    break;
+                }
+            }
+            var link = this.get(ASSIGNROLELINK);
+            if (allroles) {
+                this.get(CONTAINER).addClass('hasAllRoles');
+            } else {
+                if (!link) {
+                    var m = this.get(MANIPULATOR);
+                    link = Y.Node.create('<div class="addrole">&nbsp;</div>');
+                    link.on('click', m.addRole, m, this);
+                    this.get(CONTAINER).one('.col_role').insert(link, 0);
+                    this.set(ASSIGNROLELINK, link);
+                }
+                this.get(CONTAINER).removeClass('hasAllRoles');
+            }
+        },
+        addRoleToDisplay : function(roleId, roleTitle) {
+            var m = this.get(MANIPULATOR);
+            var container = this.get(CONTAINER);
+            var role = Y.Node.create('<div class="role role_'+roleId+'">'+roleTitle+'<a class="unassignrolelink"><img src="'+M.util.image_url('t/delete', 'moodle')+'" alt="" /></a></div>');
+            var link = role.one('.unassignrolelink');
+            link.roleId = roleId;
+            link.on('click', m.removeRole, m, this, link.roleId);
+            container.one('.col_role .roles').append(role);
+            this._toggleCurrentRole(link.roleId, true);
+        },
+        removeRoleFromDisplay : function(roleId) {
+            var container = this.get(CONTAINER);
+            container.all('.role_'+roleId).remove();
+            this._toggleCurrentRole(roleId, false);
+        },
+        _toggleCurrentRole : function(roleId, hasRole) {
+            var roles = this.get(CURRENTROLES);
+            if (hasRole) {
+                roles[roleId] = true;
+            } else {
+                roles[roleId] = false;
+            }
+            this.set(CURRENTROLES, roles);
+            this._checkIfHasAllRoles();
+        }
+    });
+
+    var ROLEPANEL = function(config) {
+        ROLEPANEL.superclass.constructor.apply(this, arguments);
+    }
+    ROLEPANEL.NAME = MOD_PANEL;
+    ROLEPANEL.ATTRS = {
+        elementNode : {
+            setter : function(node) {
+                var n = Y.one(node);
+                if (!n) {
+                    Y.fail(MOD_PANEL+': Invalid element node');
+                }
+                return n;
+            }
+        },
+        contentNode : {
+            setter : function(node) {
+                var n = Y.one(node);
+                if (!n) {
+                    Y.fail(MOD_PANEL+': Invalid content node');
+                }
+                return n;
+            }
+        },
+        manipulator : {
+            validator: Y.Lang.isObject
+        }
+    }
+    Y.extend(ROLEPANEL, Y.Base, {
+        user : null,
+        roles : [],
+        initializer : function() {
+            var i, m = this.get(MANIPULATOR);
+            var element = Y.Node.create('<div class="enrolpanel roleassign"><div class="container"><div class="header"><h2>'+M.str.role.assignroles+'</h2><div class="close"></div></div><div class="content"></div></div></div>');
+            var content = element.one('.content');
+            var roles = m.get(ASSIGNABLEROLES);
+            for (i in roles) {
+                var button = Y.Node.create('<input type="button" value="'+roles[i]+'" id="add_assignable_role_'+i+'" />');
+                button.on('click', this.submit, this, i);
+                content.append(button);
+            }
+            Y.one(document.body).append(element);
+            this.set('elementNode', element);
+            this.set('contentNode', content);
+            element.one('.header .close').on('click', this.hide, this);
+        },
+        display : function(user) {
+            var currentroles = user.get(CURRENTROLES);
+            for (var i in currentroles) {
+                if (currentroles[i] === true) {
+                    this.get('contentNode').one('#add_assignable_role_'+i).setAttribute('disabled', 'disabled');
+                    this.roles.push(i);
+                }
+            }
+            this.user = user;
+            var roles = this.user.get(CONTAINER).one('.col_role .roles');
+            var x = roles.getX() + 10;
+            var y = roles.getY() + this.user.get(CONTAINER).get('offsetHeight') - 10 + Y.one(window).get('scrollTop');
+            this.get('elementNode').setXY([x, y]);
+            this.get('elementNode').addClass('visible');
+            this.displayed = true;
+        },
+        hide : function() {
+            for (var i in this.roles) {
+                this.get('contentNode').one('#add_assignable_role_'+this.roles[i]).removeAttribute('disabled');
+            }
+            this.roles = [];
+            this.user = null;
+            this.get('elementNode').removeClass('visible');
+            this.displayed = false;
+            return this;
+        },
+        submit : function(e, roleid) {
+            this.fire('submit', roleid, this.user.get(USERID));
+        }
+    });
+    Y.augment(ROLEPANEL, Y.EventTarget);
+
+    M.enrol = M.enrol || {};
+    M.enrol.rolemanager = {
+        instance : null,
+        init : function(config) {
+            M.enrol.rolemanager.instance = new ROLE(config);
+            return M.enrol.rolemanager.instance;
+        }
+    }
+    
+}, '@VERSION@', {requires:['base','node','io','json-parse','test']});
\ No newline at end of file
Index: moodle/lang/en/enrol.php
--- moodle/lang/en/enrol.php Base (1.1)
+++ moodle/lang/en/enrol.php Locally Modified (Based On 1.1)
@@ -26,10 +26,15 @@
 
 $string['actenrolshhdr'] = 'Active course enrolment plugins';
 $string['addinstance'] = 'Add method';
+$string['ajaxoneuserfound'] = '1 user found';
+$string['ajaxxusersfound'] = '[users] users found';
+$string['ajaxnext25'] = 'Next 25...';
 $string['configenrolplugins'] = 'Please select all required plugins and arrange then in appropriate order.';
 $string['defaultenrol'] = 'Add instance to new courses';
 $string['defaultenrol_desc'] = 'It is possible to add this plugin to all new courses by default.';
 $string['deleteinstanceconfirm'] = 'Do you really want to delete enrol plugin instance "{$a->name}" with {$a->users} enrolled users?';
+$string['durationdays'] = '%d days';
+$string['enrol'] = 'Enrol';
 $string['enrolcandidates'] = 'Not enrolled users';
 $string['enrolcandidatesmatching'] = 'Matching not enrolled users';
 $string['enrolledusers'] = 'Enrolled users';
@@ -39,14 +44,19 @@
 $string['enrolmentnew'] = 'New enrolment in {$a}';
 $string['enrolmentnewuser'] = '{$a->user} has enrolled in course "{$a->course}"';
 $string['enrolments'] = 'Enrolments';
+$string['enrolmentoptions'] = 'Enrolment options';
 $string['enrolperiod'] = 'Enrolment duration';
 $string['enrolusage'] = 'Instances / enrolments';
+$string['enrolusers'] = 'Enrol users';
 $string['enroltimeend'] = 'Enrolment ends';
 $string['enroltimestart'] = 'Enrolment starts';
+$string['errajaxfailedenrol'] = 'Failed to enrol user';
+$string['errajaxsearch'] = 'Error when searching users';
 $string['manageenrols'] = 'Manage enrol plugins';
 $string['manageinstance'] = 'Manage';
 $string['noexistingparticipants'] = 'No existing participants';
 $string['noguestaccess'] = 'Guests can not access this course, please try to log in.';
+$string['none'] = 'None';
 $string['notenrollable'] = 'This course is not enrollable at the moment.';
 $string['notenrolledusers'] = 'Other users';
 $string['participationactive'] = 'Active';
@@ -55,9 +65,12 @@
 $string['periodend'] = 'until {$a}';
 $string['periodstart'] = 'from {$a}';
 $string['periodstartend'] = 'from {$a->start} until {$a->end}';
+$string['startdatetoday'] = 'Today';
 $string['unenrol'] = 'Unenrol';
 $string['unenrolconfirm'] = 'Do you really want to unenrol user "{$a->user}" from course "{$a->course}"?';
 $string['unenrolme'] = 'Unenrol me from {$a}';
 $string['unenrolroleusers'] = 'Unenrol users';
 $string['uninstallconfirm'] = 'You are about to completely delete the enrol plugin \'{$a}\'.  This will completely delete everything in the database associated with this enrolment type.  Are you SURE you want to continue?';
 $string['uninstalldeletefiles'] = 'All data associated with the enrol plugin \'{$a->plugin}\' has been deleted from the database.  To complete the deletion (and prevent the plugin re-installing itself), you should now delete this directory from your server: {$a->directory}';
+$string['unlimitedduration'] = 'Unlmited';
+$string['usersearch'] = 'Search ';
Index: moodle/lib/ajax/getnavbranch.php
--- moodle/lib/ajax/getnavbranch.php Base (1.11)
+++ moodle/lib/ajax/getnavbranch.php Locally Modified (Based On 1.11)
@@ -25,6 +25,8 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+define('AJAX_SCRIPT', true);
+
 /** Include config */
 require_once(dirname(__FILE__) . '/../../config.php');
 /** Include course lib for its functions */
Index: moodle/pix/t/enroladd.gif
MIME: application/octet-stream; encoding: Base64; length: 288
R0lGODlhDAAMAKUiAIKCgouLjI2NjaKioq2trK+vr7W1tra2tri4t7u7u729
vMDAwMTExcXFxcfHx83OzdDP0NHS0dXV1NfX193d3d7f3+Hh4eLi4uPi4uXl
5ejo6Ozs7PHx8fLx8fLy8vPz8/f49////wAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEKAD8ALAAAAAAMAAwAAAY9
wJ9wSCz+DBMLxiiMhEId5k/y5BQFi8fmCfJ0lr/CczzOCAlkckUIQDA0z8+F
0ig6nhSp4gmRBg4JA1JSQQA7
Index: moodle/theme/base/style/core.css
--- moodle/theme/base/style/core.css Base (1.44)
+++ moodle/theme/base/style/core.css Locally Modified (Based On 1.44)
@@ -598,3 +598,26 @@
  * Registration
  */
 #page-admin-registration-register .registration_textfield {width: 300px;}
+
+/**
+ * Enrol
+ */
+.userenrolment {width:100%;border-collapse: collapse;}
+.userenrolment td {padding:0;height:41px;}
+.userenrolment .subfield {margin-right:5px;}
+.userenrolment .col_userdetails .subfield_picture {float:left;}
+.userenrolment .col_lastseen {width:150px;}
+.userenrolment .col_role {width:262px;}
+.userenrolment .col_role .roles {margin-right:30px;}
+.userenrolment .col_role .role {float:left;padding:3px;margin:3px;}
+.userenrolment .col_role .role a {margin-left:3px;cursor:pointer;}
+.userenrolment .col_role .addrole {float:right;width:18px;margin:3px;height:18px;text-align:center;}
+.userenrolment .col_role .addrole a img {vertical-align:bottom;}
+.userenrolment .hasAllRoles .col_role .addrole {display:none;}
+.userenrolment .col_group .groups {margin-right:30px;}
+.userenrolment .col_group .group {float:left;padding:3px;margin:3px;white-space:nowrap;}
+.userenrolment .col_group .group a {margin-left:3px;cursor:pointer;}
+.userenrolment .col_group .addgroup {float:right;width:18px;margin:3px;height:18px;text-align:center;}
+.userenrolment .col_group .addgroup a img {vertical-align:bottom;}
+.userenrolment .col_enrol .enrolment {float:left;padding:3px;margin:3px;}
+.userenrolment .col_enrol .enrolment a {float:right;margin-left:3px;}
\ No newline at end of file
Index: moodle/theme/standard/style/core.css
--- moodle/theme/standard/style/core.css Base (1.10)
+++ moodle/theme/standard/style/core.css Locally Modified (Based On 1.10)
@@ -417,3 +417,25 @@
 .smartselect .smartselect_submenu {border-color:#999;}
 .smartselect .smartselect_menu,
 .smartselect .smartselect_submenu {-moz-border-radius:4px;-webkit-border-radius: 5px;}
+
+/**
+ * Enrol
+ */
+.userenrolment {font-size:90%;border:1px solid #999;}
+.userenrolment tr.r0 {background-color:#F9F9F9;}
+.userenrolment tr.r1 {background-color:#F3F3F3;}
+.userenrolment td {border:1px solid #E9E9E9;border-top-color:#F6F6F6;border-right-color:#EEE;border-left-color:#F3F3F3;}
+.userenrolment td.c0 {border-left-color:#999;}
+.userenrolment td.c4 {border-right-color:#999;}
+.userenrolment .col_userdetails {padding:3px;min-width:35%;}
+.userenrolment .col_role .roles {position:relative;}
+.userenrolment .col_role .role {line-height:10px;font-size:10px;}
+.userenrolment .col_role .role a img {height:8px;}
+.userenrolment .col_role .addrole {background-color:#DDD;border:1px outset #EEE;-moz-border-radius:5px;}
+.userenrolment .col_group {max-width:300px;}
+.userenrolment .col_group .group {line-height:10px;font-size:10px;}
+.userenrolment .col_group .group a img {height:8px;}
+.userenrolment .col_group .addgroup {background-color:#DDD;border:1px outset #EEE;-moz-border-radius:5px;}
+.userenrolment .col_enrol {max-width:300px;}
+.userenrolment .col_enrol .enrolment {border:1px outset #E6E6E6;background-color:#EEE;line-height:10px;font-size:10px;-moz-border-radius:5px;}
+.path-enrol .enrolusersbutton.instance1 {float:right;}
\ No newline at end of file
