Ticket #6470: admin-urlpatterns-6470.diff

File admin-urlpatterns-6470.diff, 19.5 KB (added by Mnewman, 7 years ago)

An update to the patch to apply cleanly to r8129

  • django/core/urlresolvers.py

     
    211211        # urlconf_name is a string representing the module containing urlconfs.
    212212        self.regex = re.compile(regex, re.UNICODE)
    213213        self.urlconf_name = urlconf_name
     214        if not isinstance(self.urlconf_name, basestring):
     215            self._urlconf_module = self.urlconf_name
    214216        self.callback = None
    215217        self.default_kwargs = default_kwargs or {}
    216218        self._reverse_dict = {}
  • django/contrib/admin/options.py

     
    44from django.forms.models import BaseInlineFormset
    55from django.contrib.contenttypes.models import ContentType
    66from django.contrib.admin import widgets
    7 from django.contrib.admin.util import quote, unquote, get_deleted_objects
     7from django.contrib.admin.util import quote, unquote, get_deleted_objects, admin_perm_test
    88from django.core.exceptions import ImproperlyConfigured, PermissionDenied
    99from django.db import models, transaction
    1010from django.http import Http404, HttpResponse, HttpResponseRedirect
     
    1515from django.utils.translation import ugettext as _
    1616from django.utils.encoding import force_unicode
    1717import sets
     18import types
    1819
    1920HORIZONTAL, VERTICAL = 1, 2
    2021# returns the <ul> class for a given radio_admin field
     
    237238                raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.")
    238239            if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
    239240                raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
     241   
     242    def _get_urls(self):
     243        from django.conf.urls.defaults import patterns, url
     244        urls_module = types.ModuleType('%s.urls' % self.__class__.__name__)
     245        info = self.model._meta.app_label, self.model._meta.module_name
     246        urlpatterns = patterns('',
     247            url(r'^$', self.changelist_view, name='admin_%s_%s_changelist' % info),
     248            url(r'^add/$', self.add_view, name='admin_%s_%s_add' % info),
     249            url(r'^(.+)/history/$', self.history_view, name='admin_%s_%s_history' % info),
     250            url(r'^(.+)/delete/$', self.delete_view, name='admin_%s_%s_delete' % info),
     251            url(r'^(.+)/$', self.change_view, name='admin_%s_%s_change' % info),
     252        )
     253        urls_module.urlpatterns = urlpatterns
     254        return urls_module
     255    urls = property(_get_urls)
    240256
    241         # Delegate to the appropriate method, based on the URL.
    242         if url is None:
    243             return self.changelist_view(request)
    244         elif url.endswith('add'):
    245             return self.add_view(request)
    246         elif url.endswith('history'):
    247             return self.history_view(request, unquote(url[:-8]))
    248         elif url.endswith('delete'):
    249             return self.delete_view(request, unquote(url[:-7]))
    250         else:
    251             return self.change_view(request, unquote(url))
    252 
    253257    def _media(self):
    254258        from django.conf import settings
    255259
     
    450454            'content_type_id': ContentType.objects.get_for_model(self.model).id,
    451455            'save_as': self.save_as,
    452456            'save_on_top': self.save_on_top,
    453             'root_path': self.admin_site.root_path,
    454457        })
    455458        return render_to_response(self.change_form_template or [
    456459            "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
    457460            "admin/%s/change_form.html" % app_label,
    458461            "admin/change_form.html"
    459462        ], context, context_instance=template.RequestContext(request))
     463   
    460464
    461465    def add_view(self, request, form_url='', extra_context=None):
    462466        "The 'add' admin view for this model."
     
    510514            'media': mark_safe(media),
    511515            'inline_admin_formsets': inline_admin_formsets,
    512516            'errors': AdminErrorList(form, inline_formsets),
    513             'root_path': self.admin_site.root_path,
    514517        }
    515518        context.update(extra_context or {})
    516519        return self.render_change_form(request, context, add=True)
    517 
     520    add_view = admin_perm_test(add_view)
     521   
    518522    def change_view(self, request, object_id, extra_context=None):
    519523        "The 'change' admin view for this model."
    520524        model = self.model
     
    522526        app_label = opts.app_label
    523527
    524528        try:
    525             obj = model._default_manager.get(pk=object_id)
     529            obj = model._default_manager.get(pk=unquote(object_id))
    526530        except model.DoesNotExist:
    527531            # Don't raise Http404 just yet, because we haven't checked
    528532            # permissions yet. We don't want an unauthenticated user to be able
     
    573577            'media': mark_safe(media),
    574578            'inline_admin_formsets': inline_admin_formsets,
    575579            'errors': AdminErrorList(form, inline_formsets),
    576             'root_path': self.admin_site.root_path,
    577580        }
    578581        context.update(extra_context or {})
    579582        return self.render_change_form(request, context, change=True, obj=obj)
     583    change_view = admin_perm_test(change_view)
    580584
    581585    def changelist_view(self, request, extra_context=None):
    582586        "The 'change list' admin view for this model."
     
    603607            'is_popup': cl.is_popup,
    604608            'cl': cl,
    605609            'has_add_permission': self.has_add_permission(request),
    606             'root_path': self.admin_site.root_path,
    607610        }
    608611        context.update(extra_context or {})
    609612        return render_to_response(self.change_list_template or [
     
    611614            'admin/%s/change_list.html' % app_label,
    612615            'admin/change_list.html'
    613616        ], context, context_instance=template.RequestContext(request))
     617    changelist_view = admin_perm_test(changelist_view)
    614618
    615619    def delete_view(self, request, object_id, extra_context=None):
    616620        "The 'delete' admin view for this model."
     
    619623        app_label = opts.app_label
    620624
    621625        try:
    622             obj = self.model._default_manager.get(pk=object_id)
     626            obj = self.model._default_manager.get(pk=unquote(object_id))
    623627        except self.model.DoesNotExist:
    624628            # Don't raise Http404 just yet, because we haven't checked
    625629            # permissions yet. We don't want an unauthenticated user to be able
     
    634638
    635639        # Populate deleted_objects, a data structure of all related objects that
    636640        # will also be deleted.
    637         deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), quote(object_id), escape(obj))), []]
     641        deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), object_id, escape(obj))), []]
    638642        perms_needed = sets.Set()
    639643        get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site)
    640644
     
    656660            "deleted_objects": deleted_objects,
    657661            "perms_lacking": perms_needed,
    658662            "opts": opts,
    659             "root_path": self.admin_site.root_path,
    660663        }
    661664        context.update(extra_context or {})
    662665        return render_to_response(self.delete_confirmation_template or [
     
    664667            "admin/%s/delete_confirmation.html" % app_label,
    665668            "admin/delete_confirmation.html"
    666669        ], context, context_instance=template.RequestContext(request))
     670    delete_view = admin_perm_test(delete_view)
    667671
    668672    def history_view(self, request, object_id, extra_context=None):
    669673        "The 'history' admin view for this model."
     
    681685            'action_list': action_list,
    682686            'module_name': capfirst(opts.verbose_name_plural),
    683687            'object': obj,
    684             'root_path': self.admin_site.root_path,
    685688        }
    686689        context.update(extra_context or {})
    687690        return render_to_response(self.object_history_template or [
     
    689692            "admin/%s/object_history.html" % opts.app_label,
    690693            "admin/object_history.html"
    691694        ], context, context_instance=template.RequestContext(request))
     695    history_view = admin_perm_test(history_view)
    692696
    693697class InlineModelAdmin(BaseModelAdmin):
    694698    """
  • django/contrib/admin/util.py

     
    66from django.utils.encoding import force_unicode
    77from django.utils.translation import ugettext as _
    88
    9 
    109def quote(s):
    1110    """
    1211    Ensure that primary key values do not confuse the admin URLs by escaping
     
    137136            p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
    138137            if not user.has_perm(p):
    139138                perms_needed.add(related.opts.verbose_name)
     139
     140def admin_perm_test(func):
     141    def inner(admin_site_or_modeladmin, request, *args, **kwargs):
     142        if hasattr(admin_site_or_modeladmin, 'has_permission'):
     143            admin_site = admin_site_or_modeladmin
     144        else:
     145            admin_site = admin_site_or_modeladmin.admin_site
     146        if not admin_site.has_permission(request):
     147            return admin_site.login(request)
     148        # User has right permisssions show the view
     149        return func(admin_site_or_modeladmin, request, *args, **kwargs)
     150    return inner
  • django/contrib/admin/sites.py

     
    11from django import http, template
    22from django.contrib.admin import ModelAdmin
     3from django.contrib.admin.util import admin_perm_test
    34from django.contrib.auth import authenticate, login
    45from django.core.exceptions import ImproperlyConfigured
    56from django.db.models.base import ModelBase
     
    1314import cPickle as pickle
    1415import datetime
    1516import md5
     17import types
    1618import re
    1719
    1820ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
    19 LOGIN_FORM_KEY = 'this_is_the_login_form'
    2021
    2122USER_CHANGE_PASSWORD_URL_RE = re.compile('auth/user/(\d+)/password')
    2223
     24LOGIN_FORM_KEY = 'this_is_the_login_form'
     25
    2326class AlreadyRegistered(Exception):
    2427    pass
    2528
     
    116119        """
    117120        return request.user.is_authenticated() and request.user.is_staff
    118121
    119     def root(self, request, url):
    120         """
    121         Handles main URL routing for the admin app.
     122    def _get_urls(self):
     123        from django.conf.urls.defaults import patterns, url, include
     124        from django.core.urlresolvers import RegexURLResolver
     125        urls_module = types.ModuleType('%s.urls' % self.__class__.__name__)
     126        urlpatterns = patterns('',
     127            url(r'^$', self.index, name='admin_index'),
     128            url(r'^logout/$', self.logout, name='admin_logout'),
     129            url(r'^password_change/$', self.password_change, name='admin_password_change'),
     130            url(r'^password_change_done/$', self.password_change_done, name='admin_password_change_done'),
     131            url(r'^jsi18n/$', self.i18n_javascript, name='admin_jsi18n'),
     132            url(r'^auth/user/(\d+)/password/$', self.user_change_password, name='admin_change_password'),
     133            url('^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', 'django.views.defaults.shortcut')
     134        )
     135        for model, model_admin in self._registry.iteritems():
     136            urlpatterns += patterns('',
     137                url('^%s/%s/' % (model._meta.app_label, model._meta.module_name), include(model_admin.urls))
     138            )
     139        urls_module.urlpatterns = urlpatterns
     140        return urls_module
     141    urls = property(_get_urls)
    122142
    123         `url` is the remainder of the URL -- e.g. 'comments/comment/'.
    124         """
    125         if request.method == 'GET' and not request.path.endswith('/'):
    126             return http.HttpResponseRedirect(request.path + '/')
    127        
    128         # Figure out the admin base URL path and stash it for later use
    129         self.root_path = re.sub(re.escape(url) + '$', '', request.path)
    130        
    131         url = url.rstrip('/') # Trim trailing slash, if it exists.
    132 
    133         # The 'logout' view doesn't require that the person is logged in.
    134         if url == 'logout':
    135             return self.logout(request)
    136        
    137         # Check permission to continue or display login form.
    138         if not self.has_permission(request):
    139             return self.login(request)
    140 
    141         if url == '':
    142             return self.index(request)
    143         elif url == 'password_change':
    144             return self.password_change(request)
    145         elif url == 'password_change/done':
    146             return self.password_change_done(request)
    147         elif url == 'jsi18n':
    148             return self.i18n_javascript(request)
    149         # urls starting with 'r/' are for the "show in web" links
    150         elif url.startswith('r/'):
    151             from django.views.defaults import shortcut
    152             return shortcut(request, *url.split('/')[1:])
    153         else:
    154             match = USER_CHANGE_PASSWORD_URL_RE.match(url)
    155             if match:
    156                 return self.user_change_password(request, match.group(1))
    157                
    158             if '/' in url:
    159                 return self.model_page(request, *url.split('/', 2))
    160 
    161         raise http.Http404('The requested admin page does not exist.')
    162 
    163     def model_page(self, request, app_label, model_name, rest_of_url=None):
    164         """
    165         Handles the model-specific functionality of the admin site, delegating
    166         to the appropriate ModelAdmin class.
    167         """
    168         from django.db import models
    169         model = models.get_model(app_label, model_name)
    170         if model is None:
    171             raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
    172         try:
    173             admin_obj = self._registry[model]
    174         except KeyError:
    175             raise http.Http404("This model exists but has not been registered with the admin site.")
    176         return admin_obj(request, rest_of_url)
    177     model_page = never_cache(model_page)
    178 
    179143    def password_change(self, request):
    180144        """
    181145        Handles the "change password" task -- both form display and validation.
    182146        """
    183147        from django.contrib.auth.views import password_change
    184148        return password_change(request)
     149    passoword_change = admin_perm_test(password_change)
    185150
    186151    def password_change_done(self, request):
    187152        """
     
    189154        """
    190155        from django.contrib.auth.views import password_change_done
    191156        return password_change_done(request)
     157    password_change_done = admin_perm_test(password_change_done)
    192158
    193159    def user_change_password(self, request, id):
    194160        """
     
    196162        """
    197163        from django.contrib.auth.views import user_change_password
    198164        return user_change_password(request, id)
     165    user_change_password = admin_perm_test(user_change_password)
    199166
    200167    def i18n_javascript(self, request):
    201168        """
     
    210177        else:
    211178            from django.views.i18n import null_javascript_catalog as javascript_catalog
    212179        return javascript_catalog(request, packages='django.conf')
     180    i18n_javascript = admin_perm_test(i18n_javascript)
    213181
    214182    def logout(self, request):
    215183        """
     
    226194        Displays the login form for the given HttpRequest.
    227195        """
    228196        from django.contrib.auth.models import User
    229 
    230197        # If this isn't already the login page, display it.
    231198        if not request.POST.has_key(LOGIN_FORM_KEY):
    232199            if request.POST:
     
    266233                login(request, user)
    267234                if request.POST.has_key('post_data'):
    268235                    post_data = _decode_post_data(request.POST['post_data'])
     236                    request.session.delete_test_cookie()
    269237                    if post_data and not post_data.has_key(LOGIN_FORM_KEY):
    270238                        # overwrite request.POST with the saved post_data, and continue
    271239                        request.POST = post_data
    272240                        request.user = user
    273                         return self.root(request, request.path.split(self.root_path)[-1])
     241                        from django.core.urlresolvers import resolve
     242                        ret_view, vargs, vkwargs = resolve(request.path)
     243                        return ret_view(request, *vargs, **vkwargs)
    274244                    else:
    275                         request.session.delete_test_cookie()
    276245                        return http.HttpResponseRedirect(request.path)
    277246            else:
    278247                return self.display_login_form(request, ERROR_MESSAGE)
     
    324293        context = {
    325294            'title': _('Site administration'),
    326295            'app_list': app_list,
    327             'root_path': self.root_path,
    328296        }
    329297        context.update(extra_context or {})
    330298        return render_to_response(self.index_template or 'admin/index.html', context,
    331299            context_instance=template.RequestContext(request)
    332300        )
    333     index = never_cache(index)
     301    index = never_cache(admin_perm_test(index))
    334302
    335303    def display_login_form(self, request, error_message='', extra_context=None):
    336304        request.session.set_test_cookie()
     
    342310            post_data = _encode_post_data(request.POST)
    343311        else:
    344312            post_data = _encode_post_data({})
    345        
     313
    346314        context = {
    347315            'title': _('Log in'),
    348316            'app_path': request.path,
    349317            'post_data': post_data,
    350318            'error_message': error_message,
    351             'root_path': self.root_path,
    352319        }
    353320        context.update(extra_context or {})
    354321        return render_to_response(self.login_template or 'admin/login.html', context,
    355322            context_instance=template.RequestContext(request)
    356323        )
    357324
    358 
    359325# This global object represents the default admin site, for the common case.
    360326# You can instantiate AdminSite in your own code to create a custom admin site.
    361327site = AdminSite()
  • django/contrib/auth/admin.py

     
    5858            'opts': User._meta,
    5959            'save_as': False,
    6060            'username_help_text': User._meta.get_field('username').help_text,
    61             'root_path': self.admin_site.root_path,
    6261        }, context_instance=template.RequestContext(request))
    6362
    6463admin.site.register(Group, GroupAdmin)
  • tests/regressiontests/admin_views/tests.py

     
    8686            '/test_admin/admin/admin_views/article/add'
    8787        )
    8888        self.assertRedirects(request,
    89             '/test_admin/admin/admin_views/article/add/'
     89            '/test_admin/admin/admin_views/article/add/', status_code=301
    9090        )
    9191
    9292    def testLogin(self):
  • tests/regressiontests/admin_views/urls.py

     
    33
    44urlpatterns = patterns('',
    55    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    6     (r'^admin/(.*)', admin.site.root),
     6    (r'^admin/', include(admin.site.urls)),
    77)
Back to Top