Index: sites.py
===================================================================
--- sites.py	(revision 7739)
+++ sites.py	(working copy)
@@ -39,8 +39,39 @@
         raise SuspiciousOperation, "User may have tampered with session cookie."
     return pickle.loads(pickled)
 
-class AdminSite(object):
+class instance_decorator(object):
+    '''
+    Applies one or more decorators to the given instance method.
+
+    Normal decorators do not work well within class definitions,
+    because the decorated function will be passed the `self`
+    parameter. This descriptor avoids that problem by decorating the
+    function after it has been bound as an instance method.
+    '''
+    def __init__(self, *decorators):
+        self._decorators = decorators
+
+    def __get__(self, instance, owner):
+        f = new.instancemethod(self.f, instance, owner)
+        for dec in self._decorators:
+            f = dec(f)
+        return f
+
+    def __call__(self, f):
+        self.f = f
+        return self
+
+
+def _has_permission(request):
     """
+    Returns True if the given HttpRequest has permission to view
+    *at least one* page in the admin site.
+    """
+    return request.user.is_authenticated() and request.user.is_staff
+
+    
+class AdminSite(BaseRegexURLResolver):
+    """
     An AdminSite object encapsulates an instance of the Django admin application, ready
     to be hooked in to your URLConf. Models are registered with the AdminSite using the
     register() method, and the root() method can then be used as a Django view function
@@ -50,7 +81,8 @@
     index_template = None
     login_template = None
     
-    def __init__(self):
+    def __init__(self, regex):
+        super(AdminSite, self).__init__(regex)
         self._registry = {} # model_class class -> admin_class instance
 
     def register(self, model_or_iterable, admin_class=None, **options):
@@ -87,58 +119,16 @@
                 raise NotRegistered('The model %s is not registered' % model.__name__)
             del self._registry[model]
 
-    def has_permission(self, request):
-        """
-        Returns True if the given HttpRequest has permission to view
-        *at least one* page in the admin site.
-        """
-        return request.user.is_authenticated() and request.user.is_staff
+    def get_url_patterns(self):
+        # FIXME: I don't know how to handle the "show in web" links
+        return [
+            url(r'^logout/$', self.logout),
+            url(r'^$', self.index),
+            url(r'^password_change/$', self.password_change),
+            url(r'^password_change/done/$', self.password_change_done),
+            url(r'jsi18n/$', self.i18n_javascript)
+            ]
 
-    def root(self, request, url):
-        """ 
-        Handles main URL routing for the admin app.
-
-        `url` is the remainder of the URL -- e.g. 'comments/comment/'.
-        """
-        # Figure out the admin base URL path and stash it for later use
-        self.root_path = re.sub(re.escape(url) + '$', '', request.path)
-        
-        url = url.rstrip('/') # Trim trailing slash, if it exists.
-
-        # The 'logout' view doesn't require that the person is logged in.
-        if url == 'logout':
-            return self.logout(request)
-
-        if not self.has_permission(request):
-            response = self.login(request)
-            if response:
-                # make sure that there is a response before returning
-                # this addresses any post data that might persist from
-                # expired sessions and continue through (#5999)
-                return response
-
-        if url == '':
-            return self.index(request)
-        elif url == 'password_change':
-            return self.password_change(request)
-        elif url == 'password_change/done':
-            return self.password_change_done(request)
-        elif url == 'jsi18n':
-            return self.i18n_javascript(request)
-        # urls starting with 'r/' are for the "show in web" links
-        elif url.startswith('r/'):
-            from django.views.defaults import shortcut
-            return shortcut(request, *url.split('/')[1:])
-        else:
-            match = USER_CHANGE_PASSWORD_URL_RE.match(url)
-            if match:
-                return self.user_change_password(request, match.group(1))
-                
-            if '/' in url:
-                return self.model_page(request, *url.split('/', 2))
-
-        raise http.Http404('The requested admin page does not exist.')
-
     def model_page(self, request, app_label, model_name, rest_of_url=None):
         """
         Handles the model-specific functionality of the admin site, delegating
@@ -161,6 +151,7 @@
         """
         from django.contrib.auth.views import password_change
         return password_change(request)
+    password_change = instance_decorator(password_change, _has_permission)
 
     def password_change_done(self, request):
         """
@@ -168,6 +159,7 @@
         """
         from django.contrib.auth.views import password_change_done
         return password_change_done(request)
+    password_change_done = instance_decorator(password_change_done, _has_permission)
 
     def user_change_password(self, request, id):
         """
