Code

Ticket #10061: admin-urls.13.diff

File admin-urls.13.diff, 14.4 KB (added by Alex, 5 years ago)

Probably just a whitespace change, but I can't remember if I've had to resolve any merge conflicts

Line 
1diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
2index 8297eca..6a7e884 100644
3--- a/django/contrib/admin/options.py
4+++ b/django/contrib/admin/options.py
5@@ -235,13 +235,13 @@ class ModelAdmin(BaseModelAdmin):
6             url(r'^add/$',
7                 wrap(self.add_view),
8                 name='%sadmin_%s_%s_add' % info),
9-            url(r'^(.+)/history/$',
10+            url(r'^(?P<object_id>.+)/history/$',
11                 wrap(self.history_view),
12                 name='%sadmin_%s_%s_history' % info),
13-            url(r'^(.+)/delete/$',
14+            url(r'^(?P<object_id>.+)/delete/$',
15                 wrap(self.delete_view),
16                 name='%sadmin_%s_%s_delete' % info),
17-            url(r'^(.+)/$',
18+            url(r'^(?P<object_id>.+)/$',
19                 wrap(self.change_view),
20                 name='%sadmin_%s_%s_change' % info),
21         )
22@@ -581,6 +581,7 @@ class ModelAdmin(BaseModelAdmin):
23             'save_as': self.save_as,
24             'save_on_top': self.save_on_top,
25             'root_path': self.admin_site.root_path,
26+            'admin_site': self.admin_site.name,
27         })
28         return render_to_response(self.change_form_template or [
29             "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
30@@ -778,6 +779,7 @@ class ModelAdmin(BaseModelAdmin):
31             'errors': helpers.AdminErrorList(form, formsets),
32             'root_path': self.admin_site.root_path,
33             'app_label': opts.app_label,
34+            'admin_site': self.admin_site.name,
35         }
36         context.update(extra_context or {})
37         return self.render_change_form(request, context, form_url=form_url, add=True)
38@@ -866,6 +868,7 @@ class ModelAdmin(BaseModelAdmin):
39             'inline_admin_formsets': inline_admin_formsets,
40             'errors': helpers.AdminErrorList(form, formsets),
41             'root_path': self.admin_site.root_path,
42+            'admin_site': self.admin_site.name,
43             'app_label': opts.app_label,
44         }
45         context.update(extra_context or {})
46@@ -971,6 +974,7 @@ class ModelAdmin(BaseModelAdmin):
47             'media': media,
48             'has_add_permission': self.has_add_permission(request),
49             'root_path': self.admin_site.root_path,
50+            'admin_site': self.admin_site.name,
51             'app_label': app_label,
52             'action_form': action_form,
53             'actions_on_top': self.actions_on_top,
54@@ -1029,6 +1033,7 @@ class ModelAdmin(BaseModelAdmin):
55             "perms_lacking": perms_needed,
56             "opts": opts,
57             "root_path": self.admin_site.root_path,
58+            'admin_site': self.admin_site.name,
59             "app_label": app_label,
60         }
61         context.update(extra_context or {})
62@@ -1056,6 +1061,7 @@ class ModelAdmin(BaseModelAdmin):
63             'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
64             'object': obj,
65             'root_path': self.admin_site.root_path,
66+            'admin_site': self.admin_site.name,
67             'app_label': app_label,
68         }
69         context.update(extra_context or {})
70diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
71index 6e9ef11..1ad86fd 100644
72--- a/django/contrib/admin/sites.py
73+++ b/django/contrib/admin/sites.py
74@@ -4,6 +4,7 @@ from django.contrib.admin import ModelAdmin
75 from django.contrib.admin import actions
76 from django.contrib.auth import authenticate, login
77 from django.db.models.base import ModelBase
78+from django.core.urlresolvers import reverse
79 from django.core.exceptions import ImproperlyConfigured
80 from django.shortcuts import render_to_response
81 from django.utils.functional import update_wrapper
82@@ -40,10 +41,7 @@ class AdminSite(object):
83 
84     def __init__(self, name=None):
85         self._registry = {} # model_class class -> admin_class instance
86-        # TODO Root path is used to calculate urls under the old root() method
87-        # in order to maintain backwards compatibility we are leaving that in
88-        # so root_path isn't needed, not sure what to do about this.
89-        self.root_path = 'admin/'
90+        self.root_path = None
91         if name is None:
92             name = ''
93         else:
94@@ -199,7 +197,7 @@ class AdminSite(object):
95                 name='%sadmin_index' % self.name),
96             url(r'^logout/$',
97                 wrap(self.logout),
98-                name='%sadmin_logout'),
99+                name='%sadmin_logout' % self.name),
100             url(r'^password_change/$',
101                 wrap(self.password_change),
102                 name='%sadmin_password_change' % self.name),
103@@ -233,8 +231,11 @@ class AdminSite(object):
104         Handles the "change password" task -- both form display and validation.
105         """
106         from django.contrib.auth.views import password_change
107-        return password_change(request,
108-            post_change_redirect='%spassword_change/done/' % self.root_path)
109+        if self.root_path is not None:
110+            url = '%spassword_change/done/' % self.root_path
111+        else:
112+            url = reverse('%sadmin_password_change_done' % self.name)
113+        return password_change(request, post_change_redirect=url)
114 
115     def password_change_done(self, request):
116         """
117@@ -360,6 +361,7 @@ class AdminSite(object):
118             'title': _('Site administration'),
119             'app_list': app_list,
120             'root_path': self.root_path,
121+            'admin_site': self.name
122         }
123         context.update(extra_context or {})
124         return render_to_response(self.index_template or 'admin/index.html', context,
125@@ -374,6 +376,7 @@ class AdminSite(object):
126             'app_path': request.get_full_path(),
127             'error_message': error_message,
128             'root_path': self.root_path,
129+            'admin_site': self.name,
130         }
131         context.update(extra_context or {})
132         return render_to_response(self.login_template or 'admin/login.html', context,
133@@ -417,6 +420,7 @@ class AdminSite(object):
134             'title': _('%s administration') % capfirst(app_label),
135             'app_list': [app_dict],
136             'root_path': self.root_path,
137+            'admin_site': self.name,
138         }
139         context.update(extra_context or {})
140         return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label,
141diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
142index 8cab439..cbbbd54 100644
143--- a/django/contrib/admin/templates/admin/base.html
144+++ b/django/contrib/admin/templates/admin/base.html
145@@ -23,7 +23,34 @@
146         {% block branding %}{% endblock %}
147         </div>
148         {% if user.is_authenticated and user.is_staff %}
149-        <div id="user-tools">{% trans 'Welcome,' %} <strong>{% firstof user.first_name user.username %}</strong>. {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div>
150+        <div id="user-tools">
151+            {% trans 'Welcome,' %}
152+            <strong>{% firstof user.first_name user.username %}</strong>.
153+            {% block userlinks %}
154+                {% url django-admindocs-docroot as docsroot %}
155+                {% if docsroot %}
156+                    <a href="{{ docsroot }}">
157+                        {% trans 'Documentation' %}
158+                    </a> /
159+                {% endif %}
160+                {% url admin_site:admin_password_change as password_change_url %}
161+                {% if password_change_url %}
162+                    <a href="{{ password_change_url }}">
163+                {% else %}
164+                    <a href="{{ root_path }}password_change/">
165+                {% endif %}
166+                    {% trans 'Change password' %}
167+                </a> /
168+                {% url admin_site:admin_logout as logout_url %}
169+                {% if logout_url %}
170+                    <a href="{{ logout_url }}">
171+                {% else %}
172+                    <a href="{{ root_path }}logout/">
173+                {% endif %}
174+                    {% trans 'Log out' %}
175+                </a>
176+            {% endblock %}
177+        </div>
178         {% endif %}
179         {% block nav-global %}{% endblock %}
180     </div>
181diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py
182index 34691c0..48dc3e5 100644
183--- a/django/contrib/auth/admin.py
184+++ b/django/contrib/auth/admin.py
185@@ -92,6 +92,7 @@ class UserAdmin(admin.ModelAdmin):
186             'save_as': False,
187             'username_help_text': self.model._meta.get_field('username').help_text,
188             'root_path': self.admin_site.root_path,
189+            'admin_site': self.admin_site.name,
190             'app_label': self.model._meta.app_label,           
191         }, context_instance=template.RequestContext(request))
192 
193@@ -122,6 +123,7 @@ class UserAdmin(admin.ModelAdmin):
194             'save_as': False,
195             'show_save': True,
196             'root_path': self.admin_site.root_path,
197+            'admin_site': self.admin_site.name,
198         }, context_instance=RequestContext(request))
199 
200 
201diff --git a/django/contrib/comments/views/moderation.py b/django/contrib/comments/views/moderation.py
202index 3334b09..880c6d7 100644
203--- a/django/contrib/comments/views/moderation.py
204+++ b/django/contrib/comments/views/moderation.py
205@@ -7,6 +7,7 @@ from django.core.paginator import Paginator, InvalidPage
206 from django.http import Http404
207 from django.contrib import comments
208 from django.contrib.comments import signals
209+from django.contrib import admin
210 
211 #@login_required
212 def flag(request, comment_id, next=None):
213@@ -185,7 +186,8 @@ def moderation_queue(request):
214         'previous': page - 1,
215         'pages': paginator.num_pages,
216         'hits' : paginator.count,
217-        'page_range' : paginator.page_range
218+        'page_range' : paginator.page_range,
219+        'admin_site': admin.site.name,
220     }, context_instance=template.RequestContext(request))
221 
222 moderation_queue = permission_required("comments.can_moderate")(moderation_queue)
223diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
224index ac83756..32ba34c 100644
225--- a/django/core/urlresolvers.py
226+++ b/django/core/urlresolvers.py
227@@ -257,6 +257,8 @@ def resolve(path, urlconf=None):
228     return get_resolver(urlconf).resolve(path)
229 
230 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
231+    if isinstance(viewname, basestring):
232+        viewname = ''.join(viewname.split(':'))
233     args = args or []
234     kwargs = kwargs or {}
235     if prefix is None:
236diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
237index a61dd8b..630078c 100644
238--- a/django/template/defaulttags.py
239+++ b/django/template/defaulttags.py
240@@ -357,6 +357,14 @@ class URLNode(Node):
241 
242     def render(self, context):
243         from django.core.urlresolvers import reverse, NoReverseMatch
244+        view_name_parts = []
245+        for part in self.view_name[:-1]:
246+            try:
247+                view_name_parts.append(Variable(part).resolve(context))
248+            except VariableDoesNotExist:
249+                view_name_parts.append(part)
250+        view_name_parts.append(self.view_name[-1])
251+        view_name = ':'.join(view_name_parts)
252         args = [arg.resolve(context) for arg in self.args]
253         kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
254                        for k, v in self.kwargs.items()])
255@@ -367,17 +375,17 @@ class URLNode(Node):
256         # {% url ... as var %} construct in which cause return nothing.
257         url = ''
258         try:
259-            url = reverse(self.view_name, args=args, kwargs=kwargs)
260+            url = reverse(view_name, args=args, kwargs=kwargs)
261         except NoReverseMatch, e:
262             if settings.SETTINGS_MODULE:
263                 project_name = settings.SETTINGS_MODULE.split('.')[0]
264                 try:
265-                    url = reverse(project_name + '.' + self.view_name,
266+                    url = reverse(project_name + '.' + view_name,
267                               args=args, kwargs=kwargs)
268                 except NoReverseMatch:
269                     if self.asvar is None:
270                         # Re-raise the original exception, not the one with
271-                        # the path relative to the project. This makes a
272+                        # the path relative to the project. This makes a
273                         # better error message.
274                         raise e
275             else:
276@@ -1105,6 +1113,10 @@ def url(parser, token):
277         raise TemplateSyntaxError("'%s' takes at least one argument"
278                                   " (path to a view)" % bits[0])
279     viewname = bits[1]
280+    if ':' in viewname:
281+        viewname = viewname.split(':')
282+    else:
283+        viewname = [viewname]
284     args = []
285     kwargs = {}
286     asvar = None
287diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
288index 8e7010b..283a41b 100644
289--- a/tests/regressiontests/admin_views/tests.py
290+++ b/tests/regressiontests/admin_views/tests.py
291@@ -203,6 +203,11 @@ class AdminViewBasicTest(TestCase):
292         response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'})
293         self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
294 
295+    def testLogoutAndPasswordChangeURLs(self):
296+        response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
297+        self.failIf('<a href="/test_admin/%s/logout/">' % self.urlbit not in response.content)
298+        self.failIf('<a href="/test_admin/%s/password_change/">' % self.urlbit not in response.content)
299+
300     def testNamedGroupFieldChoicesChangeList(self):
301         """
302         Ensures the admin changelist shows correct values in the relevant column
303diff --git a/tests/urls.py b/tests/urls.py
304index 6704829..31faea5 100644
305--- a/tests/urls.py
306+++ b/tests/urls.py
307@@ -21,12 +21,13 @@ urlpatterns = patterns('',
308     # test urlconf for middleware tests
309     (r'^middleware/', include('regressiontests.middleware.urls')),
310 
311+    # admin widget tests
312+    (r'widget_admin/', include('regressiontests.admin_widgets.urls')),
313+
314     # admin view tests
315     (r'^test_admin/', include('regressiontests.admin_views.urls')),
316     (r'^generic_inline_admin/', include('regressiontests.generic_inline_admin.urls')),
317 
318-    # admin widget tests
319-    (r'widget_admin/', include('regressiontests.admin_widgets.urls')),
320 
321     (r'^utils/', include('regressiontests.utils.urls')),
322