]> git.mjollnir.org Git - moodle.git/commitdiff
user selection: MDL-16992 Improve the user selector used on the assign roles and...
authortjhunt <tjhunt>
Mon, 27 Oct 2008 07:30:44 +0000 (07:30 +0000)
committertjhunt <tjhunt>
Mon, 27 Oct 2008 07:30:44 +0000 (07:30 +0000)
user/selector/lib.php [new file with mode: 0644]
user/selector/test.php [new file with mode: 0644]

diff --git a/user/selector/lib.php b/user/selector/lib.php
new file mode 100644 (file)
index 0000000..a8f4b72
--- /dev/null
@@ -0,0 +1,366 @@
+<?php  // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.org                                            //
+//                                                                       //
+// Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com     //
+//                                                                       //
+// This program 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 2 of the License, or     //
+// (at your option) any later version.                                   //
+//                                                                       //
+// This program 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:                          //
+//                                                                       //
+//          http://www.gnu.org/copyleft/gpl.html                         //
+//                                                                       //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * Code for ajax user selectors.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package userselector
+ */
+
+/**
+ * The default size of a user selector.
+ */
+define('USER_SELECTOR_DEFAULT_ROWS', 20);
+
+/**
+ * Base class for user selectors.
+ */
+abstract class user_selector_base {
+    /** The control name (and id) in the HTML. */
+    protected $name;
+    /** Extra fields to search on and return in addition to firstname and lastname. */
+    protected $extrafields;
+    /** Whether the conrol should allow selection of many users, or just one. */
+    protected $multiselect = true;
+    /** The height this control should have, in rows. */
+    protected $rows = USER_SELECTOR_DEFAULT_ROWS;
+    /** A list of userids that should not be returned by this control. */
+    protected $exclude = array();
+    /** A list of the users who are selected. */
+    protected $selected = null;
+
+    // This is used by get selected users, 
+    private $validatinguserids = null;
+
+    // Public API ==============================================================
+
+    /**
+     * Constructor
+     *
+     * @param string $name the control name/id for use in the HTML.
+     */
+    public function __construct($name) {
+        global $CFG;
+        $this->name = $name;
+        if (empty($CFG->extrauserselectorfields)) {
+            $this->extrafields = array();
+        } else {
+            $this->extrafields = explode(',', $CFG->extrauserselectorfields);
+        }
+    }
+
+    /**
+     * All to the list of user ids that this control will not select. For example,
+     * on the role assign page, we do not list the users who already have the role
+     * in question.
+     *
+     * @param array $arrayofuserids the user ids to exclude.
+     */
+    public function exclude($arrayofuserids) {
+        $this->exclude = array_unique(array_merge($this->exclude, $arrayofuserids));
+    }
+
+    /**
+     * Clear the list of excluded user ids.
+     */
+    public function clear_exclusions() {
+        $exclude = array();
+    }
+
+    /**
+     * @return array the list of user ids that this control will not select.
+     */
+    public function get_exclusions() {
+        return clone($this->exclude);
+    }
+
+    /**
+     * @return array the userids that were selected. This is a more sophisticated version
+     * of optional_param($this->name, array(), PARAM_INTEGER) that validates the
+     * returned list of ids against the rules for this user selector.
+     */
+    public function get_selected_users() {
+        // Do a lazy load.
+        if (is_null($this->selected)) {
+            $this->selected = $this->load_selected_users();
+        }
+        return $this->selected;
+    }
+
+    /**
+     * Output this user_selector as HTML.
+     * @param boolean $return if true, return the HTML as a string instead of outputting it.
+     * @return mixed if $return is true, returns the HTML as a string, otherwise returns nothing.
+     */
+    public function display($return = false) {
+        // Ensure that the list of previously selected users is up to date.
+        $this->get_selected_users();
+
+        // Get the list of requested users, and if there is only one, set a flag to autoselect it.
+        $search = optional_param($this->name . '_searchtext', '', PARAM_RAW);
+        $groupedusers = $this->find_users($search);
+        $select = false;
+        if (empty($groupedusers) && empty($this->selected)) {
+            $groupedusers = array(get_string('nomatchingusers') => array());
+        } else if (count($groupedusers) == 1 && count(reset($groupedusers)) == 1) {
+            $select = true;
+        }
+
+        // Output the select.
+        $name = $this->name;
+        $multiselect = '';
+        if ($this->multiselect) {
+            $name .= '[]';
+            $multiselect = 'multiple="multiple" ';
+        }
+        $output = '<div class="userselector" id="' . $this->name . '_wrapper">' . "\n" .
+                '<select name="' . $name . '" id="' . $this->name . '" ' .
+                $multiselect . 'size="' . $this->rows . '">' . "\n";
+        foreach ($groupedusers as $groupname => $users) {
+            $output .= $this->output_optgroup($groupname, $users, $select);
+        }
+        if (!empty($this->selected)) {
+            $output .= $this->output_optgroup(get_string('previouslyselectedusers'), $this->selected, true);
+        }
+
+        // Output the search controls.
+        $output .= "</select>\n<div>\n";
+        $output .= '<input type="text" name="' . $this->name . '_searchtext" id="' .
+                $this->name . '_searchtext" value="' . s($search) . '" />';
+        $output .= '<input type="submit" name="' . $this->name . '_searchbutton" id="' .
+                $this->name . '_searchbutton" value="' . $this->search_button_caption() . '" />';
+        $output .= "</div>\n</div>\n\n";
+
+        // This method trashes $this->selected, so reset it so if someone tries to
+        // Use it again, it is rebuilt.
+        $this->selected = null;
+
+        // Return or output it.
+        if ($return) {
+            return $output;
+        } else {
+            echo $output;
+        }
+    }
+
+    /**
+     * The height this control will be displayed, in rows.
+     *
+     * @param integer $numrows the desired height.
+     */
+    public function set_rows($numrows) {
+        $this->rows = $numrows;
+    }
+
+    /**
+     * @return integer the height this control will be displayed, in rows.
+     */
+    public function get_rows() {
+        return $this->rows;
+    }
+
+    /**
+     * Whether this control will allow selection of many, or just one user.
+     *
+     * @param boolean $multiselect true = allow multiple selection.
+     */
+    public function set_multiselect($multiselect) {
+        $this->multiselect = $multiselect;
+    }
+
+    /**
+     * @return boolean whether this control will allow selection of more than one user.
+     */
+    public function is_multiselect() {
+        return $this->multiselect;
+    }
+
+    /**
+     * @return string the id/name that this control will have in the HTML.
+     */
+    public function get_name() {
+        return $this->name;
+    }
+
+    // API for sublasses =======================================================
+
+    /**
+     * Search the database for users matching the $search string, and any other
+     * conditions that apply. The SQL for testing whether a user matches the
+     * search string should be obtained by calling the search_sql method.
+     *
+     * @param string $search the search string.
+     * @return array An array of arrays of users. The array keys of the outer
+     *      array should be the string names of optgroups. The keys of the inner
+     *      arrays should be userids, and the values should be user objects
+     *      containing at least the list of fields returned by the method
+     *      required_fields_sql().
+     */
+    protected abstract function find_users($search);
+
+    protected function get_options() {
+        return array(
+            'class' => get_class($this),
+            'exclude' => $this->exclude,
+        );
+    }
+
+    // Inner workings ==========================================================
+
+    protected function load_selected_users() {
+        // See if we got anything.
+        $userids = optional_param($this->name, array(), PARAM_INTEGER);
+        if (empty($userids)) {
+            return array();
+        }
+
+        // If we did, use the find_users method to validate the ids.
+        $this->validatinguserids = $userids;
+        $groupedusers = $this->find_users('');
+        $this->validatinguserids = null;
+
+        // Aggregate the resulting list back into a single one.
+        $users = array();
+        foreach ($groupedusers as $group) {
+            $users += $group;
+        }
+        return $users;
+    }
+
+    protected function required_fields_sql($u) {
+        // Raw list of fields.
+        $fields = array('id', 'firstname', 'lastname');
+        $fields = array_merge($fields, $this->extrafields);
+
+        // Prepend the table alias.
+        if ($u) {
+            foreach ($fields as &$field) {
+                $field = $u . '.' . $field;
+            }
+        }
+        return implode(',', $fields);
+    }
+
+    protected function search_sql($search, $u) {
+        global $DB;
+        $params = array();
+        $tests = array();
+
+        // If we have a $search string, put a field LIKE '$search%' condition on each field.
+        if ($search) {
+            $conditions = array(
+                $DB->sql_fullname($u . '.firstname', $u . '.lastname'),
+                $conditions[] = $u . '.lastname'
+            );
+            foreach ($this->extrafields as $field) {
+                $conditions[] = $u . '.' . $field;
+            }
+            $ilike = ' ' . $DB->sql_ilike() . ' ?';
+            foreach ($conditions as &$condition) {
+                $condition .= $ilike;
+                $params[] = $search . '%';
+            }
+            $tests[] = '(' . implode(' OR ', $conditions) . ')';
+        }
+
+        // If we are being asked to exclude any users, do that.
+        if (!empty($this->exclude)) {
+            list($usertest, $userparams) = $DB->get_in_or_equal($this->exclude, SQL_PARAMS_QM, '', false);
+            $tests[] = $u . '.id ' . $usertest;
+            $params = array_merge($params, $userparams);
+        }
+
+        // If we are validating a set list of userids, add an id IN (...) test.
+        if (!empty($this->validatinguserids)) {
+            list($usertest, $userparams) = $DB->get_in_or_equal($this->validatinguserids);
+            $tests[] = $u . '.id ' . $usertest;
+            $params = array_merge($params, $userparams);
+        }
+
+        if (empty($tests)) {
+            $tests[] = '1 = 1';
+        }
+
+        // Combing the conditions and return.
+        return array(implode(' AND ', $tests), $params);
+    }
+
+    protected function output_optgroup($groupname, $users, $select) {
+        $output = '<optgroup label="' . s($groupname) . ' (' . count($users) . ')">' . "\n";
+        if (!empty($users)) {
+            foreach ($users as $user) {
+                if ($select || isset($this->selected[$user->id])) {
+                    $selectattr = ' selected="selected"';
+                } else {
+                    $selectattr = '';
+                }
+                unset($this->selected[$user->id]);
+                $output .= '<option' . $selectattr . ' value="' . $user->id . '">' .
+                        $this->output_user($user) . "</option>\n";
+            }
+        } else {
+            $output .= '<option disabled="disabled">&nbsp;</option>' . "\n";
+        }
+        $output .= "</optgroup>\n";
+        return $output;
+    }
+
+    /**
+     * Convert a user object to a string suitable for displaying as an option in the dropdown.
+     *
+     * @param object $user the user to display.
+     * @return string a string representation to display.
+     */
+    protected function output_user($user) {
+        $bits = array(
+            fullname($user)
+        );
+        foreach ($this->extrafields as $field) {
+            $bits[] = $user->$field;
+        }
+        return implode(', ', $bits);
+    }
+
+    /**
+     * @return string the caption for the search button.
+     */
+    protected function search_button_caption() {
+        return get_string('search');
+    }
+}
+
+class role_assign_user_selector extends user_selector_base {
+    protected function find_users($search) {
+        return array(); // TODO
+    }
+}
+
+class group_members_user_selector extends user_selector_base {
+    protected function find_users($search) {
+        return array(); // TODO
+    }
+}
+?>
\ No newline at end of file
diff --git a/user/selector/test.php b/user/selector/test.php
new file mode 100644 (file)
index 0000000..e7f6d46
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+require_once(dirname(__FILE__) . '/../../config.php');
+require_once($CFG->dirroot . '/user/selector/lib.php');
+
+class test_user_selector extends user_selector_base {
+    public function __construct($name) {
+        parent::__construct($name);
+    }
+
+    protected function find_users($search) {
+        global $DB;
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+        $sql = 'SELECT ' . $this->required_fields_sql('u') .
+                ' FROM {user} u' .
+                ' WHERE ' . $wherecondition;
+        $users = $DB->get_recordset_sql($sql, $params);
+        $groupedusers = array();
+        if ($search) {
+            $groupname = "Users matching '" . $search . "'";
+        } else {
+            $groupname = 'All users';
+        }
+        foreach ($users as $user) {
+            $groupedusers[$groupname][$user->id] = $user;
+        }
+        return $groupedusers;
+    }
+}
+
+print_header();
+
+$userselector = new test_user_selector('myuserselector');
+
+$users = $userselector->get_selected_users();
+if (!empty($users)) {
+    print_heading('Users that were selected');
+    echo '<ul>';
+    foreach ($users as $user) {
+        echo '<li>', fullname($user), '</li>';
+    }
+    echo '</ul>';
+}
+
+echo '<form action="test.php"><div><label for="myuserselector">Select users</label>';
+$userselector->display();
+echo '</div></form>';
+
+print_footer();
+?>
\ No newline at end of file