summaryrefslogtreecommitdiff
path: root/django/contrib/admin
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2006-05-02 01:31:56 +0000
committerAdrian Holovaty <adrian@holovaty.com>2006-05-02 01:31:56 +0000
commitf69cf70ed813a8cd7e1f963a14ae39103e8d5265 (patch)
treed3b32e84cd66573b3833ddf662af020f8ef2f7a8 /django/contrib/admin
parentd5dbeaa9be359a4c794885c2e9f1b5a7e5e51fb8 (diff)
MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards-incompatible. Please read http://code.djangoproject.com/wiki/RemovingTheMagic for upgrade instructions.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@2809 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/contrib/admin')
-rw-r--r--django/contrib/admin/filterspecs.py20
-rw-r--r--django/contrib/admin/media/css/base.css15
-rw-r--r--django/contrib/admin/media/css/changelists.css18
-rw-r--r--django/contrib/admin/media/css/dashboard.css10
-rw-r--r--django/contrib/admin/media/css/forms.css60
-rw-r--r--django/contrib/admin/media/css/global.css304
-rw-r--r--django/contrib/admin/media/css/layout.css29
-rw-r--r--django/contrib/admin/media/css/login.css13
-rw-r--r--django/contrib/admin/media/css/patch-iewin.css5
-rw-r--r--django/contrib/admin/media/css/widgets.css101
-rw-r--r--django/contrib/admin/media/img/admin/deleted-overlay.gifbin0 -> 45 bytes
-rw-r--r--django/contrib/admin/media/img/admin/inline-delete-8bit.pngbin0 -> 477 bytes
-rw-r--r--django/contrib/admin/media/img/admin/inline-delete.pngbin0 -> 781 bytes
-rw-r--r--django/contrib/admin/media/img/admin/inline-restore-8bit.pngbin0 -> 447 bytes
-rw-r--r--django/contrib/admin/media/img/admin/inline-restore.pngbin0 -> 623 bytes
-rw-r--r--django/contrib/admin/media/img/admin/inline-splitter-bg.gifbin0 -> 102 bytes
-rw-r--r--django/contrib/admin/media/js/admin/RelatedObjectLookups.js2
-rw-r--r--django/contrib/admin/models.py51
-rw-r--r--django/contrib/admin/models/__init__.py1
-rw-r--r--django/contrib/admin/models/admin.py50
-rw-r--r--django/contrib/admin/templates/admin/404.html2
-rw-r--r--django/contrib/admin/templates/admin/500.html2
-rw-r--r--django/contrib/admin/templates/admin/base_site.html2
-rw-r--r--django/contrib/admin/templates/admin/change_form.html47
-rw-r--r--django/contrib/admin/templates/admin/change_list.html5
-rw-r--r--django/contrib/admin/templates/admin/delete_confirmation.html12
-rw-r--r--django/contrib/admin/templates/admin/edit_inline_stacked.html2
-rw-r--r--django/contrib/admin/templates/admin/field_line.html8
-rw-r--r--django/contrib/admin/templates/admin/index.html13
-rw-r--r--django/contrib/admin/templates/admin/login.html31
-rw-r--r--django/contrib/admin/templates/admin/object_history.html12
-rw-r--r--django/contrib/admin/templates/admin/search_form.html2
-rw-r--r--django/contrib/admin/templates/admin/template_validator.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/bookmarklets.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/index.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/missing_docutils.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/model_detail.html4
-rw-r--r--django/contrib/admin/templates/admin_doc/model_index.html17
-rw-r--r--django/contrib/admin/templates/admin_doc/template_detail.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/template_filter_index.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/template_tag_index.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/view_detail.html2
-rw-r--r--django/contrib/admin/templates/admin_doc/view_index.html2
-rw-r--r--django/contrib/admin/templates/registration/logged_out.html2
-rw-r--r--django/contrib/admin/templates/registration/password_change_done.html2
-rw-r--r--django/contrib/admin/templates/registration/password_change_form.html2
-rw-r--r--django/contrib/admin/templates/registration/password_reset_done.html2
-rw-r--r--django/contrib/admin/templates/registration/password_reset_form.html2
-rw-r--r--django/contrib/admin/templates/widget/foreign.html20
-rw-r--r--django/contrib/admin/templates/widget/many_to_many.html2
-rw-r--r--django/contrib/admin/templates/widget/one_to_one.html3
-rw-r--r--django/contrib/admin/templatetags/admin_list.py112
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py123
-rw-r--r--django/contrib/admin/templatetags/adminapplist.py19
-rw-r--r--django/contrib/admin/templatetags/adminmedia.py7
-rw-r--r--django/contrib/admin/templatetags/log.py6
-rw-r--r--django/contrib/admin/urls.py31
-rw-r--r--django/contrib/admin/urls/__init__.py0
-rw-r--r--django/contrib/admin/urls/admin.py58
-rw-r--r--django/contrib/admin/utils.py6
-rw-r--r--django/contrib/admin/views/decorators.py30
-rw-r--r--django/contrib/admin/views/doc.py145
-rw-r--r--django/contrib/admin/views/main.py835
-rw-r--r--django/contrib/admin/views/template.py24
64 files changed, 1203 insertions, 1084 deletions
diff --git a/django/contrib/admin/filterspecs.py b/django/contrib/admin/filterspecs.py
index 31a6ba37c9..0284f13114 100644
--- a/django/contrib/admin/filterspecs.py
+++ b/django/contrib/admin/filterspecs.py
@@ -6,7 +6,7 @@ Each filter subclass knows how to display a filter for a field that passes a
certain test -- e.g. being a DateField or ForeignKey.
"""
-from django.core import meta
+from django.db import models
import datetime
class FilterSpec(object):
@@ -50,13 +50,13 @@ class FilterSpec(object):
class RelatedFilterSpec(FilterSpec):
def __init__(self, f, request, params):
super(RelatedFilterSpec, self).__init__(f, request, params)
- if isinstance(f, meta.ManyToManyField):
- self.lookup_title = f.rel.to.verbose_name
+ if isinstance(f, models.ManyToManyField):
+ self.lookup_title = f.rel.to._meta.verbose_name
else:
self.lookup_title = f.verbose_name
- self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
+ self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to._meta.pk.name)
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
- self.lookup_choices = f.rel.to.get_model_module().get_list()
+ self.lookup_choices = f.rel.to._default_manager.all()
def has_output(self):
return len(self.lookup_choices) > 1
@@ -69,7 +69,7 @@ class RelatedFilterSpec(FilterSpec):
'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
'display': _('All')}
for val in self.lookup_choices:
- pk_val = getattr(val, self.field.rel.to.pk.attname)
+ pk_val = getattr(val, self.field.rel.to._meta.pk.attname)
yield {'selected': self.lookup_val == str(pk_val),
'query_string': cl.get_query_string( {self.lookup_kwarg: pk_val}),
'display': val}
@@ -103,7 +103,7 @@ class DateFieldFilterSpec(FilterSpec):
today = datetime.date.today()
one_week_ago = today - datetime.timedelta(days=7)
- today_str = isinstance(self.field, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
+ today_str = isinstance(self.field, models.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
self.links = (
(_('Any date'), {}),
@@ -126,7 +126,7 @@ class DateFieldFilterSpec(FilterSpec):
'query_string': cl.get_query_string( param_dict, self.field_generic),
'display': title}
-FilterSpec.register(lambda f: isinstance(f, meta.DateField), DateFieldFilterSpec)
+FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
class BooleanFieldFilterSpec(FilterSpec):
def __init__(self, f, request, params):
@@ -144,9 +144,9 @@ class BooleanFieldFilterSpec(FilterSpec):
yield {'selected': self.lookup_val == v and not self.lookup_val2,
'query_string': cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]),
'display': k}
- if isinstance(self.field, meta.NullBooleanField):
+ if isinstance(self.field, models.NullBooleanField):
yield {'selected': self.lookup_val2 == 'True',
'query_string': cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
'display': _('Unknown')}
-FilterSpec.register(lambda f: isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField), BooleanFieldFilterSpec)
+FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)
diff --git a/django/contrib/admin/media/css/base.css b/django/contrib/admin/media/css/base.css
index 7c4437f454..88f7d9a95a 100644
--- a/django/contrib/admin/media/css/base.css
+++ b/django/contrib/admin/media/css/base.css
@@ -1,3 +1,14 @@
-@import url(global.css);
-@import url(changelists.css);
+/*
+ DJANGO Admin
+ by Wilson Miner wilson@lawrence.com
+*/
+
+/* Block IE 5 */
+@import "null?\"\{";
+
+/* Import other styles */
+@import url('global.css');
+@import url('layout.css');
+
+/* Import patch for IE 6 Windows */
/*\*/ @import "patch-iewin.css"; /**/ \ No newline at end of file
diff --git a/django/contrib/admin/media/css/changelists.css b/django/contrib/admin/media/css/changelists.css
index 7ff59c5e6b..2269c9fe20 100644
--- a/django/contrib/admin/media/css/changelists.css
+++ b/django/contrib/admin/media/css/changelists.css
@@ -1,16 +1,13 @@
-/*
- DJANGO Admin Changelist Styles
- by Wilson Miner wilson@lawrence.com
- Copyright (c) 2005 Lawrence Journal-World
-*/
+@import url('base.css');
+/* CHANGELISTS */
#changelist { position:relative; width:100%; }
#changelist table { width:100%; }
.change-list .filtered table { border-right:1px solid #ddd; }
.change-list .filtered { min-height:400px; _height:400px; }
.change-list .filtered { background:white url(../img/admin/changelist-bg.gif) top right repeat-y !important; }
.change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { margin-right:160px !important; width:auto !important; }
-.change-list .filtered table tbody th { padding-right:10px; }
+.change-list .filtered table tbody th { padding-right:1em; }
#changelist .toplinks { border-bottom:1px solid #ccc !important; }
#changelist .paginator { color:#666; border-top:1px solid #eee; border-bottom:1px solid #eee; background:white url(../img/admin/nav-bg.gif) 0 180% repeat-x; overflow:hidden; }
.change-list .filtered .paginator { border-right:1px solid #ddd; }
@@ -42,3 +39,12 @@
.change-list ul.toplinks li { float: left; width: 9em; padding:3px 6px; font-weight: bold; list-style-type:none; }
.change-list ul.toplinks .date-back a { color:#999; }
.change-list ul.toplinks .date-back a:hover { color:#036; }
+
+/* PAGINATOR */
+.paginator { font-size:11px; padding-top:10px; padding-bottom:10px; line-height:22px; margin:0; border-top:1px solid #ddd; }
+.paginator a:link, .paginator a:visited { padding:2px 6px; border:solid 1px #ccc; background:white; text-decoration:none; }
+.paginator a.showall { padding:0 !important; border:none !important; }
+.paginator a.showall:hover { color:#036 !important; background:transparent !important; }
+.paginator .end { border-width:2px !important; margin-right:6px; }
+.paginator .this-page { padding:2px 6px; font-weight:bold; font-size:13px; vertical-align:top; }
+.paginator a:hover { color:white; background:#5b80b2; border-color:#036; }
diff --git a/django/contrib/admin/media/css/dashboard.css b/django/contrib/admin/media/css/dashboard.css
new file mode 100644
index 0000000000..d27797324b
--- /dev/null
+++ b/django/contrib/admin/media/css/dashboard.css
@@ -0,0 +1,10 @@
+@import url('base.css');
+
+/* DASHBOARD */
+.dashboard .module table th { width:100%; }
+.dashboard .module table td { white-space:nowrap; }
+.dashboard .module table td a { display:block; padding-right:.6em; }
+
+/* RECENT ACTIONS MODULE */
+.module ul.actionlist { margin-left:0; }
+ul.actionlist li { list-style-type:none; } \ No newline at end of file
diff --git a/django/contrib/admin/media/css/forms.css b/django/contrib/admin/media/css/forms.css
new file mode 100644
index 0000000000..b66f268fec
--- /dev/null
+++ b/django/contrib/admin/media/css/forms.css
@@ -0,0 +1,60 @@
+@import url('base.css');
+@import url('widgets.css');
+
+/* FORM ROWS */
+.form-row { overflow:hidden; padding:8px 12px; font-size:11px; border-bottom:1px solid #eee; }
+.form-row img, .form-row input { vertical-align:middle; }
+form .form-row p { padding-left:0; font-size:11px; }
+
+/* FORM LABELS */
+form h4 { margin:0 !important; padding:0 !important; border:none !important; }
+label { font-weight:normal !important; color:#666; font-size:12px; }
+label.inline { margin-left:20px; }
+.required label, label.required { font-weight:bold !important; color:#333 !important; }
+
+/* RADIO BUTTONS */
+form ul.radiolist li { list-style-type:none; }
+form ul.radiolist label { float:none; display:inline; }
+form ul.inline { margin-left:0; padding:0; }
+form ul.inline li { float:left; padding-right:7px; }
+
+/* ALIGNED FIELDSETS */
+.aligned label { display:block; padding:0 1em 3px 0; float:left; width:8em; }
+.aligned label.inline { display:inline; float:none; }
+.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { width:350px; }
+form .aligned p, form .aligned ul { margin-left:7em; padding-left:30px; }
+form .aligned table p { margin-left:0; padding-left:0; }
+form .aligned p.help { padding-left:38px; }
+.aligned .vCheckboxLabel { float:none !important; display:inline; padding-left:4px; }
+.colM .aligned .vLargeTextField, colM .aligned .vXMLLargeTextField { width:610px; }
+.checkbox-row p.help { margin-left:0; padding-left:0 !important; }
+
+/* WIDE FIELDSETS */
+.wide label { width:15em !important; }
+form .wide p { margin-left:15em; }
+form .wide p.help { padding-left:38px; }
+.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { width:450px; }
+
+/* COLLAPSED FIELDSETS */
+fieldset.collapsed * { display:none; }
+fieldset.collapsed h2, fieldset.collapsed { display:block !important; }
+fieldset.collapsed h2 { background-image:url(../img/admin/nav-bg.gif); background-position:bottom left; color:#999; }
+fieldset.collapsed .collapse-toggle { padding:3px 5px !important; background:transparent; display:inline !important;}
+
+/* MONOSPACE TEXTAREAS */
+fieldset.monospace textarea { font-family:"Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; }
+
+/* SUBMIT ROW */
+.submit-row { padding:5px 7px; text-align:right; background:white url(../img/admin/nav-bg.gif) 0 100% repeat-x; border:1px solid #ccc; margin:5px 0; }
+.submit-row input { margin:0 0 0 5px; }
+.submit-row p { margin-top:0.3em; }
+.submit-row .deletelink { background:url(../img/admin/icon_deletelink.gif) 0 50% no-repeat; padding-left:14px; }
+
+/* CUSTOM FORM FIELDS */
+.vSelectMultipleField { vertical-align:top !important; }
+.vCheckboxField { border:none; }
+.vDateField, .vTimeField { margin-right:2px; }
+.vURLField { width:30em; }
+.vLargeTextField, .vXMLLargeTextField { width:48em; }
+.flatpages-flatpage #id_content { height:40.2em; }
+.module table .vPositiveSmallIntegerField { width:2.2em; } \ No newline at end of file
diff --git a/django/contrib/admin/media/css/global.css b/django/contrib/admin/media/css/global.css
index 765e752d48..67e37324e5 100644
--- a/django/contrib/admin/media/css/global.css
+++ b/django/contrib/admin/media/css/global.css
@@ -1,19 +1,14 @@
-/*
- DJANGO Admin Global Styles
- by Wilson Miner wilson@lawrence.com
- Copyright (c) 2005 Lawrence Journal-World
-*/
-
-body { margin:0; padding:0; font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif; color:#333; background:#fff; }
+body { margin:0; padding:0; font-size:12px; font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif; color:#333; background:#fff; }
/* LINKS */
a:link, a:visited { color: #5b80b2; text-decoration:none; }
a:hover { color: #036; }
a img { border:none; }
-/* GLOBAL DEFAULTS */
-p, ol, ul, dl { margin:.2em 0 .8em 0; font-size:12px; }
+/* GLOBAL DEFAULTS */
+p, ol, ul, dl { margin:.2em 0 .8em 0; }
p { padding:0; line-height:140%; }
+
h1,h2,h3,h4,h5 { font-weight:bold; }
h1 { font-size:18px; color:#666; padding:0 6px 0 0; margin:0 0 .2em 0; }
h2 { font-size:16px; margin:1em 0 .5em 0; }
@@ -21,6 +16,7 @@ h2.subhead { font-weight:normal;margin-top:0; }
h3 { font-size:14px; margin:.8em 0 .3em 0; color:#666; font-weight:bold; }
h4 { font-size:12px; margin:1em 0 .8em 0; padding-bottom:3px; }
h5 { font-size:10px; margin:1.5em 0 .5em 0; color:#666; text-transform:uppercase; letter-spacing:1px; }
+
ul li { list-style-type:square; padding:1px 0; }
ul.plainlist { margin-left:0 !important; }
ul.plainlist li { list-style-type:none; }
@@ -28,150 +24,83 @@ li ul { margin-bottom:0; }
li, dt, dd { font-size:11px; line-height:14px; }
dt { font-weight:bold; margin-top:4px; }
dd { margin-left:0; }
+
form { margin:0; padding:0; }
fieldset { margin:0; padding:0; }
+
blockquote { font-size:11px; color:#777; margin-left:2px; padding-left:10px; border-left:5px solid #ddd; }
code, pre { font-family:"Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; background:inherit; color:#666; font-size:11px; }
pre.literal-block { margin:10px; background:#eee; padding:6px 8px; }
code strong { color:#930; }
hr { clear:both; color:#eee; background-color:#eee; height:1px; border:none; margin:0; padding:0; font-size:1px; line-height:1px; }
-/* PAGE STRUCTURE */
-#container { position:relative; width:100%; min-width:760px; }
-#content { margin:10px 15px; }
-#header { width:100%; }
-#content-main { float:left; width:100%; }
-#content-related { float:right; width:220px; position:relative; margin-right:-230px; }
-#footer { clear:both; padding:10px; }
-
-/* COLUMN TYPES */
-.colMS { margin-right:245px !important; }
-.colSM { margin-left:245px !important; }
-.colSM #content-related { float:left; margin-right:0; margin-left:-230px; }
-.colSM #content-main { float:right; }
-.popup .colM { width:95%; }
-.subcol { float:left; width:46%; margin-right:15px; }
-.dashboard #content { width:500px; }
-
-/* HEADER */
-#header { background:#417690; color:#ffc; min-height:2.4em; overflow:hidden; }
-#header a:link, #header a:visited { color:white; }
-#header a:hover { text-decoration:underline; }
-#branding h1 { padding:0.5em 10px 0 10px; font-size:18px; margin:0; font-weight:normal; color:#f4f379; }
-#branding h2 { padding:0 10px 0.8em 10px; font-size:14px; margin:0; font-weight:normal; color:#ffc; }
-#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
-
-/* SIDEBAR */
-#content-related h3 { font-size:12px; color:#666; margin-bottom:3px; }
-#content-related h4 { font-size:11px; }
+/* TEXT STYLES & MODIFIERS */
+.small { font-size:11px; }
+.tiny { font-size:10px; }
+p.tiny { margin-top:-2px; }
+.mini { font-size:9px; }
+p.mini { margin-top:-3px; }
+.help, p.help { font-size:10px !important; color:#999; }
+p img, h1 img, h2 img, h3 img, h4 img, td img { vertical-align:middle; }
+.quiet, a.quiet:link, a.quiet:visited { color:#999 !important;font-weight:normal !important; }
+.quiet strong { font-weight:bold !important; }
+.float-right { float:right; }
+.float-left { float:left; }
+.clear { clear:both; }
+.align-left { text-align:left; }
+.align-right { text-align:right; }
+.example { margin:10px 0; padding:5px 10px; background:#efefef; }
+.nowrap { white-space:nowrap; }
-/* TABLES */
+/* TABLES */
table { border-collapse:collapse; border-color:#ccc; }
td, th { font-size:11px; line-height:13px; border-bottom:1px solid #eee; vertical-align:top; padding:5px; font-family:"Lucida Grande", Verdana, Arial, sans-serif; }
-th { text-align:left; font-size:12px; }
-thead th { font-weight:bold; color:#666; padding:2px 5px; font-size:11px; background:#e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; border-left:1px solid #ddd; border-bottom:1px solid #ddd; }
-thead th:first-child { border-left:none !important; }
-.superwide table th, .superwide table td, .superwide table input, .superwide table select { font-size:10px; }
-.module table { border-collapse: collapse; }
+th { text-align:left; font-size:12px; font-weight:bold; }
+thead th,
+tfoot td { color:#666; padding:2px 5px; font-size:11px; background:#e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; border-left:1px solid #ddd; border-bottom:1px solid #ddd; }
+tfoot td { border-bottom:none; border-top:1px solid #ddd; }
+thead th:first-child,
+tfoot td:first-child { border-left:none !important; }
thead th.optional { font-weight:normal !important; }
-#home-page table.module tr:hover { background:#EDF3FE; }
fieldset table { border-right:1px solid #eee; }
tr.row-label td { font-size:9px; padding-top:2px; padding-bottom:0; border-bottom:none; color:#666; margin-top:-1px; }
tr.alt { background:#f6f6f6; }
.row1 { background:#EDF3FE; }
.row2 { background:white; }
-table#change-history { width:100%; }
-table#change-history tbody th { width:16em; }
-/* TABLE SORTING */
+/* SORTABLE TABLES */
thead th a:link, thead th a:visited { color:#666; display:block; }
table thead th.sorted { background-position:bottom left !important; }
table thead th.sorted a { padding-right:13px; }
table thead th.ascending a { background:url(../img/admin/arrow-down.gif) right .4em no-repeat; }
table thead th.descending a { background:url(../img/admin/arrow-up.gif) right .4em no-repeat; }
-/* MODULES */
-.module { border:1px solid #ccc; margin-bottom:5px; background:white; }
-.module p, .module ul, .module h3, .module h4, .module dl, .module pre { padding-left:10px; padding-right:10px; }
-.module blockquote { margin-left:12px; }
-.module ul, .module ol { margin-left:1.5em; }
-.module h2, .module caption { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; background:#7CA0C7 url(../img/admin/default-bg.gif) left top repeat-x; color:white; font-weight:bold; }
-.module caption { border:1px solid #ccc; border-bottom:none; }
-.module h3 { margin-top:.6em; }
-#content-related .module h2 { background:#eee url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; }
-#content-main .verbose .actionlist { float:right; font-size:10px; width:17em; position:relative; top:-1.6em; margin:0 8px; }
-
-/* DASHBOARD */
-.dashboard .module table th { width:100%; }
-.dashboard .module table td { white-space:nowrap; }
-.dashboard .module table td a { display:block; padding-right:.6em; }
+/* ORDERABLE TABLES */
+table.orderable tbody tr td:hover { cursor:move; }
+table.orderable tbody tr td:first-child { padding-left:14px; background-image:url(../img/admin/nav-bg-grabber.gif); background-repeat:repeat-y; }
+table.orderable-initalized .order-cell, body>tr>td.order-cell { display:none; }
-/* RECENT ACTIONS MODULE */
-.module ul.actionlist { margin-left:0; }
-ul.actionlist li { list-style-type:none; }
-
-/* FORM DEFAULTS */
-input, textarea, select { margin:2px 0; padding:2px 3px; vertical-align:middle; border:1px solid #ccc; font-family:"Lucida Grande", Verdana, Arial, sans-serif; font-weight:normal; font-size:11px; }
+/* FORM DEFAULTS */
+input, textarea, select { margin:2px 0; padding:2px 3px; vertical-align:middle; font-family:"Lucida Grande", Verdana, Arial, sans-serif; font-weight:normal; font-size:11px; }
textarea { vertical-align:top !important; }
-input[type=checkbox], input[type=radio] { border:none; }
+input[type=text], input[type=password], textarea, select, .vTextField { border:1px solid #ccc; }
/* FORM BUTTONS */
input[type=submit], input[type=button], .submit-row input { background:white url(../img/admin/nav-bg.gif) bottom repeat-x; padding:3px; color:black; }
input[type=submit]:active, input[type=button]:active { background-image:url(../img/admin/nav-bg-reverse.gif); background-position:top; }
input[type=submit].default, .submit-row input.default { border:2px solid #5b80b2; background:#7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x; font-weight:bold; color:white; }
input[type=submit].default:active { background-image:url(../img/admin/default-bg-reverse.gif); background-position:top; }
-.submit-row { padding:5px 7px; text-align:right; background:#ffc; border:1px solid #ccc; margin:5px 0; }
-.submit-row input { margin:0 0 0 5px; }
-.submit-row .float-left { padding-top:.1em; }
-
-/* FORM ROWS */
-.form-row { clear:both; padding:8px 12px; font-size:11px; }
-html>body .form-row { border-bottom:1px solid #eee; }
-.form-row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
-.form-row img, .form-row input { vertical-align:middle; }
-form .form-row p { padding-left:0; font-size:11px; }
-
-/* FORM LABELS */
-form h4 { margin:0 !important; padding:0 !important; border:none !important; }
-label { font-weight:normal !important; color:#666; font-size:12px; }
-label.inline { margin-left:20px; }
-.required label, label.required { font-weight:bold !important; color:#333 !important; }
-
-/* RADIO BUTTONS */
-form ul.radiolist li { list-style-type:none; }
-form ul.radiolist label { float:none; display:inline; }
-form ul.inline { margin-left:0; padding:0; }
-form ul.inline li { float:left; padding-right:7px; }
-
-/* ALIGNED FIELDSETS */
-.aligned label { display:block; padding:0 1em 3px 0; float:left; text-align:left; width:8em; }
-.aligned label.inline { display:inline; float:none; }
-.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { width:350px; }
-form .aligned p, form .aligned ul { margin-left:7em; padding-left:30px; }
-form .aligned table p { margin-left:0; padding-left:0; }
-form .aligned p.help { padding-left:38px; }
-.aligned .vCheckboxLabel { float:none !important; display:inline; }
-.colM .aligned .vLargeTextField, colM .aligned .vXMLLargeTextField { width:610px; }
-.checkbox-row p.help { margin-left:0; padding-left:0 !important; }
-
-/* WIDE FIELDSETS */
-.wide label { width:15em !important; }
-form .wide p { margin-left:15em; }
-form .wide p.help { padding-left:38px; }
-.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { width:450px; }
-/* COLLAPSED FIELDSETS */
-fieldset.collapsed * { display:none; }
-fieldset.collapsed h2, fieldset.collapsed { display:block !important; }
-fieldset.collapsed .collapse-toggle { display: inline !important; }
-fieldset.collapse h2 a.collapse-toggle { color:#ffc; }
-fieldset.collapse h2 a.collapse-toggle:hover { text-decoration:underline; }
-.hidden { display:none; }
-
-/* MONOSPACE TEXTAREAS */
-fieldset.monospace textarea { font-family:"Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; }
+/* MODULES */
+.module { border:1px solid #ccc; margin-bottom:5px; background:white; }
+.module p, .module ul, .module h3, .module h4, .module dl, .module pre { padding-left:10px; padding-right:10px; }
+.module blockquote { margin-left:12px; }
+.module ul, .module ol { margin-left:1.5em; }
+.module h3 { margin-top:.6em; }
+.module h2, .module caption { margin:0; padding:2px 5px 3px 5px; font-size:11px; text-align:left; font-weight:bold; background:#7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x; color:white; }
+.module table { border-collapse: collapse; }
-/* MESSAGES & ERRORS */
+/* MESSAGES & ERRORS */
ul.messagelist { padding:0 0 5px 0; margin:0; }
ul.messagelist li { font-size:12px; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border-bottom:1px solid #ddd; color:#666; background:#ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat; }
.errornote { font-size:12px !important; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border:1px solid red; color:red;background:#ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; }
@@ -183,17 +112,21 @@ td ul.errorlist li { margin:0 !important; }
.error input, .error select { border:1px solid red; }
div.system-message { background: #ffc; margin: 10px; padding: 6px 8px; font-size: .8em; }
div.system-message p.system-message-title { padding:4px 5px 4px 25px; margin:0; color:red; background:#ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; }
+.description { font-size:12px; padding:5px 0 0 12px; }
-/* ACTION ICONS */
+/* BREADCRUMBS */
+div.breadcrumbs { background:white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; padding:2px 8px 3px 8px; font-size:11px; color:#999; border-top:1px solid white; border-bottom:1px solid #ccc; text-align:left; }
+
+/* ACTION ICONS */
.addlink { padding-left:12px; background:url(../img/admin/icon_addlink.gif) 0 .2em no-repeat; }
.changelink { padding-left:12px; background:url(../img/admin/icon_changelink.gif) 0 .2em no-repeat; }
-.deletelink { padding-left:12px; background:url(../img/admin/icon_deletelink.gif) 0 .2em no-repeat; }
+.deletelink { padding-left:12px; background:url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat; }
a.deletelink:link, a.deletelink:visited { color:#CC3434; }
a.deletelink:hover { color:#993333; }
-/* OBJECT TOOLS */
-.object-tools { font-size:10px; font-weight:bold; font-family:Arial,Helvetica,sans-serif; padding-left:0; margin-bottom:5px; float:right; position:relative; margin-top:-2.4em; margin-bottom:-2em; }
-.form-row .object-tools { margin-top:0; margin-bottom:0; }
+/* OBJECT TOOLS */
+.object-tools { font-size:10px; font-weight:bold; font-family:Arial,Helvetica,sans-serif; padding-left:0; float:right; position:relative; margin-top:-2.4em; margin-bottom:-2em; }
+.form-row .object-tools { margin-top:5px; margin-bottom:5px; float:none; height:2em; padding-left:3.5em; }
.object-tools li { display:block; float:left; background:url(../img/admin/tool-left.gif) 0 0 no-repeat; padding:0 0 0 8px; margin-left:2px; height:16px; }
.object-tools li:hover { background:url(../img/admin/tool-left_over.gif) 0 0 no-repeat; }
.object-tools a:link, .object-tools a:visited { display:block; float:left; color:white; padding:.1em 14px .1em 8px; height:14px; background:#999 url(../img/admin/tool-right.gif) 100% 0 no-repeat; }
@@ -203,123 +136,6 @@ a.deletelink:hover { color:#993333; }
.object-tools a.addlink { background:#999 url(../img/admin/tooltag-add.gif) top right no-repeat; padding-right:28px; }
.object-tools a.addlink:hover { background:#5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat; }
-/* INLINE CONTROLS */
-#inline-controls { font-weight:bold; font-size:12px; }
-#inline-specific-controls { margin-left:6px; padding:0 8px; border-left:6px solid #ccc; }
-
-/* BREADCRUMBS */
-p.breadcrumbs { font-size:11px; color:#ccc;text-align:left; } /* old breadcrumbs style */
-div.breadcrumbs { background:white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; padding:2px 8px 3px 8px; font-size:11px; color:#999; border-top:1px solid white; border-bottom:1px solid #ccc; text-align:left; }
-
-/* SELECTOR (FILTER INTERFACE) */
-.selector { width:580px; float:left; }
-.selector select { width:270px; height:170px; }
-.selector-available, .selector-chosen { float:left; width:270px; text-align:center; margin-bottom:5px; }
-.selector-available h2, .selector-chosen h2 { border:1px solid #ccc; }
-.selector .selector-available h2 { background:white url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; }
-.selector .selector-filter { background:white; border:1px solid #ccc; border-width:0 1px; padding:3px; color:#999; font-size:10px; margin:0; text-align:left; }
-.selector .selector-chosen .selector-filter { padding:4px 5px; }
-.selector .selector-available input { width:230px; }
-.selector ul.selector-chooser { float:left; width:22px; height:50px; background:url(../img/admin/chooser-bg.gif) top center no-repeat; margin:13% 3px 0 3px; padding:0; }
-.selector-chooser li { margin:0; padding:3px; list-style-type:none; }
-.selector select { margin-bottom:5px; margin-top:0; }
-.selector-add, .selector-remove { width:16px; height:16px; display:block; text-indent:-3000px; }
-.selector-add { background:url(../img/admin/selector-add.gif) top center no-repeat; margin-bottom:2px; }
-.selector-remove { background:url(../img/admin/selector-remove.gif) top center no-repeat; }
-a.selector-chooseall, a.selector-clearall { display:block; width:6em; text-align:left; margin-left:auto; margin-right:auto; font-weight:bold; color:#666; padding:3px 0 3px 18px; }
-a.selector-chooseall:hover, a.selector-clearall:hover { color:#036; }
-a.selector-chooseall { width:7em; background:url(../img/admin/selector-addall.gif) left center no-repeat; }
-a.selector-clearall { background:url(../img/admin/selector-removeall.gif) left center no-repeat; }
-
-/* Stacked selectors for long items */
-.stacked { float:left; width:500px; }
-.stacked select { width:480px; height:100px; }
-.stacked .selector-available, .stacked .selector-chosen { width:480px; }
-.stacked .selector-available { margin-bottom:0; }
-.stacked .selector-available input { width:442px; }
-.stacked ul.selector-chooser { height:22px; width:50px; margin:0 0 3px 40%; background:url(../img/admin/chooser_stacked-bg.gif) top center no-repeat; }
-.stacked .selector-chooser li { float:left; padding:3px 3px 3px 5px; }
-.stacked .selector-chooseall, .stacked .selector-clearall { display:none; }
-.stacked .selector-add { background-image:url(../img/admin/selector_stacked-add.gif); }
-.stacked .selector-remove { background-image:url(../img/admin/selector_stacked-remove.gif); }
-
-/* DATE AND TIME */
-p.datetime { line-height:20px; margin:0; padding:0; color:#666; font-size:11px; font-weight:bold; }
-.datetime span { font-size:11px; font-weight:normal; color:#ccc; white-space:nowrap; }
-.vDateField { margin-left:4px; }
-table p.datetime { font-size:10px; margin-left:0; padding-left:0; }
-
-/* FILE UPLOADS */
-p.file-upload { line-height:20px; margin:0; padding:0; color:#666; font-size:11px; font-weight:bold; }
-.file-upload a { font-weight:normal; }
-.file-upload .deletelink { margin-left:5px; }
-
-/* CALENDARS & CLOCKS */
-.calendarbox, .clockbox { margin:5px auto; font-size:11px; width: 16em; text-align: center; background:white; position:relative; }
-.clockbox { width:9em; }
-.calendar { margin:0; padding: 0; }
-.calendar table { margin: 0; padding: 0; border-collapse:collapse; background:white; width:99%; }
-.calendar caption, .calendarbox h2 { margin: 0; font-size:11px; text-align:center; border-top:none; }
-.calendar th { font-size:10px; color:#666; padding:2px 3px; text-align:center; background:#e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x; border-bottom:1px solid #ddd; }
-.calendar td { font-size:11px; text-align: center; padding: 0; border-top:1px solid #eee; border-bottom:none; }
-.calendar td.selected a { background: #C9DBED; }
-.calendar td.nonday { background:#efefef; }
-.calendar td.today a { background:#ffc; }
-.calendar td a, .timelist a { display: block; font-weight:bold; padding:4px; text-decoration: none; color:#444; }
-.calendar td a:hover, .timelist a:hover { background: #5b80b2; color:white; }
-.calendar td a:active, .timelist a:active { background: #036; color:white; }
-.calendarnav { font-size:10px; text-align: center; color:#ccc; margin:0; padding:1px 3px; }
-.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover { color: #999; }
-.calendar-shortcuts { background:white; font-size:10px; line-height:11px; border-top:1px solid #eee; padding:3px 0 4px; color:#ccc; }
-.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { display:block; position:absolute; font-weight:bold; font-size:12px; background:#C9DBED url(../img/admin/default-bg.gif) bottom left repeat-x; padding:1px 4px 2px 4px; color:white; }
-.calendarnav-previous:hover, .calendarnav-next:hover { background:#036; }
-.calendarnav-previous { top:0; left:0; }
-.calendarnav-next { top:0; right:0; }
-.calendar-cancel { margin:0 !important; padding:0; font-size:10px; background:#e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x; border-top:1px solid #ddd; }
-.calendar-cancel a { padding:2px; color:#999; }
-ul.timelist, .timelist li { list-style-type:none; margin:0; padding:0; }
-.timelist a { padding:2px; }
-
-/* ORDERING WIDGET */
-ul#orderthese { position:absolute; top:8em; right:0; width:240px; padding:0; margin:0; list-style-type:none; }
-ul#orderthese li { list-style-type:none; display:block; padding:0; margin:6px 0; width:214px; background:#f6f6f6; white-space:nowrap; overflow:hidden; }
-ul#orderthese li span { display:block; border:1px solid #e7e7e7; background:transparent url(../img/admin/nav-bg-grabber.gif) top left repeat-y; font-size:10px !important; padding:4px 6px 4px 12px; }
-ul#orderthese span:hover { background-color:#efefef; }
-
-/* PAGINATOR */
-.paginator { font-size:11px; padding-top:10px; padding-bottom:10px; line-height:22px; margin:0; border-top:1px solid #ddd; }
-.paginator a:link, .paginator a:visited { padding:2px 6px; border:solid 1px #ccc; background:white; text-decoration:none; }
-.paginator a.showall { padding:0 !important; border:none !important; }
-.paginator a.showall:hover { color:#036 !important; background:transparent !important; }
-.paginator .end { border-width:2px !important; margin-right:6px; }
-.paginator .this-page { padding:2px 6px; font-weight:bold; font-size:13px; vertical-align:top; }
-.paginator a:hover { color:white; background:#5b80b2; border-color:#036; }
-
-/* TEXT STYLES & MODIFIERS */
-.small { font-size:11px; }
-.tiny { font-size:10px; }
-p.tiny { margin-top:-2px; }
-.mini { font-size:9px; }
-p.mini { margin-top:-3px; }
-.help, p.help { font-size:10px !important; color:#999; }
-p img, h1 img, h2 img, h3 img, h4 img, td img { vertical-align:middle; }
-.quiet, a.quiet:link, a.quiet:visited { color:#999 !important;font-weight:normal !important; }
-.quiet strong { font-weight:bold !important; }
-.float-right { float:right; }
-.float-left { float:left; }
-.clear { clear:both; }
-.align-left { text-align:left; }
-.align-right { text-align:right; }
-.example { margin:10px 0; padding:5px 10px; background:#efefef; }
-.nowrap { white-space:nowrap; }
-
-/* CUSTOM FORM FIELDS */
-.vSelectMultipleField { vertical-align:top !important; }
-.vCheckboxField { border:none; }
-.vDateField, .vTimeField { margin-right:2px; }
-.vFileUploadField { border:none; }
-.vURLField { width:380px; }
-.vLargeTextField, .vXMLLargeTextField { width:480px; }
-.colM .vLargeTextField, .colM .vXMLLargeTextField { width:720px; }
-body.core-flatfile #id_content { height: 400px; }
-.module table .vPositiveSmallIntegerField { width: 22px; } \ No newline at end of file
+/* OBJECT HISTORY */
+table#change-history { width:100%; }
+table#change-history tbody th { width:16em; }
diff --git a/django/contrib/admin/media/css/layout.css b/django/contrib/admin/media/css/layout.css
new file mode 100644
index 0000000000..19c9286b85
--- /dev/null
+++ b/django/contrib/admin/media/css/layout.css
@@ -0,0 +1,29 @@
+/* PAGE STRUCTURE */
+#container { position:relative; width:100%; min-width:760px; }
+#content { margin:10px 15px; }
+#header { width:100%; }
+#content-main { float:left; width:100%; }
+#content-related { float:right; width:220px; position:relative; margin-right:-230px; }
+#footer { clear:both; padding:10px; }
+
+/* COLUMN TYPES */
+.colMS { margin-right:245px !important; }
+.colSM { margin-left:245px !important; }
+.colSM #content-related { float:left; margin-right:0; margin-left:-230px; }
+.colSM #content-main { float:right; }
+.popup .colM { width:95%; }
+.subcol { float:left; width:46%; margin-right:15px; }
+.dashboard #content { width:500px; }
+
+/* HEADER */
+#header { background:#417690; color:#ffc; overflow:hidden; }
+#header a:link, #header a:visited { color:white; }
+#header a:hover { text-decoration:underline; }
+#branding h1 { padding:0 10px; font-size:18px; margin:8px 0; font-weight:normal; color:#f4f379; }
+#branding h2 { padding:0 10px; font-size:14px; margin:-8px 0 8px 0; font-weight:normal; color:#ffc; }
+#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
+
+/* SIDEBAR */
+#content-related h3 { font-size:12px; color:#666; margin-bottom:3px; }
+#content-related h4 { font-size:11px; }
+#content-related .module h2 { background:#eee url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; } \ No newline at end of file
diff --git a/django/contrib/admin/media/css/login.css b/django/contrib/admin/media/css/login.css
new file mode 100644
index 0000000000..041135f31e
--- /dev/null
+++ b/django/contrib/admin/media/css/login.css
@@ -0,0 +1,13 @@
+@import url('base.css');
+@import url('layout.css');
+
+/* LOGIN FORM */
+body.login { background:#eee; }
+.login #container { background:white; border:1px solid #ccc; width:28em; min-width:300px; margin-left:auto; margin-right:auto; margin-top:100px; }
+.login #content-main { width:100%; }
+.login form { margin-top:1em; }
+.login .form-row { padding:4px 0; float:left; width:100%; }
+.login .form-row label { float:left; width:7em; padding-right:0.5em; line-height:2em; text-align:right; font-size:1em; color:#333; }
+.login .form-row #id_username, .login .form-row #id_password { width:16em; }
+.login span.help { font-size:10px; display:block; }
+.login .submit-row { clear:both; padding:1em 0 0 7.4em; } \ No newline at end of file
diff --git a/django/contrib/admin/media/css/patch-iewin.css b/django/contrib/admin/media/css/patch-iewin.css
index 46531325df..b2e6a4c560 100644
--- a/django/contrib/admin/media/css/patch-iewin.css
+++ b/django/contrib/admin/media/css/patch-iewin.css
@@ -1,7 +1,6 @@
* html #container { position:static; } /* keep header from flowing off the page */
* html .colMS #content-related { margin-right:0; margin-left:10px; position:static; } /* put the right sidebars back on the page */
* html .colSM #content-related { margin-right:10px; margin-left:-115px; position:static; } /* put the left sidebars back on the page */
+* html .form-row { height:1%; }
* html .dashboard #content { width:768px; } /* proper fixed width for dashboard in IE6 */
-* html .dashboard #content-main { width:535px; } /* proper fixed width for dashboard in IE6 */
-* html #content { width /**/: 768px; } /* fixed width for IE5 */
-* html #content-main { width /**/: 535px; } /* fixed width for IE5 */ \ No newline at end of file
+* html .dashboard #content-main { width:535px; } /* proper fixed width for dashboard in IE6 */ \ No newline at end of file
diff --git a/django/contrib/admin/media/css/widgets.css b/django/contrib/admin/media/css/widgets.css
new file mode 100644
index 0000000000..bf526bfd66
--- /dev/null
+++ b/django/contrib/admin/media/css/widgets.css
@@ -0,0 +1,101 @@
+/* SELECTOR (FILTER INTERFACE) */
+.selector { width:580px; float:left; }
+.selector select { width:270px; height:17.2em; }
+.selector-available, .selector-chosen { float:left; width:270px; text-align:center; margin-bottom:5px; }
+.selector-available h2, .selector-chosen h2 { border:1px solid #ccc; }
+.selector .selector-available h2 { background:white url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; }
+.selector .selector-filter { background:white; border:1px solid #ccc; border-width:0 1px; padding:3px; color:#999; font-size:10px; margin:0; text-align:left; }
+.selector .selector-chosen .selector-filter { padding:4px 5px; }
+.selector .selector-available input { width:230px; }
+.selector ul.selector-chooser { float:left; width:22px; height:50px; background:url(../img/admin/chooser-bg.gif) top center no-repeat; margin:8em 3px 0 3px; padding:0; }
+.selector-chooser li { margin:0; padding:3px; list-style-type:none; }
+.selector select { margin-bottom:5px; margin-top:0; }
+.selector-add, .selector-remove { width:16px; height:16px; display:block; text-indent:-3000px; }
+.selector-add { background:url(../img/admin/selector-add.gif) top center no-repeat; margin-bottom:2px; }
+.selector-remove { background:url(../img/admin/selector-remove.gif) top center no-repeat; }
+a.selector-chooseall, a.selector-clearall { display:block; width:6em; text-align:left; margin-left:auto; margin-right:auto; font-weight:bold; color:#666; padding:3px 0 3px 18px; }
+a.selector-chooseall:hover, a.selector-clearall:hover { color:#036; }
+a.selector-chooseall { width:7em; background:url(../img/admin/selector-addall.gif) left center no-repeat; }
+a.selector-clearall { background:url(../img/admin/selector-removeall.gif) left center no-repeat; }
+
+/* STACKED SELECTORS */
+.stacked { float:left; width:500px; }
+.stacked select { width:480px; height:10.1em; }
+.stacked .selector-available, .stacked .selector-chosen { width:480px; }
+.stacked .selector-available { margin-bottom:0; }
+.stacked .selector-available input { width:442px; }
+.stacked ul.selector-chooser { height:22px; width:50px; margin:0 0 3px 40%; background:url(../img/admin/chooser_stacked-bg.gif) top center no-repeat; }
+.stacked .selector-chooser li { float:left; padding:3px 3px 3px 5px; }
+.stacked .selector-chooseall, .stacked .selector-clearall { display:none; }
+.stacked .selector-add { background-image:url(../img/admin/selector_stacked-add.gif); }
+.stacked .selector-remove { background-image:url(../img/admin/selector_stacked-remove.gif); }
+
+/* DATE AND TIME */
+p.datetime { line-height:20px; margin:0; padding:0; color:#666; font-size:11px; font-weight:bold; }
+.datetime span { font-size:11px; color:#ccc; font-weight:normal; white-space:nowrap; }
+.vDateField { margin-left:4px; }
+table p.datetime { font-size:10px; margin-left:0; padding-left:0; }
+
+/* FILE UPLOADS */
+p.file-upload { line-height:20px; margin:0; padding:0; color:#666; font-size:11px; font-weight:bold; }
+.file-upload a { font-weight:normal; }
+.file-upload .deletelink { margin-left:5px; }
+
+/* CALENDARS & CLOCKS */
+.calendarbox, .clockbox { margin:5px auto; font-size:11px; width:16em; text-align:center; background:white; position:relative; }
+.clockbox { width:9em; }
+.calendar { margin:0; padding: 0; }
+.calendar table { margin:0; padding:0; border-collapse:collapse; background:white; width:99%; }
+.calendar caption, .calendarbox h2 { margin: 0; font-size:11px; text-align:center; border-top:none; }
+.calendar th { font-size:10px; color:#666; padding:2px 3px; text-align:center; background:#e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x; border-bottom:1px solid #ddd; }
+.calendar td { font-size:11px; text-align: center; padding: 0; border-top:1px solid #eee; border-bottom:none; }
+.calendar td.selected a { background: #C9DBED; }
+.calendar td.nonday { background:#efefef; }
+.calendar td.today a { background:#ffc; }
+.calendar td a, .timelist a { display: block; font-weight:bold; padding:4px; text-decoration: none; color:#444; }
+.calendar td a:hover, .timelist a:hover { background: #5b80b2; color:white; }
+.calendar td a:active, .timelist a:active { background: #036; color:white; }
+.calendarnav { font-size:10px; text-align: center; color:#ccc; margin:0; padding:1px 3px; }
+.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover { color: #999; }
+.calendar-shortcuts { background:white; font-size:10px; line-height:11px; border-top:1px solid #eee; padding:3px 0 4px; color:#ccc; }
+.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { display:block; position:absolute; font-weight:bold; font-size:12px; background:#C9DBED url(../img/admin/default-bg.gif) bottom left repeat-x; padding:1px 4px 2px 4px; color:white; }
+.calendarnav-previous:hover, .calendarnav-next:hover { background:#036; }
+.calendarnav-previous { top:0; left:0; }
+.calendarnav-next { top:0; right:0; }
+.calendar-cancel { margin:0 !important; padding:0; font-size:10px; background:#e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x; border-top:1px solid #ddd; }
+.calendar-cancel a { padding:2px; color:#999; }
+ul.timelist, .timelist li { list-style-type:none; margin:0; padding:0; }
+.timelist a { padding:2px; }
+
+/* INLINE ORDERER */
+ul.orderer { position:relative; padding:0 !important; margin:0 !important; list-style-type:none; }
+ul.orderer li { list-style-type:none; display:block; padding:0; margin:0; border:1px solid #bbb; border-width:0 1px 1px 0; white-space:nowrap; overflow:hidden; background:#e2e2e2 url(../img/admin/nav-bg-grabber.gif) repeat-y; }
+ul.orderer li:hover { cursor:move; background-color:#ddd; }
+ul.orderer li a.selector { margin-left:12px; overflow:hidden; width:83%; font-size:10px !important; padding:0.6em 0; }
+ul.orderer li a:link, ul.orderer li a:visited { color:#333; }
+ul.orderer li .inline-deletelink { position:absolute; right:4px; margin-top:0.6em; }
+ul.orderer li.selected { background-color:#f8f8f8; border-right-color:#f8f8f8; }
+ul.orderer li.deleted { background:#bbb url(../img/admin/deleted-overlay.gif); }
+ul.orderer li.deleted a:link, ul.orderer li.deleted a:visited { color:#888; }
+ul.orderer li.deleted .inline-deletelink { background-image:url(../img/admin/inline-restore.png); }
+ul.orderer li.deleted:hover, ul.orderer li.deleted a.selector:hover { cursor:default; }
+
+/* EDIT INLINE */
+.inline-deletelink { display:block; text-indent:-9999px; background:transparent url(../img/admin/inline-delete.png) no-repeat; width:15px; height:15px; margin:0.4em 0; border: 0px none; }
+.inline-deletelink:hover { background-position:-15px 0; cursor:pointer; }
+.editinline button.addlink { border: 0px none; color: #5b80b2; font-size: 100%; cursor: pointer; }
+.editinline button.addlink:hover { color: #036; cursor: pointer; }
+.editinline table .help { text-align:right; float:right; padding-left:2em; }
+.editinline tfoot .addlink { white-space:nowrap; }
+.editinline table thead th:last-child { border-left:none; }
+.editinline tr.deleted { background:#ddd url(../img/admin/deleted-overlay.gif); }
+.editinline tr.deleted .inline-deletelink { background-image:url(../img/admin/inline-restore.png); }
+.editinline tr.deleted td:hover { cursor:default; }
+.editinline tr.deleted td:first-child { background-image:none !important; }
+
+/* EDIT INLINE - STACKED */
+.editinline-stacked { min-width:758px; }
+.editinline-stacked .inline-object { margin-left:210px; background:white; }
+.editinline-stacked .inline-source { float:left; width:200px; background:#f8f8f8; }
+.editinline-stacked .inline-splitter { float:left; width:9px; background:#f8f8f8 url(../img/admin/inline-splitter-bg.gif) 50% 50% no-repeat; border-right:1px solid #ccc; }
+.editinline-stacked .controls { clear:both; background:#e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; padding:3px 4px; font-size:11px; border-top:1px solid #ddd; } \ No newline at end of file
diff --git a/django/contrib/admin/media/img/admin/deleted-overlay.gif b/django/contrib/admin/media/img/admin/deleted-overlay.gif
new file mode 100644
index 0000000000..dc3828fe06
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/deleted-overlay.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/inline-delete-8bit.png b/django/contrib/admin/media/img/admin/inline-delete-8bit.png
new file mode 100644
index 0000000000..95caf59a8d
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/inline-delete-8bit.png
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/inline-delete.png b/django/contrib/admin/media/img/admin/inline-delete.png
new file mode 100644
index 0000000000..d59bcd2444
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/inline-delete.png
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/inline-restore-8bit.png b/django/contrib/admin/media/img/admin/inline-restore-8bit.png
new file mode 100644
index 0000000000..e087c8ead3
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/inline-restore-8bit.png
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/inline-restore.png b/django/contrib/admin/media/img/admin/inline-restore.png
new file mode 100644
index 0000000000..efdd92ac39
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/inline-restore.png
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/inline-splitter-bg.gif b/django/contrib/admin/media/img/admin/inline-splitter-bg.gif
new file mode 100644
index 0000000000..32ac5b3498
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/inline-splitter-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
index 72c4e95eca..cb84790f44 100644
--- a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
+++ b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
@@ -39,7 +39,7 @@ function dismissAddAnotherPopup(win, newId, newRepr) {
if (elem.nodeName == 'SELECT') {
var o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
- elem.selectedIndex = elem.length - 1;
+ o.selected = true;
} else if (elem.nodeName == 'INPUT') {
elem.value = newId;
}
diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py
new file mode 100644
index 0000000000..022d20bed9
--- /dev/null
+++ b/django/contrib/admin/models.py
@@ -0,0 +1,51 @@
+from django.db import models
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.auth.models import User
+from django.utils.translation import gettext_lazy as _
+
+ADDITION = 1
+CHANGE = 2
+DELETION = 3
+
+class LogEntryManager(models.Manager):
+ def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
+ e = self.model(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message)
+ e.save()
+
+class LogEntry(models.Model):
+ action_time = models.DateTimeField(_('action time'), auto_now=True)
+ user = models.ForeignKey(User)
+ content_type = models.ForeignKey(ContentType, blank=True, null=True)
+ object_id = models.TextField(_('object id'), blank=True, null=True)
+ object_repr = models.CharField(_('object repr'), maxlength=200)
+ action_flag = models.PositiveSmallIntegerField(_('action flag'))
+ change_message = models.TextField(_('change message'), blank=True)
+ objects = LogEntryManager()
+ class Meta:
+ verbose_name = _('log entry')
+ verbose_name_plural = _('log entries')
+ db_table = 'django_admin_log'
+ ordering = ('-action_time',)
+
+ def __repr__(self):
+ return str(self.action_time)
+
+ def is_addition(self):
+ return self.action_flag == ADDITION
+
+ def is_change(self):
+ return self.action_flag == CHANGE
+
+ def is_deletion(self):
+ return self.action_flag == DELETION
+
+ def get_edited_object(self):
+ "Returns the edited object represented by this log entry"
+ return self.content_type.get_object_for_this_type(pk=self.object_id)
+
+ def get_admin_url(self):
+ """
+ Returns the admin URL to edit the object represented by this log entry.
+ This is relative to the Django admin index page.
+ """
+ return "%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id)
diff --git a/django/contrib/admin/models/__init__.py b/django/contrib/admin/models/__init__.py
deleted file mode 100644
index e11e2df093..0000000000
--- a/django/contrib/admin/models/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__all__ = ['admin']
diff --git a/django/contrib/admin/models/admin.py b/django/contrib/admin/models/admin.py
deleted file mode 100644
index b7bcda192b..0000000000
--- a/django/contrib/admin/models/admin.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from django.core import meta
-from django.models import auth, core
-from django.utils.translation import gettext_lazy as _
-
-class LogEntry(meta.Model):
- action_time = meta.DateTimeField(_('action time'), auto_now=True)
- user = meta.ForeignKey(auth.User)
- content_type = meta.ForeignKey(core.ContentType, blank=True, null=True)
- object_id = meta.TextField(_('object id'), blank=True, null=True)
- object_repr = meta.CharField(_('object repr'), maxlength=200)
- action_flag = meta.PositiveSmallIntegerField(_('action flag'))
- change_message = meta.TextField(_('change message'), blank=True)
- class META:
- module_name = 'log'
- verbose_name = _('log entry')
- verbose_name_plural = _('log entries')
- db_table = 'django_admin_log'
- ordering = ('-action_time',)
- module_constants = {
- 'ADDITION': 1,
- 'CHANGE': 2,
- 'DELETION': 3,
- }
-
- def __repr__(self):
- return str(self.action_time)
-
- def is_addition(self):
- return self.action_flag == ADDITION
-
- def is_change(self):
- return self.action_flag == CHANGE
-
- def is_deletion(self):
- return self.action_flag == DELETION
-
- def get_edited_object(self):
- "Returns the edited object represented by this log entry"
- return self.get_content_type().get_object_for_this_type(pk=self.object_id)
-
- def get_admin_url(self):
- """
- Returns the admin URL to edit the object represented by this log entry.
- This is relative to the Django admin index page.
- """
- return "%s/%s/%s/" % (self.get_content_type().get_package(), self.get_content_type().python_module_name, self.object_id)
-
- def _module_log_action(user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
- e = LogEntry(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message)
- e.save()
diff --git a/django/contrib/admin/templates/admin/404.html b/django/contrib/admin/templates/admin/404.html
index d791f565ba..9bf4293e76 100644
--- a/django/contrib/admin/templates/admin/404.html
+++ b/django/contrib/admin/templates/admin/404.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block title %}{% trans 'Page not found' %}{% endblock %}
diff --git a/django/contrib/admin/templates/admin/500.html b/django/contrib/admin/templates/admin/500.html
index 9d3e3de32c..b30e43170d 100644
--- a/django/contrib/admin/templates/admin/500.html
+++ b/django/contrib/admin/templates/admin/500.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans "Home" %}</a> &rsaquo; {% trans "Server error" %}</div>{% endblock %}
diff --git a/django/contrib/admin/templates/admin/base_site.html b/django/contrib/admin/templates/admin/base_site.html
index b4d285b779..b867bd29bd 100644
--- a/django/contrib/admin/templates/admin/base_site.html
+++ b/django/contrib/admin/templates/admin/base_site.html
@@ -1,4 +1,4 @@
-{% extends "admin/base" %}
+{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}
diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html
index 02d5760509..a667087f5f 100644
--- a/django/contrib/admin/templates/admin/change_form.html
+++ b/django/contrib/admin/templates/admin/change_form.html
@@ -1,36 +1,39 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n admin_modify adminmedia %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../jsi18n/"></script>
-{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
+{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
{% endblock %}
-{% block coltype %}{{ bound_manipulator.coltype }}{% endblock %}
-{% block bodyclass %}{{ app_label }}-{{ bound_manipulator.object_name.lower }} change-form{% endblock %}
+{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
+{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %}
+{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
{% block userlinks %}<a href="../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}{% if not is_popup %}
<div class="breadcrumbs">
<a href="../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../">{{ bound_manipulator.verbose_name_plural|capfirst }}</a> &rsaquo;
- {% if add %}{% trans "Add" %} {{ bound_manipulator.verbose_name }}{% else %}{{ bound_manipulator.original|striptags|truncatewords:"18" }}{% endif %}
+ <a href="../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
+ {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|striptags|truncatewords:"18" }}{% endif %}
</div>
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
{% if change %}{% if not is_popup %}
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
- {% if bound_manipulator.has_absolute_url %}<li><a href="/r/{{ bound_manipulator.content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
+ {% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
</ul>
{% endif %}{% endif %}
-<form {{ bound_manipulator.form_enc_attrib }} action="{{ form_url }}" method="post">{% block form_top %}{% endblock %}
+<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post">{% block form_top %}{% endblock %}
+<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
-{% if bound_manipulator.save_on_top %}{% submit_row bound_manipulator %}{% endif %}
+{% if opts.admin.save_on_top %}{% submit_row %}{% endif %}
{% if form.error_dict %}
<p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{% endif %}
-{% for bound_field_set in bound_manipulator.bound_field_sets %}
+{% for bound_field_set in bound_field_sets %}
<fieldset class="module aligned {{ bound_field_set.classes }}">
{% if bound_field_set.name %}<h2>{{ bound_field_set.name }}</h2>{% endif %}
+ {% if bound_field_set.description %}<div class="description">{{ bound_field_set.description }}</div>{% endif %}
{% for bound_field_line in bound_field_set %}
{% admin_field_line bound_field_line %}
{% for bound_field in bound_field_line %}
@@ -41,7 +44,7 @@
{% endfor %}
{% block after_field_sets %}{% endblock %}
{% if change %}
- {% if bound_manipulator.ordered_objects %}
+ {% if ordered_objects %}
<fieldset class="module"><h2>{% trans "Ordering" %}</h2>
<div class="form-row{% if form.order_.errors %} error{% endif %} ">
{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
@@ -49,27 +52,17 @@
</div></fieldset>
{% endif %}
{% endif %}
-{% for related_object in bound_manipulator.inline_related_objects %}{% edit_inline related_object %}{% endfor %}
+{% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %}
{% block after_related_objects %}{% endblock %}
-{% submit_row bound_manipulator %}
+{% submit_row %}
{% if add %}
- <script type="text/javascript">document.getElementById("{{ bound_manipulator.first_form_field_id }}").focus();</script>
+ <script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script>
{% endif %}
-{% if bound_manipulator.auto_populated_fields %}
+{% if auto_populated_fields %}
<script type="text/javascript">
- {% auto_populated_field_script bound_manipulator.auto_populated_fields change %}
+ {% auto_populated_field_script auto_populated_fields change %}
</script>
{% endif %}
-{% if change %}
- {% if bound_manipulator.ordered_objects %}
- {% if form.order_objects %}<ul id="orderthese">
- {% for object in form.order_objects %}
- <li id="p{% object_pk bound_manipulator object %}">
- <span id="handlep{% object_pk bound_manipulator object %}">{{ object|truncatewords:"5" }}</span>
- </li>
- {% endfor %}
- </ul>{% endif %}
- {% endif %}
-{% endif %}
+</div>
</form></div>
{% endblock %}
diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html
index 17b388b699..5b54bfb8cc 100644
--- a/django/contrib/admin/templates/admin/change_list.html
+++ b/django/contrib/admin/templates/admin/change_list.html
@@ -1,8 +1,9 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load adminmedia admin_list i18n %}
+{% block stylesheet %}{% admin_media_prefix %}css/changelists.css{% endblock %}
{% block bodyclass %}change-list{% endblock %}
{% block userlinks %}<a href="../../doc/">{% trans 'Documentation' %}</a> / <a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
-{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
+{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}</div>{% endblock %}{% endif %}
{% block coltype %}flex{% endblock %}
{% block content %}
<div id="content-main">
diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html
index 7fba4cebd7..f907c18a16 100644
--- a/django/contrib/admin/templates/admin/delete_confirmation.html
+++ b/django/contrib/admin/templates/admin/delete_confirmation.html
@@ -1,6 +1,14 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+ <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
+ <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
+ <a href="../">{{ object|striptags|truncatewords:"18" }}</a> &rsaquo;
+ {% trans 'Delete' %}
+</div>
+{% endblock %}
{% block content %}
{% if perms_lacking %}
<p>{% blocktrans %}Deleting the {{ object_name }} '{{ object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
@@ -13,8 +21,10 @@
<p>{% blocktrans %}Are you sure you want to delete the {{ object_name }} "{{ object }}"? All of the following related items will be deleted:{% endblocktrans %}</p>
<ul>{{ deleted_objects|unordered_list }}</ul>
<form action="" method="post">
+ <div>
<input type="hidden" name="post" value="yes" />
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
+ </div>
</form>
{% endif %}
{% endblock %}
diff --git a/django/contrib/admin/templates/admin/edit_inline_stacked.html b/django/contrib/admin/templates/admin/edit_inline_stacked.html
index 62549ef82d..45aa0a4f58 100644
--- a/django/contrib/admin/templates/admin/edit_inline_stacked.html
+++ b/django/contrib/admin/templates/admin/edit_inline_stacked.html
@@ -13,4 +13,4 @@
{% endif %}
{% endfor %}
{% endfor %}
-</fieldset> \ No newline at end of file
+</fieldset>
diff --git a/django/contrib/admin/templates/admin/field_line.html b/django/contrib/admin/templates/admin/field_line.html
index 10f37d5dc9..b7e2fc2ae0 100644
--- a/django/contrib/admin/templates/admin/field_line.html
+++ b/django/contrib/admin/templates/admin/field_line.html
@@ -9,14 +9,6 @@
{% if not bound_field.has_label_first %}
{% field_label bound_field %}
{% 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 %}
{% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %}
{% endfor %}
</div>
diff --git a/django/contrib/admin/templates/admin/index.html b/django/contrib/admin/templates/admin/index.html
index aabd4eecca..246086861b 100644
--- a/django/contrib/admin/templates/admin/index.html
+++ b/django/contrib/admin/templates/admin/index.html
@@ -1,6 +1,7 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
+{% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %}dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %}
@@ -13,14 +14,14 @@
{% if app_list %}
{% for app in app_list %}
<div class="module">
- <h2>{{ app.name }}</h2>
- <table>
+ <table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
+ <caption>{{ app.name }}</caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
- <th><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
+ <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
- <th>{{ model.name }}</th>
+ <th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
@@ -57,7 +58,7 @@
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
- <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{{ entry.get_content_type.name|capfirst }}</span></li>
+ <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{{ entry.content_type.name|capfirst }}</span></li>
{% endfor %}
</ul>
{% endif %}
diff --git a/django/contrib/admin/templates/admin/login.html b/django/contrib/admin/templates/admin/login.html
index ea823e1020..5f338f703e 100644
--- a/django/contrib/admin/templates/admin/login.html
+++ b/django/contrib/admin/templates/admin/login.html
@@ -1,6 +1,9 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
+{% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/login.css{% endblock %}
+{% block bodyclass %}login{% endblock %}
+{% block content_title %}{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
@@ -9,20 +12,18 @@
<p class="errornote">{{ error_message }}</p>
{% endif %}
<div id="content-main">
-<form action="{{ app_path }}" method="post">
-
-<p class="aligned">
-<label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
-</p>
-<p class="aligned">
-<label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
-<input type="hidden" name="this_is_the_login_form" value="1" />
-<input type="hidden" name="post_data" value="{{ post_data }}" />{% comment %} <span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
-</p>
-
-<div class="aligned ">
-<label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" />
-</div>
+<form action="{{ app_path }}" method="post" id="login-form">
+ <div class="form-row">
+ <label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
+ </div>
+ <div class="form-row">
+ <label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
+ <input type="hidden" name="this_is_the_login_form" value="1" />
+ <input type="hidden" name="post_data" value="{{ post_data }}" /> {% comment %}<span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
+ </div>
+ <div class="submit-row">
+ <label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" />
+ </div>
</form>
<script type="text/javascript">
diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html
index b182f40acb..0dbe7af743 100644
--- a/django/contrib/admin/templates/admin/object_history.html
+++ b/django/contrib/admin/templates/admin/object_history.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}
@@ -15,16 +15,16 @@
<table id="change-history">
<thead>
<tr>
- <th>{% trans 'Date/time' %}</th>
- <th>{% trans 'User' %}</th>
- <th>{% trans 'Action' %}</th>
+ <th scope="col">{% trans 'Date/time' %}</th>
+ <th scope="col">{% trans 'User' %}</th>
+ <th scope="col">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
{% for action in action_list %}
<tr>
- <th>{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th>
- <td>{{ action.get_user.username }}{% if action.get_user.first_name %} ({{ action.get_user.first_name }} {{ action.get_user.last_name }}){% endif %}</td>
+ <th scope="row">{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th>
+ <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name }} {{ action.user.last_name }}){% endif %}</td>
<td>{{ action.change_message}}</td>
</tr>
{% endfor %}
diff --git a/django/contrib/admin/templates/admin/search_form.html b/django/contrib/admin/templates/admin/search_form.html
index c2d6cf59c5..24eced9ef9 100644
--- a/django/contrib/admin/templates/admin/search_form.html
+++ b/django/contrib/admin/templates/admin/search_form.html
@@ -3,7 +3,7 @@
{% if cl.lookup_opts.admin.search_fields %}
<div id="toolbar"><form id="changelist-search" action="" method="get">
<div><!-- DIV needed for valid HTML -->
-<label><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>
<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" />
<input type="submit" value="{% trans 'Go' %}" />
{% if show_result_count %}
diff --git a/django/contrib/admin/templates/admin/template_validator.html b/django/contrib/admin/templates/admin/template_validator.html
index f9ac09a77d..422e90261f 100644
--- a/django/contrib/admin/templates/admin/template_validator.html
+++ b/django/contrib/admin/templates/admin/template_validator.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% block content %}
diff --git a/django/contrib/admin/templates/admin_doc/bookmarklets.html b/django/contrib/admin/templates/admin_doc/bookmarklets.html
index 708473750a..fa5942926f 100644
--- a/django/contrib/admin/templates/admin_doc/bookmarklets.html
+++ b/django/contrib/admin/templates/admin_doc/bookmarklets.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/index.html b/django/contrib/admin/templates/admin_doc/index.html
index 6665f4bb42..331774d2b3 100644
--- a/django/contrib/admin/templates/admin_doc/index.html
+++ b/django/contrib/admin/templates/admin_doc/index.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
{% block userlinks %}<a href="../password_change/">{% trans 'Change password' %}</a> / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/missing_docutils.html b/django/contrib/admin/templates/admin_doc/missing_docutils.html
index a7cf7e95d7..d0b571f957 100644
--- a/django/contrib/admin/templates/admin_doc/missing_docutils.html
+++ b/django/contrib/admin/templates/admin_doc/missing_docutils.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
{% block userlinks %}<a href="../password_change/">{% trans 'Change password' %}</a> / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/model_detail.html b/django/contrib/admin/templates/admin_doc/model_detail.html
index 0eb2971048..953bb4bf54 100644
--- a/django/contrib/admin/templates/admin_doc/model_detail.html
+++ b/django/contrib/admin/templates/admin_doc/model_detail.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block extrahead %}
@@ -41,6 +41,6 @@
</table>
</div>
-<p class="small"><a href="../">&lsaquo; Back to Models Documentation</p>
+<p class="small"><a href="../">&lsaquo; Back to Models Documentation</a></p>
</div>
{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/model_index.html b/django/contrib/admin/templates/admin_doc/model_index.html
index 3183b577e1..c681da75c9 100644
--- a/django/contrib/admin/templates/admin_doc/model_index.html
+++ b/django/contrib/admin/templates/admin_doc/model_index.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Models</div>{% endblock %}
@@ -8,18 +8,19 @@
{% block content %}
-<h1>Models Documentation</h1>
+<h1>Model documentation</h1>
+
+{% regroup models by app_label as grouped_models %}
<div id="content-main">
-{% regroup models|dictsort:"module" by module as grouped_models %}
{% for group in grouped_models %}
<div class="module">
-<h2 id='{{ group.grouper }}'>{{ group.grouper }}</h2>
+<h2 id="{{ group.grouper }}">{{ group.grouper|capfirst }}</h2>
<table class="xfull">
{% for model in group.list %}
<tr>
-<th><a href="{{ model.name }}/">{{ model.class }}</a></th>
+<th><a href="{{ model.app_label }}.{{ model.object_name.lower }}/">{{ model.object_name }}</a></th>
</tr>
{% endfor %}
</table>
@@ -32,11 +33,11 @@
{% block sidebar %}
<div id="content-related" class="sidebar">
<div class="module">
-<h2>Model Groups Quick List</h2>
+<h2>Model groups</h2>
<ul>
-{% regroup models|dictsort:"module" by module as grouped_models %}
+{% regroup models by app_label as grouped_models %}
{% for group in grouped_models %}
- <li><a href="#{{ group.grouper }}">{{ group.grouper }}</a></li>
+ <li><a href="#{{ group.grouper }}">{{ group.grouper|capfirst }}</a></li>
{% endfor %}
</ul>
</div>
diff --git a/django/contrib/admin/templates/admin_doc/template_detail.html b/django/contrib/admin/templates/admin_doc/template_detail.html
index d2a2831098..df67f1856b 100644
--- a/django/contrib/admin/templates/admin_doc/template_detail.html
+++ b/django/contrib/admin/templates/admin_doc/template_detail.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; Templates &rsaquo; {{ name }}</div>{% endblock %}
{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/template_filter_index.html b/django/contrib/admin/templates/admin_doc/template_filter_index.html
index 30ddee9d64..72344c16cb 100644
--- a/django/contrib/admin/templates/admin_doc/template_filter_index.html
+++ b/django/contrib/admin/templates/admin_doc/template_filter_index.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; filters</div>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/template_tag_index.html b/django/contrib/admin/templates/admin_doc/template_tag_index.html
index 9051ae5dde..287475ab09 100644
--- a/django/contrib/admin/templates/admin_doc/template_tag_index.html
+++ b/django/contrib/admin/templates/admin_doc/template_tag_index.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Tags</div>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/view_detail.html b/django/contrib/admin/templates/admin_doc/view_detail.html
index a7e920e0d6..ba90399358 100644
--- a/django/contrib/admin/templates/admin_doc/view_detail.html
+++ b/django/contrib/admin/templates/admin_doc/view_detail.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Views</a> &rsaquo; {{ name }}</div>{% endblock %}
{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/view_index.html b/django/contrib/admin/templates/admin_doc/view_index.html
index a054ceb66b..caab8a2e71 100644
--- a/django/contrib/admin/templates/admin_doc/view_index.html
+++ b/django/contrib/admin/templates/admin_doc/view_index.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
diff --git a/django/contrib/admin/templates/registration/logged_out.html b/django/contrib/admin/templates/registration/logged_out.html
index 756f82aadc..d339ef0a49 100644
--- a/django/contrib/admin/templates/registration/logged_out.html
+++ b/django/contrib/admin/templates/registration/logged_out.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a></div>{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_done.html b/django/contrib/admin/templates/registration/password_change_done.html
index f163e55a68..85e1bf216c 100644
--- a/django/contrib/admin/templates/registration/password_change_done.html
+++ b/django/contrib/admin/templates/registration/password_change_done.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html
index 096e66ce13..036d56212c 100644
--- a/django/contrib/admin/templates/registration/password_change_form.html
+++ b/django/contrib/admin/templates/registration/password_change_form.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% 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 %}
diff --git a/django/contrib/admin/templates/registration/password_reset_done.html b/django/contrib/admin/templates/registration/password_reset_done.html
index dff293c931..f97b5688c2 100644
--- a/django/contrib/admin/templates/registration/password_reset_done.html
+++ b/django/contrib/admin/templates/registration/password_reset_done.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html
index 1b6a2c9a17..423821ba60 100644
--- a/django/contrib/admin/templates/registration/password_reset_form.html
+++ b/django/contrib/admin/templates/registration/password_reset_form.html
@@ -1,4 +1,4 @@
-{% extends "admin/base_site" %}
+{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
diff --git a/django/contrib/admin/templates/widget/foreign.html b/django/contrib/admin/templates/widget/foreign.html
index 582d65df7f..6b43d044bd 100644
--- a/django/contrib/admin/templates/widget/foreign.html
+++ b/django/contrib/admin/templates/widget/foreign.html
@@ -1,12 +1,20 @@
{% 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.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/?{% for limit_choice in bound_field.field.rel.limit_choices_to.items %}{% if not forloop.first %}{{"&"|escape}}{% 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.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" 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 %}
+ {% 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.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/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>
+ <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 %}
diff --git a/django/contrib/admin/templates/widget/many_to_many.html b/django/contrib/admin/templates/widget/many_to_many.html
index 151fe04f30..a93aa65f73 100644
--- a/django/contrib/admin/templates/widget/many_to_many.html
+++ b/django/contrib/admin/templates/widget/many_to_many.html
@@ -1 +1 @@
-{% include "widget/foreign" %}
+{% include "widget/foreign.html" %}
diff --git a/django/contrib/admin/templates/widget/one_to_one.html b/django/contrib/admin/templates/widget/one_to_one.html
index 151fe04f30..a79a12314f 100644
--- a/django/contrib/admin/templates/widget/one_to_one.html
+++ b/django/contrib/admin/templates/widget/one_to_one.html
@@ -1 +1,2 @@
-{% include "widget/foreign" %}
+{% 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 %}
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index b87fd15e60..5a6b6a24e9 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -1,14 +1,15 @@
-from django.contrib.admin.views.main import MAX_SHOW_ALL_ALLOWED, DEFAULT_RESULTS_PER_PAGE, ALL_VAR
+from django import template
+from django.conf import settings
+from django.contrib.admin.views.main import MAX_SHOW_ALL_ALLOWED, ALL_VAR
from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
-from django.core import meta, template
from django.core.exceptions import ObjectDoesNotExist
+from django.db import models
from django.utils import dateformat
from django.utils.html import escape
from django.utils.text import capfirst
from django.utils.translation import get_date_formats
-from django.conf.settings import ADMIN_MEDIA_PREFIX
-from django.core.template import Library
+from django.template import Library
register = Library()
@@ -16,11 +17,11 @@ DOT = '.'
def paginator_number(cl,i):
if i == DOT:
- return '... '
+ return '... '
elif i == cl.page_num:
- return '<span class="this-page">%d</span> ' % (i+1)
+ return '<span class="this-page">%d</span> ' % (i+1)
else:
- return '<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1)
+ return '<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1)
paginator_number = register.simple_tag(paginator_number)
def pagination(cl):
@@ -64,7 +65,7 @@ def pagination(cl):
'ALL_VAR': ALL_VAR,
'1': 1,
}
-pagination = register.inclusion_tag('admin/pagination')(pagination)
+pagination = register.inclusion_tag('admin/pagination.html')(pagination)
def result_headers(cl):
lookup_opts = cl.lookup_opts
@@ -72,22 +73,22 @@ def result_headers(cl):
for i, field_name in enumerate(lookup_opts.admin.list_display):
try:
f = lookup_opts.get_field(field_name)
- except meta.FieldDoesNotExist:
+ except models.FieldDoesNotExist:
# For non-field list_display values, check for the function
# attribute "short_description". If that doesn't exist, fall
- # back to the method name. And __repr__ is a special-case.
- if field_name == '__repr__':
+ # back to the method name. And __str__ is a special-case.
+ if field_name == '__str__':
header = lookup_opts.verbose_name
else:
- func = getattr(cl.mod.Klass, field_name) # Let AttributeErrors propagate.
+ attr = getattr(cl.model, field_name) # Let AttributeErrors propagate.
try:
- header = func.short_description
+ header = attr.short_description
except AttributeError:
- header = func.__name__.replace('_', ' ')
+ header = field_name.replace('_', ' ')
# Non-field list_display values don't get ordering capability.
yield {"text": header}
else:
- if isinstance(f.rel, meta.ManyToOneRel) and f.null:
+ if isinstance(f.rel, models.ManyToOneRel) and f.null:
yield {"text": f.verbose_name}
else:
th_classes = []
@@ -108,34 +109,37 @@ def items_for_result(cl, result):
row_class = ''
try:
f = cl.lookup_opts.get_field(field_name)
- except meta.FieldDoesNotExist:
- # For non-field list_display values, the value is a method
- # name. Execute the method.
+ except models.FieldDoesNotExist:
+ # For non-field list_display values, the value is either a method
+ # or a property.
try:
- func = getattr(result, field_name)
- result_repr = str(func())
+ attr = getattr(result, field_name)
+ allow_tags = getattr(attr, 'allow_tags', False)
+ if callable(attr):
+ attr = attr()
+ result_repr = str(attr)
except AttributeError, ObjectDoesNotExist:
result_repr = EMPTY_CHANGELIST_VALUE
else:
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
- if not getattr(func, 'allow_tags', False):
+ if not allow_tags:
result_repr = escape(result_repr)
else:
field_val = getattr(result, f.attname)
- if isinstance(f.rel, meta.ManyToOneRel):
+ if isinstance(f.rel, models.ManyToOneRel):
if field_val is not None:
- result_repr = getattr(result, 'get_%s' % f.name)()
+ result_repr = getattr(result, f.name)
else:
result_repr = EMPTY_CHANGELIST_VALUE
# Dates and times are special: They're formatted in a certain way.
- elif isinstance(f, meta.DateField) or isinstance(f, meta.TimeField):
+ elif isinstance(f, models.DateField) or isinstance(f, models.TimeField):
if field_val:
(date_format, datetime_format, time_format) = get_date_formats()
- if isinstance(f, meta.DateTimeField):
+ if isinstance(f, models.DateTimeField):
result_repr = capfirst(dateformat.format(field_val, datetime_format))
- elif isinstance(f, meta.TimeField):
+ elif isinstance(f, models.TimeField):
result_repr = capfirst(dateformat.time_format(field_val, time_format))
else:
result_repr = capfirst(dateformat.format(field_val, date_format))
@@ -143,15 +147,15 @@ def items_for_result(cl, result):
result_repr = EMPTY_CHANGELIST_VALUE
row_class = ' class="nowrap"'
# Booleans are special: We use images.
- elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
+ elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
- result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
+ result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
# ImageFields are special: Use a thumbnail.
- elif isinstance(f, meta.ImageField):
+ elif isinstance(f, models.ImageField):
from django.parts.media.photos import get_thumbnail_url
result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
# FloatFields are special: Zero-pad the decimals.
- elif isinstance(f, meta.FloatField):
+ elif isinstance(f, models.FloatField):
if field_val is not None:
result_repr = ('%%.%sf' % f.decimal_places) % field_val
else:
@@ -163,7 +167,7 @@ def items_for_result(cl, result):
else:
result_repr = escape(str(field_val))
if result_repr == '':
- result_repr = '&nbsp;'
+ result_repr = '&nbsp;'
if first: # First column is a special case
first = False
url = cl.url_for_result(result)
@@ -181,28 +185,20 @@ def result_list(cl):
return {'cl': cl,
'result_headers': list(result_headers(cl)),
'results': list(results(cl))}
-result_list = register.inclusion_tag("admin/change_list_results")(result_list)
+result_list = register.inclusion_tag("admin/change_list_results.html")(result_list)
def date_hierarchy(cl):
- lookup_opts, params, lookup_params, lookup_mod = \
- cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
-
- if lookup_opts.admin.date_hierarchy:
- field_name = lookup_opts.admin.date_hierarchy
-
+ if cl.lookup_opts.admin.date_hierarchy:
+ field_name = cl.lookup_opts.admin.date_hierarchy
year_field = '%s__year' % field_name
month_field = '%s__month' % field_name
day_field = '%s__day' % field_name
field_generic = '%s__' % field_name
- year_lookup = params.get(year_field)
- month_lookup = params.get(month_field)
- day_lookup = params.get(day_field)
-
- def link(d):
- return cl.get_query_string(d, [field_generic])
+ year_lookup = cl.params.get(year_field)
+ month_lookup = cl.params.get(month_field)
+ day_lookup = cl.params.get(day_field)
- def get_dates(unit, params):
- return getattr(lookup_mod, 'get_%s_list' % field_name)(unit, **params)
+ link = lambda d: cl.get_query_string(d, [field_generic])
if year_lookup and month_lookup and day_lookup:
month_name = MONTHS[int(month_lookup)]
@@ -215,9 +211,7 @@ def date_hierarchy(cl):
'choices': [{'title': "%s %s" % (month_name, day_lookup)}]
}
elif year_lookup and month_lookup:
- date_lookup_params = lookup_params.copy()
- date_lookup_params.update({year_field: year_lookup, month_field: month_lookup})
- days = get_dates('day', date_lookup_params)
+ days = cl.query_set.filter(**{year_field: year_lookup, month_field: month_lookup}).dates(field_name, 'day')
return {
'show': True,
'back': {
@@ -230,9 +224,7 @@ def date_hierarchy(cl):
} for day in days]
}
elif year_lookup:
- date_lookup_params = lookup_params.copy()
- date_lookup_params.update({year_field: year_lookup})
- months = get_dates('month', date_lookup_params)
+ months = cl.query_set.filter(**{year_field: year_lookup}).dates(field_name, 'month')
return {
'show' : True,
'back': {
@@ -240,20 +232,20 @@ def date_hierarchy(cl):
'title': _('All dates')
},
'choices': [{
- 'link': link( {year_field: year_lookup, month_field: month.month}),
- 'title': "%s %s" % (month.strftime('%B') , month.year)
+ 'link': link({year_field: year_lookup, month_field: month.month}),
+ 'title': "%s %s" % (month.strftime('%B'), month.year)
} for month in months]
}
else:
- years = get_dates('year', lookup_params)
+ years = cl.query_set.dates(field_name, 'year')
return {
'show': True,
'choices': [{
'link': link({year_field: year.year}),
'title': year.year
- } for year in years ]
+ } for year in years]
}
-date_hierarchy = register.inclusion_tag('admin/date_hierarchy')(date_hierarchy)
+date_hierarchy = register.inclusion_tag('admin/date_hierarchy.html')(date_hierarchy)
def search_form(cl):
return {
@@ -261,12 +253,12 @@ def search_form(cl):
'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
'search_var': SEARCH_VAR
}
-search_form = register.inclusion_tag('admin/search_form')(search_form)
+search_form = register.inclusion_tag('admin/search_form.html')(search_form)
def filter(cl, spec):
return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
-filter = register.inclusion_tag('admin/filter')(filter)
+filter = register.inclusion_tag('admin/filter.html')(filter)
def filters(cl):
return {'cl': cl}
-filters = register.inclusion_tag('admin/filters')(filters)
+filters = register.inclusion_tag('admin/filters.html')(filters)
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 6135e74eb4..9f646214ce 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -1,11 +1,13 @@
-from django.core import template, template_loader, meta
+from django import template
+from django.contrib.admin.views.main import AdminBoundField
+from django.template import loader
from django.utils.html import escape
from django.utils.text import capfirst
from django.utils.functional import curry
-from django.contrib.admin.views.main import AdminBoundField
-from django.core.meta.fields import BoundField, Field
-from django.core.meta import BoundRelatedObject, TABULAR, STACKED
-from django.conf.settings import ADMIN_MEDIA_PREFIX
+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()
@@ -16,30 +18,28 @@ def class_name_to_underscored(name):
return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
def include_admin_script(script_path):
- return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
+ return '<script type="text/javascript" src="%s%s"></script>' % (settings.ADMIN_MEDIA_PREFIX, script_path)
include_admin_script = register.simple_tag(include_admin_script)
-def submit_row(context, bound_manipulator):
+def submit_row(context):
+ opts = context['opts']
change = context['change']
- add = context['add']
- show_delete = context['show_delete']
- has_delete_permission = context['has_delete_permission']
is_popup = context['is_popup']
return {
- 'onclick_attrib': (bound_manipulator.ordered_objects and change
+ 'onclick_attrib': (opts.get_ordered_objects() and change
and 'onclick="submitOrderForm();"' or ''),
- 'show_delete_link': (not is_popup and has_delete_permission
- and (change or show_delete)),
- 'show_save_as_new': not is_popup and change and bound_manipulator.save_as,
- 'show_save_and_add_another': not is_popup and (not bound_manipulator.save_as or add),
- 'show_save_and_continue': not is_popup,
+ 'show_delete_link': (not is_popup and context['has_delete_permission']
+ and (change or context['show_delete'])),
+ 'show_save_as_new': not is_popup and change and opts.admin.save_as,
+ 'show_save_and_add_another': not is_popup and (not opts.admin.save_as or context['add']),
+ 'show_save_and_continue': not is_popup and context['has_change_permission'],
'show_save': True
}
-submit_row = register.inclusion_tag('admin/submit_line', 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, meta.BooleanField):
+ if isinstance(bound_field.field, models.BooleanField):
class_names.append("vCheckboxLabel")
colon = ""
else:
@@ -64,16 +64,15 @@ class FieldWidgetNode(template.Node):
if not cls.nodelists.has_key(klass):
try:
field_class_name = klass.__name__
- template_name = "widget/%s" % \
- class_name_to_underscored(field_class_name)
- nodelist = template_loader.get_template(template_name).nodelist
+ template_name = "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 = template_loader.get_template("widget/default").nodelist
+ cls.default = loader.get_template("widget/default.html").nodelist
nodelist = cls.default
cls.nodelists[klass] = nodelist
@@ -97,21 +96,22 @@ class FieldWrapper(object):
self.field = field
def needs_header(self):
- return not isinstance(self.field, meta.AutoField)
+ return not isinstance(self.field, models.AutoField)
def header_class_attribute(self):
return self.field.blank and ' class="optional"' or ''
def use_raw_id_admin(self):
- return isinstance(self.field.rel, (meta.ManyToOneRel, meta.ManyToManyRel)) \
+ 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):
+ 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):
@@ -120,29 +120,25 @@ class TabularBoundRelatedObject(BoundRelatedObject):
fields = self.relation.editable_fields()
- self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping, fields)
- for field_mapping in self.field_mappings]
+ 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"
+ 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.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields)
- for field_mapping in self.field_mappings]
+ 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"
-
-bound_related_object_overrides = {
- TABULAR: TabularBoundRelatedObject,
- STACKED: StackedBoundRelatedObject,
-}
+ return "admin/edit_inline_stacked.html"
class EditInlineNode(template.Node):
def __init__(self, rel_var):
@@ -150,21 +146,16 @@ class EditInlineNode(template.Node):
def render(self, context):
relation = template.resolve_variable(self.rel_var, context)
-
context.push()
-
- klass = relation.field.rel.edit_inline
- bound_related_object_class = bound_related_object_overrides.get(klass, klass)
-
+ if relation.field.rel.edit_inline == models.TABULAR:
+ bound_related_object_class = TabularBoundRelatedObject
+ else:
+ bound_related_object_class = StackedBoundRelatedObject
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 = template_loader.get_template(bound_related_object.template_name())
-
+ t = loader.get_template(bound_related_object.template_name())
output = t.render(context)
-
context.pop()
return output
@@ -191,30 +182,30 @@ 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, meta.ManyToManyRel) and f.rel.filter_interface:
- return '<script type="text/javascript">addEvent(window, "load", function(e) {' \
+ if f.rel and isinstance(f.rel, models.ManyToManyRel) and f.rel.filter_interface:
+ return '<script type="text/javascript">addEvent(window, "load", function(e) {' \
' SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (
- f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
+ f.name, f.verbose_name, f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX)
else:
return ''
filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
-def do_one_arg_tag(node_factory, parser,token):
- tokens = token.contents.split()
- if len(tokens) != 2:
- raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0])
- return node_factory(tokens[1])
-
-def register_one_arg_tag(node):
- tag_name = class_name_to_underscored(node.__name__)
- parse_func = curry(do_one_arg_tag, node)
- register.tag(tag_name, parse_func)
+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)
-register_one_arg_tag(FieldWidgetNode)
-register_one_arg_tag(EditInlineNode)
+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, BoundField)):
+ if isinstance(argument_val, AdminBoundField):
bound_fields = [argument_val]
else:
bound_fields = [bf for bf in argument_val]
@@ -229,7 +220,7 @@ def admin_field_line(context, argument_val):
break
# Assumes BooleanFields won't be stacked next to each other!
- if isinstance(bound_fields[0].field, meta.BooleanField):
+ if isinstance(bound_fields[0].field, models.BooleanField):
class_names.append('checkbox-row')
return {
@@ -238,8 +229,4 @@ def admin_field_line(context, argument_val):
'bound_fields': bound_fields,
'class_names': " ".join(class_names),
}
-admin_field_line = register.inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
-
-def object_pk(bound_manip, ordered_obj):
- return bound_manip.get_ordered_object_pk(ordered_obj)
-object_pk = register.simple_tag(object_pk)
+admin_field_line = register.inclusion_tag('admin/field_line.html', takes_context=True)(admin_field_line)
diff --git a/django/contrib/admin/templatetags/adminapplist.py b/django/contrib/admin/templatetags/adminapplist.py
index 7a91516ebc..c328ddf203 100644
--- a/django/contrib/admin/templatetags/adminapplist.py
+++ b/django/contrib/admin/templatetags/adminapplist.py
@@ -1,4 +1,5 @@
-from django.core import template
+from django import template
+from django.db.models import get_models
register = template.Library()
@@ -7,20 +8,24 @@ class AdminApplistNode(template.Node):
self.varname = varname
def render(self, context):
- from django.core import meta
+ from django.db import models
from django.utils.text import capfirst
app_list = []
user = context['user']
- for app in meta.get_installed_model_modules():
- app_label = app.__name__[app.__name__.rindex('.')+1:]
+ 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:
+ for m in app_models:
if m._meta.admin:
- module_name = m._meta.module_name
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())),
@@ -32,7 +37,7 @@ class AdminApplistNode(template.Node):
if True in perms.values():
model_list.append({
'name': capfirst(m._meta.verbose_name_plural),
- 'admin_url': '%s/%s/' % (app_label, m._meta.module_name),
+ 'admin_url': '%s/%s/' % (app_label, m.__name__.lower()),
'perms': perms,
})
diff --git a/django/contrib/admin/templatetags/adminmedia.py b/django/contrib/admin/templatetags/adminmedia.py
index cd513f6c81..266c017e3d 100644
--- a/django/contrib/admin/templatetags/adminmedia.py
+++ b/django/contrib/admin/templatetags/adminmedia.py
@@ -1,10 +1,11 @@
-from django.core.template import Library
+from django.template import Library
+
register = Library()
def admin_media_prefix():
try:
- from django.conf.settings import ADMIN_MEDIA_PREFIX
+ from django.conf import settings
except ImportError:
return ''
- return ADMIN_MEDIA_PREFIX
+ return settings.ADMIN_MEDIA_PREFIX
admin_media_prefix = register.simple_tag(admin_media_prefix)
diff --git a/django/contrib/admin/templatetags/log.py b/django/contrib/admin/templatetags/log.py
index 013e07c80f..5caba2b795 100644
--- a/django/contrib/admin/templatetags/log.py
+++ b/django/contrib/admin/templatetags/log.py
@@ -1,5 +1,5 @@
-from django.models.admin import log
-from django.core import template
+from django import template
+from django.contrib.admin.models import LogEntry
register = template.Library()
@@ -13,7 +13,7 @@ class AdminLogNode(template.Node):
def render(self, context):
if self.user is not None and not self.user.isdigit():
self.user = context[self.user].id
- context[self.varname] = log.get_list(user__id__exact=self.user, limit=self.limit, select_related=True)
+ context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
return ''
class DoGetAdminLog:
diff --git a/django/contrib/admin/urls.py b/django/contrib/admin/urls.py
new file mode 100644
index 0000000000..dde848d766
--- /dev/null
+++ b/django/contrib/admin/urls.py
@@ -0,0 +1,31 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+ ('^$', 'django.contrib.admin.views.main.index'),
+ ('^r/(\d+)/(\d+)/$', 'django.views.defaults.shortcut'),
+ ('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'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/jump/$', 'django.contrib.admin.views.doc.jump_to_view'),
+ ('^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/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'),
+)
diff --git a/django/contrib/admin/urls/__init__.py b/django/contrib/admin/urls/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/django/contrib/admin/urls/__init__.py
+++ /dev/null
diff --git a/django/contrib/admin/urls/admin.py b/django/contrib/admin/urls/admin.py
deleted file mode 100644
index 3f4dbab419..0000000000
--- a/django/contrib/admin/urls/admin.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from django.conf.urls.defaults import *
-from django.conf.settings import INSTALLED_APPS
-
-urlpatterns = (
- ('^$', 'django.contrib.admin.views.main.index'),
- ('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'django.conf'}),
- ('^logout/$', 'django.views.auth.login.logout'),
- ('^password_change/$', 'django.views.registration.passwords.password_change'),
- ('^password_change/done/$', 'django.views.registration.passwords.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/jump/$', 'django.contrib.admin.views.doc.jump_to_view'),
- ('^doc/views/(?P<view>[^/]+)/$', 'django.contrib.admin.views.doc.view_detail'),
- ('^doc/models/$', 'django.contrib.admin.views.doc.model_index'),
- ('^doc/models/(?P<model>[^/]+)/$', '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'),
-)
-
-if 'ellington.events' in INSTALLED_APPS:
- urlpatterns += (
- ("^events/usersubmittedevents/(?P<object_id>\d+)/$", 'ellington.events.views.admin.user_submitted_event_change_stage'),
- ("^events/usersubmittedevents/(?P<object_id>\d+)/delete/$", 'ellington.events.views.admin.user_submitted_event_delete_stage'),
- )
-
-if 'ellington.news' in INSTALLED_APPS:
- urlpatterns += (
- ("^stories/preview/$", 'ellington.news.views.admin.story_preview'),
- ("^stories/js/inlinecontrols/$", 'ellington.news.views.admin.inlinecontrols_js'),
- ("^stories/js/inlinecontrols/(?P<label>[-\w]+)/$", 'ellington.news.views.admin.inlinecontrols_js_specific'),
- )
-
-if 'ellington.alerts' in INSTALLED_APPS:
- urlpatterns += (
- ("^alerts/send/$", 'ellington.alerts.views.admin.send_alert_form'),
- ("^alerts/send/do/$", 'ellington.alerts.views.admin.send_alert_action'),
- )
-
-if 'ellington.media' in INSTALLED_APPS:
- urlpatterns += (
- ('^media/photos/caption/(?P<photo_id>\d+)/$', 'ellington.media.views.admin.get_exif_caption'),
- )
-
-urlpatterns += (
- # Metasystem admin pages
- ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.contrib.admin.views.main.change_list'),
- ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
- ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/history/$', 'django.contrib.admin.views.main.history'),
- ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/delete/$', 'django.contrib.admin.views.main.delete_stage'),
- ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.contrib.admin.views.main.change_stage'),
-)
-urlpatterns = patterns('', *urlpatterns)
diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
index 80a8f2a773..c6c794c75e 100644
--- a/django/contrib/admin/utils.py
+++ b/django/contrib/admin/utils.py
@@ -82,18 +82,18 @@ ROLES = {
def create_reference_role(rolename, urlbase):
def _role(name, rawtext, text, lineno, inliner, options={}, content=[]):
- node = docutils.nodes.reference(rawtext, text, refuri=(urlbase % (inliner.document.settings.link_base, text)), **options)
+ node = docutils.nodes.reference(rawtext, text, refuri=(urlbase % (inliner.document.settings.link_base, text.lower())), **options)
return [node], []
docutils.parsers.rst.roles.register_canonical_role(rolename, _role)
def default_reference_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
context = inliner.document.settings.default_reference_context
- node = docutils.nodes.reference(rawtext, text, refuri=(ROLES[context] % (inliner.document.settings.link_base, text)), **options)
+ node = docutils.nodes.reference(rawtext, text, refuri=(ROLES[context] % (inliner.document.settings.link_base, text.lower())), **options)
return [node], []
if docutils_is_available:
docutils.parsers.rst.roles.register_canonical_role('cmsreference', default_reference_role)
docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE = 'cmsreference'
- for (name, urlbase) in ROLES.items():
+ for name, urlbase in ROLES.items():
create_reference_role(name, urlbase)
diff --git a/django/contrib/admin/views/decorators.py b/django/contrib/admin/views/decorators.py
index 5ddc17fa85..d984077dfb 100644
--- a/django/contrib/admin/views/decorators.py
+++ b/django/contrib/admin/views/decorators.py
@@ -1,7 +1,7 @@
-from django.core.extensions import DjangoContext, render_to_response
-from django.conf.settings import SECRET_KEY
-from django.models.auth import users
-from django.utils import httpwrappers
+from django import http, template
+from django.conf import settings
+from django.contrib.auth.models import User, SESSION_KEY
+from django.shortcuts import render_to_response
from django.utils.translation import gettext_lazy
import base64, datetime, md5
import cPickle as pickle
@@ -19,22 +19,22 @@ def _display_login_form(request, error_message=''):
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
- return render_to_response('admin/login', {
+ return render_to_response('admin/login.html', {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'error_message': error_message
- }, context_instance=DjangoContext(request))
+ }, context_instance=template.RequestContext(request))
def _encode_post_data(post_data):
pickled = pickle.dumps(post_data)
- pickled_md5 = md5.new(pickled + SECRET_KEY).hexdigest()
+ pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5)
def _decode_post_data(encoded_data):
encoded_data = base64.decodestring(encoded_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
- if md5.new(pickled + SECRET_KEY).hexdigest() != tamper_check:
+ 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)
@@ -53,7 +53,7 @@ def staff_member_required(view_func):
request.POST = _decode_post_data(request.POST['post_data'])
return view_func(request, *args, **kwargs)
- assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.middleware.sessions.SessionMiddleware'."
+ assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
# If this isn't already the login page, display it.
if not request.POST.has_key(LOGIN_FORM_KEY):
@@ -71,14 +71,14 @@ def staff_member_required(view_func):
# Check the password.
username = request.POST.get('username', '')
try:
- user = users.get_object(username__exact=username, is_staff__exact=True)
- except users.UserDoesNotExist:
+ user = User.objects.get(username=username, is_staff=True)
+ except User.DoesNotExist:
message = ERROR_MESSAGE
if '@' in username:
# Mistakenly entered e-mail address instead of username? Look it up.
try:
- user = users.get_object(email__exact=username)
- except users.UserDoesNotExist:
+ user = User.objects.get(email=username)
+ except User.DoesNotExist:
message = _("Usernames cannot contain the '@' character.")
else:
message = _("Your e-mail address is not your username. Try '%s' instead.") % user.username
@@ -87,7 +87,7 @@ def staff_member_required(view_func):
# The user data is correct; log in the user in and continue.
else:
if user.check_password(request.POST.get('password', '')):
- request.session[users.SESSION_KEY] = user.id
+ request.session[SESSION_KEY] = user.id
user.last_login = datetime.datetime.now()
user.save()
if request.POST.has_key('post_data'):
@@ -99,7 +99,7 @@ def staff_member_required(view_func):
return view_func(request, *args, **kwargs)
else:
request.session.delete_test_cookie()
- return httpwrappers.HttpResponseRedirect(request.path)
+ return http.HttpResponseRedirect(request.path)
else:
return _display_login_form(request, ERROR_MESSAGE)
diff --git a/django/contrib/admin/views/doc.py b/django/contrib/admin/views/doc.py
index 9a6d3ec400..f3675b6adf 100644
--- a/django/contrib/admin/views/doc.py
+++ b/django/contrib/admin/views/doc.py
@@ -1,12 +1,14 @@
-from django.core import meta
-from django import templatetags
+from django import template, templatetags
+from django.template import RequestContext
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
-from django.models.core import sites
-from django.core.extensions import DjangoContext, render_to_response
-from django.core.exceptions import Http404, ViewDoesNotExist
-from django.core import template, urlresolvers
+from django.db import models
+from django.shortcuts import render_to_response
+from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
+from django.http import Http404, get_host
+from django.core import urlresolvers
from django.contrib.admin import utils
+from django.contrib.sites.models import Site
import inspect, os, re
# Exclude methods starting with these strings from documentation
@@ -15,15 +17,15 @@ MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
def doc_index(request):
if not utils.docutils_is_available:
return missing_docutils_page(request)
- return render_to_response('admin_doc/index', context_instance=DjangoContext(request))
+ return render_to_response('admin_doc/index.html', context_instance=RequestContext(request))
doc_index = staff_member_required(doc_index)
def bookmarklets(request):
# Hack! This couples this view to the URL it lives at.
admin_root = request.path[:-len('doc/bookmarklets/')]
- return render_to_response('admin_doc/bookmarklets', {
- 'admin_url': "%s://%s%s" % (os.environ.get('HTTPS') == 'on' and 'https' or 'http', request.META['HTTP_HOST'], admin_root),
- }, context_instance=DjangoContext(request))
+ return render_to_response('admin_doc/bookmarklets.html', {
+ 'admin_url': "%s://%s%s" % (os.environ.get('HTTPS') == 'on' and 'https' or 'http', get_host(request), admin_root),
+ }, context_instance=RequestContext(request))
bookmarklets = staff_member_required(bookmarklets)
def template_tag_index(request):
@@ -54,7 +56,7 @@ def template_tag_index(request):
'library': tag_library,
})
- return render_to_response('admin_doc/template_tag_index', {'tags': tags}, context_instance=DjangoContext(request))
+ return render_to_response('admin_doc/template_tag_index.html', {'tags': tags}, context_instance=RequestContext(request))
template_tag_index = staff_member_required(template_tag_index)
def template_filter_index(request):
@@ -84,16 +86,20 @@ def template_filter_index(request):
'meta': metadata,
'library': tag_library,
})
- return render_to_response('admin_doc/template_filter_index', {'filters': filters}, context_instance=DjangoContext(request))
+ return render_to_response('admin_doc/template_filter_index.html', {'filters': filters}, context_instance=RequestContext(request))
template_filter_index = staff_member_required(template_filter_index)
def view_index(request):
if not utils.docutils_is_available:
return missing_docutils_page(request)
+ if settings.ADMIN_FOR:
+ settings_modules = [__import__(m, '', '', ['']) for m in settings.ADMIN_FOR]
+ else:
+ settings_modules = [settings]
+
views = []
- for site_settings_module in settings.ADMIN_FOR:
- settings_mod = __import__(site_settings_module, '', '', [''])
+ for settings_mod in settings_modules:
urlconf = __import__(settings_mod.ROOT_URLCONF, '', '', [''])
view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
for (func, regex) in view_functions:
@@ -101,10 +107,10 @@ def view_index(request):
'name': func.__name__,
'module': func.__module__,
'site_id': settings_mod.SITE_ID,
- 'site': sites.get_object(pk=settings_mod.SITE_ID),
+ 'site': Site.objects.get(pk=settings_mod.SITE_ID),
'url': simplify_regex(regex),
})
- return render_to_response('admin_doc/view_index', {'views': views}, context_instance=DjangoContext(request))
+ return render_to_response('admin_doc/view_index.html', {'views': views}, context_instance=RequestContext(request))
view_index = staff_member_required(view_index)
def view_detail(request, view):
@@ -123,51 +129,63 @@ def view_detail(request, view):
body = utils.parse_rst(body, 'view', 'view:' + view)
for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'model', 'view:' + view)
- return render_to_response('admin_doc/view_detail', {
+ return render_to_response('admin_doc/view_detail.html', {
'name': view,
'summary': title,
'body': body,
'meta': metadata,
- }, context_instance=DjangoContext(request))
+ }, context_instance=RequestContext(request))
view_detail = staff_member_required(view_detail)
def model_index(request):
if not utils.docutils_is_available:
return missing_docutils_page(request)
- models = []
- for app in meta.get_installed_model_modules():
- for model in app._MODELS:
- opts = model._meta
- models.append({
- 'name': '%s.%s' % (opts.app_label, opts.module_name),
- 'module': opts.app_label,
- 'class': opts.module_name,
- })
- return render_to_response('admin_doc/model_index', {'models': models}, context_instance=DjangoContext(request))
+ 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))
model_index = staff_member_required(model_index)
-def model_detail(request, model):
+def model_detail(request, app_label, model_name):
if not utils.docutils_is_available:
return missing_docutils_page(request)
+ # Get the model class.
try:
- model = meta.get_app(model)
- except ImportError:
- raise Http404
- opts = model.Klass._meta
+ app_mod = models.get_app(app_label)
+ except ImproperlyConfigured:
+ raise Http404, "App %r not found" % app_label
+ model = None
+ for m in models.get_models(app_mod):
+ if m._meta.object_name.lower() == model_name:
+ model = m
+ break
+ if model is None:
+ raise Http404, "Model %r not found in app %r" % (model_name, app_label)
- # Gather fields/field descriptions
+ opts = model._meta
+
+ # Gather fields/field descriptions.
fields = []
for field in opts.fields:
+ # ForeignKey is a special case since the field will actually be a
+ # descriptor that returns the other object
+ if isinstance(field, models.ForeignKey):
+ data_type = related_object_name = field.rel.to.__name__
+ app_label = field.rel.to._meta.app_label
+ verbose = utils.parse_rst(("the related `%s.%s` object" % (app_label, data_type)), 'model', 'model:' + data_type)
+ else:
+ data_type = get_readable_field_data_type(field)
+ verbose = field.verbose_name
fields.append({
'name': field.name,
- 'data_type': get_readable_field_data_type(field),
- 'verbose': field.verbose_name,
+ 'data_type': data_type,
+ 'verbose': verbose,
'help': field.help_text,
})
- for func_name, func in model.Klass.__dict__.items():
- if callable(func) and len(inspect.getargspec(func)[0]) == 0:
+
+ # Gather model methods.
+ for func_name, func in model.__dict__.items():
+ if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
try:
for exclude in MODEL_METHODS_EXCLUDE:
if func_name.startswith(exclude):
@@ -182,12 +200,26 @@ def model_detail(request, model):
'data_type': get_return_data_type(func_name),
'verbose': verbose,
})
- return render_to_response('admin_doc/model_detail', {
- 'name': '%s.%s' % (opts.app_label, opts.module_name),
- 'summary': "Fields on %s objects" % opts.verbose_name,
+
+ # Gather related objects
+ for rel in opts.get_all_related_objects():
+ verbose = "related `%s.%s` objects" % (rel.opts.app_label, rel.opts.object_name)
+ accessor = rel.get_accessor_name()
+ fields.append({
+ 'name' : "%s.all" % accessor,
+ 'verbose' : utils.parse_rst("all " + verbose , 'model', 'model:' + opts.module_name),
+ })
+ fields.append({
+ 'name' : "%s.count" % accessor,
+ 'verbose' : utils.parse_rst("number of " + verbose , 'model', 'model:' + opts.module_name),
+ })
+
+ return render_to_response('admin_doc/model_detail.html', {
+ 'name': '%s.%s' % (opts.app_label, opts.object_name),
+ 'summary': "Fields on %s objects" % opts.object_name,
'description': model.__doc__,
'fields': fields,
- }, context_instance=DjangoContext(request))
+ }, context_instance=RequestContext(request))
model_detail = staff_member_required(model_detail)
def template_detail(request, template):
@@ -201,13 +233,13 @@ def template_detail(request, template):
'exists': os.path.exists(template_file),
'contents': lambda: os.path.exists(template_file) and open(template_file).read() or '',
'site_id': settings_mod.SITE_ID,
- 'site': sites.get_object(pk=settings_mod.SITE_ID),
+ 'site': Site.objects.get(pk=settings_mod.SITE_ID),
'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
})
- return render_to_response('admin_doc/template_detail', {
+ return render_to_response('admin_doc/template_detail.html', {
'name': template,
'templates': templates,
- }, context_instance=DjangoContext(request))
+ }, context_instance=RequestContext(request))
template_detail = staff_member_required(template_detail)
####################
@@ -216,7 +248,7 @@ template_detail = staff_member_required(template_detail)
def missing_docutils_page(request):
"""Display an error message for people without docutils"""
- return render_to_response('admin_doc/missing_docutils')
+ return render_to_response('admin_doc/missing_docutils.html')
def load_all_installed_template_libraries():
# Load/register all template tag libraries from installed apps.
@@ -271,9 +303,6 @@ DATA_TYPE_MAPPING = {
}
def get_readable_field_data_type(field):
- # ForeignKey is a special case. Use the field type of the relation.
- if field.get_internal_type() == 'ForeignKey':
- field = field.rel.get_related_field()
return DATA_TYPE_MAPPING[field.get_internal_type()] % field.__dict__
def extract_views_from_urlpatterns(urlpatterns, base=''):
@@ -295,15 +324,23 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
raise TypeError, "%s does not appear to be a urlpattern object" % p
return views
-# Clean up urlpattern regexes into something somewhat readable by Mere Humans:
-# turns something like "^(?P<sport_slug>\w+)/athletes/(?P<athlete_slug>\w+)/$"
-# into "<sport_slug>/athletes/<athlete_slug>/"
-
named_group_matcher = re.compile(r'\(\?P(<\w+>).+?\)')
+non_named_group_matcher = re.compile(r'\(.*?\)')
def simplify_regex(pattern):
+ """
+ Clean up urlpattern regexes into something somewhat readable by Mere Humans:
+ turns something like "^(?P<sport_slug>\w+)/athletes/(?P<athlete_slug>\w+)/$"
+ into "<sport_slug>/athletes/<athlete_slug>/"
+ """
+ # handle named groups first
pattern = named_group_matcher.sub(lambda m: m.group(1), pattern)
- pattern = pattern.replace('^', '').replace('$', '').replace('?', '').replace('//', '/')
+
+ # handle non-named groups
+ pattern = non_named_group_matcher.sub("<var>", pattern)
+
+ # clean up any outstanding regex-y characters.
+ pattern = pattern.replace('^', '').replace('$', '').replace('?', '').replace('//', '/').replace('\\', '')
if not pattern.startswith('/'):
pattern = '/' + pattern
return pattern
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 3d92a7d949..9913914858 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -1,32 +1,34 @@
-# Generic admin views.
-from django.contrib.admin.views.decorators import staff_member_required
+from django import forms, template
+from django.conf import settings
from django.contrib.admin.filterspecs import FilterSpec
-from django.core import formfields, meta, template
-from django.core.template import loader
-from django.core.meta.fields import BoundField, BoundFieldLine, BoundFieldSet
-from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
-from django.core.extensions import DjangoContext as Context
-from django.core.extensions import get_object_or_404, render_to_response
+from django.contrib.admin.views.decorators import staff_member_required
+from django.views.decorators.cache import never_cache
+from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
from django.core.paginator import ObjectPaginator, InvalidPage
-from django.conf.settings import ADMIN_MEDIA_PREFIX
-try:
- from django.models.admin import log
-except ImportError:
- raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS."
-from django.utils.html import escape
-from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
-from django.utils.text import capfirst, get_text_list
+from django.shortcuts import get_object_or_404, render_to_response
+from django.db import models
+from django.db.models.query import handle_legacy_orderlist, QuerySet
+from django.http import Http404, HttpResponse, HttpResponseRedirect
+from django.template import loader
from django.utils import dateformat
from django.utils.dates import MONTHS
from django.utils.html import escape
+from django.utils.text import capfirst, get_text_list
import operator
-# The system will display a "Show all" link only if the total result count
-# is less than or equal to this setting.
-MAX_SHOW_ALL_ALLOWED = 200
+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."
-DEFAULT_RESULTS_PER_PAGE = 100
+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
+# total result count is less than or equal to this setting.
+MAX_SHOW_ALL_ALLOWED = 200
+# Changelist settings
ALL_VAR = 'all'
ORDER_VAR = 'o'
ORDER_TYPE_VAR = 'ot'
@@ -34,229 +36,59 @@ PAGE_VAR = 'p'
SEARCH_VAR = 'q'
IS_POPUP_VAR = 'pop'
-# Text to display within changelist table cells if the value is blank.
+# Text to display within change-list table cells if the value is blank.
EMPTY_CHANGELIST_VALUE = '(None)'
-def _get_mod_opts(app_label, module_name):
- "Helper function that returns a tuple of (module, opts), raising Http404 if necessary."
- try:
- mod = meta.get_module(app_label, module_name)
- except ImportError:
- raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS.
- opts = mod.Klass._meta
- if not opts.admin:
- raise Http404 # This object is valid but has no admin interface.
- return mod, opts
-
-def index(request):
- return render_to_response('admin/index', {'title': _('Site administration')}, context_instance=Context(request))
-index = staff_member_required(index)
+use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOneRel, models.ManyToManyRel)) and field.rel.raw_id_admin
class IncorrectLookupParameters(Exception):
pass
-class ChangeList(object):
- def __init__(self, request, app_label, module_name):
- self.get_modules_and_options(app_label, module_name, request)
- self.get_search_parameters(request)
- self.get_ordering()
- self.query = request.GET.get(SEARCH_VAR, '')
- self.get_lookup_params()
- self.get_results(request)
- self.title = (self.is_popup
- and _('Select %s') % self.opts.verbose_name
- or _('Select %s to change') % self.opts.verbose_name)
- self.get_filters(request)
- self.pk_attname = self.lookup_opts.pk.attname
-
- def get_filters(self, request):
- self.filter_specs = []
- if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
- filter_fields = [self.lookup_opts.get_field(field_name) \
- for field_name in self.lookup_opts.admin.list_filter]
- for f in filter_fields:
- spec = FilterSpec.create(f, request, self.params)
- if spec and spec.has_output():
- self.filter_specs.append(spec)
- self.has_filters = bool(self.filter_specs)
+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 autoamtically
+ 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 get_query_string(self, new_params={}, remove=[]):
- p = self.params.copy()
- for r in remove:
- for k in p.keys():
- if k.startswith(r):
- del p[k]
- for k, v in new_params.items():
- if p.has_key(k) and v is None:
- del p[k]
- elif v is not None:
- p[k] = v
- return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
-
- def get_modules_and_options(self, app_label, module_name, request):
- self.mod, self.opts = _get_mod_opts(app_label, module_name)
- if not request.user.has_perm(app_label + '.' + self.opts.get_change_permission()):
- raise PermissionDenied
-
- self.lookup_mod, self.lookup_opts = self.mod, self.opts
-
- def get_search_parameters(self, request):
- # Get search parameters from the query string.
- try:
- self.page_num = int(request.GET.get(PAGE_VAR, 0))
- except ValueError:
- self.page_num = 0
- self.show_all = request.GET.has_key(ALL_VAR)
- self.is_popup = request.GET.has_key(IS_POPUP_VAR)
- self.params = dict(request.GET.items())
- if self.params.has_key(PAGE_VAR):
- del self.params[PAGE_VAR]
-
- def get_results(self, request):
- lookup_mod, lookup_params, show_all, page_num = \
- self.lookup_mod, self.lookup_params, self.show_all, self.page_num
- # Get the results.
- try:
- paginator = ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE)
- # Naked except! Because we don't have any other way of validating "params".
- # They might be invalid if the keyword arguments are incorrect, or if the
- # values are not in the correct type (which would result in a database
- # error).
- except:
- raise IncorrectLookupParameters()
-
- # Get the total number of objects, with no filters applied.
- real_lookup_params = lookup_params.copy()
- del real_lookup_params['order_by']
- if real_lookup_params:
- full_result_count = lookup_mod.get_count()
- else:
- full_result_count = paginator.hits
- del real_lookup_params
- result_count = paginator.hits
- can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
- multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
-
- # Get the list of objects to display on this page.
- if (show_all and can_show_all) or not multi_page:
- result_list = lookup_mod.get_list(**lookup_params)
- else:
+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:
- result_list = paginator.get_page(page_num)
- except InvalidPage:
- result_list = []
- (self.result_count, self.full_result_count, self.result_list,
- self.can_show_all, self.multi_page, self.paginator) = (result_count,
- full_result_count, result_list, can_show_all, multi_page, paginator )
-
- def url_for_result(self, result):
- return "%s/" % getattr(result, self.pk_attname)
-
- def get_ordering(self):
- lookup_opts, params = self.lookup_opts, self.params
- # For ordering, first check the "ordering" parameter in the admin options,
- # then check the object's default ordering. If neither of those exist,
- # order descending by ID by default. Finally, look for manually-specified
- # ordering from the query string.
- ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
-
- # Normalize it to new-style ordering.
- ordering = meta.handle_legacy_orderlist(ordering)
-
- if ordering[0].startswith('-'):
- order_field, order_type = ordering[0][1:], 'desc'
- else:
- order_field, order_type = ordering[0], 'asc'
- if params.has_key(ORDER_VAR):
- try:
- try:
- f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
- except meta.FieldDoesNotExist:
- pass
- else:
- if not isinstance(f.rel, meta.ManyToOneRel) or not f.null:
- order_field = f.name
- except (IndexError, ValueError):
- pass # Invalid ordering specified. Just use the default.
- if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
- order_type = params[ORDER_TYPE_VAR]
- self.order_field, self.order_type = order_field, order_type
-
- def get_lookup_params(self):
- # Prepare the lookup parameters for the API lookup.
- (params, order_field, lookup_opts, order_type, opts, query) = \
- (self.params, self.order_field, self.lookup_opts, self.order_type, self.opts, self.query)
-
- lookup_params = params.copy()
- for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
- if lookup_params.has_key(i):
- del lookup_params[i]
- # If the order-by field is a field with a relationship, order by the value
- # in the related table.
- lookup_order_field = order_field
- try:
- f = lookup_opts.get_field(order_field)
- except meta.FieldDoesNotExist:
- pass
- else:
- if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOneRel):
- f = lookup_opts.get_field(order_field)
- rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column
- lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering)
- # Use select_related if one of the list_display options is a field with a
- # relationship.
- if lookup_opts.admin.list_select_related:
- lookup_params['select_related'] = True
+ myappend(mychr(myatoi(item[:2], 16))
+ + item[2:])
+ except ValueError:
+ myappend('_' + item)
else:
- for field_name in lookup_opts.admin.list_display:
- try:
- f = lookup_opts.get_field(field_name)
- except meta.FieldDoesNotExist:
- pass
- else:
- if isinstance(f.rel, meta.ManyToOneRel):
- lookup_params['select_related'] = True
- break
- lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,)
- if lookup_opts.admin.search_fields and query:
- complex_queries = []
- for bit in query.split():
- or_queries = []
- for field_name in lookup_opts.admin.search_fields:
- or_queries.append(meta.Q(**{'%s__icontains' % field_name: bit}))
- complex_queries.append(reduce(operator.or_, or_queries))
- lookup_params['complex'] = reduce(operator.and_, complex_queries)
- if opts.one_to_one_field:
- lookup_params.update(opts.one_to_one_field.rel.limit_choices_to)
- self.lookup_params = lookup_params
-
-def change_list(request, app_label, module_name):
- try:
- cl = ChangeList(request, app_label, module_name)
- except IncorrectLookupParameters:
- return HttpResponseRedirect(request.path)
-
- c = Context(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' % (app_label, cl.opts.object_name.lower()),
- 'admin/%s/change_list' % app_label,
- 'admin/change_list'], context_instance=c)
-change_list = staff_member_required(change_list)
-
-use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOneRel, meta.ManyToManyRel)) and field.rel.raw_id_admin
+ myappend('_' + item)
+ return "".join(res)
-def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_sets):
+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(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField):
+ 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 ordered_objects:
+ 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)
@@ -264,29 +96,30 @@ def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_se
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' )
+ js.append('js/admin/CollapsedFieldsets.js')
for field_line in field_set:
try:
for f in field_line:
- if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface:
+ 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(BoundField):
+class AdminBoundField(object):
def __init__(self, field, field_mapping, original):
- super(AdminBoundField, self).__init__(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, meta.BooleanField)
+ 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, meta.DateTimeField)
- self.is_file_field = isinstance(field, meta.FileField)
- self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOneRel) or isinstance(field.rel, meta.ManyToManyRel) and field.rel.to.admin
- self.hidden = isinstance(self.field, meta.AutoField)
+ 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 = []
@@ -298,26 +131,22 @@ class AdminBoundField(BoundField):
self.cell_class_attribute = ' class="%s" ' % ' '.join(classes)
self._repr_filled = False
- def _fetch_existing_display(self, func_name):
- class_dict = self.original.__class__.__dict__
- func = class_dict.get(func_name)
- return func(self.original)
+ if field.rel:
+ self.related_url = '../../../%s/%s/' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name.lower())
- def _fill_existing_display(self):
- if getattr(self, '_display_filled', False):
- return
- # HACK
- if isinstance(self.field.rel, meta.ManyToOneRel):
- func_name = 'get_%s' % self.field.name
- self._display = self._fetch_existing_display(func_name)
- elif isinstance(self.field.rel, meta.ManyToManyRel):
- func_name = 'get_%s_list' % self.field.rel.singular
- self._display = ", ".join([str(obj) for obj in self._fetch_existing_display(func_name)])
- self._display_filled = True
+ def original_value(self):
+ if self.original:
+ return self.original.__dict__[self.field.column]
def existing_display(self):
- self._fill_existing_display()
- return self._display
+ try:
+ return self._display
+ except AttributeError:
+ if isinstance(self.field.rel, models.ManyToOneRel):
+ self._display = getattr(self.original, 'get_%s' % self.field.name)()
+ elif isinstance(self.field.rel, models.ManyToManyRel):
+ self._display = ", ".join([str(obj) for obj in getattr(self.original, 'get_%s_list' % self.field.rel.singular)()])
+ return self._display
def __repr__(self):
return repr(self.__dict__)
@@ -325,94 +154,114 @@ class AdminBoundField(BoundField):
def html_error_list(self):
return " ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors])
-class AdminBoundFieldLine(BoundFieldLine):
+ 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):
- super(AdminBoundFieldLine, self).__init__(field_line, field_mapping, original, AdminBoundField)
+ self.bound_fields = [field.bind(field_mapping, original, AdminBoundField) for field in field_line]
for bound_field in self:
bound_field.first = True
break
-class AdminBoundFieldSet(BoundFieldSet):
- def __init__(self, field_set, field_mapping, original):
- super(AdminBoundFieldSet, self).__init__(field_set, field_mapping, original, AdminBoundFieldLine)
-
-class BoundManipulator(object):
- def __init__(self, opts, manipulator, field_mapping):
- self.inline_related_objects = opts.get_followed_related_objects(manipulator.follow)
- self.original = hasattr(manipulator, 'original_object') and manipulator.original_object or None
- self.bound_field_sets = [field_set.bind(field_mapping, self.original, AdminBoundFieldSet)
- for field_set in opts.admin.get_field_sets(opts)]
- self.ordered_objects = opts.get_ordered_objects()[:]
-
-class AdminBoundManipulator(BoundManipulator):
- def __init__(self, opts, manipulator, field_mapping):
- super(AdminBoundManipulator, self).__init__(opts, manipulator, field_mapping)
- field_sets = opts.admin.get_field_sets(opts)
-
- self.auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
- self.javascript_imports = get_javascript_imports(opts, self.auto_populated_fields, self.ordered_objects, field_sets);
-
- self.coltype = self.ordered_objects and 'colMS' or 'colM'
- self.has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
- self.form_enc_attrib = opts.has_field_type(meta.FileField) and \
- 'enctype="multipart/form-data" ' or ''
+ def __iter__(self):
+ for bound_field in self.bound_fields:
+ yield bound_field
- self.first_form_field_id = self.bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
- self.ordered_object_pk_names = [o.pk.name for o in self.ordered_objects]
+ def __len__(self):
+ return len(self.bound_fields)
- self.save_on_top = opts.admin.save_on_top
- self.save_as = opts.admin.save_as
+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]
- self.content_type_id = opts.get_content_type_id()
- self.verbose_name_plural = opts.verbose_name_plural
- self.verbose_name = opts.verbose_name
- self.object_name = opts.object_name
+ def __iter__(self):
+ for bound_field_line in self.bound_field_lines:
+ yield bound_field_line
- def get_ordered_object_pk(self, ordered_obj):
- for name in self.ordered_object_pk_names:
- if hasattr(ordered_obj, name):
- return str(getattr(ordered_obj, name))
- return ""
+ def __len__(self):
+ return len(self.bound_field_lines)
-def render_change_form(opts, manipulator, app_label, context, add=False, change=False, show_delete=False, form_url=''):
+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,
- 'bound_manipulator': AdminBoundManipulator(opts, manipulator, context['form']),
'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': form_url,
- 'app_label': app_label,
+ '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" % (app_label, opts.object_name.lower() ),
- "admin/%s/change_form" % app_label ,
- "admin/change_form"], context_instance=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 log_add_message(user, opts,manipulator,new_object):
- pk_value = getattr(new_object, opts.pk.attname)
- log.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), log.ADDITION)
+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
-def add_stage(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None):
- mod, opts = _get_mod_opts(app_label, module_name)
if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
raise PermissionDenied
- manipulator = mod.AddManipulator()
+
+ 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(meta.FileField):
+
+ 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 and not request.POST.has_key("_preview"):
+ if not errors:
new_object = manipulator.save(new_data)
- log_add_message(request.user, opts,manipulator,new_object)
- msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name':opts.verbose_name, 'obj':new_object}
- pk_value = getattr(new_object,opts.pk.attname)
+ pk_value = new_object._get_pk_val()
+ LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, str(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.add_message(msg + ' ' + _("You may edit it again below."))
+ 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)
@@ -420,10 +269,10 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
(pk_value, str(new_object).replace('"', '\\"')))
elif request.POST.has_key("_addanother"):
- request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
+ request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect(request.path)
else:
- request.user.add_message(msg)
+ request.user.message_set.create(message=msg)
return HttpResponseRedirect(post_url)
else:
# Add default data.
@@ -435,73 +284,80 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
errors = {}
# Populate the FormWrapper.
- form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline=True)
+ form = forms.FormWrapper(manipulator, new_data, errors)
- c = Context(request, {
+ c = template.RequestContext(request, {
'title': _('Add %s') % opts.verbose_name,
'form': form,
'is_popup': request.REQUEST.has_key('_popup'),
'show_delete': show_delete,
})
+
if object_id_override is not None:
c['object_id'] = object_id_override
- return render_change_form(opts, manipulator, app_label, c, add=True)
-add_stage = staff_member_required(add_stage)
+ return render_change_form(model, manipulator, c, add=True)
+add_stage = staff_member_required(never_cache(add_stage))
-def log_change_message(user, opts,manipulator,new_object):
- pk_value = getattr(new_object, opts.pk.column)
- # 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.')
- log.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), log.CHANGE, change_message)
+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
-def change_stage(request, app_label, module_name, object_id):
- mod, opts = _get_mod_opts(app_label, module_name)
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
raise PermissionDenied
+
if request.POST and request.POST.has_key("_saveasnew"):
- return add_stage(request, app_label, module_name, form_url='../add/')
+ return add_stage(request, app_label, model_name, form_url='../../add/')
+
try:
- manipulator = mod.ChangeManipulator(object_id)
+ manipulator = model.ChangeManipulator(object_id)
except ObjectDoesNotExist:
raise Http404
if request.POST:
new_data = request.POST.copy()
- if opts.has_field_type(meta.FileField):
+
+ 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 and not request.POST.has_key("_preview"):
+
+ if not errors:
new_object = manipulator.save(new_data)
- log_change_message(request.user,opts,manipulator,new_object)
- msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj':new_object}
- pk_value = getattr(new_object,opts.pk.attname)
+ 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, str(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.add_message(msg + ' ' + _("You may edit it again below."))
+ 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.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
+ request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
return HttpResponseRedirect("../%s/" % pk_value)
elif request.POST.has_key("_addanother"):
- request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
+ request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect("../add/")
else:
- request.user.add_message(msg)
+ request.user.message_set.create(message=msg)
return HttpResponseRedirect("../")
else:
# Populate new_data with a "flattened" version of the current data.
@@ -519,7 +375,7 @@ def change_stage(request, app_label, module_name, object_id):
errors = {}
# Populate the FormWrapper.
- form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline = True)
+ form = forms.FormWrapper(manipulator, new_data, errors)
form.original = manipulator.original_object
form.order_objects = []
@@ -528,20 +384,19 @@ def change_stage(request, app_label, module_name, object_id):
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_method_name_part())
+ related.get_accessor_name())
orig_list = func()
form.order_objects.extend(orig_list)
- c = Context(request, {
+ c = template.RequestContext(request, {
'title': _('Change %s') % opts.verbose_name,
'form': form,
'object_id': object_id,
'original': manipulator.original_object,
- 'is_popup' : request.REQUEST.has_key('_popup')
+ 'is_popup': request.REQUEST.has_key('_popup'),
})
-
- return render_change_form(opts,manipulator, app_label, c, change=True)
-change_stage = staff_member_required(change_stage)
+ 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
@@ -559,10 +414,10 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
- rel_opts_name = related.get_method_name_part()
- if isinstance(related.field.rel, meta.OneToOneRel):
+ rel_opts_name = related.get_accessor_name()
+ if isinstance(related.field.rel, models.OneToOneRel):
try:
- sub_obj = getattr(obj, 'get_%s' % rel_opts_name)()
+ sub_obj = getattr(obj, rel_opts_name)
except ObjectDoesNotExist:
pass
else:
@@ -579,12 +434,12 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
- (capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.module_name,
- getattr(sub_obj, related.opts.pk.attname), sub_obj), []])
+ (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, 'get_%s_list' % rel_opts_name)():
+ 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
@@ -593,7 +448,7 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
- (capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.module_name, getattr(sub_obj, related.opts.pk.attname), escape(str(sub_obj))), []])
+ (capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(str(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.
@@ -605,21 +460,21 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if related.opts in opts_seen:
continue
opts_seen.append(related.opts)
- rel_opts_name = related.get_method_name_part()
+ rel_opts_name = related.get_accessor_name()
has_related_objs = False
- for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)():
+ 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, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
- {'fieldname': related.field.name, 'name': related.opts.verbose_name, 'obj': escape(str(sub_obj))}, []])
+ {'fieldname': related.field.verbose_name, 'name': related.opts.verbose_name, 'obj': escape(str(sub_obj))}, []])
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [
- (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': related.field.name, 'name':related.opts.verbose_name}) + \
+ (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': related.field.verbose_name, 'name':related.opts.verbose_name}) + \
(' <a href="../../../../%s/%s/%s/">%s</a>' % \
- (related.opts.app_label, related.opts.module_name, getattr(sub_obj, related.opts.pk.attname), escape(str(sub_obj)))), []])
+ (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(str(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:
@@ -627,12 +482,16 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
-def delete_stage(request, app_label, module_name, object_id):
+def delete_stage(request, app_label, model_name, object_id):
import sets
- mod, opts = _get_mod_opts(app_label, module_name)
+ 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(mod, pk=object_id)
+ obj = get_object_or_404(model, pk=object_id)
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
@@ -645,28 +504,240 @@ def delete_stage(request, app_label, module_name, object_id):
raise PermissionDenied
obj_display = str(obj)
obj.delete()
- log.log_action(request.user.id, opts.get_content_type_id(), object_id, obj_display, log.DELETION)
- request.user.add_message(_('The %(name)s "%(obj)s" was deleted successfully.') % {'name':opts.verbose_name, 'obj':obj_display})
+ 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': opts.verbose_name, 'obj': obj_display})
return HttpResponseRedirect("../../")
- return render_to_response('admin/delete_confirmation', {
+ extra_context = {
"title": _("Are you sure?"),
"object_name": opts.verbose_name,
"object": obj,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
- }, context_instance=Context(request))
-delete_stage = staff_member_required(delete_stage)
+ "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, module_name, object_id):
- mod, opts = _get_mod_opts(app_label, module_name)
- action_list = log.get_list(object_id__exact=object_id, content_type__id__exact=opts.get_content_type_id(),
- order_by=("action_time",), select_related=True)
+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(mod, pk=object_id)
- return render_to_response('admin/object_history', {
+ obj = get_object_or_404(model, pk=object_id)
+ extra_context = {
'title': _('Change history: %s') % obj,
'action_list': action_list,
- 'module_name': capfirst(opts.verbose_name_plural),
+ 'module_name': capfirst(model._meta.verbose_name_plural),
'object': obj,
- }, context_instance=Context(request))
-history = staff_member_required(history)
+ }
+ 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):
+ def __init__(self, request, model):
+ self.model = model
+ self.opts = model._meta
+ self.lookup_opts = self.opts
+ self.manager = self.opts.admin.manager
+
+ # Get search parameters from the query string.
+ try:
+ self.page_num = int(request.GET.get(PAGE_VAR, 0))
+ except ValueError:
+ self.page_num = 0
+ self.show_all = request.GET.has_key(ALL_VAR)
+ self.is_popup = request.GET.has_key(IS_POPUP_VAR)
+ self.params = dict(request.GET.items())
+ if self.params.has_key(PAGE_VAR):
+ del self.params[PAGE_VAR]
+
+ self.order_field, self.order_type = self.get_ordering()
+ self.query = request.GET.get(SEARCH_VAR, '')
+ self.query_set = self.get_query_set()
+ self.get_results(request)
+ self.title = (self.is_popup and _('Select %s') % self.opts.verbose_name or _('Select %s to change') % self.opts.verbose_name)
+ self.filter_specs, self.has_filters = self.get_filters(request)
+ self.pk_attname = self.lookup_opts.pk.attname
+
+ def get_filters(self, request):
+ filter_specs = []
+ if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
+ filter_fields = [self.lookup_opts.get_field(field_name) \
+ for field_name in self.lookup_opts.admin.list_filter]
+ for f in filter_fields:
+ spec = FilterSpec.create(f, request, self.params)
+ if spec and spec.has_output():
+ filter_specs.append(spec)
+ return filter_specs, bool(filter_specs)
+
+ def get_query_string(self, new_params={}, remove=[]):
+ p = self.params.copy()
+ for r in remove:
+ for k in p.keys():
+ if k.startswith(r):
+ del p[k]
+ for k, v in new_params.items():
+ if p.has_key(k) and v is None:
+ del p[k]
+ elif v is not None:
+ p[k] = v
+ return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
+
+ def get_results(self, request):
+ paginator = ObjectPaginator(self.query_set, self.lookup_opts.admin.list_per_page)
+
+ # Get the number of objects, with admin filters applied.
+ try:
+ result_count = paginator.hits
+ # Naked except! Because we don't have any other way of validating
+ # "params". They might be invalid if the keyword arguments are
+ # incorrect, or if the values are not in the correct type (which would
+ # result in a database error).
+ except:
+ raise IncorrectLookupParameters
+
+ # Get the total number of objects, with no admin filters applied.
+ # Perform a slight optimization: Check to see whether any filters were
+ # given. If not, use paginator.hits to calculate the number of objects,
+ # because we've already done paginator.hits and the value is cached.
+ if isinstance(self.query_set._filters, models.Q) and not self.query_set._filters.kwargs:
+ full_result_count = result_count
+ else:
+ full_result_count = self.manager.count()
+
+ can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
+ multi_page = result_count > self.lookup_opts.admin.list_per_page
+
+ # Get the list of objects to display on this page.
+ if (self.show_all and can_show_all) or not multi_page:
+ result_list = list(self.query_set)
+ else:
+ try:
+ result_list = paginator.get_page(self.page_num)
+ except InvalidPage:
+ result_list = ()
+
+ self.result_count = result_count
+ self.full_result_count = full_result_count
+ self.result_list = result_list
+ self.can_show_all = can_show_all
+ self.multi_page = multi_page
+ self.paginator = paginator
+
+ def get_ordering(self):
+ lookup_opts, params = self.lookup_opts, self.params
+ # For ordering, first check the "ordering" parameter in the admin options,
+ # then check the object's default ordering. If neither of those exist,
+ # order descending by ID by default. Finally, look for manually-specified
+ # ordering from the query string.
+ ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
+
+ # Normalize it to new-style ordering.
+ ordering = handle_legacy_orderlist(ordering)
+
+ if ordering[0].startswith('-'):
+ order_field, order_type = ordering[0][1:], 'desc'
+ else:
+ order_field, order_type = ordering[0], 'asc'
+ if params.has_key(ORDER_VAR):
+ try:
+ try:
+ f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
+ except models.FieldDoesNotExist:
+ pass
+ else:
+ if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
+ order_field = f.name
+ except (IndexError, ValueError):
+ pass # Invalid ordering specified. Just use the default.
+ if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
+ order_type = params[ORDER_TYPE_VAR]
+ return order_field, order_type
+
+ def get_query_set(self):
+ qs = self.manager.get_query_set()
+ 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):
+ if lookup_params.has_key(i):
+ del lookup_params[i]
+
+ # Apply lookup parameters from the query string.
+ qs = qs.filter(**lookup_params)
+
+ # Use select_related() if one of the list_display options is a field
+ # with a relationship.
+ if self.lookup_opts.admin.list_select_related:
+ qs = qs.select_related()
+ else:
+ for field_name in self.lookup_opts.admin.list_display:
+ try:
+ f = self.lookup_opts.get_field(field_name)
+ except models.FieldDoesNotExist:
+ pass
+ else:
+ if isinstance(f.rel, models.ManyToOneRel):
+ qs = qs.select_related()
+ break
+
+ # Calculate lookup_order_field.
+ # If the order-by field is a field with a relationship, order by the
+ # value in the related table.
+ lookup_order_field = self.order_field
+ try:
+ f = self.lookup_opts.get_field(self.order_field, many_to_many=False)
+ except models.FieldDoesNotExist:
+ pass
+ else:
+ if isinstance(f.rel, models.OneToOneRel):
+ # For OneToOneFields, don't try to order by the related object's ordering criteria.
+ pass
+ elif isinstance(f.rel, models.ManyToOneRel):
+ rel_ordering = f.rel.to._meta.ordering and f.rel.to._meta.ordering[0] or f.rel.to._meta.pk.column
+ lookup_order_field = '%s.%s' % (f.rel.to._meta.db_table, rel_ordering)
+
+ # Set ordering.
+ qs = qs.order_by((self.order_type == 'desc' and '-' or '') + lookup_order_field)
+
+ # Apply keyword searches.
+ if self.lookup_opts.admin.search_fields and self.query:
+ for bit in self.query.split():
+ or_queries = [models.Q(**{'%s__icontains' % field_name: bit}) for field_name in self.lookup_opts.admin.search_fields]
+ other_qs = QuerySet(self.model)
+ other_qs = other_qs.filter(reduce(operator.or_, or_queries))
+ qs = qs & other_qs
+
+ if self.opts.one_to_one_field:
+ qs = qs.filter(**self.opts.one_to_one_field.rel.limit_choices_to)
+
+ return qs
+
+ def url_for_result(self, result):
+ 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:
+ return HttpResponseRedirect(request.path)
+ 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))
diff --git a/django/contrib/admin/views/template.py b/django/contrib/admin/views/template.py
index 3effd57c10..f73b9e4218 100644
--- a/django/contrib/admin/views/template.py
+++ b/django/contrib/admin/views/template.py
@@ -1,9 +1,9 @@
from django.contrib.admin.views.decorators import staff_member_required
-from django.core import formfields, validators
-from django.core import template
-from django.core.template import loader
-from django.core.extensions import DjangoContext, render_to_response
-from django.models.core import sites
+from django.core import validators
+from django import template, forms
+from django.template import loader
+from django.shortcuts import render_to_response
+from django.contrib.sites.models import Site
from django.conf import settings
def template_validator(request):
@@ -23,19 +23,19 @@ def template_validator(request):
errors = manipulator.get_validation_errors(new_data)
if not errors:
request.user.add_message('The template is valid.')
- return render_to_response('admin/template_validator', {
+ return render_to_response('admin/template_validator.html', {
'title': 'Template validator',
- 'form': formfields.FormWrapper(manipulator, new_data, errors),
- }, context_instance=DjangoContext(request))
+ 'form': forms.FormWrapper(manipulator, new_data, errors),
+ }, context_instance=template.RequestContext(request))
template_validator = staff_member_required(template_validator)
-class TemplateValidator(formfields.Manipulator):
+class TemplateValidator(forms.Manipulator):
def __init__(self, settings_modules):
self.settings_modules = settings_modules
- site_list = sites.get_in_bulk(settings_modules.keys()).values()
+ site_list = Site.objects.get_in_bulk(settings_modules.keys()).values()
self.fields = (
- formfields.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
- formfields.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
+ forms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
+ forms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
)
def isValidTemplate(self, field_data, all_data):