Commits

Leho Kraav committed 460f516

trac:#2456 patches from brad 5 years ago

Comments (0)

Files changed (5)

t2456-brad-5y-back/userdir-for-r2637.diff

+Index: trac/env.py
+===================================================================
+--- trac/env.py	(revision 2637)
++++ trac/env.py	(working copy)
+@@ -21,6 +21,7 @@
+ from trac.core import Component, ComponentManager, implements, Interface, \
+                       ExtensionPoint, TracError
+ from trac.db import DatabaseManager
++from trac.userdir import *
+ 
+ __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
+ 
+@@ -60,6 +61,7 @@
+      * wiki and ticket attachments.
+     """   
+     setup_participants = ExtensionPoint(IEnvironmentSetupParticipant)
++    userdirs = ExtensionPoint(IUserDirectory)
+ 
+     def __init__(self, path, create=False, options=[]):
+         """Initialize the Trac environment.
+@@ -231,30 +233,19 @@
+         logid = self.path # Env-path provides process-unique ID
+         self.log = logger_factory(logtype, logfile, loglevel, logid)
+ 
+-    def get_known_users(self, cnx=None):
+-        """Generator that yields information about all known users, i.e. users
+-        that have logged in to this Trac environment and possibly set their name
+-        and email.
+-
+-        This function generates one tuple for every user, of the form
+-        (username, name, email) ordered alpha-numerically by username.
+-
+-        @param cnx: the database connection; if ommitted, a new connection is
+-                    retrieved
+-        """
++    def get_known_users(self, cnx=None, attrs=None, limit=None):
++                
+         if not cnx:
+             cnx = self.get_db_cnx()
+-        cursor = cnx.cursor()
+-        cursor.execute("SELECT DISTINCT s.sid, n.var_value, e.var_value "
+-                       "FROM session AS s "
+-                       " LEFT JOIN session AS n ON (n.sid=s.sid "
+-                       "  AND n.authenticated=1 AND n.var_name = 'name') "
+-                       " LEFT JOIN session AS e ON (e.sid=s.sid "
+-                       "  AND e.authenticated=1 AND e.var_name = 'email') "
+-                       "WHERE s.authenticated=1 ORDER BY s.sid")
+-        for username,name,email in cursor:
+-            yield username, name, email
+-
++        
++        # should accumulate users from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for user in userdir.get_known_users(cnx, attrs, limit):
++                yield user
++        
++        # yield username, name, email
++        
++    
+     def backup(self, dest=None):
+         """Simple SQLite-specific backup of the database.
+ 
+Index: trac/ticket/api.py
+===================================================================
+--- trac/ticket/api.py	(revision 2637)
++++ trac/ticket/api.py	(working copy)
+@@ -56,8 +56,8 @@
+         if self.config.getbool('ticket', 'restrict_owner'):
+             field['type'] = 'select'
+             users = []
+-            for username, name, email in self.env.get_known_users(db):
+-                users.append(username)
++            for user in self.env.get_known_users(cnx=db):
++                users.append(user['username'])
+             field['options'] = users
+             field['optional'] = True
+         else:
+Index: trac/versioncontrol/web_ui/log.py
+===================================================================
+--- trac/versioncontrol/web_ui/log.py	(revision 2637)
++++ trac/versioncontrol/web_ui/log.py	(working copy)
+@@ -153,9 +153,9 @@
+         if format == 'rss':
+             # Get the email addresses of all known users
+             email_map = {}
+-            for username,name,email in self.env.get_known_users():
+-                if email:
+-                    email_map[username] = email
++            for user in self.env.get_known_users(attrs=('email')):
++                if user['email']:
++                    email_map[user['username']] = user['email']
+             for cs in changes.values():
+                 cs['message'] = util.escape(cs['message'])
+                 cs['shortlog'] = util.escape(cs['shortlog'].replace('\n', ' '))
+Index: trac/Timeline.py
+===================================================================
+--- trac/Timeline.py	(revision 2637)
++++ trac/Timeline.py	(working copy)
+@@ -143,9 +143,9 @@
+ 
+         # Get the email addresses of all known users
+         email_map = {}
+-        for username, name, email in self.env.get_known_users():
+-            if email:
+-                email_map[username] = email
++        for user in self.env.get_known_users(attrs=('email')):
++            if user['email']:
++                email_map[user['username']] = user['email']
+ 
+         idx = 0
+         for kind, href, title, date, author, message in events:
+Index: trac/tests/env.py
+===================================================================
+--- trac/tests/env.py	(revision 2637)
++++ trac/tests/env.py	(working copy)
+@@ -34,8 +34,8 @@
+                             ('joe', 'email', 'joe@example.com'),
+                             ('jane', 'name', 'Jane')])
+         users = {}
+-        for username,name,email in self.env.get_known_users(self.db):
+-            users[username] = (name, email)
++        for user in self.env.get_known_users(cnx=self.db):
++            users[user['username']] = (user['name'], user['email'])
+ 
+         assert not users.has_key('anonymous')
+         self.assertEqual(('Tom', 'tom@example.com'), users['tom'])
+Index: trac/userdir.py
+===================================================================
+--- trac/userdir.py	(revision 0)
++++ trac/userdir.py	(revision 0)
+@@ -0,0 +1,105 @@
++# -*- coding: iso8859-1 -*-
++#
++# Copyright (C) 2003-2005 Edgewall Software
++# Copyright (C) 2003-2005 Jonas Borgstr�m <jonas@edgewall.com>
++# All rights reserved.
++#
++# This software is licensed as described in the file COPYING, which
++# you should have received as part of this distribution. The terms
++# are also available at http://trac.edgewall.com/license.html.
++#
++# This software consists of voluntary contributions made by many
++# individuals. For the exact contribution history, see the revision
++# history and logs, available at http://projects.edgewall.com/trac/.
++#
++# Author: Bruce Christensen <me@brucec.net>
++# Author: Brad Anderson <brad@dsource.org>
++
++from trac.core import Component, implements, Interface, ExtensionPoint, TracError
++
++class IUserDirectory(Interface):
++    def get_known_users(cnx=None, attrs=('name', 'email'), limit=None):
++        """Generator that yields information about known users.
++        
++        Generates a dictionary for each known user, of the form
++        {   'username': 'john',
++            'attr1': attr1 value,
++            'attr2': attr2 value,
++            ...
++            }
++        
++        Raises KeyError if any of attrs isn't supported.
++        
++        @param cnx:   db connection object
++        @param attrs: tuple of attributes to be included in the generated dict
++                      (in addition to 'username', which is always included)
++        @param limit: maximum number of results to generate. None means no limit.
++        """
++        pass
++
++    def get_user_attribute(user, attr):
++        """Provides values of one or more attributes of a user.
++
++        Raises UnknownUserError if the requested user doesn't exist.
++
++        Raises KeyError if the specified attribute isn't supported.
++
++        @param user: a username
++        @param attr: the name of the attribute to return. Can also be a tuple of
++                    attribute names.
++        @return:     the value of a user attribute, or, if attr is a tuple, a
++                    dictionary of attributes like get_known_users generates
++        """
++        pass
++
++    def get_supported_attributes():
++        """
++        @return: tuple of supported attribute names. Always includes 'username'.
++        """
++        pass
++
++
++class SessionUserDirectory(Component):
++    implements (IUserDirectory)
++    
++    # This plugin basically duplicates the existing functionality in Trac, providing a
++    # list of users based on the 'session' table.  It makes no attempt to use the 'limit'
++    # parameter of the function right now.
++    
++    # IUserDirectory methods
++    def get_known_users(self, cnx=None, attrs=None, limit=None):
++        """Generator that yields information about all known users, i.e. users
++        that have logged in to this Trac environment and possibly set their name
++        and email.
++
++        This function generates one dictionary for every user, of the form
++        {'username':username,'attr1':attr1,...}
++
++        @param cnx: the database connection
++        @param attrs: attributes of the user to return
++        @param limit: limit the number of users returned
++
++        """
++
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT s.sid, n.var_value, e.var_value "
++                       "FROM session AS s "
++                       " LEFT JOIN session AS n ON (n.sid=s.sid "
++                       "  AND n.authenticated=1 AND n.var_name = 'name') "
++                       " LEFT JOIN session AS e ON (e.sid=s.sid "
++                       "  AND e.authenticated=1 AND e.var_name = 'email') "
++                       "WHERE s.authenticated=1 ORDER BY s.sid")
++        for username,name,email in cursor:
++            user = {}
++            user['username'] = username  # required key/value
++            if attrs and 'name' in attrs:
++                user['name'] = name
++            if attrs and 'email' in attrs:
++                user['email'] = email
++            yield user
++    
++    def get_user_attribute(self, user, attr):
++        pass
++    
++    def get_supported_attributes(self):
++        pass

t2456-brad-5y-back/userdir-for-r2659.diff

+Index: trac/env.py
+===================================================================
+--- trac/env.py	(revision 2659)
++++ trac/env.py	(working copy)
+@@ -21,6 +21,7 @@
+ from trac.core import Component, ComponentManager, implements, Interface, \
+                       ExtensionPoint, TracError
+ from trac.db import DatabaseManager
++from trac.userdir import *
+ 
+ __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
+ 
+@@ -60,6 +61,7 @@
+      * wiki and ticket attachments.
+     """   
+     setup_participants = ExtensionPoint(IEnvironmentSetupParticipant)
++    userdirs = ExtensionPoint(IUserDirectory)
+ 
+     def __init__(self, path, create=False, options=[]):
+         """Initialize the Trac environment.
+@@ -231,30 +233,24 @@
+         logid = self.path # Env-path provides process-unique ID
+         self.log = logger_factory(logtype, logfile, loglevel, logid)
+ 
+-    def get_known_users(self, cnx=None):
+-        """Generator that yields information about all known users, i.e. users
+-        that have logged in to this Trac environment and possibly set their name
+-        and email.
+-
+-        This function generates one tuple for every user, of the form
+-        (username, name, email) ordered alpha-numerically by username.
+-
+-        @param cnx: the database connection; if ommitted, a new connection is
+-                    retrieved
+-        """
++    def get_known_user_info(self, cnx=None, limit=None):
+         if not cnx:
+             cnx = self.get_db_cnx()
+-        cursor = cnx.cursor()
+-        cursor.execute("SELECT DISTINCT s.sid, n.var_value, e.var_value "
+-                       "FROM session AS s "
+-                       " LEFT JOIN session AS n ON (n.sid=s.sid "
+-                       "  AND n.authenticated=1 AND n.var_name = 'name') "
+-                       " LEFT JOIN session AS e ON (e.sid=s.sid "
+-                       "  AND e.authenticated=1 AND e.var_name = 'email') "
+-                       "WHERE s.authenticated=1 ORDER BY s.sid")
+-        for username,name,email in cursor:
+-            yield username, name, email
++        
++        # should accumulate user_info from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username, name, email in userdir.get_known_user_info(cnx, limit):
++                yield username, name, email
+ 
++    def get_known_users(self, cnx=None, limit=None):
++        if not cnx:
++            cnx = self.get_db_cnx()
++        
++        # should accumulate users from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username in userdir.get_known_users(cnx, limit):
++                yield username
++    
+     def backup(self, dest=None):
+         """Simple SQLite-specific backup of the database.
+ 
+Index: trac/ticket/api.py
+===================================================================
+--- trac/ticket/api.py	(revision 2659)
++++ trac/ticket/api.py	(working copy)
+@@ -56,7 +56,7 @@
+         if self.config.getbool('ticket', 'restrict_owner'):
+             field['type'] = 'select'
+             users = []
+-            for username, name, email in self.env.get_known_users(db):
++            for username in self.env.get_known_users(cnx=db):
+                 users.append(username)
+             field['options'] = users
+             field['optional'] = True
+Index: trac/versioncontrol/web_ui/log.py
+===================================================================
+--- trac/versioncontrol/web_ui/log.py	(revision 2659)
++++ trac/versioncontrol/web_ui/log.py	(working copy)
+@@ -153,7 +153,7 @@
+         if format == 'rss':
+             # Get the email addresses of all known users
+             email_map = {}
+-            for username,name,email in self.env.get_known_users():
++            for username,name,email in self.env.get_known_user_info():
+                 if email:
+                     email_map[username] = email
+             for cs in changes.values():
+Index: trac/Timeline.py
+===================================================================
+--- trac/Timeline.py	(revision 2659)
++++ trac/Timeline.py	(working copy)
+@@ -143,7 +143,7 @@
+ 
+         # Get the email addresses of all known users
+         email_map = {}
+-        for username, name, email in self.env.get_known_users():
++        for username, name, email in self.env.get_known_user_info():
+             if email:
+                 email_map[username] = email
+ 
+Index: trac/tests/env.py
+===================================================================
+--- trac/tests/env.py	(revision 2659)
++++ trac/tests/env.py	(working copy)
+@@ -34,7 +34,7 @@
+                             ('joe', 'email', 'joe@example.com'),
+                             ('jane', 'name', 'Jane')])
+         users = {}
+-        for username,name,email in self.env.get_known_users(self.db):
++        for username,name,email in self.env.get_known_user_info(self.db):
+             users[username] = (name, email)
+ 
+         assert not users.has_key('anonymous')
+Index: trac/userdir.py
+===================================================================
+--- trac/userdir.py	(revision 0)
++++ trac/userdir.py	(revision 0)
+@@ -0,0 +1,129 @@
++# -*- coding: iso8859-1 -*-
++#
++# Copyright (C) 2003-2005 Edgewall Software
++# Copyright (C) 2005 Brad Anderson <brad@dsource.org>
++# All rights reserved.
++#
++# This software is licensed as described in the file COPYING, which
++# you should have received as part of this distribution. The terms
++# are also available at http://trac.edgewall.com/license.html.
++#
++# This software consists of voluntary contributions made by many
++# individuals. For the exact contribution history, see the revision
++# history and logs, available at http://projects.edgewall.com/trac/.
++#
++# Author: Brad Anderson <brad@dsource.org>
++
++from trac.core import Component, implements, Interface, ExtensionPoint, TracError
++
++class IUserDirectory(Interface):
++    def get_known_user_info(self, cnx=None, limit=None):
++        """Generator that yields information about known users.
++        
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return (username, name, email)
++            if plugin does not support name or email, return None in their place
++        """
++        pass
++
++    def get_known_users(self, cnx=None, limit=None):
++        """
++        Generator that yields a list of known users.
++
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return username
++        """
++        pass
++    
++    def get_user_attribute(self, user, attr):
++        """Provides values of one or more attributes of a user.
++
++        Raises UnknownUserError if the requested user doesn't exist.
++
++        Raises KeyError if the specified attribute isn't supported.
++
++        @param user: a username
++        @param attr: the name of the attribute to return. Can also be a tuple of
++                    attribute names.
++        @return:     the value of a user attribute, or, if attr is a tuple, a
++                    dictionary of attributes like get_known_users generates
++        """
++        pass
++
++    def get_supported_attributes(self):
++        """
++        @return: tuple of supported attribute names. Always includes 'username'.
++        """
++        pass
++
++
++class DefaultUserDirectory(Component):
++    implements (IUserDirectory)
++    
++    # This plugin basically duplicates the existing functionality in Trac, providing a
++    # list of users based on the 'session' table.  
++    
++    # IUserDirectory methods
++    
++    def get_known_user_info(self, cnx=None, limit=None):
++        """Generator that yields information about all known users, i.e. users
++        that have logged in to this Trac environment and possibly set their name
++        and email.
++
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx: the database connection
++        @param attrs: attributes of the user to return
++        @param limit: limit the number of users returned
++
++        @return (username, name, email)
++        """
++
++        if not cnx:
++            pass  # FIXME - need to implement other plugin here to get cnx?  For now, the
++                  #         DefaultUserDirectory is fine, b/c the method in env gets a cnx.
++                  #         But what about other plugins?
++        
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT s.sid, n.var_value, e.var_value "
++                       "FROM session AS s "
++                       " LEFT JOIN session AS n ON (n.sid=s.sid "
++                       "  AND n.authenticated=1 AND n.var_name = 'name') "
++                       " LEFT JOIN session AS e ON (e.sid=s.sid "
++                       "  AND e.authenticated=1 AND e.var_name = 'email') "
++                       "WHERE s.authenticated=1 ORDER BY s.sid")
++        iter = 0
++        for username,name,email in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username,name,email
++    
++    def get_known_users(self, cnx=None, limit=None):
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT sid "
++                       "FROM session "
++                       "WHERE authenticated=1 "
++                       "ORDER BY sid")
++        iter = 0
++        for username in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username[0]
++            
++    def get_user_attribute(self, user, attr):
++        pass
++    
++    def get_supported_attributes(self):
++        pass

t2456-brad-5y-back/userdir-for-r3271.diff

+Index: trac/env.py
+===================================================================
+--- trac/env.py	(revision 3271)
++++ trac/env.py	(working copy)
+@@ -21,6 +21,7 @@
+ from trac.core import Component, ComponentManager, implements, Interface, \
+                       ExtensionPoint, TracError
+ from trac.db import DatabaseManager
++from trac.userdir import *
+ from trac.versioncontrol import RepositoryManager
+ 
+ __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
+@@ -61,6 +62,7 @@
+      * wiki and ticket attachments.
+     """   
+     setup_participants = ExtensionPoint(IEnvironmentSetupParticipant)
++    userdirs = ExtensionPoint(IUserDirectory)
+ 
+     base_url = Option('trac', 'base_url', '',
+         """Base URL of the Trac deployment.""")
+@@ -259,30 +261,22 @@
+             logfile = os.path.join(self.get_log_dir(), logfile)
+         self.log = logger_factory(logtype, logfile, self.log_level, self.path)
+ 
+-    def get_known_users(self, cnx=None):
+-        """Generator that yields information about all known users, i.e. users
+-        that have logged in to this Trac environment and possibly set their name
+-        and email.
++    def get_known_user_info(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate user_info from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username, name, email in userdir.get_known_user_info(limit):
++                yield username, name, email
+ 
+-        This function generates one tuple for every user, of the form
+-        (username, name, email) ordered alpha-numerically by username.
++    def get_known_users(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate users from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username in userdir.get_known_users(limit):
++                yield username
+ 
+-        @param cnx: the database connection; if ommitted, a new connection is
+-                    retrieved
+-        """
+-        if not cnx:
+-            cnx = self.get_db_cnx()
+-        cursor = cnx.cursor()
+-        cursor.execute("SELECT DISTINCT s.sid, n.value, e.value "
+-                       "FROM session AS s "
+-                       " LEFT JOIN session_attribute AS n ON (n.sid=s.sid "
+-                       "  and n.authenticated=1 AND n.name = 'name') "
+-                       " LEFT JOIN session_attribute AS e ON (e.sid=s.sid "
+-                       "  AND e.authenticated=1 AND e.name = 'email') "
+-                       "WHERE s.authenticated=1 ORDER BY s.sid")
+-        for username,name,email in cursor:
+-            yield username, name, email
+-
+     def backup(self, dest=None):
+         """Simple SQLite-specific backup of the database.
+ 
+Index: trac/ticket/api.py
+===================================================================
+--- trac/ticket/api.py	(revision 3271)
++++ trac/ticket/api.py	(working copy)
+@@ -100,7 +100,7 @@
+         if self.restrict_owner:
+             field['type'] = 'select'
+             users = []
+-            for username, name, email in self.env.get_known_users(db):
++            for username in self.env.get_known_users():
+                 users.append(username)
+             field['options'] = users
+             field['optional'] = True
+Index: trac/versioncontrol/web_ui/log.py
+===================================================================
+--- trac/versioncontrol/web_ui/log.py	(revision 3271)
++++ trac/versioncontrol/web_ui/log.py	(working copy)
+@@ -163,7 +163,7 @@
+         if format == 'rss':
+             # Get the email addresses of all known users
+             email_map = {}
+-            for username,name,email in self.env.get_known_users():
++            for username,name,email in self.env.get_known_user_info():
+                 if email:
+                     email_map[username] = email
+             for cs in changes.values():
+Index: trac/Timeline.py
+===================================================================
+--- trac/Timeline.py	(revision 3271)
++++ trac/Timeline.py	(working copy)
+@@ -154,7 +154,7 @@
+ 
+         # Get the email addresses of all known users
+         email_map = {}
+-        for username, name, email in self.env.get_known_users():
++        for username, name, email in self.env.get_known_user_info():
+             if email:
+                 email_map[username] = email
+ 
+Index: trac/tests/env.py
+===================================================================
+--- trac/tests/env.py	(revision 3271)
++++ trac/tests/env.py	(working copy)
+@@ -23,8 +23,8 @@
+         """Testing env.get_version"""
+         assert self.env.get_version() == db_default.db_version
+ 
+-    def test_get_known_users(self):
+-        """Testing env.get_known_users"""
++    def test_get_known_user_info(self):
++        """Testing env.get_known_user_info"""
+         cursor = self.db.cursor()
+         cursor.executemany("INSERT INTO session VALUES (%s,%s,0)",
+                            [('123', 0),('tom', 1), ('joe', 1), ('jane', 1)])
+@@ -35,7 +35,7 @@
+                             ('joe', 1, 'email', 'joe@example.com'),
+                             ('jane', 1, 'name', 'Jane')])
+         users = {}
+-        for username,name,email in self.env.get_known_users(self.db):
++        for username,name,email in self.env.get_known_user_info():
+             users[username] = (name, email)
+ 
+         assert not users.has_key('anonymous')
+Index: trac/userdir.py
+===================================================================
+--- trac/userdir.py	(revision 0)
++++ trac/userdir.py	(revision 0)
+@@ -0,0 +1,125 @@
++# -*- coding: iso8859-1 -*-
++#
++# Copyright (C) 2003-2005 Edgewall Software
++# Copyright (C) 2005 Brad Anderson <brad@dsource.org>
++# All rights reserved.
++#
++# This software is licensed as described in the file COPYING, which
++# you should have received as part of this distribution. The terms
++# are also available at http://trac.edgewall.com/license.html.
++#
++# This software consists of voluntary contributions made by many
++# individuals. For the exact contribution history, see the revision
++# history and logs, available at http://projects.edgewall.com/trac/.
++#
++# Author: Brad Anderson <brad@dsource.org>
++
++from trac.core import Component, implements, Interface, ExtensionPoint, TracError
++
++class IUserDirectory(Interface):
++    def get_known_user_info(self, limit=None):
++        """Generator that yields information about known users.
++        
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return (username, name, email)
++            if plugin does not support name or email, return None in their place
++        """
++        pass
++
++    def get_known_users(self, limit=None):
++        """
++        Generator that yields a list of known users.
++
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return username
++        """
++        pass
++    
++    def get_user_attribute(self, user, attr):
++        """Provides values of one or more attributes of a user.
++
++        Raises UnknownUserError if the requested user doesn't exist.
++
++        Raises KeyError if the specified attribute isn't supported.
++
++        @param user: a username
++        @param attr: the name of the attribute to return. Can also be a tuple of
++                    attribute names.
++        @return:     the value of a user attribute, or, if attr is a tuple, a
++                    dictionary of attributes like get_known_users generates
++        """
++        pass
++
++    def get_supported_attributes(self):
++        """
++        @return: tuple of supported attribute names. Always includes 'username'.
++        """
++        pass
++
++
++class DefaultUserDirectory(Component):
++    implements (IUserDirectory)
++    
++    # This plugin basically duplicates the existing functionality in Trac, providing a
++    # list of users based on the 'session' table.  
++    
++    # IUserDirectory methods
++    
++    def get_known_user_info(self, limit=None):
++        """Generator that yields information about all known users, i.e. users
++        that have logged in to this Trac environment and possibly set their name
++        and email.
++
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx: the database connection
++        @param attrs: attributes of the user to return
++        @param limit: limit the number of users returned
++
++        @return (username, name, email)
++        """
++        cnx = self.env.get_db_cnx()
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT s.sid, n.value, e.value "
++                       "FROM session AS s "
++                       " LEFT JOIN session_attribute AS n ON (n.sid=s.sid "
++                       "  and n.authenticated=1 AND n.name = 'name') "
++                       " LEFT JOIN session_attribute AS e ON (e.sid=s.sid "
++                       "  AND e.authenticated=1 AND e.name = 'email') "
++                       "WHERE s.authenticated=1 ORDER BY s.sid")
++        iter = 0
++        for username,name,email in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username,name,email
++    
++    def get_known_users(self, limit=None):
++        cnx = self.env.get_db_cnx()
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT sid "
++                       "FROM session "
++                       "WHERE authenticated=1 "
++                       "ORDER BY sid")
++        iter = 0
++        for username in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username[0]
++            
++    def get_user_attribute(self, user, attr):
++        pass
++    
++    def get_supported_attributes(self):
++        pass

t2456-brad-5y-back/userdir-for-r4673-0.10-stable.diff

+Index: trac/env.py
+===================================================================
+--- trac/env.py	(revision 4673)
++++ trac/env.py	(working copy)
+@@ -21,6 +21,7 @@
+ from trac.core import Component, ComponentManager, implements, Interface, \
+                       ExtensionPoint, TracError
+ from trac.db import DatabaseManager
++from trac.userdir import *
+ from trac.versioncontrol import RepositoryManager
+ 
+ __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
+@@ -61,6 +62,7 @@
+      * wiki and ticket attachments.
+     """   
+     setup_participants = ExtensionPoint(IEnvironmentSetupParticipant)
++    userdirs = ExtensionPoint(IUserDirectory)
+ 
+     base_url = Option('trac', 'base_url', '',
+         """Base URL of the Trac deployment.
+@@ -296,30 +298,22 @@
+         self.log = logger_factory(logtype, logfile, self.log_level, self.path,
+                                   format=format)
+ 
+-    def get_known_users(self, cnx=None):
+-        """Generator that yields information about all known users, i.e. users
+-        that have logged in to this Trac environment and possibly set their name
+-        and email.
++    def get_known_user_info(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate user_info from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username, name, email in userdir.get_known_user_info(limit):
++                yield username, name, email
+ 
+-        This function generates one tuple for every user, of the form
+-        (username, name, email) ordered alpha-numerically by username.
++    def get_known_users(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate users from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username in userdir.get_known_users(limit):
++                yield username
+ 
+-        @param cnx: the database connection; if ommitted, a new connection is
+-                    retrieved
+-        """
+-        if not cnx:
+-            cnx = self.get_db_cnx()
+-        cursor = cnx.cursor()
+-        cursor.execute("SELECT DISTINCT s.sid, n.value, e.value "
+-                       "FROM session AS s "
+-                       " LEFT JOIN session_attribute AS n ON (n.sid=s.sid "
+-                       "  and n.authenticated=1 AND n.name = 'name') "
+-                       " LEFT JOIN session_attribute AS e ON (e.sid=s.sid "
+-                       "  AND e.authenticated=1 AND e.name = 'email') "
+-                       "WHERE s.authenticated=1 ORDER BY s.sid")
+-        for username,name,email in cursor:
+-            yield username, name, email
+-
+     def backup(self, dest=None):
+         """Simple SQLite-specific backup of the database.
+ 
+Index: trac/ticket/api.py
+===================================================================
+--- trac/ticket/api.py	(revision 4673)
++++ trac/ticket/api.py	(working copy)
+@@ -100,8 +100,9 @@
+         if self.restrict_owner:
+             field['type'] = 'select'
+             users = []
++
+             perm = PermissionSystem(self.env)
+-            for username, name, email in self.env.get_known_users(db):
++            for username in self.env.get_known_users():
+                 if perm.get_user_permissions(username).get('TICKET_MODIFY'):
+                     users.append(username)
+             field['options'] = users
+Index: trac/versioncontrol/web_ui/log.py
+===================================================================
+--- trac/versioncontrol/web_ui/log.py	(revision 4673)
++++ trac/versioncontrol/web_ui/log.py	(working copy)
+@@ -164,7 +164,7 @@
+         if format == 'rss':
+             # Get the email addresses of all known users
+             email_map = {}
+-            for username,name,email in self.env.get_known_users():
++            for username,name,email in self.env.get_known_user_info():
+                 if email:
+                     email_map[username] = email
+             for cs in changes.values():
+Index: trac/Timeline.py
+===================================================================
+--- trac/Timeline.py	(revision 4673)
++++ trac/Timeline.py	(working copy)
+@@ -155,7 +155,7 @@
+ 
+         # Get the email addresses of all known users
+         email_map = {}
+-        for username, name, email in self.env.get_known_users():
++        for username, name, email in self.env.get_known_user_info():
+             if email:
+                 email_map[username] = email
+ 
+Index: trac/tests/env.py
+===================================================================
+--- trac/tests/env.py	(revision 4673)
++++ trac/tests/env.py	(working copy)
+@@ -23,8 +23,8 @@
+         """Testing env.get_version"""
+         assert self.env.get_version() == db_default.db_version
+ 
+-    def test_get_known_users(self):
+-        """Testing env.get_known_users"""
++    def test_get_known_user_info(self):
++        """Testing env.get_known_user_info"""
+         cursor = self.db.cursor()
+         cursor.executemany("INSERT INTO session VALUES (%s,%s,0)",
+                            [('123', 0),('tom', 1), ('joe', 1), ('jane', 1)])
+@@ -35,7 +35,7 @@
+                             ('joe', 1, 'email', 'joe@example.com'),
+                             ('jane', 1, 'name', 'Jane')])
+         users = {}
+-        for username,name,email in self.env.get_known_users(self.db):
++        for username,name,email in self.env.get_known_user_info():
+             users[username] = (name, email)
+ 
+         assert not users.has_key('anonymous')

t2456-brad-5y-back/userdir-for-r4673-0.10-stable2.diff

+Index: trac/env.py
+===================================================================
+--- trac/env.py	(revision 4673)
++++ trac/env.py	(working copy)
+@@ -21,6 +21,7 @@
+ from trac.core import Component, ComponentManager, implements, Interface, \
+                       ExtensionPoint, TracError
+ from trac.db import DatabaseManager
++from trac.userdir import *
+ from trac.versioncontrol import RepositoryManager
+ 
+ __all__ = ['Environment', 'IEnvironmentSetupParticipant', 'open_environment']
+@@ -61,6 +62,7 @@
+      * wiki and ticket attachments.
+     """   
+     setup_participants = ExtensionPoint(IEnvironmentSetupParticipant)
++    userdirs = ExtensionPoint(IUserDirectory)
+ 
+     base_url = Option('trac', 'base_url', '',
+         """Base URL of the Trac deployment.
+@@ -296,30 +298,22 @@
+         self.log = logger_factory(logtype, logfile, self.log_level, self.path,
+                                   format=format)
+ 
+-    def get_known_users(self, cnx=None):
+-        """Generator that yields information about all known users, i.e. users
+-        that have logged in to this Trac environment and possibly set their name
+-        and email.
++    def get_known_user_info(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate user_info from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username, name, email in userdir.get_known_user_info(limit):
++                yield username, name, email
+ 
+-        This function generates one tuple for every user, of the form
+-        (username, name, email) ordered alpha-numerically by username.
++    def get_known_users(self, limit=None):
++        cnx = self.get_db_cnx()
++        
++        # should accumulate users from all IUserDirectory providers - desirable?
++        for userdir in self.userdirs:
++            for username in userdir.get_known_users(limit):
++                yield username
+ 
+-        @param cnx: the database connection; if ommitted, a new connection is
+-                    retrieved
+-        """
+-        if not cnx:
+-            cnx = self.get_db_cnx()
+-        cursor = cnx.cursor()
+-        cursor.execute("SELECT DISTINCT s.sid, n.value, e.value "
+-                       "FROM session AS s "
+-                       " LEFT JOIN session_attribute AS n ON (n.sid=s.sid "
+-                       "  and n.authenticated=1 AND n.name = 'name') "
+-                       " LEFT JOIN session_attribute AS e ON (e.sid=s.sid "
+-                       "  AND e.authenticated=1 AND e.name = 'email') "
+-                       "WHERE s.authenticated=1 ORDER BY s.sid")
+-        for username,name,email in cursor:
+-            yield username, name, email
+-
+     def backup(self, dest=None):
+         """Simple SQLite-specific backup of the database.
+ 
+Index: trac/ticket/api.py
+===================================================================
+--- trac/ticket/api.py	(revision 4673)
++++ trac/ticket/api.py	(working copy)
+@@ -100,8 +100,9 @@
+         if self.restrict_owner:
+             field['type'] = 'select'
+             users = []
++
+             perm = PermissionSystem(self.env)
+-            for username, name, email in self.env.get_known_users(db):
++            for username in self.env.get_known_users():
+                 if perm.get_user_permissions(username).get('TICKET_MODIFY'):
+                     users.append(username)
+             field['options'] = users
+Index: trac/versioncontrol/web_ui/log.py
+===================================================================
+--- trac/versioncontrol/web_ui/log.py	(revision 4673)
++++ trac/versioncontrol/web_ui/log.py	(working copy)
+@@ -164,7 +164,7 @@
+         if format == 'rss':
+             # Get the email addresses of all known users
+             email_map = {}
+-            for username,name,email in self.env.get_known_users():
++            for username,name,email in self.env.get_known_user_info():
+                 if email:
+                     email_map[username] = email
+             for cs in changes.values():
+Index: trac/Timeline.py
+===================================================================
+--- trac/Timeline.py	(revision 4673)
++++ trac/Timeline.py	(working copy)
+@@ -155,7 +155,7 @@
+ 
+         # Get the email addresses of all known users
+         email_map = {}
+-        for username, name, email in self.env.get_known_users():
++        for username, name, email in self.env.get_known_user_info():
+             if email:
+                 email_map[username] = email
+ 
+Index: trac/tests/env.py
+===================================================================
+--- trac/tests/env.py	(revision 4673)
++++ trac/tests/env.py	(working copy)
+@@ -23,8 +23,8 @@
+         """Testing env.get_version"""
+         assert self.env.get_version() == db_default.db_version
+ 
+-    def test_get_known_users(self):
+-        """Testing env.get_known_users"""
++    def test_get_known_user_info(self):
++        """Testing env.get_known_user_info"""
+         cursor = self.db.cursor()
+         cursor.executemany("INSERT INTO session VALUES (%s,%s,0)",
+                            [('123', 0),('tom', 1), ('joe', 1), ('jane', 1)])
+@@ -35,7 +35,7 @@
+                             ('joe', 1, 'email', 'joe@example.com'),
+                             ('jane', 1, 'name', 'Jane')])
+         users = {}
+-        for username,name,email in self.env.get_known_users(self.db):
++        for username,name,email in self.env.get_known_user_info():
+             users[username] = (name, email)
+ 
+         assert not users.has_key('anonymous')
+Index: trac/notification.py
+===================================================================
+--- trac/notification.py        (revision 4673)
++++ trac/notification.py        (working copy)
+@@ -163,7 +163,7 @@
+         self._init_pref_encoding()
+         # Get the email addresses of all known users
+         self.email_map = {}
+-        for username, name, email in self.env.get_known_users(self.db):
++        for username, name, email in self.env.get_known_user_info():
+             if email:
+                 self.email_map[username] = email
+
+Index: trac/userdir.py
+===================================================================
+--- trac/userdir.py	(revision 0)
++++ trac/userdir.py	(revision 0)
+@@ -0,0 +1,125 @@
++# -*- coding: iso8859-1 -*-
++#
++# Copyright (C) 2003-2005 Edgewall Software
++# Copyright (C) 2005 Brad Anderson <brad@dsource.org>
++# All rights reserved.
++#
++# This software is licensed as described in the file COPYING, which
++# you should have received as part of this distribution. The terms
++# are also available at http://trac.edgewall.com/license.html.
++#
++# This software consists of voluntary contributions made by many
++# individuals. For the exact contribution history, see the revision
++# history and logs, available at http://projects.edgewall.com/trac/.
++#
++# Author: Brad Anderson <brad@dsource.org>
++
++from trac.core import Component, implements, Interface, ExtensionPoint, TracError
++
++class IUserDirectory(Interface):
++    def get_known_user_info(self, limit=None):
++        """Generator that yields information about known users.
++        
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return (username, name, email)
++            if plugin does not support name or email, return None in their place
++        """
++        pass
++
++    def get_known_users(self, limit=None):
++        """
++        Generator that yields a list of known users.
++
++        @param cnx:   db connection object
++        @param limit: maximum number of results to generate. None means no limit.
++        
++        @return username
++        """
++        pass
++    
++    def get_user_attribute(self, user, attr):
++        """Provides values of one or more attributes of a user.
++
++        Raises UnknownUserError if the requested user doesn't exist.
++
++        Raises KeyError if the specified attribute isn't supported.
++
++        @param user: a username
++        @param attr: the name of the attribute to return. Can also be a tuple of
++                    attribute names.
++        @return:     the value of a user attribute, or, if attr is a tuple, a
++                    dictionary of attributes like get_known_users generates
++        """
++        pass
++
++    def get_supported_attributes(self):
++        """
++        @return: tuple of supported attribute names. Always includes 'username'.
++        """
++        pass
++
++
++class DefaultUserDirectory(Component):
++    implements (IUserDirectory)
++    
++    # This plugin basically duplicates the existing functionality in Trac, providing a
++    # list of users based on the 'session' table.  
++    
++    # IUserDirectory methods
++    
++    def get_known_user_info(self, limit=None):
++        """Generator that yields information about all known users, i.e. users
++        that have logged in to this Trac environment and possibly set their name
++        and email.
++
++        This function generates one tuple for every user, of the form
++        (username, name, email) ordered alpha-numerically by username.
++        
++        @param cnx: the database connection
++        @param attrs: attributes of the user to return
++        @param limit: limit the number of users returned
++
++        @return (username, name, email)
++        """
++        cnx = self.env.get_db_cnx()
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT s.sid, n.value, e.value "
++                       "FROM session AS s "
++                       " LEFT JOIN session_attribute AS n ON (n.sid=s.sid "
++                       "  and n.authenticated=1 AND n.name = 'name') "
++                       " LEFT JOIN session_attribute AS e ON (e.sid=s.sid "
++                       "  AND e.authenticated=1 AND e.name = 'email') "
++                       "WHERE s.authenticated=1 ORDER BY s.sid")
++        iter = 0
++        for username,name,email in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username,name,email
++    
++    def get_known_users(self, limit=None):
++        cnx = self.env.get_db_cnx()
++        cursor = cnx.cursor()
++        cursor.execute("SELECT DISTINCT sid "
++                       "FROM session "
++                       "WHERE authenticated=1 "
++                       "ORDER BY sid")
++        iter = 0
++        for username in cursor:
++            if limit:
++                iter += 1
++                if iter > limit:
++                    break
++            yield username[0]
++            
++    def get_user_attribute(self, user, attr):
++        pass
++    
++    def get_supported_attributes(self):
++        pass