Django

Code

root/django/branches/newforms-admin/django/contrib/admin/sites.py

Revision 7933, 14.1 kB (checked in by brosner, 1 month ago)

newforms-admin: Fixed #7553 -- Reverted [7824] in favor of a better fix in #7553. The never_cache decorator is no longer special casing None. Thanks Michael Newman for the patch.

Line 
1 from django import http, template
2 from django.contrib.admin import ModelAdmin
3 from django.contrib.auth import authenticate, login
4 from django.db.models.base import ModelBase
5 from django.shortcuts import render_to_response
6 from django.utils.safestring import mark_safe
7 from django.utils.text import capfirst
8 from django.utils.translation import ugettext_lazy, ugettext as _
9 from django.views.decorators.cache import never_cache
10 from django.conf import settings
11 import base64
12 import cPickle as pickle
13 import datetime
14 import md5
15 import re
16
17 ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
18 LOGIN_FORM_KEY = 'this_is_the_login_form'
19
20 USER_CHANGE_PASSWORD_URL_RE = re.compile('auth/user/(\d+)/password')
21
22 class AlreadyRegistered(Exception):
23     pass
24
25 class NotRegistered(Exception):
26     pass
27
28 def _encode_post_data(post_data):
29     from django.conf import settings
30     pickled = pickle.dumps(post_data)
31     pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
32     return base64.encodestring(pickled + pickled_md5)
33
34 def _decode_post_data(encoded_data):
35     from django.conf import settings
36     encoded_data = base64.decodestring(encoded_data)
37     pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
38     if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
39         from django.core.exceptions import SuspiciousOperation
40         raise SuspiciousOperation, "User may have tampered with session cookie."
41     return pickle.loads(pickled)
42
43 class AdminSite(object):
44     """
45     An AdminSite object encapsulates an instance of the Django admin application, ready
46     to be hooked in to your URLConf. Models are registered with the AdminSite using the
47     register() method, and the root() method can then be used as a Django view function
48     that presents a full admin interface for the collection of registered models.
49     """
50    
51     index_template = None
52     login_template = None
53    
54     def __init__(self):
55         self._registry = {} # model_class class -> admin_class instance
56
57     def register(self, model_or_iterable, admin_class=None, **options):
58         """
59         Registers the given model(s) with the given admin class.
60
61         The model(s) should be Model classes, not instances.
62
63         If an admin class isn't given, it will use ModelAdmin (the default
64         admin options). If keyword arguments are given -- e.g., list_display --
65         they'll be applied as options to the admin class.
66
67         If a model is already registered, this will raise AlreadyRegistered.
68         """
69         do_validate = admin_class and settings.DEBUG
70         if do_validate:
71             # don't import the humongous validation code unless required
72             from django.contrib.admin.validation import validate
73         admin_class = admin_class or ModelAdmin
74         # TODO: Handle options
75         if isinstance(model_or_iterable, ModelBase):
76             model_or_iterable = [model_or_iterable]
77         for model in model_or_iterable:
78             if model in self._registry:
79                 raise AlreadyRegistered('The model %s is already registered' % model.__name__)
80             if do_validate:
81                 validate(admin_class, model)
82             self._registry[model] = admin_class(model, self)
83
84     def unregister(self, model_or_iterable):
85         """
86         Unregisters the given model(s).
87
88         If a model isn't already registered, this will raise NotRegistered.
89         """
90         if isinstance(model_or_iterable, ModelBase):
91             model_or_iterable = [model_or_iterable]
92         for model in model_or_iterable:
93             if model not in self._registry:
94                 raise NotRegistered('The model %s is not registered' % model.__name__)
95             del self._registry[model]
96
97     def has_permission(self, request):
98         """
99         Returns True if the given HttpRequest has permission to view
100         *at least one* page in the admin site.
101         """
102         return request.user.is_authenticated() and request.user.is_staff
103
104     def root(self, request, url):
105         """
106         Handles main URL routing for the admin app.
107
108         `url` is the remainder of the URL -- e.g. 'comments/comment/'.
109         """
110         if request.method == 'GET' and not request.path.endswith('/'):
111             return http.HttpResponseRedirect(request.path + '/')
112        
113         # Figure out the admin base URL path and stash it for later use
114         self.root_path = re.sub(re.escape(url) + '$', '', request.path)
115        
116         url = url.rstrip('/') # Trim trailing slash, if it exists.
117
118         # The 'logout' view doesn't require that the person is logged in.
119         if url == 'logout':
120             return self.logout(request)
121        
122         # Check permission to continue or display login form.
123         if not self.has_permission(request):
124             return self.login(request)
125
126         if url == '':
127             return self.index(request)
128         elif url == 'password_change':
129             return self.password_change(request)
130         elif url == 'password_change/done':
131             return self.password_change_done(request)
132         elif url == 'jsi18n':
133             return self.i18n_javascript(request)
134         # urls starting with 'r/' are for the "show in web" links
135         elif url.startswith('r/'):
136             from django.views.defaults import shortcut
137             return shortcut(request, *url.split('/')[1:])
138         else:
139             match = USER_CHANGE_PASSWORD_URL_RE.match(url)
140             if match:
141                 return self.user_change_password(request, match.group(1))
142                
143             if '/' in url:
144                 return self.model_page(request, *url.split('/', 2))
145
146         raise http.Http404('The requested admin page does not exist.')
147
148     def model_page(self, request, app_label, model_name, rest_of_url=None):
149         """
150         Handles the model-specific functionality of the admin site, delegating
151         to the appropriate ModelAdmin class.
152         """
153         from django.db import models
154         model = models.get_model(app_label, model_name)
155         if model is None:
156             raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
157         try:
158             admin_obj = self._registry[model]
159         except KeyError:
160             raise http.Http404("This model exists but has not been registered with the admin site.")
161         return admin_obj(request, rest_of_url)
162     model_page = never_cache(model_page)
163
164     def password_change(self, request):
165         """
166         Handles the "change password" task -- both form display and validation.
167         """
168         from django.contrib.auth.views import password_change
169         return password_change(request)
170
171     def password_change_done(self, request):
172         """
173         Displays the "success" page after a password change.
174         """
175         from django.contrib.auth.views import password_change_done
176         return password_change_done(request)
177
178     def user_change_password(self, request, id):
179         """
180         Handles the "user change password" task
181         """
182         from django.contrib.auth.views import user_change_password
183         return user_change_password(request, id)
184
185     def i18n_javascript(self, request):
186         """
187         Displays the i18n JavaScript that the Django admin requires.
188
189         This takes into account the USE_I18N setting. If it's set to False, the
190         generated JavaScript will be leaner and faster.
191         """
192         from django.conf import settings
193         if settings.USE_I18N:
194             from django.views.i18n import javascript_catalog
195         else:
196             from django.views.i18n import null_javascript_catalog as javascript_catalog
197         return javascript_catalog(request, packages='django.conf')
198
199     def logout(self, request):
200         """
201         Logs out the user for the given HttpRequest.
202
203         This should *not* assume the user is already logged in.
204         """
205         from django.contrib.auth.views import logout
206         return logout(request)
207     logout = never_cache(logout)
208
209     def login(self, request):
210         """
211         Displays the login form for the given HttpRequest.
212         """
213         from django.contrib.auth.models import User
214
215         # If this isn't already the login page, display it.
216         if not request.POST.has_key(LOGIN_FORM_KEY):
217             if request.POST:
218                 message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.")
219             else:
220                 message = ""
221             return self.display_login_form(request, message)
222
223         # Check that the user accepts cookies.
224         if not request.session.test_cookie_worked():
225             message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
226             return self.display_login_form(request, message)
227
228         # Check the password.
229         username = request.POST.get('username', None)
230         password = request.POST.get('password', None)
231         user = authenticate(username=username, password=password)
232         if user is None:
233             message = ERROR_MESSAGE
234             if u'@' in username:
235                 # Mistakenly entered e-mail address instead of username? Look it up.
236                 try:
237                     user = User.objects.get(email=username)
238                 except (User.DoesNotExist, User.MultipleObjectsReturned):
239                     message = _("Usernames cannot contain the '@' character.")
240                 else:
241                     if user.check_password(password):
242                         message = _("Your e-mail address is not your username."
243                                     " Try '%s' instead.") % user.username
244                     else:
245                         message = _("Usernames cannot contain the '@' character.")
246             return self.display_login_form(request, message)
247
248         # The user data is correct; log in the user in and continue.
249         else:
250             if user.is_active and user.is_staff:
251                 login(request, user)
252                 # TODO: set last_login with an event.
253                 user.last_login = datetime.datetime.now()
254                 user.save()
255                 if request.POST.has_key('post_data'):
256                     post_data = _decode_post_data(request.POST['post_data'])
257                     if post_data and not post_data.has_key(LOGIN_FORM_KEY):
258                         # overwrite request.POST with the saved post_data, and continue
259                         request.POST = post_data
260                         request.user = user
261                         return self.root(request, request.path.split(self.root_path)[-1])
262                     else:
263                         request.session.delete_test_cookie()
264                         return http.HttpResponseRedirect(request.path)
265             else:
266                 return self.display_login_form(request, ERROR_MESSAGE)
267     login = never_cache(login)
268
269     def index(self, request, extra_context=None):
270         """
271         Displays the main admin index page, which lists all of the installed
272         apps that have been registered in this site.
273         """
274         app_dict = {}
275         user = request.user
276         for model, model_admin in self._registry.items():
277             app_label = model._meta.app_label
278             has_module_perms = user.has_module_perms(app_label)
279
280             if has_module_perms:
281                 perms = {
282                     'add': model_admin.has_add_permission(request),
283                     'change': model_admin.has_change_permission(request),
284                     'delete': model_admin.has_delete_permission(request),
285                 }
286
287                 # Check whether user has any perm for this module.
288                 # If so, add the module to the model_list.
289                 if True in perms.values():
290                     model_dict = {
291                         'name': capfirst(model._meta.verbose_name_plural),
292                         'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
293                         'perms': perms,
294                     }
295                     if app_label in app_dict:
296                         app_dict[app_label]['models'].append(model_dict)
297                     else:
298                         app_dict[app_label] = {
299                             'name': app_label.title(),
300                             'has_module_perms': has_module_perms,
301                             'models': [model_dict],
302                         }
303
304         # Sort the apps alphabetically.
305         app_list = app_dict.values()
306         app_list.sort(lambda x, y: cmp(x['name'], y['name']))
307
308         # Sort the models alphabetically within each app.
309         for app in app_list:
310             app['models'].sort(lambda x, y: cmp(x['name'], y['name']))
311        
312         context = {
313             'title': _('Site administration'),
314             'app_list': app_list,
315             'root_path': self.root_path,
316         }
317         context.update(extra_context or {})
318         return render_to_response(self.index_template or 'admin/index.html', context,
319             context_instance=template.RequestContext(request)
320         )
321     index = never_cache(index)
322
323     def display_login_form(self, request, error_message='', extra_context=None):
324         request.session.set_test_cookie()
325         if request.POST and request.POST.has_key('post_data'):
326             # User has failed login BUT has previously saved post data.
327             post_data = request.POST['post_data']
328         elif request.POST:
329             # User's session must have expired; save their post data.
330             post_data = _encode_post_data(request.POST)
331         else:
332             post_data = _encode_post_data({})
333        
334         context = {
335             'title': _('Log in'),
336             'app_path': request.path,
337             'post_data': post_data,
338             'error_message': error_message,
339             'root_path': self.root_path,
340         }
341         context.update(extra_context or {})
342         return render_to_response(self.login_template or 'admin/login.html', context,
343             context_instance=template.RequestContext(request)
344         )
345
346
347 # This global object represents the default admin site, for the common case.
348 # You can instantiate AdminSite in your own code to create a custom admin site.
349 site = AdminSite()
Note: See TracBrowser for help on using the browser.