1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: Merged revisions 7921,7926-7928,7938-7941,7945-7947,7949-7950,7952,7955-7956,7961,7964-7968,7970-7978 via svnmerge from trunk.

This includes the newforms-admin branch, and thus is backwards-incompatible.  The geographic admin is _not_ in this changeset, and is forthcoming.


git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7979 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2008-07-19 13:30:47 +00:00
parent 5bf3565a26
commit 149e731c3c
207 changed files with 9442 additions and 3617 deletions

10
AUTHORS
View File

@ -74,6 +74,7 @@ answer newbie questions, and generally made Django that much better:
Arvis Bickovskis <viestards.lists@gmail.com> Arvis Bickovskis <viestards.lists@gmail.com>
Paul Bissex <http://e-scribe.com/> Paul Bissex <http://e-scribe.com/>
Simon Blanchard Simon Blanchard
David Blewett <david@dawninglight.net>
Matt Boersma <ogghead@gmail.com> Matt Boersma <ogghead@gmail.com>
boobsd@gmail.com boobsd@gmail.com
Andrew Brehaut <http://brehaut.net/blog> Andrew Brehaut <http://brehaut.net/blog>
@ -172,6 +173,7 @@ answer newbie questions, and generally made Django that much better:
Espen Grindhaug <http://grindhaug.org/> Espen Grindhaug <http://grindhaug.org/>
Thomas Güttler <hv@tbz-pariv.de> Thomas Güttler <hv@tbz-pariv.de>
dAniel hAhler dAniel hAhler
hambaloney
Brian Harring <ferringb@gmail.com> Brian Harring <ferringb@gmail.com>
Brant Harris Brant Harris
Hawkeye Hawkeye
@ -194,6 +196,7 @@ answer newbie questions, and generally made Django that much better:
Baurzhan Ismagulov <ibr@radix50.net> Baurzhan Ismagulov <ibr@radix50.net>
james_027@yahoo.com james_027@yahoo.com
jcrasta@gmail.com jcrasta@gmail.com
jdetaeye
Zak Johnson <zakj@nox.cx> Zak Johnson <zakj@nox.cx>
Nis Jørgensen <nis@superlativ.dk> Nis Jørgensen <nis@superlativ.dk>
Michael Josephson <http://www.sdjournal.com/> Michael Josephson <http://www.sdjournal.com/>
@ -241,11 +244,13 @@ answer newbie questions, and generally made Django that much better:
Waylan Limberg <waylan@gmail.com> Waylan Limberg <waylan@gmail.com>
limodou limodou
Philip Lindborg <philip.lindborg@gmail.com> Philip Lindborg <philip.lindborg@gmail.com>
Simon Litchfield <simon@quo.com.au>
Daniel Lindsley <polarcowz@gmail.com> Daniel Lindsley <polarcowz@gmail.com>
Trey Long <trey@ktrl.com> Trey Long <trey@ktrl.com>
msaelices <msaelices@gmail.com> msaelices <msaelices@gmail.com>
Matt McClanahan <http://mmcc.cx/> Matt McClanahan <http://mmcc.cx/>
Martin Maney <http://www.chipy.org/Martin_Maney> Martin Maney <http://www.chipy.org/Martin_Maney>
Petr Marhoun <petr.marhoun@gmail.com>
masonsimon+django@gmail.com masonsimon+django@gmail.com
Manuzhai Manuzhai
Petr Marhoun <petr.marhoun@gmail.com> Petr Marhoun <petr.marhoun@gmail.com>
@ -258,6 +263,7 @@ answer newbie questions, and generally made Django that much better:
mattycakes@gmail.com mattycakes@gmail.com
Jason McBrayer <http://www.carcosa.net/jason/> Jason McBrayer <http://www.carcosa.net/jason/>
mccutchen@gmail.com mccutchen@gmail.com
Christian Metts
michael.mcewan@gmail.com michael.mcewan@gmail.com
michal@plovarna.cz michal@plovarna.cz
Slawek Mikula <slawek dot mikula at gmail dot com> Slawek Mikula <slawek dot mikula at gmail dot com>
@ -270,6 +276,7 @@ answer newbie questions, and generally made Django that much better:
Eric Moritz <http://eric.themoritzfamily.com/> Eric Moritz <http://eric.themoritzfamily.com/>
mrmachine <real.human@mrmachine.net> mrmachine <real.human@mrmachine.net>
Robin Munn <http://www.geekforgod.com/> Robin Munn <http://www.geekforgod.com/>
msundstr
Robert Myers <myer0052@gmail.com> Robert Myers <myer0052@gmail.com>
Nebojša Dorđević Nebojša Dorđević
Doug Napoleone <doug@dougma.com> Doug Napoleone <doug@dougma.com>
@ -290,6 +297,7 @@ answer newbie questions, and generally made Django that much better:
peter@mymart.com peter@mymart.com
pgross@thoughtworks.com pgross@thoughtworks.com
phaedo <http://phaedo.cx/> phaedo <http://phaedo.cx/>
Julien Phalip <http://www.julienphalip.com>
phil@produxion.net phil@produxion.net
phil.h.smith@gmail.com phil.h.smith@gmail.com
Gustavo Picon Gustavo Picon
@ -298,6 +306,7 @@ answer newbie questions, and generally made Django that much better:
Mihai Preda <mihai_preda@yahoo.com> Mihai Preda <mihai_preda@yahoo.com>
Daniel Poelzleithner <http://poelzi.org/> Daniel Poelzleithner <http://poelzi.org/>
polpak@yahoo.com polpak@yahoo.com
Matthias Pronk <django@masida.nl>
Jyrki Pulliainen <jyrki.pulliainen@gmail.com> Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
Johann Queuniet <johann.queuniet@adh.naellia.eu> Johann Queuniet <johann.queuniet@adh.naellia.eu>
Jan Rademaker Jan Rademaker
@ -314,6 +323,7 @@ answer newbie questions, and generally made Django that much better:
Matt Riggott Matt Riggott
Henrique Romano <onaiort@gmail.com> Henrique Romano <onaiort@gmail.com>
Armin Ronacher Armin Ronacher
Daniel Roseman <http://roseman.org.uk/>
Brian Rosner <brosner@gmail.com> Brian Rosner <brosner@gmail.com>
Oliver Rutherfurd <http://rutherfurd.net/> Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno ryankanno

View File

@ -1,9 +1,15 @@
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
# Uncomment this for admin:
#from django.contrib import admin
urlpatterns = patterns('', urlpatterns = patterns('',
# Example: # Example:
# (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), # (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
# Uncomment this for admin docs:
#(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment this for admin: # Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')), #('^admin/(.*)', admin.site.root),
) )

View File

@ -0,0 +1,16 @@
from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
from django.contrib.admin.options import StackedInline, TabularInline
from django.contrib.admin.sites import AdminSite, site
def autodiscover():
"""
Auto-discover INSTALLED_APPS admin.py modules and fail silently when
not present. This forces an import on them to register any admin bits they
may want.
"""
from django.conf import settings
for app in settings.INSTALLED_APPS:
try:
__import__("%s.admin" % app)
except ImportError:
pass

View File

@ -15,7 +15,7 @@ import datetime
class FilterSpec(object): class FilterSpec(object):
filter_specs = [] filter_specs = []
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
self.field = f self.field = f
self.params = params self.params = params
@ -23,10 +23,10 @@ class FilterSpec(object):
cls.filter_specs.append((test, factory)) cls.filter_specs.append((test, factory))
register = classmethod(register) register = classmethod(register)
def create(cls, f, request, params, model): def create(cls, f, request, params, model, model_admin):
for test, factory in cls.filter_specs: for test, factory in cls.filter_specs:
if test(f): if test(f):
return factory(f, request, params, model) return factory(f, request, params, model, model_admin)
create = classmethod(create) create = classmethod(create)
def has_output(self): def has_output(self):
@ -52,8 +52,8 @@ class FilterSpec(object):
return mark_safe("".join(t)) return mark_safe("".join(t))
class RelatedFilterSpec(FilterSpec): class RelatedFilterSpec(FilterSpec):
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
super(RelatedFilterSpec, self).__init__(f, request, params, model) super(RelatedFilterSpec, self).__init__(f, request, params, model, model_admin)
if isinstance(f, models.ManyToManyField): if isinstance(f, models.ManyToManyField):
self.lookup_title = f.rel.to._meta.verbose_name self.lookup_title = f.rel.to._meta.verbose_name
else: else:
@ -81,8 +81,8 @@ class RelatedFilterSpec(FilterSpec):
FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec) FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
class ChoicesFilterSpec(FilterSpec): class ChoicesFilterSpec(FilterSpec):
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
super(ChoicesFilterSpec, self).__init__(f, request, params, model) super(ChoicesFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_kwarg = '%s__exact' % f.name self.lookup_kwarg = '%s__exact' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@ -98,8 +98,8 @@ class ChoicesFilterSpec(FilterSpec):
FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec) FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
class DateFieldFilterSpec(FilterSpec): class DateFieldFilterSpec(FilterSpec):
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
super(DateFieldFilterSpec, self).__init__(f, request, params, model) super(DateFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
self.field_generic = '%s__' % self.field.name self.field_generic = '%s__' % self.field.name
@ -133,8 +133,8 @@ class DateFieldFilterSpec(FilterSpec):
FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec) FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
class BooleanFieldFilterSpec(FilterSpec): class BooleanFieldFilterSpec(FilterSpec):
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
super(BooleanFieldFilterSpec, self).__init__(f, request, params, model) super(BooleanFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_kwarg = '%s__exact' % f.name self.lookup_kwarg = '%s__exact' % f.name
self.lookup_kwarg2 = '%s__isnull' % f.name self.lookup_kwarg2 = '%s__isnull' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@ -159,10 +159,10 @@ FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f
# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much # if a field is eligible to use the BooleanFieldFilterSpec, that'd be much
# more appropriate, and the AllValuesFilterSpec won't get used for it. # more appropriate, and the AllValuesFilterSpec won't get used for it.
class AllValuesFilterSpec(FilterSpec): class AllValuesFilterSpec(FilterSpec):
def __init__(self, f, request, params, model): def __init__(self, f, request, params, model, model_admin):
super(AllValuesFilterSpec, self).__init__(f, request, params, model) super(AllValuesFilterSpec, self).__init__(f, request, params, model, model_admin)
self.lookup_val = request.GET.get(f.name, None) self.lookup_val = request.GET.get(f.name, None)
self.lookup_choices = model._meta.admin.manager.distinct().order_by(f.name).values(f.name) self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name)
def title(self): def title(self):
return self.field.verbose_name return self.field.verbose_name

View File

@ -58,3 +58,24 @@ fieldset.monospace textarea { font-family:"Bitstream Vera Sans Mono",Monaco,"Cou
.vLargeTextField, .vXMLLargeTextField { width:48em; } .vLargeTextField, .vXMLLargeTextField { width:48em; }
.flatpages-flatpage #id_content { height:40.2em; } .flatpages-flatpage #id_content { height:40.2em; }
.module table .vPositiveSmallIntegerField { width:2.2em; } .module table .vPositiveSmallIntegerField { width:2.2em; }
/* x unsorted */
.inline-group {padding:10px; padding-bottom:5px; background:#eee; margin:10px 0;}
.inline-group h3.header {margin:-5px -10px 5px -10px; background:#bbb; color:#fff; padding:2px 5px 3px 5px; font-size:11px}
.inline-related {margin-bottom:15px; position:relative;}
.last-related {margin-bottom:0px;}
.inline-related h2 { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; font-weight:bold; color:#888; }
.inline-related h2 b {font-weight:normal; color:#aaa;}
.inline-related h2 span.delete {padding-left:20px; position:absolute; top:0px; right:5px;}
.inline-related h2 span.delete label {margin-left:2px; padding-top:1px;}
.inline-related fieldset {background:#fbfbfb;}
.inline-related fieldset.module h2 { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; font-weight:bold; background:#bcd; color:#fff; }
.inline-related.tabular fieldset.module table {width:100%;}
.inline-group .tabular tr.has_original td {padding-top:2em;}
.inline-group .tabular tr td.original { padding:2px 0 0 0; width:0; _position:relative; }
.inline-group .tabular th.original {width:0px; padding:0;}
.inline-group .tabular td.original p {position:absolute; left:0; height:1.1em; padding:2px 7px; overflow:hidden; font-size:9px; font-weight:bold; color:#666; _width:700px; }
.inline-group ul.tools {padding:0; margin: 0; list-style:none;}
.inline-group ul.tools li {display:inline; padding:0 5px;}
.inline-group ul.tools a.add {background:url(../img/admin/icon_addlink.gif) 0 50% no-repeat; padding-left:14px;}

View File

@ -1,81 +0,0 @@
/*
SelectFilter - Turns a multiple-select box into a filter interface.
Requires SelectBox.js and addevent.js.
*/
function findForm(node) {
// returns the node of the form containing the given node
if (node.tagName.toLowerCase() != 'form') {
return findForm(node.parentNode);
}
return node;
}
var SelectFilter = {
init: function(field_id) {
var from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID
// Create the INPUT input box
var input_box = document.createElement('input');
input_box.id = field_id + '_input';
input_box.setAttribute('type', 'text');
from_box.parentNode.insertBefore(input_box, from_box);
from_box.parentNode.insertBefore(document.createElement('br'), input_box.nextSibling);
// Create the TO box
var to_box = document.createElement('select');
to_box.id = field_id + '_to';
to_box.setAttribute('multiple', 'multiple');
to_box.setAttribute('size', from_box.size);
from_box.parentNode.insertBefore(to_box, from_box.nextSibling);
to_box.setAttribute('name', from_box.getAttribute('name'));
from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
// Give the filters a CSS hook
from_box.setAttribute('class', 'filtered');
to_box.setAttribute('class', 'filtered');
// Set up the JavaScript event handlers for the select box filter interface
addEvent(input_box, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
addEvent(input_box, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
addEvent(from_box, 'focus', function() { input_box.focus(); });
addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
SelectBox.init(field_id + '_from');
SelectBox.init(field_id + '_to');
// Move selected from_box options to to_box
SelectBox.move(field_id + '_from', field_id + '_to');
},
filter_key_up: function(event, field_id) {
from = document.getElementById(field_id + '_from');
// don't submit form if user pressed Enter
if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
from.selectedIndex = 0;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = 0;
return false;
}
var temp = from.selectedIndex;
SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
from.selectedIndex = temp;
return true;
},
filter_key_down: function(event, field_id) {
from = document.getElementById(field_id + '_from');
// right arrow -- move across
if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
var old_index = from.selectedIndex;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
return false;
}
// down arrow -- wrap around
if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
}
// up arrow -- wrap around
if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
}
return true;
}
}

View File

@ -47,7 +47,7 @@ var CollapsedFieldsets = {
// Returns true if any fields in the fieldset have validation errors. // Returns true if any fields in the fieldset have validation errors.
var divs = fs.getElementsByTagName('div'); var divs = fs.getElementsByTagName('div');
for (var i=0; i<divs.length; i++) { for (var i=0; i<divs.length; i++) {
if (divs[i].className.match(/\berror\b/)) { if (divs[i].className.match(/\berrors\b/)) {
return true; return true;
} }
} }

View File

@ -1,4 +1,4 @@
// Handles related-objects functionality: lookup link for raw_id_admin=True // Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links. // and Add Another links.
function html_unescape(text) { function html_unescape(text) {
@ -29,7 +29,7 @@ function showRelatedObjectLookupPopup(triggeringLink) {
function dismissRelatedLookupPopup(win, chosenId) { function dismissRelatedLookupPopup(win, chosenId) {
var name = win.name.replace(/___/g, '.'); var name = win.name.replace(/___/g, '.');
var elem = document.getElementById(name); var elem = document.getElementById(name);
if (elem.className.indexOf('vRawIdAdminField') != -1 && elem.value) { if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId; elem.value += ',' + chosenId;
} else { } else {
document.getElementById(name).value = chosenId; document.getElementById(name).value = chosenId;

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.admin.util import quote
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -50,4 +51,4 @@ class LogEntry(models.Model):
Returns the admin URL to edit the object represented by this log entry. Returns the admin URL to edit the object represented by this log entry.
This is relative to the Django admin index page. This is relative to the Django admin index page.
""" """
return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id)) return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id)))

View File

@ -0,0 +1,795 @@
from django import oldforms, template
from django import forms
from django.forms.formsets import all_valid
from django.forms.models import modelform_factory, inlineformset_factory
from django.forms.models import BaseInlineFormset
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin import widgets
from django.contrib.admin.util import quote, unquote, get_deleted_objects
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.db import models, transaction
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render_to_response
from django.utils.html import escape
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
HORIZONTAL, VERTICAL = 1, 2
# returns the <ul> class for a given radio_admin field
get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
class IncorrectLookupParameters(Exception):
pass
def flatten_fieldsets(fieldsets):
"""Returns a list of field names from an admin fieldsets structure."""
field_names = []
for name, opts in fieldsets:
for field in opts['fields']:
# type checking feels dirty, but it seems like the best way here
if type(field) == tuple:
field_names.extend(field)
else:
field_names.append(field)
return field_names
class AdminForm(object):
def __init__(self, form, fieldsets, prepopulated_fields):
self.form, self.fieldsets = form, fieldsets
self.prepopulated_fields = [{
'field': form[field_name],
'dependencies': [form[f] for f in dependencies]
} for field_name, dependencies in prepopulated_fields.items()]
def __iter__(self):
for name, options in self.fieldsets:
yield Fieldset(self.form, name, **options)
def first_field(self):
for bf in self.form:
return bf
def _media(self):
media = self.form.media
for fs in self:
media = media + fs.media
return media
media = property(_media)
class Fieldset(object):
def __init__(self, form, name=None, fields=(), classes=(), description=None):
self.form = form
self.name, self.fields = name, fields
self.classes = u' '.join(classes)
self.description = description
def _media(self):
from django.conf import settings
if 'collapse' in self.classes:
return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
return forms.Media()
media = property(_media)
def __iter__(self):
for field in self.fields:
yield Fieldline(self.form, field)
class Fieldline(object):
def __init__(self, form, field):
self.form = form # A django.forms.Form instance
if isinstance(field, basestring):
self.fields = [field]
else:
self.fields = field
def __iter__(self):
for i, field in enumerate(self.fields):
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]))
class AdminField(object):
def __init__(self, form, field, is_first):
self.field = form[field] # A django.forms.BoundField instance
self.is_first = is_first # Whether this field is first on the line
self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput)
def label_tag(self):
classes = []
if self.is_checkbox:
classes.append(u'vCheckboxLabel')
contents = escape(self.field.label)
else:
contents = force_unicode(escape(self.field.label)) + u':'
if self.field.field.required:
classes.append(u'required')
if not self.is_first:
classes.append(u'inline')
attrs = classes and {'class': u' '.join(classes)} or {}
return self.field.label_tag(contents=contents, attrs=attrs)
class BaseModelAdmin(object):
"""Functionality common to both ModelAdmin and InlineAdmin."""
raw_id_fields = ()
fields = None
fieldsets = None
form = forms.ModelForm
filter_vertical = ()
filter_horizontal = ()
radio_fields = {}
prepopulated_fields = {}
def formfield_for_dbfield(self, db_field, **kwargs):
"""
Hook for specifying the form Field instance for a given database Field
instance.
If kwargs are given, they're passed to the form Field's constructor.
"""
# For DateTimeFields, use a special field and widget.
if isinstance(db_field, models.DateTimeField):
kwargs['form_class'] = forms.SplitDateTimeField
kwargs['widget'] = widgets.AdminSplitDateTime()
return db_field.formfield(**kwargs)
# For DateFields, add a custom CSS class.
if isinstance(db_field, models.DateField):
kwargs['widget'] = widgets.AdminDateWidget
return db_field.formfield(**kwargs)
# For TimeFields, add a custom CSS class.
if isinstance(db_field, models.TimeField):
kwargs['widget'] = widgets.AdminTimeWidget
return db_field.formfield(**kwargs)
# For FileFields and ImageFields add a link to the current file.
if isinstance(db_field, models.ImageField) or isinstance(db_field, models.FileField):
kwargs['widget'] = widgets.AdminFileWidget
return db_field.formfield(**kwargs)
# For ForeignKey or ManyToManyFields, use a special widget.
if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
if isinstance(db_field, models.ForeignKey) and db_field.name in self.raw_id_fields:
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
elif isinstance(db_field, models.ForeignKey) and db_field.name in self.radio_fields:
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
'class': get_ul_class(self.radio_fields[db_field.name]),
})
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:
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
kwargs['help_text'] = ''
elif db_field.name in (self.filter_vertical + 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.
formfield = db_field.formfield(**kwargs)
# Don't wrap raw_id fields. Their add function is in the popup window.
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)
def _declared_fieldsets(self):
if self.fieldsets:
return self.fieldsets
elif self.fields:
return [(None, {'fields': self.fields})]
return None
declared_fieldsets = property(_declared_fieldsets)
class ModelAdmin(BaseModelAdmin):
"Encapsulates all admin options and functionality for a given model."
__metaclass__ = forms.MediaDefiningClass
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
list_select_related = False
list_per_page = 100
search_fields = ()
date_hierarchy = None
save_as = False
save_on_top = False
ordering = None
inlines = []
# Custom templates (designed to be over-ridden in subclasses)
change_form_template = None
change_list_template = None
delete_confirmation_template = None
object_history_template = None
def __init__(self, model, admin_site):
self.model = model
self.opts = model._meta
self.admin_site = admin_site
self.inline_instances = []
for inline_class in self.inlines:
inline_instance = inline_class(self.model, self.admin_site)
self.inline_instances.append(inline_instance)
super(ModelAdmin, self).__init__()
def __call__(self, request, url):
# Check that LogEntry, ContentType and the auth context processor are installed.
from django.conf import settings
if settings.DEBUG:
from django.contrib.admin.models import LogEntry
if not LogEntry._meta.installed:
raise ImproperlyConfigured("Put 'django.contrib.admin' in your INSTALLED_APPS setting in order to use the admin application.")
if not ContentType._meta.installed:
raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.")
if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
# Delegate to the appropriate method, based on the URL.
if url is None:
return self.changelist_view(request)
elif url.endswith('add'):
return self.add_view(request)
elif url.endswith('history'):
return self.history_view(request, unquote(url[:-8]))
elif url.endswith('delete'):
return self.delete_view(request, unquote(url[:-7]))
else:
return self.change_view(request, unquote(url))
def _media(self):
from django.conf import settings
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
if self.prepopulated_fields:
js.append('js/urlify.js')
if self.opts.get_ordered_objects():
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)
def has_add_permission(self, request):
"Returns True if the given request has permission to add an object."
opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission())
def has_change_permission(self, request, obj=None):
"""
Returns True if the given request has permission to change the given
Django model instance.
If `obj` is None, this should return True if the given request has
permission to change *any* object of the given type.
"""
opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission())
def has_delete_permission(self, request, obj=None):
"""
Returns True if the given request has permission to change the given
Django model instance.
If `obj` is None, this should return True if the given request has
permission to delete *any* object of the given type.
"""
opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission())
def queryset(self, request):
"""
Returns a QuerySet of all model instances that can be edited by the
admin site. This is used by changelist_view.
"""
qs = self.model._default_manager.get_query_set()
# TODO: this should be handled by some parameter to the ChangeList.
ordering = self.ordering or () # otherwise we might try to *None, which is bad ;)
if ordering:
qs = qs.order_by(*ordering)
return qs
def get_fieldsets(self, request, obj=None):
"Hook for specifying fieldsets for the add form."
if self.declared_fieldsets:
return self.declared_fieldsets
form = self.get_form(request)
return [(None, {'fields': form.base_fields.keys()})]
def get_form(self, request, obj=None):
"""
Returns a Form class for use in the admin add view. This is used by
add_view and change_view.
"""
if self.declared_fieldsets:
fields = flatten_fieldsets(self.declared_fieldsets)
else:
fields = None
return modelform_factory(self.model, form=self.form, fields=fields, formfield_callback=self.formfield_for_dbfield)
def get_formsets(self, request, obj=None):
for inline in self.inline_instances:
yield inline.get_formset(request, obj)
def save_add(self, request, form, formsets, post_url_continue):
"""
Saves the object in the "add" stage and returns an HttpResponseRedirect.
`form` is a bound Form instance that's verified to be valid.
"""
from django.contrib.admin.models import LogEntry, ADDITION
opts = self.model._meta
new_object = form.save(commit=True)
if formsets:
for formset in formsets:
# HACK: it seems like the parent obejct should be passed into
# a method of something, not just set as an attribute
formset.instance = new_object
formset.save()
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}
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request.POST.has_key("_continue"):
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
if request.POST.has_key("_popup"):
post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value)
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# 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))
return HttpResponseRedirect(request.path)
else:
request.user.message_set.create(message=msg)
# Figure out where to redirect. If the user has change permission,
# redirect to the change-list page for this object. Otherwise,
# redirect to the admin index.
if self.has_change_permission(request, None):
post_url = '../'
else:
post_url = '../../../'
return HttpResponseRedirect(post_url)
save_add = transaction.commit_on_success(save_add)
def save_change(self, request, form, formsets=None):
"""
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
opts = self.model._meta
new_object = form.save(commit=True)
pk_value = new_object._get_pk_val()
if formsets:
for formset in formsets:
formset.save()
# Construct the change message.
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".')
% {'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,
'object': changed_object})
for deleted_object in formset.deleted_objects:
change_message.append(_('Deleted %(name)s "%(object)s".')
% {'name': deleted_object._meta.verbose_name,
'object': deleted_object})
change_message = ' '.join(change_message)
if not change_message:
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}
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'):
return HttpResponseRedirect(request.path + "?_popup=1")
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})
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))
return HttpResponseRedirect("../add/")
else:
request.user.message_set.create(message=msg)
return HttpResponseRedirect("../")
save_change = transaction.commit_on_success(save_change)
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
opts = self.model._meta
app_label = opts.app_label
ordered_objects = opts.get_ordered_objects()
context.update({
'add': add,
'change': change,
'has_add_permission': self.has_add_permission(request),
'has_change_permission': self.has_change_permission(request, obj),
'has_delete_permission': self.has_delete_permission(request, obj),
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
'ordered_objects': ordered_objects,
'form_url': mark_safe(form_url),
'opts': opts,
'content_type_id': ContentType.objects.get_for_model(self.model).id,
'save_as': self.save_as,
'save_on_top': self.save_on_top,
'root_path': self.admin_site.root_path,
})
return render_to_response(self.change_form_template or [
"admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_form.html" % app_label,
"admin/change_form.html"
], context, context_instance=template.RequestContext(request))
def add_view(self, request, form_url='', extra_context=None):
"The 'add' admin view for this model."
model = self.model
opts = model._meta
app_label = opts.app_label
if not self.has_add_permission(request):
raise PermissionDenied
if self.has_change_permission(request, None):
# redirect to list view
post_url = '../'
else:
# Object list will give 'Permission Denied', so go back to admin home
post_url = '../../../'
ModelForm = self.get_form(request)
inline_formsets = []
obj = self.model()
if request.method == 'POST':
form = ModelForm(request.POST, request.FILES)
for FormSet in self.get_formsets(request):
inline_formset = FormSet(data=request.POST, files=request.FILES,
instance=obj, save_as_new=request.POST.has_key("_saveasnew"))
inline_formsets.append(inline_formset)
if all_valid(inline_formsets) and form.is_valid():
return self.save_add(request, form, inline_formsets, '../%s/')
else:
form = ModelForm(initial=dict(request.GET.items()))
for FormSet in self.get_formsets(request):
inline_formset = FormSet(instance=obj)
inline_formsets.append(inline_formset)
adminForm = AdminForm(form, list(self.get_fieldsets(request)), 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))
inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
inline_admin_formsets.append(inline_admin_formset)
context = {
'title': _('Add %s') % opts.verbose_name,
'adminform': adminForm,
'is_popup': request.REQUEST.has_key('_popup'),
'show_delete': False,
'media': mark_safe(media),
'inline_admin_formsets': inline_admin_formsets,
'errors': AdminErrorList(form, inline_formsets),
'root_path': self.admin_site.root_path,
}
context.update(extra_context or {})
return self.render_change_form(request, context, add=True)
def change_view(self, request, object_id, extra_context=None):
"The 'change' admin view for this model."
model = self.model
opts = model._meta
app_label = opts.app_label
try:
obj = model._default_manager.get(pk=object_id)
except model.DoesNotExist:
# Don't raise Http404 just yet, because we haven't checked
# permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists.
obj = None
if not self.has_change_permission(request, obj):
raise PermissionDenied
if obj is None:
raise Http404('%s object with primary key %r does not exist.' % (opts.verbose_name, escape(object_id)))
if request.POST and request.POST.has_key("_saveasnew"):
return self.add_view(request, form_url='../../add/')
ModelForm = self.get_form(request, obj)
inline_formsets = []
if request.method == 'POST':
form = ModelForm(request.POST, request.FILES, instance=obj)
for FormSet in self.get_formsets(request, obj):
inline_formset = FormSet(request.POST, request.FILES, instance=obj)
inline_formsets.append(inline_formset)
if all_valid(inline_formsets) and form.is_valid():
return self.save_change(request, form, inline_formsets)
else:
form = ModelForm(instance=obj)
for FormSet in self.get_formsets(request, obj):
inline_formset = FormSet(instance=obj)
inline_formsets.append(inline_formset)
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)
context = {
'title': _('Change %s') % opts.verbose_name,
'adminform': adminForm,
'object_id': object_id,
'original': obj,
'is_popup': request.REQUEST.has_key('_popup'),
'media': mark_safe(media),
'inline_admin_formsets': inline_admin_formsets,
'errors': AdminErrorList(form, inline_formsets),
'root_path': self.admin_site.root_path,
}
context.update(extra_context or {})
return self.render_change_form(request, context, change=True, obj=obj)
def changelist_view(self, request, extra_context=None):
"The 'change list' admin view for this model."
from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
opts = self.model._meta
app_label = opts.app_label
if not self.has_change_permission(request, None):
raise PermissionDenied
try:
cl = ChangeList(request, self.model, self.list_display, self.list_display_links, self.list_filter,
self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self)
except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
# parameter via the query string. If wacky parameters were given and
# the 'invalid=1' parameter was already in the query string, something
# is screwed up with the database, so display an error page.
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,
'cl': cl,
'has_add_permission': self.has_add_permission(request),
'root_path': self.admin_site.root_path,
}
context.update(extra_context or {})
return render_to_response(self.change_list_template or [
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
'admin/%s/change_list.html' % app_label,
'admin/change_list.html'
], context, context_instance=template.RequestContext(request))
def delete_view(self, request, object_id, extra_context=None):
"The 'delete' admin view for this model."
from django.contrib.admin.models import LogEntry, DELETION
opts = self.model._meta
app_label = opts.app_label
try:
obj = self.model._default_manager.get(pk=object_id)
except self.model.DoesNotExist:
# Don't raise Http404 just yet, because we haven't checked
# permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists.
obj = None
if not self.has_delete_permission(request, obj):
raise PermissionDenied
if obj is None:
raise Http404('%s object with primary key %r does not exist.' % (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()
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.
if perms_needed:
raise PermissionDenied
obj_display = str(obj)
obj.delete()
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, object_id, obj_display, DELETION)
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../")
return HttpResponseRedirect("../../")
context = {
"title": _("Are you sure?"),
"object_name": opts.verbose_name,
"object": obj,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
"opts": opts,
"root_path": self.admin_site.root_path,
}
context.update(extra_context or {})
return render_to_response(self.delete_confirmation_template or [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
], context, context_instance=template.RequestContext(request))
def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry
model = self.model
opts = model._meta
action_list = LogEntry.objects.filter(
object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=object_id)
context = {
'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list,
'module_name': capfirst(opts.verbose_name_plural),
'object': obj,
'root_path': self.admin_site.root_path,
}
context.update(extra_context or {})
return render_to_response(self.object_history_template or [
"admin/%s/%s/object_history.html" % (opts.app_label, opts.object_name.lower()),
"admin/%s/object_history.html" % opts.app_label,
"admin/object_history.html"
], context, context_instance=template.RequestContext(request))
class InlineModelAdmin(BaseModelAdmin):
"""
Options for inline editing of ``model`` instances.
Provide ``name`` to specify the attribute name of the ``ForeignKey`` from
``model`` to its parent. This is required if ``model`` has more than one
``ForeignKey`` to its parent.
"""
model = None
fk_name = None
formset = BaseInlineFormset
extra = 3
max_num = 0
template = None
verbose_name = None
verbose_name_plural = None
def __init__(self, parent_model, admin_site):
self.admin_site = admin_site
self.parent_model = parent_model
self.opts = self.model._meta
super(InlineModelAdmin, self).__init__()
if self.verbose_name is None:
self.verbose_name = self.model._meta.verbose_name
if self.verbose_name_plural is None:
self.verbose_name_plural = self.model._meta.verbose_name_plural
def get_formset(self, request, obj=None):
"""Returns a BaseInlineFormSet class for use in admin add/change views."""
if self.declared_fieldsets:
fields = flatten_fieldsets(self.declared_fieldsets)
else:
fields = None
return inlineformset_factory(self.parent_model, self.model,
form=self.form, formset=self.formset, fk_name=self.fk_name,
fields=fields, formfield_callback=self.formfield_for_dbfield,
extra=self.extra, max_num=self.max_num)
def get_fieldsets(self, request, obj=None):
if self.declared_fieldsets:
return self.declared_fieldsets
form = self.get_formset(request).form
return [(None, {'fields': form.base_fields.keys()})]
class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html'
class TabularInline(InlineModelAdmin):
template = 'admin/edit_inline/tabular.html'
class InlineAdminFormSet(object):
"""
A wrapper around an inline formset for use in the admin system.
"""
def __init__(self, inline, formset, fieldsets):
self.opts = inline
self.formset = formset
self.fieldsets = fieldsets
def __iter__(self):
for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, original)
for form in self.formset.extra_forms:
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
def fields(self):
for field_name in flatten_fieldsets(self.fieldsets):
yield self.formset.form.base_fields[field_name]
class InlineAdminForm(AdminForm):
"""
A wrapper around an inline form for use in the admin system.
"""
def __init__(self, formset, form, fieldsets, prepopulated_fields, original):
self.formset = formset
self.original = original
self.show_url = original and hasattr(original, 'get_absolute_url')
super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
def pk_field(self):
return AdminField(self.form, self.formset._pk_field_name, False)
def deletion_field(self):
from django.forms.formsets import DELETION_FIELD_NAME
return AdminField(self.form, DELETION_FIELD_NAME, False)
def ordering_field(self):
from django.forms.formsets import ORDERING_FIELD_NAME
return AdminField(self.form, ORDERING_FIELD_NAME, False)
class AdminErrorList(forms.util.ErrorList):
"""
Stores all errors for the form/formsets in an add/change stage view.
"""
def __init__(self, form, inline_formsets):
if form.is_bound:
self.extend(form.errors.values())
for inline_formset in inline_formsets:
self.extend(inline_formset.non_form_errors())
for errors_in_inline_form in inline_formset.errors:
self.extend(errors_in_inline_form.values())

View File

@ -0,0 +1,349 @@
from django import http, template
from django.contrib.admin import ModelAdmin
from django.contrib.auth import authenticate, login
from django.db.models.base import ModelBase
from django.shortcuts import render_to_response
from django.utils.safestring import mark_safe
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
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'
USER_CHANGE_PASSWORD_URL_RE = re.compile('auth/user/(\d+)/password')
class AlreadyRegistered(Exception):
pass
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()
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:
from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User may have tampered with session cookie."
return pickle.loads(pickled)
class AdminSite(object):
"""
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
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
def register(self, model_or_iterable, admin_class=None, **options):
"""
Registers the given model(s) with the given admin class.
The model(s) should be Model classes, not instances.
If an admin class isn't given, it will use ModelAdmin (the default
admin options). If keyword arguments are given -- e.g., list_display --
they'll be applied as options to the admin class.
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
from django.contrib.admin.validation import validate
admin_class = admin_class or ModelAdmin
# TODO: Handle options
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)
self._registry[model] = admin_class(model, self)
def unregister(self, model_or_iterable):
"""
Unregisters the given model(s).
If a model isn't already registered, this will raise NotRegistered.
"""
if isinstance(model_or_iterable, ModelBase):
model_or_iterable = [model_or_iterable]
for model in model_or_iterable:
if model not in self._registry:
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 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)
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
to the appropriate ModelAdmin class.
"""
from django.db import models
model = models.get_model(app_label, model_name)
if model is None:
raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
try:
admin_obj = self._registry[model]
except KeyError:
raise http.Http404("This model exists but has not been registered with the admin site.")
return admin_obj(request, rest_of_url)
model_page = never_cache(model_page)
def password_change(self, request):
"""
Handles the "change password" task -- both form display and validation.
"""
from django.contrib.auth.views import password_change
return password_change(request)
def password_change_done(self, request):
"""
Displays the "success" page after a password change.
"""
from django.contrib.auth.views import password_change_done
return password_change_done(request)
def user_change_password(self, request, id):
"""
Handles the "user change password" task
"""
from django.contrib.auth.views import user_change_password
return user_change_password(request, id)
def i18n_javascript(self, request):
"""
Displays the i18n JavaScript that the Django admin requires.
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:
from django.views.i18n import null_javascript_catalog as javascript_catalog
return javascript_catalog(request, packages='django.conf')
def logout(self, request):
"""
Logs out the user for the given HttpRequest.
This should *not* assume the user is already logged in.
"""
from django.contrib.auth.views import logout
return logout(request)
logout = never_cache(logout)
def login(self, request):
"""
Displays the login form for the given HttpRequest.
"""
from django.contrib.auth.models import User
# If this isn't already the login page, display it.
if not request.POST.has_key(LOGIN_FORM_KEY):
if request.POST:
message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.")
else:
message = ""
return self.display_login_form(request, message)
# Check that the user accepts cookies.
if not request.session.test_cookie_worked():
message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
return self.display_login_form(request, message)
# Check the password.
username = request.POST.get('username', None)
password = request.POST.get('password', None)
user = authenticate(username=username, password=password)
if user is None:
message = ERROR_MESSAGE
if u'@' in username:
# Mistakenly entered e-mail address instead of username? Look it up.
try:
user = User.objects.get(email=username)
except (User.DoesNotExist, User.MultipleObjectsReturned):
message = _("Usernames cannot contain the '@' character.")
else:
if user.check_password(password):
message = _("Your e-mail address is not your username."
" Try '%s' instead.") % user.username
else:
message = _("Usernames cannot contain the '@' character.")
return self.display_login_form(request, message)
# The user data is correct; log in the user in and continue.
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):
# overwrite request.POST with the saved post_data, and continue
request.POST = post_data
request.user = user
return self.root(request, request.path.split(self.root_path)[-1])
else:
request.session.delete_test_cookie()
return http.HttpResponseRedirect(request.path)
else:
return self.display_login_form(request, ERROR_MESSAGE)
login = never_cache(login)
def index(self, request, extra_context=None):
"""
Displays the main admin index page, which lists all of the installed
apps that have been registered in this site.
"""
app_dict = {}
user = request.user
for model, model_admin in self._registry.items():
app_label = model._meta.app_label
has_module_perms = user.has_module_perms(app_label)
if has_module_perms:
perms = {
'add': model_admin.has_add_permission(request),
'change': model_admin.has_change_permission(request),
'delete': model_admin.has_delete_permission(request),
}
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
'perms': perms,
}
if app_label in app_dict:
app_dict[app_label]['models'].append(model_dict)
else:
app_dict[app_label] = {
'name': app_label.title(),
'has_module_perms': has_module_perms,
'models': [model_dict],
}
# Sort the apps alphabetically.
app_list = app_dict.values()
app_list.sort(lambda x, y: cmp(x['name'], y['name']))
# 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,
context_instance=template.RequestContext(request)
)
index = never_cache(index)
def display_login_form(self, request, error_message='', extra_context=None):
request.session.set_test_cookie()
if request.POST and request.POST.has_key('post_data'):
# User has failed login BUT has previously saved post data.
post_data = request.POST['post_data']
elif request.POST:
# User's session must have expired; save their post data.
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
context = {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'error_message': error_message,
'root_path': self.root_path,
}
context.update(extra_context or {})
return render_to_response(self.login_template or 'admin/login.html', context,
context_instance=template.RequestContext(request)
)
# This global object represents the default admin site, for the common case.
# You can instantiate AdminSite in your own code to create a custom admin site.
site = AdminSite()

View File

@ -8,21 +8,26 @@
<fieldset class="module aligned"> <fieldset class="module aligned">
<div class="form-row"> <div class="form-row">
{{ form.username.html_error_list }} {{ form.username.errors }}
{# TODO: get required class on label_tag #}
<label for="id_username" class="required">{% trans 'Username' %}:</label> {{ form.username }} <label for="id_username" class="required">{% trans 'Username' %}:</label> {{ form.username }}
<p class="help">{{ username_help_text }}</p> <p class="help">{{ form.username.help_text }}</p>
</div> </div>
<div class="form-row"> <div class="form-row">
{{ form.password1.html_error_list }} {{ form.password1.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }} <label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div> </div>
<div class="form-row"> <div class="form-row">
{{ form.password2.html_error_list }} {{ form.password2.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }} <label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p> <p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div> </div>
<script type="text/javascript">document.getElementById("id_username").focus();</script>
</fieldset> </fieldset>
{% endblock %} {% endblock %}

View File

@ -2,7 +2,6 @@
{% load i18n admin_modify adminmedia %} {% load i18n admin_modify adminmedia %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../../jsi18n/"></script> <script type="text/javascript" src="../../../../jsi18n/"></script>
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
{% endblock %} {% endblock %}
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
@ -18,9 +17,9 @@
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %} <form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div> <div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if form.error_dict %} {% if form.errors %}
<p class="errornote"> <p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} {% blocktrans count form.errors.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p> </p>
{% endif %} {% endif %}
@ -29,12 +28,14 @@
<fieldset class="module aligned"> <fieldset class="module aligned">
<div class="form-row"> <div class="form-row">
{{ form.password1.html_error_list }} {{ form.password1.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }} <label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div> </div>
<div class="form-row"> <div class="form-row">
{{ form.password2.html_error_list }} {{ form.password2.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }} <label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p> <p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div> </div>
@ -45,7 +46,7 @@
<input type="submit" value="{% trans 'Change password' %}" class="default" /> <input type="submit" value="{% trans 'Change password' %}" class="default" />
</div> </div>
<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script> <script type="text/javascript">document.getElementById("id_password1").focus();</script>
</div> </div>
</form></div> </form></div>
{% endblock %} {% endblock %}

View File

@ -22,14 +22,7 @@
{% block branding %}{% endblock %} {% block branding %}{% endblock %}
</div> </div>
{% if user.is_authenticated and user.is_staff %} {% if user.is_authenticated and user.is_staff %}
<div id="user-tools"> <div id="user-tools">{% trans 'Welcome,' %} <strong>{% if user.first_name %}{{ user.first_name|escape }}{% else %}{{ user.username }}{% endif %}</strong>. {% block userlinks %}<a href="{{ root_path }}doc/">{% trans 'Documentation' %}</a> / <a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div>
{% trans 'Welcome,' %} <strong>{% if user.first_name %}{{ user.first_name|escape }}{% else %}{{ user.username }}{% endif %}</strong>.
{% block userlinks %}
<a href="{% url django.contrib.admin.views.doc.doc_index %}">{% trans 'Documentation' %}</a>
/ <a href="{% url django.contrib.auth.views.password_change %}">{% trans 'Change password' %}</a>
/ <a href="{% url django.contrib.auth.views.logout %}">{% trans 'Log out' %}</a>
{% endblock %}
</div>
{% endif %} {% endif %}
{% block nav-global %}{% endblock %} {% block nav-global %}{% endblock %}
</div> </div>

View File

@ -1,12 +1,17 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n admin_modify adminmedia %} {% load i18n admin_modify adminmedia %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../jsi18n/"></script> <script type="text/javascript" src="../../../jsi18n/"></script>
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %} {{ media }}
{% endblock %} {% endblock %}
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %} {% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
{% block breadcrumbs %}{% if not is_popup %} {% block breadcrumbs %}{% if not is_popup %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="../../../">{% trans "Home" %}</a> &rsaquo; <a href="../../../">{% trans "Home" %}</a> &rsaquo;
@ -14,6 +19,7 @@
{% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %} {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div> </div>
{% endif %}{% endblock %} {% endif %}{% endblock %}
{% block content %}<div id="content-main"> {% block content %}<div id="content-main">
{% block object-tools %} {% block object-tools %}
{% if change %}{% if not is_popup %} {% if change %}{% if not is_popup %}
@ -25,45 +31,48 @@
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %} <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div> <div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if opts.admin.save_on_top %}{% submit_row %}{% endif %} {% if save_on_top %}{% submit_row %}{% endif %}
{% if form.error_dict %} {% if errors %}
<p class="errornote"> <p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} {% blocktrans count errors.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p> </p>
<ul class="errorlist">{% for error in adminform.form.non_field_errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %} {% endif %}
{% for bound_field_set in bound_field_sets %}
<fieldset class="module aligned {{ bound_field_set.classes }}"> {% for fieldset in adminform %}
{% if bound_field_set.name %}<h2>{{ bound_field_set.name }}</h2>{% endif %} {% include "admin/includes/fieldset.html" %}
{% if bound_field_set.description %}<div class="description">{{ bound_field_set.description|safe }}</div>{% endif %}
{% for bound_field_line in bound_field_set %}
{% admin_field_line bound_field_line %}
{% for bound_field in bound_field_line %}
{% filter_interface_script_maybe bound_field %}
{% endfor %}
{% endfor %}
</fieldset>
{% endfor %} {% endfor %}
{% block after_field_sets %}{% endblock %} {% block after_field_sets %}{% endblock %}
{% if change %}
{% if ordered_objects %} {% for inline_admin_formset in inline_admin_formsets %}
<fieldset class="module"><h2>{% trans "Ordering" %}</h2> {% include inline_admin_formset.opts.template %}
<div class="form-row{% if form.order_.errors %} error{% endif %} "> {% endfor %}
{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
<p><label for="id_order_">{% trans "Order:" %}</label> {{ form.order_ }}</p>
</div></fieldset>
{% endif %}
{% endif %}
{% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %}
{% block after_related_objects %}{% endblock %} {% block after_related_objects %}{% endblock %}
{% submit_row %} {% submit_row %}
{% if add %} {% if add %}
<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script> <script type="text/javascript">document.getElementById("{{ adminform.first_field.auto_id }}").focus();</script>
{% endif %} {% endif %}
{% if auto_populated_fields %}
<script type="text/javascript"> {# JavaScript for prepopulated fields #}
{% auto_populated_field_script auto_populated_fields change %}
</script> {% if add %}
<script type="text/javascript">
{% for field in adminform.prepopulated_fields %}
document.getElementById("{{ field.field.auto_id }}").onchange = function() { this._changed = true; };
{% for dependency in field.dependencies %}
document.getElementById("{{ dependency.auto_id }}").onkeyup = function() {
var e = document.getElementById("{{ field.field.auto_id }}");
if (!e._changed) { e.value = URLify({% for innerdep in field.dependencies %}document.getElementById("{{ innerdep.auto_id }}").value{% if not forloop.last %} + ' ' + {% endif %}{% endfor %}, {{ field.field.field.max_length }}); }
}
{% endfor %}
{% endfor %}
</script>
{% endif %} {% endif %}
</div> </div>
</form></div> </form></div>
{% endblock %} {% endblock %}

View File

@ -1,9 +1,14 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load adminmedia admin_list i18n %} {% load adminmedia admin_list i18n %}
{% block stylesheet %}{% admin_media_prefix %}css/changelists.css{% endblock %} {% block stylesheet %}{% admin_media_prefix %}css/changelists.css{% endblock %}
{% block bodyclass %}change-list{% endblock %} {% block bodyclass %}change-list{% endblock %}
{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst|escape }}</div>{% endblock %}{% endif %} {% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst|escape }}</div>{% endblock %}{% endif %}
{% block coltype %}flex{% endblock %} {% block coltype %}flex{% endblock %}
{% block content %} {% block content %}
<div id="content-main"> <div id="content-main">
{% block object-tools %} {% block object-tools %}
@ -14,7 +19,18 @@
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist"> <div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
{% block search %}{% search_form cl %}{% endblock %} {% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %} {% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
{% block filters %}{% filters cl %}{% endblock %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% admin_list_filter cl spec %}
{% endfor %}
</div>
{% endif %}
{% endblock %}
{% block result_list %}{% result_list cl %}{% endblock %} {% block result_list %}{% result_list cl %}{% endblock %}
{% block pagination %}{% pagination cl %}{% endblock %} {% block pagination %}{% pagination cl %}{% endblock %}
</div> </div>

View File

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n %} {% load i18n %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="../../../../">{% trans "Home" %}</a> &rsaquo; <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
@ -8,6 +9,7 @@
{% trans 'Delete' %} {% trans 'Delete' %}
</div> </div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if perms_lacking %} {% if perms_lacking %}
<p>{% blocktrans with object|escape as escaped_object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p> <p>{% blocktrans with object|escape as escaped_object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>

View File

@ -0,0 +1,26 @@
{% load i18n %}
<div class="inline-group">
{{ inline_admin_formset.formset.management_form }}
{# <h3 class="header">{{ inline_admin_formset.opts.verbose_name_plural|title }}</h3> #}
{{ inline_admin_formset.formset.non_form_errors }}
{% for inline_admin_form in inline_admin_formset %}
<div class="inline-related {% if forloop.last %}last-related{% endif %}">
<h2><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %} #{{ forloop.counter }}{% endif %}
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
</h2>
{% if inline_admin_form.show_url %}
<p><a href="/r/{{ inline_admin_form.original.content_type_id }}/{{ inline_admin_form.original.id }}/">View on site</a></p>
{% endif %}
{% for fieldset in inline_admin_form %}
{% include "admin/includes/fieldset.html" %}
{% endfor %}
{{ inline_admin_form.pk_field.field }}
</div>
{% endfor %}
{# <ul class="tools"> #}
{# <li><a class="add" href="">Add another {{ inline_admin_formset.opts.verbose_name|title }}</a></li> #}
{# </ul> #}
</div>

View File

@ -0,0 +1,64 @@
{% load i18n %}
<div class="inline-group">
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
{{ inline_admin_formset.formset.management_form }}
<fieldset class="module">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst|escape }}</h2>
{{ inline_admin_formset.formset.non_form_errors }}
<table>
<thead><tr>
{% for field in inline_admin_formset.fields %}
{% if not field.is_hidden %}
<th {% if forloop.first %}colspan="2"{% endif %}>{{ field.label|capfirst|escape }}</th>
{% endif %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete" %}?</th>{% endif %}
</tr></thead>
{% for inline_admin_form in inline_admin_formset %}
<tr class="{% cycle row1,row2 %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}">
<td class="original">{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
{% if inline_admin_form.show_url %}<a href="/r/{{ inline_admin_form.original.content_type_id }}/{{ inline_admin_form.original.id }}/">View on site</a>{% endif %}
</p>{% endif %}
{{ inline_admin_form.pk_field.field }}
{% spaceless %}
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if field.is_hidden %} {{ field.field }} {% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endspaceless %}
</td>
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
<td class="{{ field.field.name }}">
{{ field.field.errors.as_ul }}
{{ field.field }}
</td>
{% endfor %}
{% endfor %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>{% endif %}
</tr>
{% endfor %}
</table>
</fieldset>
</div>
{# <ul class="tools"> #}
{# <li><a class="add" href="">Add another {{ inline_admin_formset.opts.verbose_name|title }}</a></li> #}
{# </ul> #}
</div>

View File

@ -1,16 +0,0 @@
{% load admin_modify %}
<fieldset class="module aligned">
{% for fcw in bound_related_object.form_field_collection_wrappers %}
<h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
{% if bound_related_object.show_url %}{% if fcw.obj.original %}
<p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p>
{% endif %}{% endif %}
{% for bound_field in fcw.bound_fields %}
{% if bound_field.hidden %}
{% field_widget bound_field %}
{% else %}
{% admin_field_line bound_field %}
{% endif %}
{% endfor %}
{% endfor %}
</fieldset>

View File

@ -1,44 +0,0 @@
{% load admin_modify %}
<fieldset class="module">
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
<thead><tr>
{% for fw in bound_related_object.field_wrapper_list %}
{% if fw.needs_header %}
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
{% endif %}
{% endfor %}
</tr></thead>
{% for fcw in bound_related_object.form_field_collection_wrappers %}
{% if change %}{% if original_row_needed %}
{% if fcw.obj.original %}
<tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
{% endif %}
{% endif %}{% endif %}
{% if fcw.obj.errors %}
<tr class="errorlist"><td colspan="{{ num_headers }}">
{{ fcw.obj.html_combined_error_list }}
</tr>
{% endif %}
<tr class="{% cycle row1,row2 %}">
{% for bound_field in fcw.bound_fields %}
{% if not bound_field.hidden %}
<td {{ bound_field.cell_class_attribute }}>
{% field_widget bound_field %}
</td>
{% endif %}
{% endfor %}
{% if bound_related_object.show_url %}<td>
{% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
</td>{% endif %}
</tr>
{% endfor %} </table>
{% for fcw in bound_related_object.form_field_collection_wrappers %}
{% for bound_field in fcw.bound_fields %}
{% if bound_field.hidden %}
{% field_widget bound_field %}
{% endif %}
{% endfor %}
{% endfor %}
</fieldset>

View File

@ -1,10 +0,0 @@
{% load admin_modify %}
<div class="{{ class_names }}" >
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
{% for bound_field in bound_fields %}
{% if bound_field.has_label_first %}{% field_label bound_field %}{% endif %}
{% field_widget bound_field %}
{% if not bound_field.has_label_first %}{% field_label bound_field %}{% endif %}
{% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text|safe }}</p>{% endif %}
{% endfor %}
</div>

View File

@ -1,7 +0,0 @@
{% load admin_list %}
{% load i18n %}
{% if cl.has_filters %}<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% filter cl spec %}
{% endfor %}</div>{% endif %}

View File

@ -0,0 +1,17 @@
<fieldset class="module aligned {{ fieldset.classes }}">
{% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
{% if fieldset.description %}<div class="description">{{ fieldset.description }}</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 }}
{% for field in line %}
{% if field.is_checkbox %}
{{ field.field }}{{ field.label_tag }}
{% else %}
{{ field.label_tag }}{{ field.field }}
{% endif %}
{% if field.field.field.help_text %}<p class="help">{{ field.field.field.help_text|safe }}</p>{% endif %}
{% endfor %}
</div>
{% endfor %}
</fieldset>

View File

@ -2,15 +2,16 @@
{% load i18n %} {% load i18n %}
{% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css{% endblock %} {% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css{% endblock %}
{% block coltype %}colMS{% endblock %} {% block coltype %}colMS{% endblock %}
{% block bodyclass %}dashboard{% endblock %} {% block bodyclass %}dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %} {% block breadcrumbs %}{% endblock %}
{% block content %} {% block content %}
<div id="content-main"> <div id="content-main">
{% load adminapplist %}
{% get_admin_app_list as app_list %}
{% if app_list %} {% if app_list %}
{% for app in app_list %} {% for app in app_list %}
<div class="module"> <div class="module">

View File

@ -4,7 +4,5 @@
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %}
{% block content %} {% block content %}
<p>{% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}</p> <p>{% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}</p>
{% endblock %} {% endblock %}

View File

@ -2,12 +2,14 @@
{% load i18n %} {% load i18n %}
{% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/login.css{% endblock %} {% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/login.css{% endblock %}
{% block bodyclass %}login{% endblock %} {% block bodyclass %}login{% endblock %}
{% block content_title %}{% endblock %} {% block content_title %}{% endblock %}
{% block breadcrumbs %}{% endblock %} {% block breadcrumbs %}{% endblock %}
{% block content %} {% block content %}
{% if error_message %} {% if error_message %}
<p class="errornote">{{ error_message }}</p> <p class="errornote">{{ error_message }}</p>
{% endif %} {% endif %}

View File

@ -1,16 +1,15 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n %} {% load i18n %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"><a href="../../../../">{% trans 'Home' %}</a> &rsaquo; <a href="../../">{{ module_name }}</a> &rsaquo; <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo; {% trans 'History' %}</div> <div class="breadcrumbs"><a href="../../../../">{% trans 'Home' %}</a> &rsaquo; <a href="../../">{{ module_name }}</a> &rsaquo; <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo; {% trans 'History' %}</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div id="content-main"> <div id="content-main">
<div class="module"> <div class="module">
{% if action_list %} {% if action_list %}
<table id="change-history"> <table id="change-history">
<thead> <thead>
<tr> <tr>
@ -29,14 +28,9 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p> <p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,6 @@
{% load adminmedia %} {% load adminmedia %}
{% load i18n %} {% load i18n %}
{% if cl.lookup_opts.admin.search_fields %} {% if cl.search_fields %}
<div id="toolbar"><form id="changelist-search" action="" method="get"> <div id="toolbar"><form id="changelist-search" action="" method="get">
<div><!-- DIV needed for valid HTML --> <div><!-- DIV needed for valid HTML -->
<label for="searchbar"><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" alt="Search" /></label> <label for="searchbar"><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" alt="Search" /></label>

View File

@ -25,3 +25,4 @@
</div> </div>
{% endblock %} {% endblock %}

View File

@ -40,3 +40,4 @@
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n %} {% load i18n %}
{% block userlinks %}<a href="../../doc/">{% trans 'Documentation' %}</a> / {% trans 'Change password' %} / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
{% block title %}{% trans 'Password change successful' %}{% endblock %} {% block title %}{% trans 'Password change successful' %}{% endblock %}

View File

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n %} {% load i18n %}
{% block userlinks %}<a href="../doc/">{% trans 'Documentation' %}</a> / {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
{% block title %}{% trans 'Password change' %}{% endblock %} {% block title %}{% trans 'Password change' %}{% endblock %}

View File

@ -12,7 +12,7 @@
<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 reset your password and e-mail the new one to you." %}</p>
<form action="" method="post"> <form action="" method="post">
{% if form.email.errors %}{{ form.email.html_error_list }}{% endif %} {% if form.email.errors %}{{ form.email.errors }}{% endif %}
<p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p> <p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p>
</form> </form>

View File

@ -1,5 +0,0 @@
{% load i18n %}
<p class="datetime">
{% trans "Date:" %} {{ bound_field.form_fields.0 }}<br />
{% trans "Time:" %} {{ bound_field.form_fields.1 }}
</p>

View File

@ -1 +0,0 @@
{% load admin_modify %}{% output_all bound_field.form_fields %}

View File

@ -1,4 +0,0 @@
{% load admin_modify i18n %}{% if bound_field.original_value %}
{% trans "Currently:" %} <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value|escape }} </a><br />
{% trans "Change:" %}{% output_all bound_field.form_fields %}
{% else %} {% output_all bound_field.form_fields %} {% endif %}

View File

@ -1,20 +0,0 @@
{% load admin_modify adminmedia %}
{% output_all bound_field.form_fields %}
{% if bound_field.raw_id_admin %}
{% if bound_field.field.rel.limit_choices_to %}
<a href="{{ bound_field.related_url }}?{% for limit_choice in bound_field.field.rel.limit_choices_to.items %}{% if not forloop.first %}&amp;{% endif %}{{ limit_choice|join:"=" }}{% endfor %}" class="related-lookup" id="lookup_{{ bound_field.element_id }}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
{% else %}
<a href="{{ bound_field.related_url }}" class="related-lookup" id="lookup_{{ bound_field.element_id }}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
{% endif %}
{% else %}
{% if bound_field.needs_add_label %}
<a href="{{ bound_field.related_url }}add/" class="add-another" id="add_{{ bound_field.element_id }}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>
{% endif %}{% endif %}
{% if change %}
{% if bound_field.field.primary_key %}
{{ bound_field.original_value }}
{% endif %}
{% if bound_field.raw_id_admin %}
{% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}
{% endif %}
{% endif %}

View File

@ -1 +0,0 @@
{% include "widget/foreign.html" %}

View File

@ -1,2 +0,0 @@
{% if add %}{% include "widget/foreign.html" %}{% endif %}
{% if change %}{% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}{% endif %}

View File

@ -71,7 +71,7 @@ pagination = register.inclusion_tag('admin/pagination.html')(pagination)
def result_headers(cl): def result_headers(cl):
lookup_opts = cl.lookup_opts lookup_opts = cl.lookup_opts
for i, field_name in enumerate(lookup_opts.admin.list_display): for i, field_name in enumerate(cl.list_display):
try: try:
f = lookup_opts.get_field(field_name) f = lookup_opts.get_field(field_name)
admin_order_field = None admin_order_field = None
@ -123,7 +123,7 @@ def _boolean_icon(field_val):
def items_for_result(cl, result): def items_for_result(cl, result):
first = True first = True
pk = cl.lookup_opts.pk.attname pk = cl.lookup_opts.pk.attname
for field_name in cl.lookup_opts.admin.list_display: for field_name in cl.list_display:
row_class = '' row_class = ''
try: try:
f = cl.lookup_opts.get_field(field_name) f = cl.lookup_opts.get_field(field_name)
@ -189,7 +189,7 @@ def items_for_result(cl, result):
if force_unicode(result_repr) == '': if force_unicode(result_repr) == '':
result_repr = mark_safe('&nbsp;') result_repr = mark_safe('&nbsp;')
# If list_display_links not defined, add the link tag to the first field # If list_display_links not defined, add the link tag to the first field
if (first and not cl.lookup_opts.admin.list_display_links) or field_name in cl.lookup_opts.admin.list_display_links: if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True:'th', False:'td'}[first] table_tag = {True:'th', False:'td'}[first]
first = False first = False
url = cl.url_for_result(result) url = cl.url_for_result(result)
@ -212,8 +212,8 @@ def result_list(cl):
result_list = register.inclusion_tag("admin/change_list_results.html")(result_list) result_list = register.inclusion_tag("admin/change_list_results.html")(result_list)
def date_hierarchy(cl): def date_hierarchy(cl):
if cl.lookup_opts.admin.date_hierarchy: if cl.date_hierarchy:
field_name = cl.lookup_opts.admin.date_hierarchy field_name = cl.date_hierarchy
year_field = '%s__year' % field_name year_field = '%s__year' % field_name
month_field = '%s__month' % field_name month_field = '%s__month' % field_name
day_field = '%s__day' % field_name day_field = '%s__day' % field_name
@ -280,10 +280,6 @@ def search_form(cl):
} }
search_form = register.inclusion_tag('admin/search_form.html')(search_form) search_form = register.inclusion_tag('admin/search_form.html')(search_form)
def filter(cl, spec): def admin_list_filter(cl, spec):
return {'title': spec.title(), 'choices' : list(spec.choices(cl))} return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
filter = register.inclusion_tag('admin/filter.html')(filter) admin_list_filter = register.inclusion_tag('admin/filter.html')(admin_list_filter)
def filters(cl):
return {'cl': cl}
filters = register.inclusion_tag('admin/filters.html')(filters)

View File

@ -1,253 +1,21 @@
from django import template from django import template
from django.contrib.admin.views.main import AdminBoundField
from django.template import loader
from django.utils.text import capfirst
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.html import escape
from django.db import models
from django.db.models.fields import Field
from django.db.models.related import BoundRelatedObject
from django.conf import settings
import re
register = template.Library() register = template.Library()
word_re = re.compile('[A-Z][a-z]+')
absolute_url_re = re.compile(r'^(?:http(?:s)?:/)?/', re.IGNORECASE)
def class_name_to_underscored(name):
return u'_'.join([s.lower() for s in word_re.findall(name)[:-1]])
def include_admin_script(script_path):
"""
Returns an HTML script element for including a script from the admin
media url (or other location if an absolute url is given).
Example usage::
{% include_admin_script "js/calendar.js" %}
could return::
<script type="text/javascript" src="/media/admin/js/calendar.js">
"""
if not absolute_url_re.match(script_path):
script_path = '%s%s' % (settings.ADMIN_MEDIA_PREFIX, script_path)
return mark_safe(u'<script type="text/javascript" src="%s"></script>'
% script_path)
include_admin_script = register.simple_tag(include_admin_script)
def submit_row(context): def submit_row(context):
opts = context['opts'] opts = context['opts']
change = context['change'] change = context['change']
is_popup = context['is_popup'] is_popup = context['is_popup']
save_as = context['save_as']
return { return {
'onclick_attrib': (opts.get_ordered_objects() and change 'onclick_attrib': (opts.get_ordered_objects() and change
and 'onclick="submitOrderForm();"' or ''), and 'onclick="submitOrderForm();"' or ''),
'show_delete_link': (not is_popup and context['has_delete_permission'] 'show_delete_link': (not is_popup and context['has_delete_permission']
and (change or context['show_delete'])), and (change or context['show_delete'])),
'show_save_as_new': not is_popup and change and opts.admin.save_as, 'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': not is_popup and (not opts.admin.save_as or context['add']), 'show_save_and_add_another': context['has_add_permission'] and
not is_popup and (not save_as or context['add']),
'show_save_and_continue': not is_popup and context['has_change_permission'], 'show_save_and_continue': not is_popup and context['has_change_permission'],
'show_save': True 'show_save': True
} }
submit_row = register.inclusion_tag('admin/submit_line.html', takes_context=True)(submit_row) submit_row = register.inclusion_tag('admin/submit_line.html', takes_context=True)(submit_row)
def field_label(bound_field):
class_names = []
if isinstance(bound_field.field, models.BooleanField):
class_names.append("vCheckboxLabel")
colon = ""
else:
if not bound_field.field.blank:
class_names.append('required')
if not bound_field.first:
class_names.append('inline')
colon = ":"
class_str = class_names and u' class="%s"' % u' '.join(class_names) or u''
return mark_safe(u'<label for="%s"%s>%s%s</label> ' %
(bound_field.element_id, class_str,
escape(force_unicode(capfirst(bound_field.field.verbose_name))),
colon))
field_label = register.simple_tag(field_label)
class FieldWidgetNode(template.Node):
nodelists = {}
default = None
def __init__(self, bound_field_var):
self.bound_field_var = template.Variable(bound_field_var)
def get_nodelist(cls, klass):
if klass not in cls.nodelists:
try:
field_class_name = klass.__name__
template_name = u"widget/%s.html" % class_name_to_underscored(field_class_name)
nodelist = loader.get_template(template_name).nodelist
except template.TemplateDoesNotExist:
super_klass = bool(klass.__bases__) and klass.__bases__[0] or None
if super_klass and super_klass != Field:
nodelist = cls.get_nodelist(super_klass)
else:
if not cls.default:
cls.default = loader.get_template("widget/default.html").nodelist
nodelist = cls.default
cls.nodelists[klass] = nodelist
return nodelist
else:
return cls.nodelists[klass]
get_nodelist = classmethod(get_nodelist)
def render(self, context):
bound_field = self.bound_field_var.resolve(context)
context.push()
context['bound_field'] = bound_field
output = self.get_nodelist(bound_field.field.__class__).render(context)
context.pop()
return output
class FieldWrapper(object):
def __init__(self, field ):
self.field = field
def needs_header(self):
return not isinstance(self.field, models.AutoField)
def header_class_attribute(self):
return self.field.blank and mark_safe(' class="optional"') or ''
def use_raw_id_admin(self):
return isinstance(self.field.rel, (models.ManyToOneRel, models.ManyToManyRel)) \
and self.field.rel.raw_id_admin
class FormFieldCollectionWrapper(object):
def __init__(self, field_mapping, fields, index):
self.field_mapping = field_mapping
self.fields = fields
self.bound_fields = [AdminBoundField(field, self.field_mapping, field_mapping['original'])
for field in self.fields]
self.index = index
class TabularBoundRelatedObject(BoundRelatedObject):
def __init__(self, related_object, field_mapping, original):
super(TabularBoundRelatedObject, self).__init__(related_object, field_mapping, original)
self.field_wrapper_list = [FieldWrapper(field) for field in self.relation.editable_fields()]
fields = self.relation.editable_fields()
self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping, fields, i)
for (i,field_mapping) in self.field_mappings.items() ]
self.original_row_needed = max([fw.use_raw_id_admin() for fw in self.field_wrapper_list])
self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
def template_name(self):
return "admin/edit_inline_tabular.html"
class StackedBoundRelatedObject(BoundRelatedObject):
def __init__(self, related_object, field_mapping, original):
super(StackedBoundRelatedObject, self).__init__(related_object, field_mapping, original)
fields = self.relation.editable_fields()
self.field_mappings.fill()
self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields, i)
for (i,field_mapping) in self.field_mappings.items()]
self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
def template_name(self):
return "admin/edit_inline_stacked.html"
class EditInlineNode(template.Node):
def __init__(self, rel_var):
self.rel_var = template.Variable(rel_var)
def render(self, context):
relation = self.rel_var.resolve(context)
context.push()
if relation.field.rel.edit_inline == models.TABULAR:
bound_related_object_class = TabularBoundRelatedObject
elif relation.field.rel.edit_inline == models.STACKED:
bound_related_object_class = StackedBoundRelatedObject
else:
bound_related_object_class = relation.field.rel.edit_inline
original = context.get('original', None)
bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
context['bound_related_object'] = bound_related_object
t = loader.get_template(bound_related_object.template_name())
output = t.render(context)
context.pop()
return output
def output_all(form_fields):
return u''.join([force_unicode(f) for f in form_fields])
output_all = register.simple_tag(output_all)
def auto_populated_field_script(auto_pop_fields, change = False):
t = []
for field in auto_pop_fields:
if change:
t.append(u'document.getElementById("id_%s")._changed = true;' % field.name)
else:
t.append(u'document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name)
add_values = u' + " " + '.join([u'document.getElementById("id_%s").value' % g for g in field.prepopulate_from])
for f in field.prepopulate_from:
t.append(u'document.getElementById("id_%s").onkeyup = function() {' \
' var e = document.getElementById("id_%s");' \
' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
f, field.name, add_values, field.max_length))
return mark_safe(u''.join(t))
auto_populated_field_script = register.simple_tag(auto_populated_field_script)
def filter_interface_script_maybe(bound_field):
f = bound_field.field
if f.rel and isinstance(f.rel, models.ManyToManyRel) and f.rel.filter_interface:
return mark_safe(u'<script type="text/javascript">addEvent(window, "load", function(e) {' \
' SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (
f.name, escape(f.verbose_name.replace('"', '\\"')), f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX))
else:
return ''
filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
def field_widget(parser, token):
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError, "%s takes 1 argument" % bits[0]
return FieldWidgetNode(bits[1])
field_widget = register.tag(field_widget)
def edit_inline(parser, token):
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError, "%s takes 1 argument" % bits[0]
return EditInlineNode(bits[1])
edit_inline = register.tag(edit_inline)
def admin_field_line(context, argument_val):
if isinstance(argument_val, AdminBoundField):
bound_fields = [argument_val]
else:
bound_fields = [bf for bf in argument_val]
add = context['add']
change = context['change']
class_names = ['form-row']
for bound_field in bound_fields:
for f in bound_field.form_fields:
if f.errors():
class_names.append('errors')
break
# Assumes BooleanFields won't be stacked next to each other!
if isinstance(bound_fields[0].field, models.BooleanField):
class_names.append('checkbox-row')
return {
'add': context['add'],
'change': context['change'],
'bound_fields': bound_fields,
'class_names': " ".join(class_names),
}
admin_field_line = register.inclusion_tag('admin/field_line.html', takes_context=True)(admin_field_line)

View File

@ -1,81 +0,0 @@
from django import template
from django.db.models import get_models
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
register = template.Library()
class AdminApplistNode(template.Node):
def __init__(self, varname):
self.varname = varname
def render(self, context):
from django.db import models
from django.utils.text import capfirst
app_list = []
user = context['user']
for app in models.get_apps():
# Determine the app_label.
app_models = get_models(app)
if not app_models:
continue
app_label = app_models[0]._meta.app_label
has_module_perms = user.has_module_perms(app_label)
if has_module_perms:
model_list = []
for m in app_models:
if m._meta.admin:
perms = {
'add': user.has_perm("%s.%s" % (app_label, m._meta.get_add_permission())),
'change': user.has_perm("%s.%s" % (app_label, m._meta.get_change_permission())),
'delete': user.has_perm("%s.%s" % (app_label, m._meta.get_delete_permission())),
}
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
model_list.append({
'name': force_unicode(capfirst(m._meta.verbose_name_plural)),
'admin_url': mark_safe(u'%s/%s/' % (force_unicode(app_label), m.__name__.lower())),
'perms': perms,
})
if model_list:
# Sort using verbose decorate-sort-undecorate pattern
# instead of key argument to sort() for python 2.3 compatibility
decorated = [(x['name'], x) for x in model_list]
decorated.sort()
model_list = [x for key, x in decorated]
app_list.append({
'name': app_label.title(),
'has_module_perms': has_module_perms,
'models': model_list,
})
context[self.varname] = app_list
return ''
def get_admin_app_list(parser, token):
"""
Returns a list of installed applications and models for which the current user
has at least one permission.
Syntax::
{% get_admin_app_list as [context_var_containing_app_list] %}
Example usage::
{% get_admin_app_list as admin_app_list %}
"""
tokens = token.contents.split()
if len(tokens) < 3:
raise template.TemplateSyntaxError, "'%s' tag requires two arguments" % tokens[0]
if tokens[1] != 'as':
raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
return AdminApplistNode(tokens[2])
register.tag('get_admin_app_list', get_admin_app_list)

View File

@ -1,43 +0,0 @@
from django.conf import settings
from django.conf.urls.defaults import *
if settings.USE_I18N:
i18n_view = 'django.views.i18n.javascript_catalog'
else:
i18n_view = 'django.views.i18n.null_javascript_catalog'
urlpatterns = patterns('',
('^$', 'django.contrib.admin.views.main.index'),
('^r/', include('django.conf.urls.shortcut')),
('^jsi18n/$', i18n_view, {'packages': 'django.conf'}),
('^logout/$', 'django.contrib.auth.views.logout'),
('^password_change/$', 'django.contrib.auth.views.password_change'),
('^password_change/done/$', 'django.contrib.auth.views.password_change_done'),
('^template_validator/$', 'django.contrib.admin.views.template.template_validator'),
# Documentation
('^doc/$', 'django.contrib.admin.views.doc.doc_index'),
('^doc/bookmarklets/$', 'django.contrib.admin.views.doc.bookmarklets'),
('^doc/tags/$', 'django.contrib.admin.views.doc.template_tag_index'),
('^doc/filters/$', 'django.contrib.admin.views.doc.template_filter_index'),
('^doc/views/$', 'django.contrib.admin.views.doc.view_index'),
('^doc/views/(?P<view>[^/]+)/$', 'django.contrib.admin.views.doc.view_detail'),
('^doc/models/$', 'django.contrib.admin.views.doc.model_index'),
('^doc/models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$', 'django.contrib.admin.views.doc.model_detail'),
# ('^doc/templates/$', 'django.views.admin.doc.template_index'),
('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'),
# "Add user" -- a special-case view
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
# "Change user password" -- another special-case view
('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'),
# Add/change/delete/history
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
('^([^/]+)/([^/]+)/(.+)/history/$', 'django.contrib.admin.views.main.history'),
('^([^/]+)/([^/]+)/(.+)/delete/$', 'django.contrib.admin.views.main.delete_stage'),
('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'),
)
del i18n_view

View File

@ -0,0 +1,139 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.encoding import force_unicode
from django.utils.translation import ugettext as _
def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
quoting is slightly different so that it doesn't get automatically
unquoted by the Web browser.
"""
if not isinstance(s, basestring):
return s
res = list(s)
for i in range(len(res)):
c = res[i]
if c in """:/_#?;@&=+$,"<>%\\""":
res[i] = '_%02X' % ord(c)
return ''.join(res)
def unquote(s):
"""
Undo the effects of quote(). Based heavily on urllib.unquote().
"""
mychr = chr
myatoi = int
list = s.split('_')
res = [list[0]]
myappend = res.append
del list[0]
for item in list:
if item[1:2]:
try:
myappend(mychr(myatoi(item[:2], 16)) + item[2:])
except ValueError:
myappend('_' + item)
else:
myappend('_' + item)
return "".join(res)
def _nest_help(obj, depth, val):
current = obj
for i in range(depth):
current = current[-1]
current.append(val)
def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth, admin_site):
"Helper function that recursively populates deleted_objects."
nh = _nest_help # Bind to local variable for performance
if current_depth > 16:
return # Avoid recursing too deep.
opts_seen = []
for related in opts.get_all_related_objects():
has_admin = related.model in admin_site._registry
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
rel_opts_name = related.get_accessor_name()
if isinstance(related.field.rel, models.OneToOneRel):
try:
sub_obj = getattr(obj, rel_opts_name)
except ObjectDoesNotExist:
pass
else:
if has_admin:
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
# We don't care about populating deleted_objects now.
continue
if related.field.rel.edit_inline or not has_admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
(escape(force_unicode(capfirst(related.opts.verbose_name))),
related.opts.app_label,
related.opts.object_name.lower(),
sub_obj._get_pk_val(), sub_obj)), []])
get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
else:
has_related_objs = False
for sub_obj in getattr(obj, rel_opts_name).all():
has_related_objs = True
if related.field.rel.edit_inline or not has_admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(escape(force_unicode(capfirst(related.opts.verbose_name))), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj))), []])
get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
# If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed.
if has_admin and has_related_objs:
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
for related in opts.get_all_related_many_to_many_objects():
has_admin = related.model in admin_site._registry
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
rel_opts_name = related.get_accessor_name()
has_related_objs = False
# related.get_accessor_name() could return None for symmetrical relationships
if rel_opts_name:
rel_objs = getattr(obj, rel_opts_name, None)
if rel_objs:
has_related_objs = True
if has_related_objs:
for sub_obj in rel_objs.all():
if related.field.rel.edit_inline or not has_admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
{'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [
mark_safe((_('One or more %(fieldname)s in %(name)s:') % {'fieldname': escape(force_unicode(related.field.verbose_name)), 'name': escape(force_unicode(related.opts.verbose_name))}) + \
(u' <a href="../../../../%s/%s/%s/">%s</a>' % \
(related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []])
# If there were related objects, and the user doesn't have
# permission to change them, add the missing perm to perms_needed.
if has_admin and has_related_objs:
p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)

View File

@ -0,0 +1,280 @@
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model
from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
from django.contrib.admin.options import HORIZONTAL, VERTICAL
def validate(cls, model):
"""
Does basic ModelAdmin option validation. Calls custom validation
classmethod in the end if it is provided in cls. The signature of the
custom validation classmethod should be: def validate(cls, model).
"""
opts = model._meta
_validate_base(cls, model)
# currying is expensive, use wrappers instead
def _check_istuplew(label, obj):
_check_istuple(cls, label, obj)
def _check_isdictw(label, obj):
_check_isdict(cls, label, obj)
def _check_field_existsw(label, field):
return _check_field_exists(cls, model, opts, label, field)
def _check_attr_existsw(label, field):
return _check_attr_exists(cls, model, opts, label, field)
# list_display
if hasattr(cls, 'list_display'):
_check_istuplew('list_display', cls.list_display)
for idx, field in enumerate(cls.list_display):
f = _check_attr_existsw("list_display[%d]" % idx, field)
if isinstance(f, models.ManyToManyField):
raise ImproperlyConfigured("`%s.list_display[%d]`, `%s` is a "
"ManyToManyField which is not supported."
% (cls.__name__, idx, field))
# list_display_links
if hasattr(cls, 'list_display_links'):
_check_istuplew('list_display_links', cls.list_display_links)
for idx, field in enumerate(cls.list_display_links):
_check_attr_existsw('list_display_links[%d]' % idx, field)
if field not in cls.list_display:
raise ImproperlyConfigured("`%s.list_display_links[%d]`"
"refers to `%s` which is not defined in `list_display`."
% (cls.__name__, idx, field))
# list_filter
if hasattr(cls, 'list_filter'):
_check_istuplew('list_filter', cls.list_filter)
for idx, field in enumerate(cls.list_filter):
_check_field_existsw('list_filter[%d]' % idx, field)
# list_per_page = 100
if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int):
raise ImproperlyConfigured("`%s.list_per_page` should be a integer."
% cls.__name__)
# search_fields = ()
if hasattr(cls, 'search_fields'):
_check_istuplew('search_fields', cls.search_fields)
# date_hierarchy = None
if cls.date_hierarchy:
f = _check_field_existsw('date_hierarchy', cls.date_hierarchy)
if not isinstance(f, (models.DateField, models.DateTimeField)):
raise ImproperlyConfigured("`%s.date_hierarchy is "
"neither an instance of DateField nor DateTimeField."
% cls.__name__)
# ordering = None
if cls.ordering:
_check_istuplew('ordering', cls.ordering)
for idx, field in enumerate(cls.ordering):
if field == '?' and len(cls.ordering) != 1:
raise ImproperlyConfigured("`%s.ordering` has the random "
"ordering marker `?`, but contains other fields as "
"well. Please either remove `?` or the other fields."
% cls.__name__)
if field == '?':
continue
if field.startswith('-'):
field = field[1:]
# Skip ordering in the format field1__field2 (FIXME: checking
# this format would be nice, but it's a little fiddly).
if '__' in field:
continue
_check_field_existsw('ordering[%d]' % idx, field)
# list_select_related = False
# save_as = False
# save_on_top = False
for attr in ('list_select_related', 'save_as', 'save_on_top'):
if not isinstance(getattr(cls, attr), bool):
raise ImproperlyConfigured("`%s.%s` should be a boolean."
% (cls.__name__, attr))
# inlines = []
if hasattr(cls, 'inlines'):
_check_istuplew('inlines', cls.inlines)
for idx, inline in enumerate(cls.inlines):
if not issubclass(inline, BaseModelAdmin):
raise ImproperlyConfigured("`%s.inlines[%d]` does not inherit "
"from BaseModelAdmin." % (cls.__name__, idx))
if not inline.model:
raise ImproperlyConfigured("`model` is a required attribute "
"of `%s.inlines[%d]`." % (cls.__name__, idx))
if not issubclass(inline.model, models.Model):
raise ImproperlyConfigured("`%s.inlines[%d].model` does not "
"inherit from models.Model." % (cls.__name__, idx))
_validate_base(inline, inline.model)
_validate_inline(inline)
def _validate_inline(cls):
# model is already verified to exist and be a Model
if cls.fk_name: # default value is None
f = _check_field_exists(cls, cls.model, cls.model._meta,
'fk_name', cls.fk_name)
if not isinstance(f, models.ForeignKey):
raise ImproperlyConfigured("`%s.fk_name is not an instance of "
"models.ForeignKey." % cls.__name__)
# extra = 3
# max_num = 0
for attr in ('extra', 'max_num'):
if not isinstance(getattr(cls, attr), int):
raise ImproperlyConfigured("`%s.%s` should be a integer."
% (cls.__name__, attr))
# formset
if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet):
raise ImproperlyConfigured("`%s.formset` does not inherit from "
"BaseModelFormSet." % cls.__name__)
def _validate_base(cls, model):
opts = model._meta
# currying is expensive, use wrappers instead
def _check_istuplew(label, obj):
_check_istuple(cls, label, obj)
def _check_isdictw(label, obj):
_check_isdict(cls, label, obj)
def _check_field_existsw(label, field):
return _check_field_exists(cls, model, opts, label, field)
def _check_form_field_existsw(label, field):
return _check_form_field_exists(cls, model, opts, label, field)
# raw_id_fields
if hasattr(cls, 'raw_id_fields'):
_check_istuplew('raw_id_fields', cls.raw_id_fields)
for idx, field in enumerate(cls.raw_id_fields):
f = _check_field_existsw('raw_id_fields', field)
if not isinstance(f, (models.ForeignKey, models.ManyToManyField)):
raise ImproperlyConfigured("`%s.raw_id_fields[%d]`, `%s` must "
"be either a ForeignKey or ManyToManyField."
% (cls.__name__, idx, field))
# fields
if cls.fields: # default value is None
_check_istuplew('fields', cls.fields)
for field in cls.fields:
_check_form_field_existsw('fields', field)
if cls.fieldsets:
raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
# fieldsets
if cls.fieldsets: # default value is None
_check_istuplew('fieldsets', cls.fieldsets)
for idx, fieldset in enumerate(cls.fieldsets):
_check_istuplew('fieldsets[%d]' % idx, fieldset)
if len(fieldset) != 2:
raise ImproperlyConfigured("`%s.fieldsets[%d]` does not "
"have exactly two elements." % (cls.__name__, idx))
_check_isdictw('fieldsets[%d][1]' % idx, fieldset[1])
if 'fields' not in fieldset[1]:
raise ImproperlyConfigured("`fields` key is required in "
"%s.fieldsets[%d][1] field options dict."
% (cls.__name__, idx))
for field in flatten_fieldsets(cls.fieldsets):
_check_form_field_existsw("fieldsets[%d][1]['fields']" % idx, field)
# form
if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm):
raise ImproperlyConfigured("%s.form does not inherit from "
"BaseModelForm." % cls.__name__)
# filter_vertical
if hasattr(cls, 'filter_vertical'):
_check_istuplew('filter_vertical', cls.filter_vertical)
for idx, field in enumerate(cls.filter_vertical):
f = _check_field_existsw('filter_vertical', field)
if not isinstance(f, models.ManyToManyField):
raise ImproperlyConfigured("`%s.filter_vertical[%d]` must be "
"a ManyToManyField." % (cls.__name__, idx))
# filter_horizontal
if hasattr(cls, 'filter_horizontal'):
_check_istuplew('filter_horizontal', cls.filter_horizontal)
for idx, field in enumerate(cls.filter_horizontal):
f = _check_field_existsw('filter_horizontal', field)
if not isinstance(f, models.ManyToManyField):
raise ImproperlyConfigured("`%s.filter_horizontal[%d]` must be "
"a ManyToManyField." % (cls.__name__, idx))
# radio_fields
if hasattr(cls, 'radio_fields'):
_check_isdictw('radio_fields', cls.radio_fields)
for field, val in cls.radio_fields.items():
f = _check_field_existsw('radio_fields', field)
if not (isinstance(f, models.ForeignKey) or f.choices):
raise ImproperlyConfigured("`%s.radio_fields['%s']` "
"is neither an instance of ForeignKey nor does "
"have choices set." % (cls.__name__, field))
if not val in (HORIZONTAL, VERTICAL):
raise ImproperlyConfigured("`%s.radio_fields['%s']` "
"is neither admin.HORIZONTAL nor admin.VERTICAL."
% (cls.__name__, field))
# prepopulated_fields
if hasattr(cls, 'prepopulated_fields'):
_check_isdictw('prepopulated_fields', cls.prepopulated_fields)
for field, val in cls.prepopulated_fields.items():
f = _check_field_existsw('prepopulated_fields', field)
if isinstance(f, (models.DateTimeField, models.ForeignKey,
models.ManyToManyField)):
raise ImproperlyConfigured("`%s.prepopulated_fields['%s']` "
"is either a DateTimeField, ForeignKey or "
"ManyToManyField. This isn't allowed."
% (cls.__name__, field))
_check_istuplew("prepopulated_fields['%s']" % field, val)
for idx, f in enumerate(val):
_check_field_existsw("prepopulated_fields['%s'][%d]"
% (f, idx), f)
def _check_istuple(cls, label, obj):
if not isinstance(obj, (list, tuple)):
raise ImproperlyConfigured("`%s.%s` must be a "
"list or tuple." % (cls.__name__, label))
def _check_isdict(cls, label, obj):
if not isinstance(obj, dict):
raise ImproperlyConfigured("`%s.%s` must be a dictionary."
% (cls.__name__, label))
def _check_field_exists(cls, model, opts, label, field):
try:
return opts.get_field(field)
except models.FieldDoesNotExist:
raise ImproperlyConfigured("`%s.%s` refers to "
"field `%s` that is missing from model `%s`."
% (cls.__name__, label, field, model.__name__))
def _check_form_field_exists(cls, model, opts, label, field):
if hasattr(cls.form, 'base_fields'):
try:
cls.form.base_fields[field]
except KeyError:
raise ImproperlyConfigured("`%s.%s` refers to field `%s` that "
"is missing from the form." % (cls.__name__, label, field))
else:
fields = fields_for_model(model)
try:
fields[field]
except KeyError:
raise ImproperlyConfigured("`%s.%s` refers to field `%s` that "
"is missing from the form." % (cls.__name__, label, field))
def _check_attr_exists(cls, model, opts, label, field):
try:
return opts.get_field(field)
except models.FieldDoesNotExist:
if not hasattr(model, field):
raise ImproperlyConfigured("`%s.%s` refers to "
"`%s` that is neither a field, method or property "
"of model `%s`."
% (cls.__name__, label, field, model.__name__))
return getattr(model, field)

View File

@ -1,78 +0,0 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django import oldforms, template
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django.utils.html import escape
from django.utils.translation import ugettext as _
def user_add_stage(request):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
manipulator = UserCreationForm()
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
if "_addanother" in request.POST:
request.user.message_set.create(message=msg)
return HttpResponseRedirect(request.path)
else:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
return HttpResponseRedirect('../%s/' % new_user.id)
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/add_form.html', {
'title': _('Add user'),
'form': form,
'is_popup': '_popup' in request.REQUEST,
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_file_field': False,
'has_absolute_url': False,
'auto_populated_fields': (),
'bound_field_sets': (),
'first_form_field_id': 'id_username',
'opts': User._meta,
'username_help_text': User._meta.get_field('username').help_text,
}, context_instance=template.RequestContext(request))
user_add_stage = staff_member_required(user_add_stage)
def user_change_password(request, id):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
user = get_object_or_404(User, pk=id)
manipulator = AdminPasswordChangeForm(user)
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('..')
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/change_password.html', {
'title': _('Change password: %s') % escape(user.username),
'form': form,
'is_popup': '_popup' in request.REQUEST,
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_absolute_url': False,
'first_form_field_id': 'id_password1',
'opts': User._meta,
'original': user,
'show_save': True,
}, context_instance=template.RequestContext(request))
user_change_password = staff_member_required(user_change_password)

View File

@ -12,7 +12,6 @@ from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
from django.utils.safestring import mark_safe
ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") 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' LOGIN_FORM_KEY = 'this_is_the_login_form'

View File

@ -1,20 +1,13 @@
from django import oldforms, template
from django.conf import settings
from django.contrib.admin.filterspecs import FilterSpec from django.contrib.admin.filterspecs import FilterSpec
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.options import IncorrectLookupParameters
from django.views.decorators.cache import never_cache from django.contrib.admin.util import quote
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
from django.core.paginator import Paginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.shortcuts import get_object_or_404, render_to_response
from django.db import models from django.db import models
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.utils.html import escape
from django.utils.text import capfirst, get_text_list
from django.utils.encoding import force_unicode, smart_str from django.utils.encoding import force_unicode, smart_str
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.http import urlencode
import operator import operator
try: try:
@ -22,13 +15,6 @@ try:
except NameError: except NameError:
from sets import Set as set # Python 2.3 fallback from sets import Set as set # Python 2.3 fallback
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
if not LogEntry._meta.installed:
raise ImproperlyConfigured, "You'll need to put 'django.contrib.admin' in your INSTALLED_APPS setting before you can use the admin application."
if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
raise ImproperlyConfigured, "You'll need to put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting before you can use the admin application."
# The system will display a "Show all" link on the change list only if the # The system will display a "Show all" link on the change list only if the
# total result count is less than or equal to this setting. # total result count is less than or equal to this setting.
MAX_SHOW_ALL_ALLOWED = 200 MAX_SHOW_ALL_ALLOWED = 200
@ -45,523 +31,20 @@ ERROR_FLAG = 'e'
# Text to display within change-list table cells if the value is blank. # Text to display within change-list table cells if the value is blank.
EMPTY_CHANGELIST_VALUE = '(None)' EMPTY_CHANGELIST_VALUE = '(None)'
use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOneRel, models.ManyToManyRel)) and field.rel.raw_id_admin
class IncorrectLookupParameters(Exception):
pass
def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
quoting is slightly different so that it doesn't get automatically
unquoted by the Web browser.
"""
if type(s) != type(''):
return s
res = list(s)
for i in range(len(res)):
c = res[i]
if c in ':/_':
res[i] = '_%02X' % ord(c)
return ''.join(res)
def unquote(s):
"""
Undo the effects of quote(). Based heavily on urllib.unquote().
"""
mychr = chr
myatoi = int
list = s.split('_')
res = [list[0]]
myappend = res.append
del list[0]
for item in list:
if item[1:2]:
try:
myappend(mychr(myatoi(item[:2], 16)) + item[2:])
except ValueError:
myappend('_' + item)
else:
myappend('_' + item)
return "".join(res)
def get_javascript_imports(opts, auto_populated_fields, field_sets):
# Put in any necessary JavaScript imports.
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
if auto_populated_fields:
js.append('js/urlify.js')
if opts.has_field_type(models.DateTimeField) or opts.has_field_type(models.TimeField) or opts.has_field_type(models.DateField):
js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
if opts.get_ordered_objects():
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
if opts.admin.js:
js.extend(opts.admin.js)
seen_collapse = False
for field_set in field_sets:
if not seen_collapse and 'collapse' in field_set.classes:
seen_collapse = True
js.append('js/admin/CollapsedFieldsets.js')
for field_line in field_set:
try:
for f in field_line:
if f.rel and isinstance(f, models.ManyToManyField) and f.rel.filter_interface:
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
raise StopIteration
except StopIteration:
break
return js
class AdminBoundField(object):
def __init__(self, field, field_mapping, original):
self.field = field
self.original = original
self.form_fields = [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
self.element_id = self.form_fields[0].get_id()
self.has_label_first = not isinstance(self.field, models.BooleanField)
self.raw_id_admin = use_raw_id_admin(field)
self.is_date_time = isinstance(field, models.DateTimeField)
self.is_file_field = isinstance(field, models.FileField)
self.needs_add_label = field.rel and (isinstance(field.rel, models.ManyToOneRel) or isinstance(field.rel, models.ManyToManyRel)) and field.rel.to._meta.admin
self.hidden = isinstance(self.field, models.AutoField)
self.first = False
classes = []
if self.raw_id_admin:
classes.append('nowrap')
if max([bool(f.errors()) for f in self.form_fields]):
classes.append('error')
if classes:
self.cell_class_attribute = u' class="%s" ' % ' '.join(classes)
self._repr_filled = False
if field.rel:
self.related_url = mark_safe(u'../../../%s/%s/'
% (field.rel.to._meta.app_label,
field.rel.to._meta.object_name.lower()))
def original_value(self):
if self.original:
return self.original.__dict__[self.field.attname]
def existing_display(self):
try:
return self._display
except AttributeError:
if isinstance(self.field.rel, models.ManyToOneRel):
self._display = force_unicode(getattr(self.original, self.field.name), strings_only=True)
elif isinstance(self.field.rel, models.ManyToManyRel):
self._display = u", ".join([force_unicode(obj) for obj in getattr(self.original, self.field.name).all()])
return self._display
def __repr__(self):
return repr(self.__dict__)
def html_error_list(self):
return mark_safe(" ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors]))
def original_url(self):
if self.is_file_field and self.original and self.field.attname:
url_method = getattr(self.original, 'get_%s_url' % self.field.attname)
if callable(url_method):
return url_method()
return ''
class AdminBoundFieldLine(object):
def __init__(self, field_line, field_mapping, original):
self.bound_fields = [field.bind(field_mapping, original, AdminBoundField) for field in field_line]
for bound_field in self:
bound_field.first = True
break
def __iter__(self):
for bound_field in self.bound_fields:
yield bound_field
def __len__(self):
return len(self.bound_fields)
class AdminBoundFieldSet(object):
def __init__(self, field_set, field_mapping, original):
self.name = field_set.name
self.classes = field_set.classes
self.description = field_set.description
self.bound_field_lines = [field_line.bind(field_mapping, original, AdminBoundFieldLine) for field_line in field_set]
def __iter__(self):
for bound_field_line in self.bound_field_lines:
yield bound_field_line
def __len__(self):
return len(self.bound_field_lines)
def render_change_form(model, manipulator, context, add=False, change=False, form_url=''):
opts = model._meta
app_label = opts.app_label
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
field_sets = opts.admin.get_field_sets(opts)
original = getattr(manipulator, 'original_object', None)
bound_field_sets = [field_set.bind(context['form'], original, AdminBoundFieldSet) for field_set in field_sets]
first_form_field_id = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
ordered_objects = opts.get_ordered_objects()
inline_related_objects = opts.get_followed_related_objects(manipulator.follow)
extra_context = {
'add': add,
'change': change,
'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()],
'has_change_permission': context['perms'][app_label][opts.get_change_permission()],
'has_file_field': opts.has_field_type(models.FileField),
'has_absolute_url': hasattr(model, 'get_absolute_url'),
'auto_populated_fields': auto_populated_fields,
'bound_field_sets': bound_field_sets,
'first_form_field_id': first_form_field_id,
'javascript_imports': get_javascript_imports(opts, auto_populated_fields, field_sets),
'ordered_objects': ordered_objects,
'inline_related_objects': inline_related_objects,
'form_url': mark_safe(form_url),
'opts': opts,
'content_type_id': ContentType.objects.get_for_model(model).id,
}
context.update(extra_context)
return render_to_response([
"admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_form.html" % app_label,
"admin/change_form.html"], context_instance=context)
def index(request):
return render_to_response('admin/index.html', {'title': _('Site administration')}, context_instance=template.RequestContext(request))
index = staff_member_required(never_cache(index))
def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None):
model = models.get_model(app_label, model_name)
if model is None:
raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
raise PermissionDenied
if post_url is None:
if request.user.has_perm(app_label + '.' + opts.get_change_permission()):
# redirect to list view
post_url = '../'
else:
# Object list will give 'Permission Denied', so go back to admin home
post_url = '../../../'
manipulator = model.AddManipulator()
if request.POST:
new_data = request.POST.copy()
if opts.has_field_type(models.FileField):
new_data.update(request.FILES)
errors = manipulator.get_validation_errors(new_data)
manipulator.do_html2python(new_data)
if not errors:
new_object = manipulator.save(new_data)
pk_value = new_object._get_pk_val()
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), ADDITION)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)}
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if "_continue" in request.POST:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
if "_popup" in request.POST:
post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value)
if "_popup" in request.POST:
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escape(new_object)))
elif "_addanother" in request.POST:
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)
return HttpResponseRedirect(post_url)
else:
# Add default data.
new_data = manipulator.flatten_data()
# Override the defaults with GET params, if they exist.
new_data.update(dict(request.GET.items()))
errors = {}
# Populate the FormWrapper.
form = oldforms.FormWrapper(manipulator, new_data, errors)
c = template.RequestContext(request, {
'title': _('Add %s') % force_unicode(opts.verbose_name),
'form': form,
'is_popup': '_popup' in request.REQUEST,
'show_delete': show_delete,
})
if object_id_override is not None:
c['object_id'] = object_id_override
return render_change_form(model, manipulator, c, add=True)
add_stage = staff_member_required(never_cache(add_stage))
def change_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name)
object_id = unquote(object_id)
if model is None:
raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
raise PermissionDenied
if request.POST and "_saveasnew" in request.POST:
return add_stage(request, app_label, model_name, form_url='../../add/')
try:
manipulator = model.ChangeManipulator(object_id)
except model.DoesNotExist:
raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
if request.POST:
new_data = request.POST.copy()
if opts.has_field_type(models.FileField):
new_data.update(request.FILES)
errors = manipulator.get_validation_errors(new_data)
manipulator.do_html2python(new_data)
if not errors:
new_object = manipulator.save(new_data)
pk_value = new_object._get_pk_val()
# Construct the change message.
change_message = []
if manipulator.fields_added:
change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and')))
if manipulator.fields_changed:
change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and')))
if manipulator.fields_deleted:
change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and')))
change_message = ' '.join(change_message)
if not change_message:
change_message = _('No fields changed.')
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), CHANGE, change_message)
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)}
if "_continue" in request.POST:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
if '_popup' in request.REQUEST:
return HttpResponseRedirect(request.path + "?_popup=1")
else:
return HttpResponseRedirect(request.path)
elif "_saveasnew" in request.POST:
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': force_unicode(new_object)})
return HttpResponseRedirect("../%s/" % pk_value)
elif "_addanother" in request.POST:
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)
return HttpResponseRedirect("../")
else:
# Populate new_data with a "flattened" version of the current data.
new_data = manipulator.flatten_data()
# TODO: do this in flatten_data...
# If the object has ordered objects on its admin page, get the existing
# order and flatten it into a comma-separated list of IDs.
id_order_list = []
for rel_obj in opts.get_ordered_objects():
id_order_list.extend(getattr(manipulator.original_object, 'get_%s_order' % rel_obj.object_name.lower())())
if id_order_list:
new_data['order_'] = ','.join(map(str, id_order_list))
errors = {}
# Populate the FormWrapper.
form = oldforms.FormWrapper(manipulator, new_data, errors)
form.original = manipulator.original_object
form.order_objects = []
#TODO Should be done in flatten_data / FormWrapper construction
for related in opts.get_followed_related_objects():
wrt = related.opts.order_with_respect_to
if wrt and wrt.rel and wrt.rel.to == opts:
func = getattr(manipulator.original_object, 'get_%s_list' %
related.get_accessor_name())
orig_list = func()
form.order_objects.extend(orig_list)
c = template.RequestContext(request, {
'title': _('Change %s') % force_unicode(opts.verbose_name),
'form': form,
'object_id': object_id,
'original': manipulator.original_object,
'is_popup': '_popup' in request.REQUEST,
})
return render_change_form(model, manipulator, c, change=True)
change_stage = staff_member_required(never_cache(change_stage))
def _nest_help(obj, depth, val):
current = obj
for i in range(depth):
current = current[-1]
current.append(val)
def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth):
"Helper function that recursively populates deleted_objects."
nh = _nest_help # Bind to local variable for performance
if current_depth > 16:
return # Avoid recursing too deep.
opts_seen = []
for related in opts.get_all_related_objects():
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
rel_opts_name = related.get_accessor_name()
if isinstance(related.field.rel, models.OneToOneRel):
try:
sub_obj = getattr(obj, rel_opts_name)
except ObjectDoesNotExist:
pass
else:
if related.opts.admin:
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
# We don't care about populating deleted_objects now.
continue
if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
(escape(force_unicode(capfirst(related.opts.verbose_name))),
related.opts.app_label,
related.opts.object_name.lower(),
sub_obj._get_pk_val(), sub_obj)), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
else:
has_related_objs = False
for sub_obj in getattr(obj, rel_opts_name).all():
has_related_objs = True
if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(escape(force_unicode(capfirst(related.opts.verbose_name))), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj))), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
# If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed.
if related.opts.admin and has_related_objs:
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
for related in opts.get_all_related_many_to_many_objects():
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
rel_opts_name = related.get_accessor_name()
has_related_objs = False
# related.get_accessor_name() could return None for symmetrical relationships
if rel_opts_name:
rel_objs = getattr(obj, rel_opts_name, None)
if rel_objs:
has_related_objs = True
if has_related_objs:
for sub_obj in rel_objs.all():
if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
{'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [
mark_safe((_('One or more %(fieldname)s in %(name)s:') % {'fieldname': escape(force_unicode(related.field.verbose_name)), 'name': escape(force_unicode(related.opts.verbose_name))}) + \
(u' <a href="../../../../%s/%s/%s/">%s</a>' % \
(related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []])
# If there were related objects, and the user doesn't have
# permission to change them, add the missing perm to perms_needed.
if related.opts.admin and has_related_objs:
p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
def delete_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name)
object_id = unquote(object_id)
if model is None:
raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
raise PermissionDenied
obj = get_object_or_404(model, pk=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))), force_unicode(object_id), escape(obj))), []]
perms_needed = set()
_get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1)
if request.POST: # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
obj_display = force_unicode(obj)
obj.delete()
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, object_id, obj_display, DELETION)
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': obj_display})
return HttpResponseRedirect("../../")
extra_context = {
"title": _("Are you sure?"),
"object_name": force_unicode(opts.verbose_name),
"object": obj,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
"opts": model._meta,
}
return render_to_response(["admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower() ),
"admin/%s/delete_confirmation.html" % app_label ,
"admin/delete_confirmation.html"], extra_context, context_instance=template.RequestContext(request))
delete_stage = staff_member_required(never_cache(delete_stage))
def history(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name)
object_id = unquote(object_id)
if model is None:
raise Http404("App %r, model %r, not found" % (app_label, model_name))
action_list = LogEntry.objects.filter(object_id=object_id,
content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=object_id)
extra_context = {
'title': _('Change history: %s') % obj,
'action_list': action_list,
'module_name': force_unicode(capfirst(model._meta.verbose_name_plural)),
'object': obj,
}
return render_to_response(["admin/%s/%s/object_history.html" % (app_label, model._meta.object_name.lower()),
"admin/%s/object_history.html" % app_label ,
"admin/object_history.html"], extra_context, context_instance=template.RequestContext(request))
history = staff_member_required(never_cache(history))
class ChangeList(object): class ChangeList(object):
def __init__(self, request, model): def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, model_admin):
self.model = model self.model = model
self.opts = model._meta self.opts = model._meta
self.lookup_opts = self.opts self.lookup_opts = self.opts
self.manager = self.opts.admin.manager self.root_query_set = model_admin.queryset(request)
self.list_display = list_display
self.list_display_links = list_display_links
self.list_filter = list_filter
self.date_hierarchy = date_hierarchy
self.search_fields = search_fields
self.list_select_related = list_select_related
self.list_per_page = list_per_page
self.model_admin = model_admin
# Get search parameters from the query string. # Get search parameters from the query string.
try: try:
@ -580,17 +63,16 @@ class ChangeList(object):
self.query = request.GET.get(SEARCH_VAR, '') self.query = request.GET.get(SEARCH_VAR, '')
self.query_set = self.get_query_set() self.query_set = self.get_query_set()
self.get_results(request) self.get_results(request)
self.title = (self.is_popup and _('Select %s') % force_unicode(self.opts.verbose_name) or _('Select %s to change') % force_unicode(self.opts.verbose_name)) self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name))
self.filter_specs, self.has_filters = self.get_filters(request) self.filter_specs, self.has_filters = self.get_filters(request)
self.pk_attname = self.lookup_opts.pk.attname self.pk_attname = self.lookup_opts.pk.attname
def get_filters(self, request): def get_filters(self, request):
filter_specs = [] filter_specs = []
if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field: if self.list_filter and not self.opts.one_to_one_field:
filter_fields = [self.lookup_opts.get_field(field_name) \ filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter]
for field_name in self.lookup_opts.admin.list_filter]
for f in filter_fields: for f in filter_fields:
spec = FilterSpec.create(f, request, self.params, self.model) spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin)
if spec and spec.has_output(): if spec and spec.has_output():
filter_specs.append(spec) filter_specs.append(spec)
return filter_specs, bool(filter_specs) return filter_specs, bool(filter_specs)
@ -604,15 +86,15 @@ class ChangeList(object):
if k.startswith(r): if k.startswith(r):
del p[k] del p[k]
for k, v in new_params.items(): for k, v in new_params.items():
if k in p and v is None: if v is None:
del p[k] if k in p:
elif v is not None: del p[k]
else:
p[k] = v p[k] = v
return mark_safe('?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')) return '?%s' % urlencode(p)
def get_results(self, request): def get_results(self, request):
paginator = Paginator(self.query_set, self.lookup_opts.admin.list_per_page) paginator = Paginator(self.query_set, self.list_per_page)
# Get the number of objects, with admin filters applied. # Get the number of objects, with admin filters applied.
try: try:
result_count = paginator.count result_count = paginator.count
@ -630,10 +112,10 @@ class ChangeList(object):
if not self.query_set.query.where: if not self.query_set.query.where:
full_result_count = result_count full_result_count = result_count
else: else:
full_result_count = self.manager.count() full_result_count = self.root_query_set.count()
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
multi_page = result_count > self.lookup_opts.admin.list_per_page multi_page = result_count > self.list_per_page
# Get the list of objects to display on this page. # Get the list of objects to display on this page.
if (self.show_all and can_show_all) or not multi_page: if (self.show_all and can_show_all) or not multi_page:
@ -657,7 +139,7 @@ class ChangeList(object):
# options, then check the object's default ordering. If neither of # options, then check the object's default ordering. If neither of
# those exist, order descending by ID by default. Finally, look for # those exist, order descending by ID by default. Finally, look for
# manually-specified ordering from the query string. # manually-specified ordering from the query string.
ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
if ordering[0].startswith('-'): if ordering[0].startswith('-'):
order_field, order_type = ordering[0][1:], 'desc' order_field, order_type = ordering[0][1:], 'desc'
@ -665,14 +147,14 @@ class ChangeList(object):
order_field, order_type = ordering[0], 'asc' order_field, order_type = ordering[0], 'asc'
if ORDER_VAR in params: if ORDER_VAR in params:
try: try:
field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])] field_name = self.list_display[int(params[ORDER_VAR])]
try: try:
f = lookup_opts.get_field(field_name) f = lookup_opts.get_field(field_name)
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
# see if field_name is a name of a non-field # See whether field_name is a name of a non-field
# that allows sorting # that allows sorting.
try: try:
attr = getattr(lookup_opts.admin.manager.model, field_name) attr = getattr(self.model, field_name)
order_field = attr.admin_order_field order_field = attr.admin_order_field
except AttributeError: except AttributeError:
pass pass
@ -686,7 +168,7 @@ class ChangeList(object):
return order_field, order_type return order_field, order_type
def get_query_set(self): def get_query_set(self):
qs = self.manager.get_query_set() qs = self.root_query_set
lookup_params = self.params.copy() # a dictionary of the query string lookup_params = self.params.copy() # a dictionary of the query string
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
if i in lookup_params: if i in lookup_params:
@ -703,10 +185,10 @@ class ChangeList(object):
# Use select_related() if one of the list_display options is a field # Use select_related() if one of the list_display options is a field
# with a relationship. # with a relationship.
if self.lookup_opts.admin.list_select_related: if self.list_select_related:
qs = qs.select_related() qs = qs.select_related()
else: else:
for field_name in self.lookup_opts.admin.list_display: for field_name in self.list_display:
try: try:
f = self.lookup_opts.get_field(field_name) f = self.lookup_opts.get_field(field_name)
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
@ -731,13 +213,17 @@ class ChangeList(object):
else: else:
return "%s__icontains" % field_name return "%s__icontains" % field_name
if self.lookup_opts.admin.search_fields and self.query: if self.search_fields and self.query:
for bit in self.query.split(): for bit in self.query.split():
or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields] or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.search_fields]
other_qs = QuerySet(self.model) other_qs = QuerySet(self.model)
other_qs.dup_select_related(qs) other_qs.dup_select_related(qs)
other_qs = other_qs.filter(reduce(operator.or_, or_queries)) other_qs = other_qs.filter(reduce(operator.or_, or_queries))
qs = qs & other_qs qs = qs & other_qs
for field_name in self.search_fields:
if '__' in field_name:
qs = qs.distinct()
break
if self.opts.one_to_one_field: if self.opts.one_to_one_field:
qs = qs.complex_filter(self.opts.one_to_one_field.rel.limit_choices_to) qs = qs.complex_filter(self.opts.one_to_one_field.rel.limit_choices_to)
@ -746,31 +232,3 @@ class ChangeList(object):
def url_for_result(self, result): def url_for_result(self, result):
return "%s/" % quote(getattr(result, self.pk_attname)) return "%s/" % quote(getattr(result, self.pk_attname))
def change_list(request, app_label, model_name):
model = models.get_model(app_label, model_name)
if model is None:
raise Http404("App %r, model %r, not found" % (app_label, model_name))
if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
raise PermissionDenied
try:
cl = ChangeList(request, model)
except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
# parameter via the query string. If wacky parameters were given and
# the 'invalid=1' parameter was already in the query string, something
# is screwed up with the database, so display an error page.
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')
c = template.RequestContext(request, {
'title': cl.title,
'is_popup': cl.is_popup,
'cl': cl,
})
c.update({'has_add_permission': c['perms'][app_label][cl.opts.get_add_permission()]}),
return render_to_response(['admin/%s/%s/change_list.html' % (app_label, cl.opts.object_name.lower()),
'admin/%s/change_list.html' % app_label,
'admin/change_list.html'], context_instance=c)
change_list = staff_member_required(never_cache(change_list))

View File

@ -0,0 +1,211 @@
"""
Form Widget classes specific to the Django admin site.
"""
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.translation import ugettext as _
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode
from django.conf import settings
class FilteredSelectMultiple(forms.SelectMultiple):
"""
A SelectMultiple with a JavaScript filter interface.
Note that the resulting JavaScript assumes that the SelectFilter2.js
library and its dependencies have been loaded in the HTML page.
"""
def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
self.verbose_name = verbose_name
self.is_stacked = is_stacked
super(FilteredSelectMultiple, self).__init__(attrs, choices)
def render(self, name, value, attrs=None, choices=()):
output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
return mark_safe(u''.join(output))
class AdminDateWidget(forms.TextInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}):
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
class AdminTimeWidget(forms.TextInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}):
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
class AdminSplitDateTime(forms.SplitDateTimeWidget):
"""
A SplitDateTime Widget that has some admin-specific styling.
"""
def __init__(self, attrs=None):
widgets = [AdminDateWidget, AdminTimeWidget]
# Note that we're calling MultiWidget, not SplitDateTimeWidget, because
# we want to define widgets.
forms.MultiWidget.__init__(self, widgets, attrs)
def format_output(self, rendered_widgets):
return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
(_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
class AdminRadioFieldRenderer(RadioFieldRenderer):
def render(self):
"""Outputs a <ul> for this set of radio fields."""
return mark_safe(u'<ul%s>\n%s\n</ul>' % (
flatatt(self.attrs),
u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
)
class AdminRadioSelect(forms.RadioSelect):
renderer = AdminRadioFieldRenderer
class AdminFileWidget(forms.FileInput):
"""
A FileField Widget that shows its current value if it has one.
"""
def __init__(self, attrs={}):
super(AdminFileWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
output = []
if value:
output.append('%s <a target="_blank" href="%s%s">%s</a> <br />%s ' % \
(_('Currently:'), settings.MEDIA_URL, value, value, _('Change:')))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
class ForeignKeyRawIdWidget(forms.TextInput):
"""
A Widget for displaying ForeignKeys in the "raw_id" interface rather than
in a <select> box.
"""
def __init__(self, rel, attrs=None):
self.rel = rel
super(ForeignKeyRawIdWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
if self.rel.limit_choices_to:
url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in self.rel.limit_choices_to.items()])
else:
url = ''
if not attrs.has_key('class'):
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
(related_url, url, name))
output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % settings.ADMIN_MEDIA_PREFIX)
if value:
output.append(self.label_for_value(value))
return mark_safe(u''.join(output))
def label_for_value(self, value):
return '&nbsp;<strong>%s</strong>' % \
truncate_words(self.rel.to.objects.get(pk=value), 14)
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
"""
A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
in a <select multiple> box.
"""
def __init__(self, rel, attrs=None):
super(ManyToManyRawIdWidget, self).__init__(rel, attrs)
def render(self, name, value, attrs=None):
attrs['class'] = 'vManyToManyRawIdAdminField'
if value:
value = ','.join([str(v) for v in value])
else:
value = ''
return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
def label_for_value(self, value):
return ''
def value_from_datadict(self, data, files, name):
value = data.get(name, None)
if value and ',' in value:
return data[name].split(',')
if value:
return [value]
return None
def _has_changed(self, initial, data):
if initial is None:
initial = []
if data is None:
data = []
if len(initial) != len(data):
return True
for pk1, pk2 in zip(initial, data):
if force_unicode(pk1) != force_unicode(pk2):
return True
return False
class RelatedFieldWidgetWrapper(forms.Widget):
"""
This class is a wrapper to a given widget to add the add icon for the
admin interface.
"""
def __init__(self, widget, rel, admin_site):
self.is_hidden = widget.is_hidden
self.needs_multipart_form = widget.needs_multipart_form
self.attrs = widget.attrs
self.choices = widget.choices
self.widget = widget
self.rel = rel
# so we can check if the related object is registered with this AdminSite
self.admin_site = admin_site
def __deepcopy__(self, memo):
obj = copy.copy(self)
obj.widget = copy.deepcopy(self.widget, memo)
obj.attrs = self.widget.attrs
memo[id(self)] = obj
return obj
def render(self, name, value, *args, **kwargs):
rel_to = self.rel.to
related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower())
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
if rel_to in self.admin_site._registry: # If the related object has an admin interface:
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
(related_url, name))
output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>' % settings.ADMIN_MEDIA_PREFIX)
return mark_safe(u''.join(output))
def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
return self.attrs
def value_from_datadict(self, data, files, name):
return self.widget.value_from_datadict(data, files, name)
def _has_changed(self, initial, data):
return self.widget._has_changed(initial, data)
def id_for_label(self, id_):
return self.widget.id_for_label(id_)

View File

@ -0,0 +1,15 @@
from django.conf.urls.defaults import *
from django.contrib.admindocs import views
urlpatterns = patterns('',
('^$', views.doc_index),
('^bookmarklets/$', views.bookmarklets),
('^tags/$', views.template_tag_index),
('^filters/$', views.template_filter_index),
('^views/$', views.view_index),
('^views/(?P<view>[^/]+)/$', views.view_detail),
('^models/$', views.model_index),
('^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$', views.model_detail),
# ('^templates/$', views.template_index),
('^templates/(?P<template>.*)/$', views.template_detail),
)

View File

@ -5,9 +5,9 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.db import models from django.db import models
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.http import Http404 from django.http import Http404, get_host
from django.core import urlresolvers from django.core import urlresolvers
from django.contrib.admin import utils from django.contrib.admindocs import utils
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -23,13 +23,18 @@ class GenericSite(object):
def doc_index(request): def doc_index(request):
if not utils.docutils_is_available: if not utils.docutils_is_available:
return missing_docutils_page(request) return missing_docutils_page(request)
return render_to_response('admin_doc/index.html', context_instance=RequestContext(request)) root_path = re.sub(re.escape('doc/') + '$', '', request.path)
return render_to_response('admin_doc/index.html', {
'root_path': root_path,
}, context_instance=RequestContext(request))
doc_index = staff_member_required(doc_index) doc_index = staff_member_required(doc_index)
def bookmarklets(request): def bookmarklets(request):
# Hack! This couples this view to the URL it lives at. # Hack! This couples this view to the URL it lives at.
admin_root = request.path[:-len('doc/bookmarklets/')] admin_root = request.path[:-len('doc/bookmarklets/')]
root_path = re.sub(re.escape('doc/bookmarklets/') + '$', '', request.path)
return render_to_response('admin_doc/bookmarklets.html', { return render_to_response('admin_doc/bookmarklets.html', {
'root_path': root_path,
'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)), 'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
bookmarklets = staff_member_required(bookmarklets) bookmarklets = staff_member_required(bookmarklets)
@ -61,8 +66,11 @@ def template_tag_index(request):
'meta': metadata, 'meta': metadata,
'library': tag_library, 'library': tag_library,
}) })
root_path = re.sub(re.escape('doc/tags/') + '$', '', request.path)
return render_to_response('admin_doc/template_tag_index.html', {'tags': tags}, context_instance=RequestContext(request)) return render_to_response('admin_doc/template_tag_index.html', {
'root_path': root_path,
'tags': tags
}, context_instance=RequestContext(request))
template_tag_index = staff_member_required(template_tag_index) template_tag_index = staff_member_required(template_tag_index)
def template_filter_index(request): def template_filter_index(request):
@ -92,7 +100,11 @@ def template_filter_index(request):
'meta': metadata, 'meta': metadata,
'library': tag_library, 'library': tag_library,
}) })
return render_to_response('admin_doc/template_filter_index.html', {'filters': filters}, context_instance=RequestContext(request)) root_path = re.sub(re.escape('doc/filters/') + '$', '', request.path)
return render_to_response('admin_doc/template_filter_index.html', {
'root_path': root_path,
'filters': filters
}, context_instance=RequestContext(request))
template_filter_index = staff_member_required(template_filter_index) template_filter_index = staff_member_required(template_filter_index)
def view_index(request): def view_index(request):
@ -120,7 +132,11 @@ def view_index(request):
'site': site_obj, 'site': site_obj,
'url': simplify_regex(regex), 'url': simplify_regex(regex),
}) })
return render_to_response('admin_doc/view_index.html', {'views': views}, context_instance=RequestContext(request)) root_path = re.sub(re.escape('doc/views/') + '$', '', request.path)
return render_to_response('admin_doc/view_index.html', {
'root_path': root_path,
'views': views
}, context_instance=RequestContext(request))
view_index = staff_member_required(view_index) view_index = staff_member_required(view_index)
def view_detail(request, view): def view_detail(request, view):
@ -139,7 +155,9 @@ def view_detail(request, view):
body = utils.parse_rst(body, 'view', _('view:') + view) body = utils.parse_rst(body, 'view', _('view:') + view)
for key in metadata: for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view) metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view)
root_path = re.sub(re.escape('doc/views/%s/' % view) + '$', '', request.path)
return render_to_response('admin_doc/view_detail.html', { return render_to_response('admin_doc/view_detail.html', {
'root_path': root_path,
'name': view, 'name': view,
'summary': title, 'summary': title,
'body': body, 'body': body,
@ -150,9 +168,12 @@ view_detail = staff_member_required(view_detail)
def model_index(request): def model_index(request):
if not utils.docutils_is_available: if not utils.docutils_is_available:
return missing_docutils_page(request) return missing_docutils_page(request)
m_list = [m._meta for m in models.get_models()] m_list = [m._meta for m in models.get_models()]
return render_to_response('admin_doc/model_index.html', {'models': m_list}, context_instance=RequestContext(request)) root_path = re.sub(re.escape('doc/models/') + '$', '', request.path)
return render_to_response('admin_doc/model_index.html', {
'root_path': root_path,
'models': m_list
}, context_instance=RequestContext(request))
model_index = staff_member_required(model_index) model_index = staff_member_required(model_index)
def model_detail(request, app_label, model_name): def model_detail(request, app_label, model_name):
@ -170,7 +191,7 @@ def model_detail(request, app_label, model_name):
model = m model = m
break break
if model is None: if model is None:
raise Http404, _("Model %(name)r not found in app %(label)r") % {'name': model_name, 'label': app_label} raise Http404, _("Model %(model_name)r not found in app %(app_label)r") % {'model_name': model_name, 'app_label': app_label}
opts = model._meta opts = model._meta
@ -182,7 +203,7 @@ def model_detail(request, app_label, model_name):
if isinstance(field, models.ForeignKey): if isinstance(field, models.ForeignKey):
data_type = related_object_name = field.rel.to.__name__ data_type = related_object_name = field.rel.to.__name__
app_label = field.rel.to._meta.app_label app_label = field.rel.to._meta.app_label
verbose = utils.parse_rst((_("the related `%(label)s.%(type)s` object") % {'label': app_label, 'type': data_type}), 'model', _('model:') + data_type) verbose = utils.parse_rst((_("the related `%(app_label)s.%(data_type)s` object") % {'app_label': app_label, 'data_type': data_type}), 'model', _('model:') + data_type)
else: else:
data_type = get_readable_field_data_type(field) data_type = get_readable_field_data_type(field)
verbose = field.verbose_name verbose = field.verbose_name
@ -213,7 +234,7 @@ def model_detail(request, app_label, model_name):
# Gather related objects # Gather related objects
for rel in opts.get_all_related_objects(): for rel in opts.get_all_related_objects():
verbose = _("related `%(label)s.%(name)s` objects") % {'label': rel.opts.app_label, 'name': rel.opts.object_name} verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name}
accessor = rel.get_accessor_name() accessor = rel.get_accessor_name()
fields.append({ fields.append({
'name' : "%s.all" % accessor, 'name' : "%s.all" % accessor,
@ -225,8 +246,9 @@ def model_detail(request, app_label, model_name):
'data_type' : 'Integer', 'data_type' : 'Integer',
'verbose' : utils.parse_rst(_("number of %s") % verbose , 'model', _('model:') + opts.module_name), 'verbose' : utils.parse_rst(_("number of %s") % verbose , 'model', _('model:') + opts.module_name),
}) })
root_path = re.sub(re.escape('doc/models/%s.%s/' % (app_label, model_name)) + '$', '', request.path)
return render_to_response('admin_doc/model_detail.html', { return render_to_response('admin_doc/model_detail.html', {
'root_path': root_path,
'name': '%s.%s' % (opts.app_label, opts.object_name), 'name': '%s.%s' % (opts.app_label, opts.object_name),
'summary': _("Fields on %s objects") % opts.object_name, 'summary': _("Fields on %s objects") % opts.object_name,
'description': model.__doc__, 'description': model.__doc__,
@ -252,7 +274,9 @@ def template_detail(request, template):
'site': site_obj, 'site': site_obj,
'order': list(settings_mod.TEMPLATE_DIRS).index(dir), 'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
}) })
root_path = re.sub(re.escape('doc/templates/%s/' % template) + '$', '', request.path)
return render_to_response('admin_doc/template_detail.html', { return render_to_response('admin_doc/template_detail.html', {
'root_path': root_path,
'name': template, 'name': template,
'templates': templates, 'templates': templates,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))

View File

@ -0,0 +1,66 @@
from django.contrib.auth.models import User, Group
from django.core.exceptions import PermissionDenied
from django import oldforms, template
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext, ugettext_lazy as _
from django.contrib import admin
class GroupAdmin(admin.ModelAdmin):
search_fields = ('name',)
ordering = ('name',)
filter_horizontal = ('permissions',)
class UserAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Groups'), {'fields': ('groups',)}),
)
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser')
search_fields = ('username', 'first_name', 'last_name', 'email')
ordering = ('username',)
filter_horizontal = ('user_permissions',)
def add_view(self, request):
# avoid a circular import. see #6718.
from django.contrib.auth.forms import UserCreationForm
if not self.has_change_permission(request):
raise PermissionDenied
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
if "_addanother" in request.POST:
request.user.message_set.create(message=msg)
return HttpResponseRedirect(request.path)
else:
request.user.message_set.create(message=msg + ' ' + ugettext("You may edit it again below."))
return HttpResponseRedirect('../%s/' % new_user.id)
else:
form = UserCreationForm()
return render_to_response('admin/auth/user/add_form.html', {
'title': _('Add user'),
'form': form,
'is_popup': '_popup' in request.REQUEST,
'add': True,
'change': False,
'has_add_permission': True,
'has_delete_permission': False,
'has_change_permission': True,
'has_file_field': False,
'has_absolute_url': False,
'auto_populated_fields': (),
'opts': User._meta,
'save_as': False,
'username_help_text': User._meta.get_field('username').help_text,
'root_path': self.admin_site.root_path,
}, context_instance=template.RequestContext(request))
admin.site.register(Group, GroupAdmin)
admin.site.register(User, UserAdmin)

View File

@ -3,63 +3,81 @@ from django.contrib.auth import authenticate
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.template import Context, loader from django.template import Context, loader
from django.core import validators from django.core import validators
from django import oldforms from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _
class UserCreationForm(oldforms.Manipulator): class UserCreationForm(forms.ModelForm):
"A form that creates a user, with no privileges, from the given username and password." """
def __init__(self): A form that creates a user, with no privileges, from the given username and password.
self.fields = ( """
oldforms.TextField(field_name='username', length=30, max_length=30, is_required=True, username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$',
validator_list=[validators.isAlphaNumeric, self.isValidUsername]), help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."),
oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True), error_message = _("This value must contain only letters, numbers and underscores."))
oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True, password1 = forms.CharField(label=_("Password"), max_length=60, widget=forms.PasswordInput)
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]), password2 = forms.CharField(label=_("Password confirmation"), max_length=60, widget=forms.PasswordInput)
)
def isValidUsername(self, field_data, all_data): class Meta:
model = User
fields = ("username",)
def clean_username(self):
username = self.cleaned_data["username"]
try: try:
User.objects.get(username=field_data) User.objects.get(username=username)
except User.DoesNotExist: except User.DoesNotExist:
return return username
raise validators.ValidationError, _('A user with that username already exists.') raise forms.ValidationError(_("A user with that username already exists."))
def save(self, new_data): def clean_password2(self):
"Creates the user." password1 = self.cleaned_data["password1"]
return User.objects.create_user(new_data['username'], '', new_data['password1']) password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
class AuthenticationForm(oldforms.Manipulator): def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class AuthenticationForm(forms.Form):
""" """
Base class for authenticating users. Extend this to get a form that accepts Base class for authenticating users. Extend this to get a form that accepts
username/password logins. username/password logins.
""" """
def __init__(self, request=None): username = forms.CharField(label=_("Username"), max_length=30)
password = forms.CharField(label=_("Password"), max_length=30, widget=forms.PasswordInput)
def __init__(self, request=None, *args, **kwargs):
""" """
If request is passed in, the manipulator will validate that cookies are If request is passed in, the form will validate that cookies are
enabled. Note that the request (a HttpRequest object) must have set a enabled. Note that the request (a HttpRequest object) must have set a
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
running this validator. running this validation.
""" """
self.request = request self.request = request
self.fields = [
oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
]
self.user_cache = None self.user_cache = None
super(AuthenticationForm, self).__init__(*args, **kwargs)
def hasCookiesEnabled(self, field_data, all_data): def clean(self):
if self.request and not self.request.session.test_cookie_worked(): username = self.cleaned_data.get('username')
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.") password = self.cleaned_data.get('password')
def isValidUser(self, field_data, all_data): if username and password:
username = field_data self.user_cache = authenticate(username=username, password=password)
password = all_data.get('password', None) if self.user_cache is None:
self.user_cache = authenticate(username=username, password=password) raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive."))
if self.user_cache is None: elif not self.user_cache.is_active:
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.") raise forms.ValidationError(_("This account is inactive."))
elif not self.user_cache.is_active:
raise validators.ValidationError, _("This account is inactive.") # TODO: determine whether this should move to its own method.
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
return self.cleaned_data
def get_user_id(self): def get_user_id(self):
if self.user_cache: if self.user_cache:
@ -69,22 +87,22 @@ class AuthenticationForm(oldforms.Manipulator):
def get_user(self): def get_user(self):
return self.user_cache return self.user_cache
class PasswordResetForm(oldforms.Manipulator): class PasswordResetForm(forms.Form):
"A form that lets a user request a password reset" email = forms.EmailField(label=_("E-mail"), max_length=40)
def __init__(self):
self.fields = (
oldforms.EmailField(field_name="email", length=40, is_required=True,
validator_list=[self.isValidUserEmail]),
)
def isValidUserEmail(self, new_data, all_data): def clean_email(self):
"Validates that a user exists with the given e-mail address" """
self.users_cache = list(User.objects.filter(email__iexact=new_data)) Validates that a user exists with the given e-mail address.
"""
email = self.cleaned_data["email"]
self.users_cache = User.objects.filter(email__iexact=email)
if len(self.users_cache) == 0: if len(self.users_cache) == 0:
raise validators.ValidationError, _("That e-mail address doesn't have an associated user account. Are you sure you've registered?") raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'): def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
"Calculates a new password randomly and sends it to the user" """
Calculates a new password randomly and sends it to the user.
"""
from django.core.mail import send_mail from django.core.mail import send_mail
for user in self.users_cache: for user in self.users_cache:
new_pass = User.objects.make_random_password() new_pass = User.objects.make_random_password()
@ -103,42 +121,69 @@ class PasswordResetForm(oldforms.Manipulator):
'domain': domain, 'domain': domain,
'site_name': site_name, 'site_name': site_name,
'user': user, 'user': user,
} }
send_mail(_('Password reset on %s') % site_name, t.render(Context(c)), None, [user.email]) send_mail(_("Password reset on %s") % site_name,
t.render(Context(c)), None, [user.email])
class PasswordChangeForm(oldforms.Manipulator): class PasswordChangeForm(forms.Form):
"A form that lets a user change his password." """
def __init__(self, user): A form that lets a user change his/her password.
"""
old_password = forms.CharField(label=_("Old password"), max_length=30, widget=forms.PasswordInput)
new_password1 = forms.CharField(label=_("New password"), max_length=30, widget=forms.PasswordInput)
new_password2 = forms.CharField(label=_("New password confirmation"), max_length=30, widget=forms.PasswordInput)
def __init__(self, user, *args, **kwargs):
self.user = user self.user = user
self.fields = ( super(PasswordChangeForm, self).__init__(*args, **kwargs)
oldforms.PasswordField(field_name="old_password", length=30, max_length=30, is_required=True,
validator_list=[self.isValidOldPassword]),
oldforms.PasswordField(field_name="new_password1", length=30, max_length=30, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
oldforms.PasswordField(field_name="new_password2", length=30, max_length=30, is_required=True),
)
def isValidOldPassword(self, new_data, all_data): def clean_old_password(self):
"Validates that the old_password field is correct." """
if not self.user.check_password(new_data): Validates that the old_password field is correct.
raise validators.ValidationError, _("Your old password was entered incorrectly. Please enter it again.") """
old_password = self.cleaned_data["old_password"]
if not self.user.check_password(old_password):
raise forms.ValidationError(_("Your old password was entered incorrectly. Please enter it again."))
return old_password
def save(self, new_data): def clean_new_password2(self):
"Saves the new password." password1 = self.cleaned_data.get('new_password1')
self.user.set_password(new_data['new_password1']) password2 = self.cleaned_data.get('new_password2')
self.user.save() if password1 and password2:
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
class AdminPasswordChangeForm(oldforms.Manipulator): def save(self, commit=True):
"A form used to change the password of a user in the admin interface." self.user.set_password(self.cleaned_data['new_password1'])
def __init__(self, user): if commit:
self.user.save()
return self.user
class AdminPasswordChangeForm(forms.Form):
"""
A form used to change the password of a user in the admin interface.
"""
password1 = forms.CharField(label=_("Password"), max_length=60, widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password (again)"), max_length=60, widget=forms.PasswordInput)
def __init__(self, user, *args, **kwargs):
self.user = user self.user = user
self.fields = ( super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
def save(self, new_data): def clean_password2(self):
"Saves the new password." password1 = self.cleaned_data.get('password1')
self.user.set_password(new_data['password1']) password2 = self.cleaned_data.get('password2')
self.user.save() if password1 and password2:
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
def save(self, commit=True):
"""
Saves the new password.
"""
self.user.set_password(self.cleaned_data["password1"])
if commit:
self.user.save()
return self.user

View File

@ -91,16 +91,12 @@ class Group(models.Model):
Beyond permissions, groups are a convenient way to categorize users to apply some label, or extended functionality, to them. For example, you could create a group 'Special users', and you could write code that would do special things to those users -- such as giving them access to a members-only portion of your site, or sending them members-only e-mail messages. Beyond permissions, groups are a convenient way to categorize users to apply some label, or extended functionality, to them. For example, you could create a group 'Special users', and you could write code that would do special things to those users -- such as giving them access to a members-only portion of your site, or sending them members-only e-mail messages.
""" """
name = models.CharField(_('name'), max_length=80, unique=True) name = models.CharField(_('name'), max_length=80, unique=True)
permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True, filter_interface=models.HORIZONTAL) permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True)
class Meta: class Meta:
verbose_name = _('group') verbose_name = _('group')
verbose_name_plural = _('groups') verbose_name_plural = _('groups')
class Admin:
search_fields = ('name',)
ordering = ('name',)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -147,26 +143,13 @@ class User(models.Model):
date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now) date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in.")) help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, filter_interface=models.HORIZONTAL) user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True)
objects = UserManager() objects = UserManager()
class Meta: class Meta:
verbose_name = _('user') verbose_name = _('user')
verbose_name_plural = _('users') verbose_name_plural = _('users')
class Admin:
fields = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Groups'), {'fields': ('groups',)}),
)
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser')
search_fields = ('username', 'first_name', 'last_name', 'email')
ordering = ('username',)
def __unicode__(self): def __unicode__(self):
return self.username return self.username

View File

@ -0,0 +1,8 @@
from django.contrib.auth.tests.basic import BASIC_TESTS, PasswordResetTest
from django.contrib.auth.tests.forms import FORM_TESTS
__test__ = {
'BASIC_TESTS': BASIC_TESTS,
'PASSWORDRESET_TESTS': PasswordResetTest,
'FORM_TESTS': FORM_TESTS,
}

View File

@ -1,4 +1,5 @@
"""
BASIC_TESTS = """
>>> from django.contrib.auth.models import User, AnonymousUser >>> from django.contrib.auth.models import User, AnonymousUser
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw') >>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
>>> u.has_usable_password() >>> u.has_usable_password()
@ -60,12 +61,13 @@ from django.core import mail
class PasswordResetTest(TestCase): class PasswordResetTest(TestCase):
fixtures = ['authtestdata.json'] fixtures = ['authtestdata.json']
urls = 'django.contrib.auth.urls' urls = 'django.contrib.auth.urls'
def test_email_not_found(self): def test_email_not_found(self):
"Error is raised if the provided email address isn't currently registered" "Error is raised if the provided email address isn't currently registered"
response = self.client.get('/password_reset/') response = self.client.get('/password_reset/')
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'}) response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
self.assertContains(response, "That e-mail address doesn&#39;t have an associated user account") self.assertContains(response, "That e-mail address doesn't have an associated user account")
self.assertEquals(len(mail.outbox), 0) self.assertEquals(len(mail.outbox), 0)
def test_email_found(self): def test_email_found(self):

View File

@ -0,0 +1,135 @@
FORM_TESTS = """
>>> from django.contrib.auth.models import User
>>> from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
>>> from django.contrib.auth.forms import PasswordChangeForm
The user already exists.
>>> user = User.objects.create_user("jsmith", "jsmith@example.com", "test123")
>>> data = {
... 'username': 'jsmith',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["username"].errors
[u'A user with that username already exists.']
The username contains invalid data.
>>> data = {
... 'username': 'jsmith@example.com',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["username"].errors
[u'This value must contain only letters, numbers and underscores.']
The verification password is incorrect.
>>> data = {
... 'username': 'jsmith2',
... 'password1': 'test123',
... 'password2': 'test',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["password2"].errors
[u"The two password fields didn't match."]
The success case.
>>> data = {
... 'username': 'jsmith2',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
True
>>> form.save()
<User: jsmith2>
The user submits an invalid username.
>>> data = {
... 'username': 'jsmith_does_not_exist',
... 'password': 'test123',
... }
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
False
>>> form.non_field_errors()
[u'Please enter a correct username and password. Note that both fields are case-sensitive.']
The user is inactive.
>>> data = {
... 'username': 'jsmith',
... 'password': 'test123',
... }
>>> user.is_active = False
>>> user.save()
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
False
>>> form.non_field_errors()
[u'This account is inactive.']
>>> user.is_active = True
>>> user.save()
The success case
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
True
>>> form.non_field_errors()
[]
The old password is incorrect.
>>> data = {
... 'old_password': 'test',
... 'new_password1': 'abc123',
... 'new_password2': 'abc123',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
False
>>> form["old_password"].errors
[u'Your old password was entered incorrectly. Please enter it again.']
The two new passwords do not match.
>>> data = {
... 'old_password': 'test123',
... 'new_password1': 'abc123',
... 'new_password2': 'abc',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
False
>>> form["new_password2"].errors
[u"The two password fields didn't match."]
The success case.
>>> data = {
... 'old_password': 'test123',
... 'new_password1': 'abc123',
... 'new_password2': 'abc123',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
True
"""

View File

@ -1,42 +1,42 @@
from django import oldforms
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm, AdminPasswordChangeForm
from django.core.exceptions import PermissionDenied
from django.shortcuts import render_to_response, get_object_or_404
from django.contrib.sites.models import Site, RequestSite from django.contrib.sites.models import Site, RequestSite
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.utils.http import urlquote from django.utils.http import urlquote
from django.utils.html import escape
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
import re
def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME): def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
"Displays the login form and handles the login action." "Displays the login form and handles the login action."
manipulator = AuthenticationForm()
redirect_to = request.REQUEST.get(redirect_field_name, '') redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.POST: if request.method == "POST":
errors = manipulator.get_validation_errors(request.POST) form = AuthenticationForm(data=request.POST)
if not errors: if form.is_valid():
# Light security check -- make sure redirect_to isn't garbage. # Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or '//' in redirect_to or ' ' in redirect_to: if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
from django.conf import settings from django.conf import settings
redirect_to = settings.LOGIN_REDIRECT_URL redirect_to = settings.LOGIN_REDIRECT_URL
from django.contrib.auth import login from django.contrib.auth import login
login(request, manipulator.get_user()) login(request, form.get_user())
if request.session.test_cookie_worked(): if request.session.test_cookie_worked():
request.session.delete_test_cookie() request.session.delete_test_cookie()
return HttpResponseRedirect(redirect_to) return HttpResponseRedirect(redirect_to)
else: else:
errors = {} form = AuthenticationForm(request)
request.session.set_test_cookie() request.session.set_test_cookie()
if Site._meta.installed: if Site._meta.installed:
current_site = Site.objects.get_current() current_site = Site.objects.get_current()
else: else:
current_site = RequestSite(request) current_site = RequestSite(request)
return render_to_response(template_name, { return render_to_response(template_name, {
'form': oldforms.FormWrapper(manipulator, request.POST, errors), 'form': form,
redirect_field_name: redirect_to, redirect_field_name: redirect_to,
'site_name': current_site.name, 'site_name': current_site.name,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@ -66,13 +66,11 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N
return HttpResponseRedirect('%s?%s=%s' % (login_url, urlquote(redirect_field_name), urlquote(next))) return HttpResponseRedirect('%s?%s=%s' % (login_url, urlquote(redirect_field_name), urlquote(next)))
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html', def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
email_template_name='registration/password_reset_email.html'): email_template_name='registration/password_reset_email.html',
new_data, errors = {}, {} password_reset_form=PasswordResetForm):
form = PasswordResetForm() if request.method == "POST":
if request.POST: form = password_reset_form(request.POST)
new_data = request.POST.copy() if form.is_valid():
errors = form.get_validation_errors(new_data)
if not errors:
if is_admin_site: if is_admin_site:
form.save(domain_override=request.META['HTTP_HOST']) form.save(domain_override=request.META['HTTP_HOST'])
else: else:
@ -81,24 +79,57 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
else: else:
form.save(domain_override=RequestSite(request).domain, email_template_name=email_template_name) form.save(domain_override=RequestSite(request).domain, email_template_name=email_template_name)
return HttpResponseRedirect('%sdone/' % request.path) return HttpResponseRedirect('%sdone/' % request.path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)}, else:
context_instance=RequestContext(request)) form = password_reset_form()
return render_to_response(template_name, {
'form': form,
}, context_instance=RequestContext(request))
def password_reset_done(request, template_name='registration/password_reset_done.html'): def password_reset_done(request, template_name='registration/password_reset_done.html'):
return render_to_response(template_name, context_instance=RequestContext(request)) return render_to_response(template_name, context_instance=RequestContext(request))
def password_change(request, template_name='registration/password_change_form.html'): def password_change(request, template_name='registration/password_change_form.html'):
new_data, errors = {}, {} if request.method == "POST":
form = PasswordChangeForm(request.user) form = PasswordChangeForm(request.user, request.POST)
if request.POST: if form.is_valid():
new_data = request.POST.copy() form.save()
errors = form.get_validation_errors(new_data)
if not errors:
form.save(new_data)
return HttpResponseRedirect('%sdone/' % request.path) return HttpResponseRedirect('%sdone/' % request.path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)}, else:
context_instance=RequestContext(request)) form = PasswordChangeForm(request.user)
return render_to_response(template_name, {
'form': form,
}, context_instance=RequestContext(request))
password_change = login_required(password_change) password_change = login_required(password_change)
def password_change_done(request, template_name='registration/password_change_done.html'): def password_change_done(request, template_name='registration/password_change_done.html'):
return render_to_response(template_name, context_instance=RequestContext(request)) return render_to_response(template_name, context_instance=RequestContext(request))
# TODO: move to admin.py in the ModelAdmin
def user_change_password(request, id):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
user = get_object_or_404(User, pk=id)
if request.method == 'POST':
form = AdminPasswordChangeForm(user, request.POST)
if form.is_valid():
new_user = form.save()
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('..')
else:
form = AdminPasswordChangeForm(user)
return render_to_response('admin/auth/user/change_password.html', {
'title': _('Change password: %s') % escape(user.username),
'form': form,
'is_popup': '_popup' in request.REQUEST,
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_absolute_url': False,
'opts': User._meta,
'original': user,
'save_as': False,
'show_save': True,
'root_path': re.sub('auth/user/(\d+)/password/$', '', request.path),
}, context_instance=RequestContext(request))

View File

@ -0,0 +1,30 @@
from django.contrib import admin
from django.contrib.comments.models import Comment, FreeComment
class CommentAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('content_type', 'object_id', 'site')}),
('Content', {'fields': ('user', 'headline', 'comment')}),
('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
)
list_display = ('user', 'submit_date', 'content_type', 'get_content_object')
list_filter = ('submit_date',)
date_hierarchy = 'submit_date'
search_fields = ('comment', 'user__username')
raw_id_fields = ('user',)
class FreeCommentAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('content_type', 'object_id', 'site')}),
('Content', {'fields': ('person_name', 'comment')}),
('Meta', {'fields': ('is_public', 'ip_address', 'approved')}),
)
list_display = ('person_name', 'submit_date', 'content_type', 'get_content_object')
list_filter = ('submit_date',)
date_hierarchy = 'submit_date'
search_fields = ('comment', 'person_name')
admin.site.register(Comment, CommentAdmin)
admin.site.register(FreeComment, FreeCommentAdmin)

View File

@ -66,7 +66,7 @@ class CommentManager(models.Manager):
class Comment(models.Model): class Comment(models.Model):
"""A comment by a registered user.""" """A comment by a registered user."""
user = models.ForeignKey(User, raw_id_admin=True) user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType) content_type = models.ForeignKey(ContentType)
object_id = models.IntegerField(_('object ID')) object_id = models.IntegerField(_('object ID'))
headline = models.CharField(_('headline'), max_length=255, blank=True) headline = models.CharField(_('headline'), max_length=255, blank=True)
@ -96,18 +96,6 @@ class Comment(models.Model):
verbose_name_plural = _('comments') verbose_name_plural = _('comments')
ordering = ('-submit_date',) ordering = ('-submit_date',)
class Admin:
fields = (
(None, {'fields': ('content_type', 'object_id', 'site')}),
('Content', {'fields': ('user', 'headline', 'comment')}),
('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
)
list_display = ('user', 'submit_date', 'content_type', 'get_content_object')
list_filter = ('submit_date',)
date_hierarchy = 'submit_date'
search_fields = ('comment', 'user__username')
def __unicode__(self): def __unicode__(self):
return "%s: %s..." % (self.user.username, self.comment[:100]) return "%s: %s..." % (self.user.username, self.comment[:100])
@ -188,17 +176,6 @@ class FreeComment(models.Model):
verbose_name_plural = _('free comments') verbose_name_plural = _('free comments')
ordering = ('-submit_date',) ordering = ('-submit_date',)
class Admin:
fields = (
(None, {'fields': ('content_type', 'object_id', 'site')}),
('Content', {'fields': ('person_name', 'comment')}),
('Meta', {'fields': ('submit_date', 'is_public', 'ip_address', 'approved')}),
)
list_display = ('person_name', 'submit_date', 'content_type', 'get_content_object')
list_filter = ('submit_date',)
date_hierarchy = 'submit_date'
search_fields = ('comment', 'person_name')
def __unicode__(self): def __unicode__(self):
return "%s: %s..." % (self.person_name, self.comment[:100]) return "%s: %s..." % (self.person_name, self.comment[:100])
@ -306,3 +283,4 @@ class ModeratorDeletion(models.Model):
def __unicode__(self): def __unicode__(self):
return _("Moderator deletion by %r") % self.user return _("Moderator deletion by %r") % self.user

View File

@ -1,3 +1,6 @@
import base64
import datetime
from django.core import validators from django.core import validators
from django import oldforms from django import oldforms
from django.core.mail import mail_admins, mail_managers from django.core.mail import mail_admins, mail_managers
@ -7,16 +10,61 @@ from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.contrib.comments.models import Comment, FreeComment, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC from django.contrib.comments.models import Comment, FreeComment, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth import authenticate
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.text import normalize_newlines from django.utils.text import normalize_newlines
from django.conf import settings from django.conf import settings
from django.utils.translation import ungettext, ugettext as _ from django.utils.translation import ungettext, ugettext as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
import base64, datetime
COMMENTS_PER_PAGE = 20 COMMENTS_PER_PAGE = 20
# TODO: This is a copy of the manipulator-based form that used to live in
# contrib.auth.forms. It should be replaced with the newforms version that
# has now been added to contrib.auth.forms when the comments app gets updated
# for newforms.
class AuthenticationForm(oldforms.Manipulator):
"""
Base class for authenticating users. Extend this to get a form that accepts
username/password logins.
"""
def __init__(self, request=None):
"""
If request is passed in, the manipulator will validate that cookies are
enabled. Note that the request (a HttpRequest object) must have set a
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
running this validator.
"""
self.request = request
self.fields = [
oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
]
self.user_cache = None
def hasCookiesEnabled(self, field_data, all_data):
if self.request and not self.request.session.test_cookie_worked():
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
def isValidUser(self, field_data, all_data):
username = field_data
password = all_data.get('password', None)
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
elif not self.user_cache.is_active:
raise validators.ValidationError, _("This account is inactive.")
def get_user_id(self):
if self.user_cache:
return self.user_cache.id
return None
def get_user(self):
return self.user_cache
class PublicCommentManipulator(AuthenticationForm): class PublicCommentManipulator(AuthenticationForm):
"Manipulator that handles public registered comments" "Manipulator that handles public registered comments"
def __init__(self, user, ratings_required, ratings_range, num_rating_choices): def __init__(self, user, ratings_required, ratings_range, num_rating_choices):

View File

@ -8,6 +8,7 @@ from django.utils.translation import get_date_formats
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.views.generic import date_based from django.views.generic import date_based
from django.utils import datetime_safe
class CalendarPlugin(DatabrowsePlugin): class CalendarPlugin(DatabrowsePlugin):
def __init__(self, field_names=None): def __init__(self, field_names=None):
@ -33,12 +34,13 @@ class CalendarPlugin(DatabrowsePlugin):
def urls(self, plugin_name, easy_instance_field): def urls(self, plugin_name, easy_instance_field):
if isinstance(easy_instance_field.field, models.DateField): if isinstance(easy_instance_field.field, models.DateField):
d = easy_instance_field.raw_value
return [mark_safe(u'%s%s/%s/%s/%s/%s/' % ( return [mark_safe(u'%s%s/%s/%s/%s/%s/' % (
easy_instance_field.model.url(), easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name, plugin_name, easy_instance_field.field.name,
easy_instance_field.raw_value.year, d.year,
easy_instance_field.raw_value.strftime('%b').lower(), datetime_safe.new_date(d).strftime('%b').lower(),
easy_instance_field.raw_value.day))] d.day))]
def model_view(self, request, model_databrowse, url): def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site self.model, self.site = model_databrowse.model, model_databrowse.site

View File

@ -0,0 +1,15 @@
from django.contrib import admin
from django.contrib.flatpages.models import FlatPage
from django.utils.translation import ugettext_lazy as _
class FlatPageAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('url', 'title', 'content', 'sites')}),
(_('Advanced options'), {'classes': ('collapse',), 'fields': ('enable_comments', 'registration_required', 'template_name')}),
)
list_display = ('url', 'title')
list_filter = ('sites', 'enable_comments', 'registration_required')
search_fields = ('url', 'title')
admin.site.register(FlatPage, FlatPageAdmin)

View File

@ -21,15 +21,6 @@ class FlatPage(models.Model):
verbose_name_plural = _('flat pages') verbose_name_plural = _('flat pages')
ordering = ('url',) ordering = ('url',)
class Admin:
fields = (
(None, {'fields': ('url', 'title', 'content', 'sites')}),
(_('Advanced options'), {'classes': 'collapse', 'fields': ('enable_comments', 'registration_required', 'template_name')}),
)
list_display = ('url', 'title')
list_filter = ('sites', 'enable_comments', 'registration_required')
search_fields = ('url', 'title')
def __unicode__(self): def __unicode__(self):
return u"%s -- %s" % (self.url, self.title) return u"%s -- %s" % (self.url, self.title)

View File

@ -1,4 +1,4 @@
from django import newforms as forms from django import forms
from django.contrib.formtools import preview from django.contrib.formtools import preview
from django import http from django import http
from django.conf import settings from django.conf import settings

View File

@ -4,7 +4,7 @@ step and storing the form's state as HTML hidden fields so that no state is
stored on the server side. stored on the server side.
""" """
from django import newforms as forms from django import forms
from django.conf import settings from django.conf import settings
from django.http import Http404 from django.http import Http404
from django.shortcuts import render_to_response from django.shortcuts import render_to_response

View File

@ -3,8 +3,8 @@
AR-specific Form helpers. AR-specific Form helpers.
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import RegexField, CharField, Select, EMPTY_VALUES from django.forms.fields import RegexField, CharField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _

View File

@ -2,9 +2,9 @@
Australian-specific Form helpers Australian-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.newforms.util import smart_unicode from django.forms.util import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -3,8 +3,8 @@
BR-specific Form helpers BR-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, CharField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, CharField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -2,9 +2,9 @@
Canada-specific Form helpers Canada-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.newforms.util import smart_unicode from django.forms.util import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -2,8 +2,8 @@
Swiss-specific Form helpers Swiss-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -2,8 +2,8 @@
Chile specific form helpers. Chile specific form helpers.
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import RegexField, Select, EMPTY_VALUES from django.forms.fields import RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode

View File

@ -2,8 +2,8 @@
DE-specific Form helpers DE-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -3,8 +3,8 @@
Spanish-specific Form helpers Spanish-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import RegexField, Select, EMPTY_VALUES from django.forms.fields import RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -3,8 +3,8 @@ FI-specific Form helpers
""" """
import re import re
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class FIZipCodeField(RegexField): class FIZipCodeField(RegexField):

View File

@ -2,8 +2,8 @@
FR-specific Form helpers FR-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -1,4 +1,4 @@
from django import newforms as forms from django import forms
DEFAULT_DATE_INPUT_FORMATS = ( DEFAULT_DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%d/%m/%Y', '%d/%m/%y', # '2006-10-25', '25/10/2006', '25/10/06' '%Y-%m-%d', '%d/%m/%Y', '%d/%m/%y', # '2006-10-25', '25/10/2006', '25/10/06'

View File

@ -2,8 +2,8 @@
India-specific Form helpers. India-specific Form helpers.
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import gettext from django.utils.translation import gettext
import re import re

View File

@ -2,9 +2,9 @@
Iceland specific form helpers. Iceland specific form helpers.
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import RegexField, EMPTY_VALUES from django.forms.fields import RegexField, EMPTY_VALUES
from django.newforms.widgets import Select from django.forms.widgets import Select
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode

View File

@ -2,8 +2,8 @@
IT-specific Form helpers IT-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check_digit from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check_digit

View File

@ -3,9 +3,9 @@ JP-specific Form helpers
""" """
from django.core import validators from django.core import validators
from django.newforms import ValidationError from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.newforms.fields import RegexField, Select from django.forms.fields import RegexField, Select
class JPPostalCodeField(RegexField): class JPPostalCodeField(RegexField):
""" """

View File

@ -2,7 +2,7 @@
Mexican-specific form helpers. Mexican-specific form helpers.
""" """
from django.newforms.fields import Select from django.forms.fields import Select
class MXStateSelect(Select): class MXStateSelect(Select):
""" """

View File

@ -4,8 +4,8 @@ NL-specific Form helpers
import re import re
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, Select, EMPTY_VALUES from django.forms.fields import Field, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode

View File

@ -3,8 +3,8 @@ Norwegian-specific Form helpers
""" """
import re, datetime import re, datetime
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class NOZipCodeField(RegexField): class NOZipCodeField(RegexField):

View File

@ -3,8 +3,8 @@
PE-specific Form helpers. PE-specific Form helpers.
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import RegexField, CharField, Select, EMPTY_VALUES from django.forms.fields import RegexField, CharField, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class PEDepartmentSelect(Select): class PEDepartmentSelect(Select):

View File

@ -4,8 +4,8 @@ Polish-specific form helpers
import re import re
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Select, RegexField from django.forms.fields import Select, RegexField
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class PLVoivodeshipSelect(Select): class PLVoivodeshipSelect(Select):

View File

@ -2,7 +2,7 @@
Slovak-specific form helpers Slovak-specific form helpers
""" """
from django.newforms.fields import Select, RegexField from django.forms.fields import Select, RegexField
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class SKRegionSelect(Select): class SKRegionSelect(Select):

View File

@ -4,8 +4,8 @@ UK-specific Form helpers
import re import re
from django.newforms.fields import CharField, Select from django.forms.fields import CharField, Select
from django.newforms import ValidationError from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class UKPostcodeField(CharField): class UKPostcodeField(CharField):

View File

@ -2,8 +2,8 @@
USA-specific Form helpers USA-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import re import re

View File

@ -2,8 +2,8 @@
South Africa-specific Form helpers South Africa-specific Form helpers
""" """
from django.newforms import ValidationError from django.forms import ValidationError
from django.newforms.fields import Field, RegexField, EMPTY_VALUES from django.forms.fields import Field, RegexField, EMPTY_VALUES
from django.utils.checksums import luhn from django.utils.checksums import luhn
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import re import re

View File

@ -3,7 +3,7 @@ from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class Redirect(models.Model): class Redirect(models.Model):
site = models.ForeignKey(Site, radio_admin=models.VERTICAL) site = models.ForeignKey(Site)
old_path = models.CharField(_('redirect from'), max_length=200, db_index=True, old_path = models.CharField(_('redirect from'), max_length=200, db_index=True,
help_text=_("This should be an absolute path, excluding the domain name. Example: '/events/search/'.")) help_text=_("This should be an absolute path, excluding the domain name. Example: '/events/search/'."))
new_path = models.CharField(_('redirect to'), max_length=200, blank=True, new_path = models.CharField(_('redirect to'), max_length=200, blank=True,
@ -16,10 +16,20 @@ class Redirect(models.Model):
unique_together=(('site', 'old_path'),) unique_together=(('site', 'old_path'),)
ordering = ('old_path',) ordering = ('old_path',)
class Admin:
list_display = ('old_path', 'new_path')
list_filter = ('site',)
search_fields = ('old_path', 'new_path')
def __unicode__(self): def __unicode__(self):
return u"%s ---> %s" % (self.old_path, self.new_path) return "%s ---> %s" % (self.old_path, self.new_path)
# Register the admin options for these models.
# TODO: Maybe this should live in a separate module admin.py, but how would we
# ensure that module was loaded?
from django.contrib import admin
class RedirectAdmin(admin.ModelAdmin):
list_display = ('old_path', 'new_path')
list_filter = ('site',)
search_fields = ('old_path', 'new_path')
radio_fields = {'site': admin.VERTICAL}
admin.site.register(Redirect, RedirectAdmin)

View File

@ -0,0 +1,9 @@
from django.contrib import admin
from django.contrib.sites.models import Site
class SiteAdmin(admin.ModelAdmin):
list_display = ('domain', 'name')
search_fields = ('domain', 'name')
admin.site.register(Site, SiteAdmin)

View File

@ -32,14 +32,12 @@ class Site(models.Model):
domain = models.CharField(_('domain name'), max_length=100) domain = models.CharField(_('domain name'), max_length=100)
name = models.CharField(_('display name'), max_length=50) name = models.CharField(_('display name'), max_length=50)
objects = SiteManager() objects = SiteManager()
class Meta: class Meta:
db_table = 'django_site' db_table = 'django_site'
verbose_name = _('site') verbose_name = _('site')
verbose_name_plural = _('sites') verbose_name_plural = _('sites')
ordering = ('domain',) ordering = ('domain',)
class Admin:
list_display = ('domain', 'name')
search_fields = ('domain', 'name')
def __unicode__(self): def __unicode__(self):
return self.domain return self.domain
@ -52,7 +50,6 @@ class Site(models.Model):
except KeyError: except KeyError:
pass pass
class RequestSite(object): class RequestSite(object):
""" """
A class that shares the primary interface of Site (i.e., it has A class that shares the primary interface of Site (i.e., it has

View File

@ -60,7 +60,6 @@ class BaseHandler(object):
def get_response(self, request): def get_response(self, request):
"Returns an HttpResponse object for the given HttpRequest" "Returns an HttpResponse object for the given HttpRequest"
from django.core import exceptions, urlresolvers from django.core import exceptions, urlresolvers
from django.core.mail import mail_admins
from django.conf import settings from django.conf import settings
# Apply request middleware # Apply request middleware
@ -122,21 +121,36 @@ class BaseHandler(object):
if settings.DEBUG_PROPAGATE_EXCEPTIONS: if settings.DEBUG_PROPAGATE_EXCEPTIONS:
raise raise
elif settings.DEBUG: return self.handle_uncaught_exception(request, resolver, exc_info)
from django.views import debug
return debug.technical_500_response(request, *exc_info) def handle_uncaught_exception(self, request, resolver, exc_info):
else: """
# When DEBUG is False, send an error message to the admins. Processing for any otherwise uncaught exceptions (those that will
subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path) generate HTTP 500 responses). Can be overridden by subclasses who want
try: customised 500 handling.
request_repr = repr(request)
except: Be *very* careful when overriding this because the error could be
request_repr = "Request repr() unavailable" caused by anything, so assuming something like the database is always
message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr) available would be an error.
mail_admins(subject, message, fail_silently=True) """
# Return an HttpResponse that displays a friendly error message. from django.conf import settings
callback, param_dict = resolver.resolve500() from django.core.mail import mail_admins
return callback(request, **param_dict)
if settings.DEBUG:
from django.views import debug
return debug.technical_500_response(request, *exc_info)
# When DEBUG is False, send an error message to the admins.
subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
try:
request_repr = repr(request)
except:
request_repr = "Request repr() unavailable"
message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
mail_admins(subject, message, fail_silently=True)
# Return an HttpResponse that displays a friendly error message.
callback, param_dict = resolver.resolve500()
return callback(request, **param_dict)
def _get_traceback(self, exc_info=None): def _get_traceback(self, exc_info=None):
"Helper function to return the traceback as a string" "Helper function to return the traceback as a string"

View File

@ -51,8 +51,6 @@ def get_validation_errors(outfile, app=None):
from PIL import Image from PIL import Image
except ImportError: except ImportError:
e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name) e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name)
if f.prepopulate_from is not None and type(f.prepopulate_from) not in (list, tuple):
e.add(opts, '"%s": prepopulate_from should be a list or tuple.' % f.name)
if f.choices: if f.choices:
if isinstance(f.choices, basestring) or not is_iterable(f.choices): if isinstance(f.choices, basestring) or not is_iterable(f.choices):
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name) e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
@ -145,54 +143,6 @@ def get_validation_errors(outfile, app=None):
if r.get_accessor_name() == rel_query_name: if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
# Check admin attribute.
if opts.admin is not None:
if not isinstance(opts.admin, models.AdminOptions):
e.add(opts, '"admin" attribute, if given, must be set to a models.AdminOptions() instance.')
else:
# list_display
if not isinstance(opts.admin.list_display, (list, tuple)):
e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
else:
for fn in opts.admin.list_display:
try:
f = opts.get_field(fn)
except models.FieldDoesNotExist:
if not hasattr(cls, fn):
e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn)
else:
if isinstance(f, models.ManyToManyField):
e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
# list_display_links
if opts.admin.list_display_links and not opts.admin.list_display:
e.add(opts, '"admin.list_display" must be defined for "admin.list_display_links" to be used.')
if not isinstance(opts.admin.list_display_links, (list, tuple)):
e.add(opts, '"admin.list_display_links", if given, must be set to a list or tuple.')
else:
for fn in opts.admin.list_display_links:
try:
f = opts.get_field(fn)
except models.FieldDoesNotExist:
if not hasattr(cls, fn):
e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
if fn not in opts.admin.list_display:
e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
# list_filter
if not isinstance(opts.admin.list_filter, (list, tuple)):
e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
else:
for fn in opts.admin.list_filter:
try:
f = opts.get_field(fn)
except models.FieldDoesNotExist:
e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
# date_hierarchy
if opts.admin.date_hierarchy:
try:
f = opts.get_field(opts.admin.date_hierarchy)
except models.FieldDoesNotExist:
e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy)
# Check ordering attribute. # Check ordering attribute.
if opts.ordering: if opts.ordering:
for field_name in opts.ordering: for field_name in opts.ordering:
@ -210,18 +160,6 @@ def get_validation_errors(outfile, app=None):
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
# Check core=True, if needed.
for related in opts.get_followed_related_objects():
if not related.edit_inline:
continue
try:
for f in related.opts.fields:
if f.core:
raise StopIteration
e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
except StopIteration:
pass
# Check unique_together. # Check unique_together.
for ut in opts.unique_together: for ut in opts.unique_together:
for field_name in ut: for field_name in ut:

View File

@ -8,6 +8,7 @@ except ImportError:
from StringIO import StringIO from StringIO import StringIO
from django.db import models from django.db import models
from django.utils.encoding import smart_str, smart_unicode from django.utils.encoding import smart_str, smart_unicode
from django.utils import datetime_safe
class SerializationError(Exception): class SerializationError(Exception):
"""Something bad happened during serialization.""" """Something bad happened during serialization."""
@ -59,7 +60,8 @@ class Serializer(object):
Convert a field's value to a string. Convert a field's value to a string.
""" """
if isinstance(field, models.DateTimeField): if isinstance(field, models.DateTimeField):
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S") d = datetime_safe.new_datetime(getattr(obj, field.name))
value = d.strftime("%Y-%m-%d %H:%M:%S")
else: else:
value = field.flatten_data(follow=None, obj=obj).get(field.name, "") value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
return smart_unicode(value) return smart_unicode(value)

View File

@ -6,6 +6,7 @@ import datetime
from django.utils import simplejson from django.utils import simplejson
from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer from django.core.serializers.python import Deserializer as PythonDeserializer
from django.utils import datetime_safe
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
@ -51,9 +52,11 @@ class DjangoJSONEncoder(simplejson.JSONEncoder):
def default(self, o): def default(self, o):
if isinstance(o, datetime.datetime): if isinstance(o, datetime.datetime):
return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT)) d = datetime_safe.new_datetime(o)
return d.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
elif isinstance(o, datetime.date): elif isinstance(o, datetime.date):
return o.strftime(self.DATE_FORMAT) d = datetime_safe.new_date(o)
return d.strftime(self.DATE_FORMAT)
elif isinstance(o, datetime.time): elif isinstance(o, datetime.time):
return o.strftime(self.TIME_FORMAT) return o.strftime(self.TIME_FORMAT)
elif isinstance(o, decimal.Decimal): elif isinstance(o, decimal.Decimal):

Some files were not shown because too many files have changed in this diff Show More