diff options
| author | Justin Bronn <jbronn@gmail.com> | 2008-08-05 17:15:33 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2008-08-05 17:15:33 +0000 |
| commit | aa239e3e5405933af6a29dac3cf587b59a099927 (patch) | |
| tree | ea2cbd139c9a8cf84c09e0b2008bff70e05927ef /django/contrib/admin | |
| parent | 45b73c9a4685809236f84046cc7ffd32a50db958 (diff) | |
gis: Merged revisions 7981-8001,8003-8011,8013-8033,8035-8036,8038-8039,8041-8063,8065-8076,8078-8139,8141-8154,8156-8214 via svnmerge from trunk.archive/attic/gis
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@8215 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/contrib/admin')
| -rw-r--r-- | django/contrib/admin/__init__.py | 7 | ||||
| -rw-r--r-- | django/contrib/admin/media/css/rtl.css | 3 | ||||
| -rw-r--r-- | django/contrib/admin/options.py | 99 | ||||
| -rw-r--r-- | django/contrib/admin/sites.py | 68 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/includes/fieldset.html | 4 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin_doc/model_detail.html | 2 | ||||
| -rw-r--r-- | django/contrib/admin/templates/registration/password_reset_complete.html | 16 | ||||
| -rw-r--r-- | django/contrib/admin/templates/registration/password_reset_confirm.html | 32 | ||||
| -rw-r--r-- | django/contrib/admin/templates/registration/password_reset_done.html | 2 | ||||
| -rw-r--r-- | django/contrib/admin/templates/registration/password_reset_email.html | 14 | ||||
| -rw-r--r-- | django/contrib/admin/templates/registration/password_reset_form.html | 2 | ||||
| -rw-r--r-- | django/contrib/admin/validation.py | 11 | ||||
| -rw-r--r-- | django/contrib/admin/views/decorators.py | 8 | ||||
| -rw-r--r-- | django/contrib/admin/views/main.py | 1 | ||||
| -rw-r--r-- | django/contrib/admin/widgets.py | 3 |
15 files changed, 180 insertions, 92 deletions
diff --git a/django/contrib/admin/__init__.py b/django/contrib/admin/__init__.py index 56b64faacb..704dc58ee4 100644 --- a/django/contrib/admin/__init__.py +++ b/django/contrib/admin/__init__.py @@ -8,9 +8,12 @@ def autodiscover(): not present. This forces an import on them to register any admin bits they may want. """ + import imp from django.conf import settings for app in settings.INSTALLED_APPS: try: - __import__("%s.admin" % app) + imp.find_module("admin", __import__(app, {}, {}, [app.split(".")[-1]]).__path__) except ImportError: - pass + # there is no app admin.py, skip it + continue + __import__("%s.admin" % app) diff --git a/django/contrib/admin/media/css/rtl.css b/django/contrib/admin/media/css/rtl.css index 1974e7c2ec..54175a7f9a 100644 --- a/django/contrib/admin/media/css/rtl.css +++ b/django/contrib/admin/media/css/rtl.css @@ -44,3 +44,6 @@ div.breadcrumbs { text-align:right; } .selector { float: right;} .selector .selector-filter { text-align: right;} + +/* x unsorted */ +.inline-related h2 { text-align:right } diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 501014a0d5..d161c58e64 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1,5 +1,4 @@ -from django import oldforms, template -from django import forms +from django import forms, template from django.forms.formsets import all_valid from django.forms.models import modelform_factory, inlineformset_factory from django.forms.models import BaseInlineFormset @@ -15,7 +14,10 @@ from django.utils.safestring import mark_safe from django.utils.text import capfirst, get_text_list from django.utils.translation import ugettext as _ from django.utils.encoding import force_unicode -import sets +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback HORIZONTAL, VERTICAL = 1, 2 # returns the <ul> class for a given radio_admin field @@ -90,7 +92,7 @@ class Fieldline(object): yield AdminField(self.form, field, is_first=(i == 0)) def errors(self): - return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields])) + return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]).strip('\n')) class AdminField(object): def __init__(self, form, field, is_first): @@ -130,6 +132,23 @@ class BaseModelAdmin(object): If kwargs are given, they're passed to the form Field's constructor. """ + + # If the field specifies choices, we don't need to look for special + # admin widgets - we just need to use a select widget of some kind. + if db_field.choices: + if db_field.name in self.radio_fields: + # If the field is named as a radio_field, use a RadioSelect + kwargs['widget'] = widgets.AdminRadioSelect( + choices=db_field.get_choices(include_blank=db_field.blank, + blank_choice=[('', _('None'))]), + attrs={ + 'class': get_ul_class(self.radio_fields[db_field.name]), + } + ) + else: + # Otherwise, use the default select widget. + return db_field.formfield(**kwargs) + # For DateTimeFields, use a special field and widget. if isinstance(db_field, models.DateTimeField): kwargs['form_class'] = forms.SplitDateTimeField @@ -162,10 +181,13 @@ class BaseModelAdmin(object): kwargs['empty_label'] = db_field.blank and _('None') or None else: if isinstance(db_field, models.ManyToManyField): - if db_field.name in self.raw_id_fields: + # If it uses an intermediary model, don't show field in admin. + if db_field.rel.through is not None: + return None + elif db_field.name in self.raw_id_fields: kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel) kwargs['help_text'] = '' - elif db_field.name in (self.filter_vertical + self.filter_horizontal): + elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) # Wrap the widget's render() method with a method that adds # extra HTML to the end of the rendered output. @@ -174,15 +196,6 @@ class BaseModelAdmin(object): if not db_field.name in self.raw_id_fields: formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site) return formfield - - if db_field.choices and db_field.name in self.radio_fields: - kwargs['widget'] = widgets.AdminRadioSelect( - choices=db_field.get_choices(include_blank=db_field.blank, - blank_choice=[('', _('None'))]), - attrs={ - 'class': get_ul_class(self.radio_fields[db_field.name]), - } - ) # For any other type of field, just call its formfield() method. return db_field.formfield(**kwargs) @@ -210,7 +223,7 @@ class ModelAdmin(BaseModelAdmin): save_on_top = False ordering = None inlines = [] - + # Custom templates (designed to be over-ridden in subclasses) change_form_template = None change_list_template = None @@ -261,7 +274,7 @@ class ModelAdmin(BaseModelAdmin): js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) if self.filter_vertical or self.filter_horizontal: js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) - + return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) media = property(_media) @@ -345,7 +358,7 @@ class ModelAdmin(BaseModelAdmin): pk_value = new_object._get_pk_val() LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, pk_value, force_unicode(new_object), ADDITION) - msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object} + msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': new_object} # Here, we distinguish between different save types by checking for # the presence of keys in request.POST. if request.POST.has_key("_continue"): @@ -359,7 +372,7 @@ class ModelAdmin(BaseModelAdmin): # escape() calls force_unicode. (escape(pk_value), escape(new_object))) elif request.POST.has_key("_addanother"): - request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) + request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) return HttpResponseRedirect(request.path) else: request.user.message_set.create(message=msg) @@ -378,7 +391,7 @@ class ModelAdmin(BaseModelAdmin): Saves the object in the "change" stage and returns an HttpResponseRedirect. `form` is a bound Form instance that's verified to be valid. - + `formsets` is a sequence of InlineFormSet instances that are verified to be valid. """ from django.contrib.admin.models import LogEntry, CHANGE @@ -394,20 +407,20 @@ class ModelAdmin(BaseModelAdmin): change_message = [] if form.changed_data: change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and'))) - + if formsets: for formset in formsets: for added_object in formset.new_objects: - change_message.append(_('Added %(name)s "%(object)s".') + change_message.append(_('Added %(name)s "%(object)s".') % {'name': added_object._meta.verbose_name, 'object': added_object}) for changed_object, changed_fields in formset.changed_objects: - change_message.append(_('Changed %(list)s for %(name)s "%(object)s".') - % {'list': get_text_list(changed_fields, _('and')), - 'name': changed_object._meta.verbose_name, + change_message.append(_('Changed %(list)s for %(name)s "%(object)s".') + % {'list': get_text_list(changed_fields, _('and')), + 'name': changed_object._meta.verbose_name, 'object': changed_object}) for deleted_object in formset.deleted_objects: - change_message.append(_('Deleted %(name)s "%(object)s".') + change_message.append(_('Deleted %(name)s "%(object)s".') % {'name': deleted_object._meta.verbose_name, 'object': deleted_object}) change_message = ' '.join(change_message) @@ -415,7 +428,7 @@ class ModelAdmin(BaseModelAdmin): change_message = _('No fields changed.') LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, pk_value, force_unicode(new_object), CHANGE, change_message) - msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object} + msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': new_object} if request.POST.has_key("_continue"): request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) if request.REQUEST.has_key('_popup'): @@ -423,10 +436,10 @@ class ModelAdmin(BaseModelAdmin): else: return HttpResponseRedirect(request.path) elif request.POST.has_key("_saveasnew"): - request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object}) + request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': new_object}) return HttpResponseRedirect("../%s/" % pk_value) elif request.POST.has_key("_addanother"): - request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) + request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) return HttpResponseRedirect("../add/") else: request.user.message_set.create(message=msg) @@ -504,7 +517,7 @@ class ModelAdmin(BaseModelAdmin): inline_admin_formsets.append(inline_admin_formset) context = { - 'title': _('Add %s') % opts.verbose_name, + 'title': _('Add %s') % force_unicode(opts.verbose_name), 'adminform': adminForm, 'is_popup': request.REQUEST.has_key('_popup'), 'show_delete': False, @@ -534,7 +547,7 @@ class ModelAdmin(BaseModelAdmin): raise PermissionDenied if obj is None: - raise Http404('%s object with primary key %r does not exist.' % (opts.verbose_name, escape(object_id))) + raise Http404('%s object with primary key %r does not exist.' % (force_unicode(opts.verbose_name), escape(object_id))) if request.POST and request.POST.has_key("_saveasnew"): return self.add_view(request, form_url='../../add/') @@ -557,17 +570,16 @@ class ModelAdmin(BaseModelAdmin): adminForm = AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields) media = self.media + adminForm.media - for fs in inline_formsets: - media = media + fs.media inline_admin_formsets = [] for inline, formset in zip(self.inline_instances, inline_formsets): fieldsets = list(inline.get_fieldsets(request, obj)) inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets) inline_admin_formsets.append(inline_admin_formset) + media = media + inline_admin_formset.media context = { - 'title': _('Change %s') % opts.verbose_name, + 'title': _('Change %s') % force_unicode(opts.verbose_name), 'adminform': adminForm, 'object_id': object_id, 'original': obj, @@ -599,7 +611,7 @@ class ModelAdmin(BaseModelAdmin): if ERROR_FLAG in request.GET.keys(): return render_to_response('admin/invalid_setup.html', {'title': _('Database error')}) return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') - + context = { 'title': cl.title, 'is_popup': cl.is_popup, @@ -632,12 +644,12 @@ class ModelAdmin(BaseModelAdmin): raise PermissionDenied if obj is None: - raise Http404('%s object with primary key %r does not exist.' % (opts.verbose_name, escape(object_id))) + raise Http404('%s object with primary key %r does not exist.' % (force_unicode(opts.verbose_name), escape(object_id))) # Populate deleted_objects, a data structure of all related objects that # will also be deleted. deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), quote(object_id), escape(obj))), []] - perms_needed = sets.Set() + perms_needed = set() get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site) if request.POST: # The user has already confirmed the deletion. @@ -650,10 +662,10 @@ class ModelAdmin(BaseModelAdmin): if not self.has_change_permission(request, None): return HttpResponseRedirect("../../../../") return HttpResponseRedirect("../../") - + context = { "title": _("Are you sure?"), - "object_name": opts.verbose_name, + "object_name": force_unicode(opts.verbose_name), "object": obj, "deleted_objects": deleted_objects, "perms_lacking": perms_needed, @@ -681,7 +693,7 @@ class ModelAdmin(BaseModelAdmin): context = { 'title': _('Change history: %s') % force_unicode(obj), 'action_list': action_list, - 'module_name': capfirst(opts.verbose_name_plural), + 'module_name': capfirst(force_unicode(opts.verbose_name_plural)), 'object': obj, 'root_path': self.admin_site.root_path, } @@ -761,6 +773,13 @@ class InlineAdminFormSet(object): for field_name in flatten_fieldsets(self.fieldsets): yield self.formset.form.base_fields[field_name] + def _media(self): + media = self.formset.media + for fs in self: + media = media + fs.media + return media + media = property(_media) + class InlineAdminForm(AdminForm): """ A wrapper around an inline form for use in the admin system. diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index bb4dc58ece..26e935e7fb 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -1,3 +1,7 @@ +import base64 +import cPickle as pickle +import re + from django import http, template from django.contrib.admin import ModelAdmin from django.contrib.auth import authenticate, login @@ -8,11 +12,7 @@ from django.utils.text import capfirst from django.utils.translation import ugettext_lazy, ugettext as _ from django.views.decorators.cache import never_cache from django.conf import settings -import base64 -import cPickle as pickle -import datetime -import md5 -import re +from django.utils.hashcompat import md5_constructor ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") LOGIN_FORM_KEY = 'this_is_the_login_form' @@ -26,16 +26,14 @@ class NotRegistered(Exception): pass def _encode_post_data(post_data): - from django.conf import settings pickled = pickle.dumps(post_data) - pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() + pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest() return base64.encodestring(pickled + pickled_md5) def _decode_post_data(encoded_data): - from django.conf import settings encoded_data = base64.decodestring(encoded_data) pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] - if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: + if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: from django.core.exceptions import SuspiciousOperation raise SuspiciousOperation, "User may have tampered with session cookie." return pickle.loads(pickled) @@ -47,10 +45,10 @@ class AdminSite(object): register() method, and the root() method can then be used as a Django view function that presents a full admin interface for the collection of registered models. """ - + index_template = None login_template = None - + def __init__(self): self._registry = {} # model_class class -> admin_class instance @@ -66,19 +64,33 @@ class AdminSite(object): If a model is already registered, this will raise AlreadyRegistered. """ - do_validate = admin_class and settings.DEBUG - if do_validate: - # don't import the humongous validation code unless required + # Don't import the humongous validation code unless required + if admin_class and settings.DEBUG: from django.contrib.admin.validation import validate - admin_class = admin_class or ModelAdmin - # TODO: Handle options + else: + validate = lambda model, adminclass: None + + if not admin_class: + admin_class = ModelAdmin if isinstance(model_or_iterable, ModelBase): model_or_iterable = [model_or_iterable] for model in model_or_iterable: if model in self._registry: raise AlreadyRegistered('The model %s is already registered' % model.__name__) - if do_validate: - validate(admin_class, model) + + # If we got **options then dynamically construct a subclass of + # admin_class with those **options. + if options: + # For reasons I don't quite understand, without a __module__ + # the created class appears to "live" in the wrong place, + # which causes issues later on. + options['__module__'] = __name__ + admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) + + # Validate (which might be a no-op) + validate(admin_class, model) + + # Instantiate the admin class to save in the registry self._registry[model] = admin_class(model, self) def unregister(self, model_or_iterable): @@ -102,23 +114,23 @@ class AdminSite(object): return request.user.is_authenticated() and request.user.is_staff def root(self, request, url): - """ + """ Handles main URL routing for the admin app. `url` is the remainder of the URL -- e.g. 'comments/comment/'. """ if request.method == 'GET' and not request.path.endswith('/'): return http.HttpResponseRedirect(request.path + '/') - + # 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) - + # Check permission to continue or display login form. if not self.has_permission(request): return self.login(request) @@ -139,7 +151,7 @@ class AdminSite(object): 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)) @@ -189,7 +201,6 @@ class AdminSite(object): This takes into account the USE_I18N setting. If it's set to False, the generated JavaScript will be leaner and faster. """ - from django.conf import settings if settings.USE_I18N: from django.views.i18n import javascript_catalog else: @@ -249,9 +260,6 @@ class AdminSite(object): else: if user.is_active and user.is_staff: login(request, user) - # TODO: set last_login with an event. - user.last_login = datetime.datetime.now() - user.save() if request.POST.has_key('post_data'): post_data = _decode_post_data(request.POST['post_data']) if post_data and not post_data.has_key(LOGIN_FORM_KEY): @@ -308,14 +316,14 @@ class AdminSite(object): # Sort the models alphabetically within each app. for app in app_list: app['models'].sort(lambda x, y: cmp(x['name'], y['name'])) - + context = { 'title': _('Site administration'), 'app_list': app_list, 'root_path': self.root_path, } context.update(extra_context or {}) - return render_to_response(self.index_template or 'admin/index.html', context, + return render_to_response(self.index_template or 'admin/index.html', context, context_instance=template.RequestContext(request) ) index = never_cache(index) @@ -330,7 +338,7 @@ class AdminSite(object): post_data = _encode_post_data(request.POST) else: post_data = _encode_post_data({}) - + context = { 'title': _('Log in'), 'app_path': request.path, diff --git a/django/contrib/admin/templates/admin/includes/fieldset.html b/django/contrib/admin/templates/admin/includes/fieldset.html index a61795cfe4..27e54c75d3 100644 --- a/django/contrib/admin/templates/admin/includes/fieldset.html +++ b/django/contrib/admin/templates/admin/includes/fieldset.html @@ -1,6 +1,6 @@ <fieldset class="module aligned {{ fieldset.classes }}"> {% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %} - {% if fieldset.description %}<div class="description">{{ fieldset.description }}</div>{% endif %} + {% if fieldset.description %}<div class="description">{{ fieldset.description|safe }}</div>{% endif %} {% for line in fieldset %} <div class="form-row{% if line.errors %} errors{% endif %} {% for field in line %}{{ field.field.name }} {% endfor %} "> {{ line.errors }} @@ -14,4 +14,4 @@ {% endfor %} </div> {% endfor %} -</fieldset>
\ No newline at end of file +</fieldset> diff --git a/django/contrib/admin/templates/admin_doc/model_detail.html b/django/contrib/admin/templates/admin_doc/model_detail.html index 81bf87db15..414397f23a 100644 --- a/django/contrib/admin/templates/admin_doc/model_detail.html +++ b/django/contrib/admin/templates/admin_doc/model_detail.html @@ -34,7 +34,7 @@ <tr> <td>{{ field.name }}</td> <td>{{ field.data_type }}</td> - <td>{% if field.verbose %}{{ field.verbose }}{% endif %}{% if field.help_text %} - {{ field.help_text }}{% endif %}</td> + <td>{% if field.verbose %}{{ field.verbose }}{% endif %}{% if field.help_text %} - {{ field.help_text|safe }}{% endif %}</td> </tr> {% endfor %} </tbody> diff --git a/django/contrib/admin/templates/registration/password_reset_complete.html b/django/contrib/admin/templates/registration/password_reset_complete.html new file mode 100644 index 0000000000..fceb167a88 --- /dev/null +++ b/django/contrib/admin/templates/registration/password_reset_complete.html @@ -0,0 +1,16 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> › {% trans 'Password reset' %}</div>{% endblock %} + +{% block title %}{% trans 'Password reset complete' %}{% endblock %} + +{% block content %} + +<h1>{% trans 'Password reset complete' %}</h1> + +<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p> + +<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p> + +{% endblock %} diff --git a/django/contrib/admin/templates/registration/password_reset_confirm.html b/django/contrib/admin/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000000..9ba0e5af27 --- /dev/null +++ b/django/contrib/admin/templates/registration/password_reset_confirm.html @@ -0,0 +1,32 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> › {% trans 'Password reset confirmation' %}</div>{% endblock %} + +{% block title %}{% trans 'Password reset' %}{% endblock %} + +{% block content %} + +{% if validlink %} + +<h1>{% trans 'Enter new password' %}</h1> + +<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p> + +<form action="" method="post"> +{% if form.new_password1.errors %}{{ form.new_password1.errors }}{% endif %} +<p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p> +{% if form.new_password2.errors %}{{ form.new_password2.errors }}{% endif %} +<p class="aligned wide"><label for="id_new_password2">{% trans 'Confirm password:' %}</label>{{ form.new_password2 }}</p> +<p><input type="submit" value="{% trans 'Change my password' %}" /></p> +</form> + +{% else %} + +<h1>{% trans 'Password reset unsuccessful' %}</h1> + +<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %} + +{% endif %} + +{% endblock %} diff --git a/django/contrib/admin/templates/registration/password_reset_done.html b/django/contrib/admin/templates/registration/password_reset_done.html index f97b5688c2..e223bdb9de 100644 --- a/django/contrib/admin/templates/registration/password_reset_done.html +++ b/django/contrib/admin/templates/registration/password_reset_done.html @@ -9,6 +9,6 @@ <h1>{% trans 'Password reset successful' %}</h1> -<p>{% trans "We've e-mailed a new password to the e-mail address you submitted. You should be receiving it shortly." %}</p> +<p>{% trans "We've e-mailed you instructions for setting your password to the e-mail address you submitted. You should be receiving it shortly." %}</p> {% endblock %} diff --git a/django/contrib/admin/templates/registration/password_reset_email.html b/django/contrib/admin/templates/registration/password_reset_email.html index f765dd0670..3b2d5b0f34 100644 --- a/django/contrib/admin/templates/registration/password_reset_email.html +++ b/django/contrib/admin/templates/registration/password_reset_email.html @@ -1,15 +1,15 @@ -{% load i18n %} +{% load i18n %}{% autoescape off %} {% trans "You're receiving this e-mail because you requested a password reset" %} {% blocktrans %}for your user account at {{ site_name }}{% endblocktrans %}. -{% blocktrans %}Your new password is: {{ new_password }}{% endblocktrans %} - -{% trans "Feel free to change this password by going to this page:" %} - -http://{{ domain }}/password_change/ - +{% trans "Please go to the following page and choose a new password:" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}/reset/{{ uid }}-{{ token }}/ +{% endblock %} {% trans "Your username, in case you've forgotten:" %} {{ user.username }} {% trans "Thanks for using our site!" %} {% blocktrans %}The {{ site_name }} team{% endblocktrans %} + +{% endautoescape %} diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html index d8c7d03f93..4ecebc77a1 100644 --- a/django/contrib/admin/templates/registration/password_reset_form.html +++ b/django/contrib/admin/templates/registration/password_reset_form.html @@ -9,7 +9,7 @@ <h1>{% trans "Password reset" %}</h1> -<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll reset your password and e-mail the new one to you." %}</p> +<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p> <form action="" method="post"> {% if form.email.errors %}{{ form.email.errors }}{% endif %} diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 2c9cb8554d..a42f2eb985 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -1,3 +1,7 @@ +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback from django.core.exceptions import ImproperlyConfigured from django.db import models @@ -165,6 +169,8 @@ def _validate_base(cls, model): _check_form_field_existsw('fields', field) if cls.fieldsets: raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__) + if len(cls.fields) > len(set(cls.fields)): + raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__) # fieldsets if cls.fieldsets: # default value is None @@ -179,7 +185,10 @@ def _validate_base(cls, model): raise ImproperlyConfigured("`fields` key is required in " "%s.fieldsets[%d][1] field options dict." % (cls.__name__, idx)) - for field in flatten_fieldsets(cls.fieldsets): + flattened_fieldsets = flatten_fieldsets(cls.fieldsets) + if len(flattened_fieldsets) > len(set(flattened_fieldsets)): + raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__) + for field in flattened_fieldsets: _check_form_field_existsw("fieldsets[%d][1]['fields']" % idx, field) # form diff --git a/django/contrib/admin/views/decorators.py b/django/contrib/admin/views/decorators.py index 57517cc821..cf0cd704c2 100644 --- a/django/contrib/admin/views/decorators.py +++ b/django/contrib/admin/views/decorators.py @@ -1,5 +1,4 @@ import base64 -import md5 import cPickle as pickle try: from functools import wraps @@ -12,6 +11,7 @@ from django.contrib.auth.models import User from django.contrib.auth import authenticate, login from django.shortcuts import render_to_response from django.utils.translation import ugettext_lazy, ugettext as _ +from django.utils.hashcompat import md5_constructor ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") LOGIN_FORM_KEY = 'this_is_the_login_form' @@ -35,13 +35,13 @@ def _display_login_form(request, error_message=''): def _encode_post_data(post_data): pickled = pickle.dumps(post_data) - pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() + pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest() return base64.encodestring(pickled + pickled_md5) def _decode_post_data(encoded_data): encoded_data = base64.decodestring(encoded_data) pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] - if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: + if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: from django.core.exceptions import SuspiciousOperation raise SuspiciousOperation, "User may have tampered with session cookie." return pickle.loads(pickled) @@ -87,7 +87,7 @@ def staff_member_required(view_func): if len(users) == 1: message = _("Your e-mail address is not your username. Try '%s' instead.") % users[0].username else: - # Either we cannot find the user, or if more than 1 + # Either we cannot find the user, or if more than 1 # we cannot guess which user is the correct one. message = _("Usernames cannot contain the '@' character.") return _display_login_form(request, message) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 926270cc68..116d9d49f1 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -6,7 +6,6 @@ from django.db import models from django.db.models.query import QuerySet from django.utils.encoding import force_unicode, smart_str from django.utils.translation import ugettext -from django.utils.safestring import mark_safe from django.utils.http import urlencode import operator diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 1139f8af60..1737920312 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -7,8 +7,7 @@ import copy from django import forms from django.forms.widgets import RadioFieldRenderer from django.forms.util import flatatt -from django.utils.datastructures import MultiValueDict -from django.utils.text import capfirst, truncate_words +from django.utils.text import truncate_words from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from django.utils.encoding import force_unicode |
