summaryrefslogtreecommitdiff
path: root/django/contrib/admin
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2005-10-19 01:09:05 +0000
committerAdrian Holovaty <adrian@holovaty.com>2005-10-19 01:09:05 +0000
commitf07e5d4f5df5ca9ca3366d7ecc4b01c490c13198 (patch)
tree1b73d89471554d058cb46bc13d17bd3687c638fa /django/contrib/admin
parentfd3d579179581b1fa460e13115471d58fec0c8f6 (diff)
Fixed #627 -- BACKWARDS-INCOMPATIBLE CHANGE. Admin is now an app, not a middleware. See BackwardsIncompatibleChanges for a full list of changes and information on how to update your code.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@948 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/contrib/admin')
-rw-r--r--django/contrib/admin/media/css/base.css2
-rw-r--r--django/contrib/admin/media/css/changelists.css44
-rw-r--r--django/contrib/admin/media/css/global.css335
-rw-r--r--django/contrib/admin/media/img/admin/arrow-down.gifbin0 -> 80 bytes
-rw-r--r--django/contrib/admin/media/img/admin/arrow-up.gifbin0 -> 838 bytes
-rw-r--r--django/contrib/admin/media/img/admin/changelist-bg.gifbin0 -> 58 bytes
-rw-r--r--django/contrib/admin/media/img/admin/chooser-bg.gifbin0 -> 199 bytes
-rw-r--r--django/contrib/admin/media/img/admin/chooser_stacked-bg.gifbin0 -> 212 bytes
-rw-r--r--django/contrib/admin/media/img/admin/default-bg-reverse.gifbin0 -> 843 bytes
-rw-r--r--django/contrib/admin/media/img/admin/default-bg.gifbin0 -> 844 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon-no.gifbin0 -> 176 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon-yes.gifbin0 -> 299 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_addlink.gifbin0 -> 119 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_alert.gifbin0 -> 145 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_calendar.gifbin0 -> 192 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_changelink.gifbin0 -> 119 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_clock.gifbin0 -> 390 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_deletelink.gifbin0 -> 181 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_error.gifbin0 -> 319 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_searchbox.pngbin0 -> 667 bytes
-rw-r--r--django/contrib/admin/media/img/admin/icon_success.gifbin0 -> 341 bytes
-rw-r--r--django/contrib/admin/media/img/admin/nav-bg-grabber.gifbin0 -> 116 bytes
-rw-r--r--django/contrib/admin/media/img/admin/nav-bg-reverse.gifbin0 -> 186 bytes
-rw-r--r--django/contrib/admin/media/img/admin/nav-bg.gifbin0 -> 273 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector-add.gifbin0 -> 606 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector-addall.gifbin0 -> 358 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector-remove.gifbin0 -> 398 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector-removeall.gifbin0 -> 355 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector-search.gifbin0 -> 552 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector_stacked-add.gifbin0 -> 612 bytes
-rw-r--r--django/contrib/admin/media/img/admin/selector_stacked-remove.gifbin0 -> 401 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tool-left.gifbin0 -> 197 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tool-left_over.gifbin0 -> 203 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tool-right.gifbin0 -> 198 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tool-right_over.gifbin0 -> 200 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tooltag-add.gifbin0 -> 932 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tooltag-add_over.gifbin0 -> 336 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tooltag-arrowright.gifbin0 -> 351 bytes
-rw-r--r--django/contrib/admin/media/img/admin/tooltag-arrowright_over.gifbin0 -> 354 bytes
-rw-r--r--django/contrib/admin/media/js/SelectBox.js109
-rw-r--r--django/contrib/admin/media/js/SelectFilter.js81
-rw-r--r--django/contrib/admin/media/js/SelectFilter2.js114
-rw-r--r--django/contrib/admin/media/js/admin/CollapsedFieldsets.js85
-rw-r--r--django/contrib/admin/media/js/admin/DateTimeShortcuts.js201
-rw-r--r--django/contrib/admin/media/js/admin/RelatedObjectLookups.js40
-rw-r--r--django/contrib/admin/media/js/admin/ordering.js137
-rw-r--r--django/contrib/admin/media/js/calendar.js139
-rw-r--r--django/contrib/admin/media/js/core.js138
-rw-r--r--django/contrib/admin/media/js/dateparse.js227
-rw-r--r--django/contrib/admin/media/js/getElementsBySelector.js167
-rw-r--r--django/contrib/admin/media/js/timeparse.js94
-rw-r--r--django/contrib/admin/media/js/urlify.js16
-rw-r--r--django/contrib/admin/templates/admin/404.html11
-rw-r--r--django/contrib/admin/templates/admin/500.html11
-rw-r--r--django/contrib/admin/templates/admin/base.html50
-rw-r--r--django/contrib/admin/templates/admin/base_site.html10
-rw-r--r--django/contrib/admin/templates/admin/delete_confirmation.html21
-rw-r--r--django/contrib/admin/templates/admin/index.html65
-rw-r--r--django/contrib/admin/templates/admin/login.html31
-rw-r--r--django/contrib/admin/templates/admin/object_history.html42
-rw-r--r--django/contrib/admin/templates/admin/template_validator.html31
-rw-r--r--django/contrib/admin/templates/admin_doc/bookmarklets.html30
-rw-r--r--django/contrib/admin/templates/admin_doc/index.html28
-rw-r--r--django/contrib/admin/templates/admin_doc/missing_docutils.html17
-rw-r--r--django/contrib/admin/templates/admin_doc/model_detail.html46
-rw-r--r--django/contrib/admin/templates/admin_doc/model_index.html44
-rw-r--r--django/contrib/admin/templates/admin_doc/template_detail.html21
-rw-r--r--django/contrib/admin/templates/admin_doc/template_filter_index.html50
-rw-r--r--django/contrib/admin/templates/admin_doc/template_tag_index.html49
-rw-r--r--django/contrib/admin/templates/admin_doc/view_detail.html26
-rw-r--r--django/contrib/admin/templates/admin_doc/view_index.html44
-rw-r--r--django/contrib/admin/templates/registration/logged_out.html11
-rw-r--r--django/contrib/admin/templates/registration/password_change_done.html13
-rw-r--r--django/contrib/admin/templates/registration/password_change_form.html25
-rw-r--r--django/contrib/admin/templates/registration/password_reset_done.html13
-rw-r--r--django/contrib/admin/templates/registration/password_reset_email.html14
-rw-r--r--django/contrib/admin/templates/registration/password_reset_form.html18
-rw-r--r--django/contrib/admin/templatetags/adminapplist.py57
-rw-r--r--django/contrib/admin/templatetags/adminmedia.py17
-rw-r--r--django/contrib/admin/templatetags/log.py51
-rw-r--r--django/contrib/admin/urls/admin.py57
-rw-r--r--django/contrib/admin/views/main.py12
-rw-r--r--django/contrib/admin/views/template.py2
83 files changed, 2839 insertions, 7 deletions
diff --git a/django/contrib/admin/media/css/base.css b/django/contrib/admin/media/css/base.css
new file mode 100644
index 0000000000..d50e5d43f5
--- /dev/null
+++ b/django/contrib/admin/media/css/base.css
@@ -0,0 +1,2 @@
+@import url(global.css);
+@import url(changelists.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
new file mode 100644
index 0000000000..7ff59c5e6b
--- /dev/null
+++ b/django/contrib/admin/media/css/changelists.css
@@ -0,0 +1,44 @@
+/*
+ DJANGO Admin Changelist Styles
+ by Wilson Miner wilson@lawrence.com
+ Copyright (c) 2005 Lawrence Journal-World
+*/
+
+#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; }
+#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; }
+
+/* CHANGELIST TABLES */
+#changelist table thead th { white-space:nowrap; }
+#changelist table tbody td { border-left: 1px solid #ddd; }
+#changelist table tfoot { color: #666; }
+
+/* TOOLBAR */
+#changelist #toolbar { padding:3px; border-bottom:1px solid #ddd; background:#e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; color:#666; }
+#changelist #toolbar form input { font-size:11px; padding:1px 2px; }
+#changelist #toolbar form #searchbar { padding:2px; }
+#changelist #changelist-search img { vertical-align:middle; }
+
+/* FILTER COLUMN */
+#changelist-filter { position:absolute; top:0; right:0; z-index:1000; width:160px; border-left:1px solid #ddd; background:#efefef; margin:0; }
+#changelist-filter h2 { font-size:11px; padding:2px 5px; border-bottom:1px solid #ddd; }
+#changelist-filter h3 { font-size:12px; margin-bottom:0; }
+#changelist-filter ul { padding-left:0;margin-left:10px;_margin-right:-10px; }
+#changelist-filter li { list-style-type:none; margin-left:0; padding-left:0; }
+#changelist-filter a { color:#999; }
+#changelist-filter a:hover { color:#036; }
+#changelist-filter li.selected { border-left:5px solid #ccc; padding-left:5px;margin-left:-10px; }
+#changelist-filter li.selected a { color:#5b80b2 !important; }
+
+/* DATE DRILLDOWN */
+.change-list ul.toplinks { display:block; background:white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; border-top:1px solid white; float:left; padding:0 !important; margin:0 !important; width:100%; }
+.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; }
diff --git a/django/contrib/admin/media/css/global.css b/django/contrib/admin/media/css/global.css
new file mode 100644
index 0000000000..453998c363
--- /dev/null
+++ b/django/contrib/admin/media/css/global.css
@@ -0,0 +1,335 @@
+/*
+ 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; }
+
+/* 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; }
+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; }
+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; }
+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; }
+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; }
+
+/* PAGE STRUCTURE */
+#container { position:relative; width:100%; min-width:720px; }
+#header { text-align:left; min-height:55px; _height:55px; }
+#content { margin:10px 15px; }
+#content-main { float:left; }
+#content-related { float:right; }
+#footer { clear:both; padding:10px; }
+
+/* COLUMN TYPES */
+.colMS, .colM, .colSM, .colM #content-main, .colM #content-main .xfull { width:758px; } /* master site width for fixed-width pages */
+.colMS #content-main, .colSM #content-main, .colMS #content-main .xfull, .colSM #content-main .xfull { width:519px; } /* main column width for 2-column pages */
+.colMS #content-related, .colSM #content-related, .colSMS #content-related { width:220px; } /* sidebar column width */
+.colSM #content-related { float:left; } .colSM #content-main { float:right; } /* swaps left and right columns */
+.colSMS #content-main { width:298px; }
+.popup .colM { width:95%; }
+.popup #content-main, .flex #content-main, .flex .xfull { width:100% !important; } /* main column width for liquid-width pages */
+.popup .flex #content-main, .popup .colM #content-main { width:100% !important; }
+.subcol { float:left; width:46%; margin-right:15px; }
+
+/* WIDTHS */
+.x50 { width:50px; }
+.x75 { width:75px; }
+.x100 { width:100px; }
+.x150 { width:150px; }
+.x200 { width:200px; }
+.x250 { width:250px; }
+.x300 { width:300px; }
+.x400 { width:400px; }
+.x500 { width:500px; }
+
+/* HEADER */
+#header { background:#417690; color:#ffc; }
+#header a:link, #header a:visited { color:white; }
+#header a:hover { text-decoration:underline; }
+#branding { float:left; width:480px; }
+#branding h1 /* client name */ { padding:8px 0 0 10px; margin:0; font-size:18px; font-weight:normal; color:#f4f379; }
+#branding h2 /* site name */ { font-size:14px; padding:0 0 8px 10px; margin:0; font-weight:normal; color:#ffc; }
+#user-tools { font-size:11px; padding:8px 8px 0 5px; text-align:right; }
+
+
+/* SIDEBAR */
+#content-related h3 { font-size:12px; color:#666; margin-bottom:3px; }
+#content-related h4 { font-size:11px; }
+
+/* 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; }
+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 */
+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 .module table { width:100%; }
+
+/* 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; }
+textarea { vertical-align:top !important; }
+input[type=checkbox], input[type=radio] { border:none; }
+
+/* FORM BUTTONS */
+input[type=submit], input[type=button], .submit-row input { background:white url(../img/admin/nav-bg.gif) bottom repeat-x; padding:3px; }
+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; }
+
+/* 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; }
+ul.errorlist { margin:0 !important; padding:0 !important; }
+.errorlist li { font-size:12px !important; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border:1px solid red; color:white; background:red url(../img/admin/icon_alert.gif) 5px .3em no-repeat; }
+td ul.errorlist { margin:0 !important; padding:0 !important; }
+td ul.errorlist li { margin:0 !important; }
+.error { background:#ffc; }
+.error input, .error select { border:1px solid red; }
+
+/* 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 50% 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 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; }
+.object-tools a:hover, .object-tools li:hover a { background:#5b80b2 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat; }
+.object-tools a.viewsitelink, .object-tools a.golink { background:#999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat; padding-right:28px; }
+.object-tools a.viewsitelink:hover, .object-tools a.golink:hover { background:#5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat; }
+.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; 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; width: 10em; text-align: center; background:white; position:relative; }
+.clockbox { width:6em; }
+.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 { 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; }
diff --git a/django/contrib/admin/media/img/admin/arrow-down.gif b/django/contrib/admin/media/img/admin/arrow-down.gif
new file mode 100644
index 0000000000..a967b9fd55
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/arrow-down.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/arrow-up.gif b/django/contrib/admin/media/img/admin/arrow-up.gif
new file mode 100644
index 0000000000..3fe4851399
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/arrow-up.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/changelist-bg.gif b/django/contrib/admin/media/img/admin/changelist-bg.gif
new file mode 100644
index 0000000000..7f4699470a
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/changelist-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/chooser-bg.gif b/django/contrib/admin/media/img/admin/chooser-bg.gif
new file mode 100644
index 0000000000..30e83c2518
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/chooser-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/chooser_stacked-bg.gif b/django/contrib/admin/media/img/admin/chooser_stacked-bg.gif
new file mode 100644
index 0000000000..5d104b6d98
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/chooser_stacked-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/default-bg-reverse.gif b/django/contrib/admin/media/img/admin/default-bg-reverse.gif
new file mode 100644
index 0000000000..0873281e51
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/default-bg-reverse.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/default-bg.gif b/django/contrib/admin/media/img/admin/default-bg.gif
new file mode 100644
index 0000000000..003aeca59f
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/default-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon-no.gif b/django/contrib/admin/media/img/admin/icon-no.gif
new file mode 100644
index 0000000000..1b4ee58145
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon-no.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon-yes.gif b/django/contrib/admin/media/img/admin/icon-yes.gif
new file mode 100644
index 0000000000..7399282740
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon-yes.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_addlink.gif b/django/contrib/admin/media/img/admin/icon_addlink.gif
new file mode 100644
index 0000000000..ee70e1adba
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_addlink.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_alert.gif b/django/contrib/admin/media/img/admin/icon_alert.gif
new file mode 100644
index 0000000000..a1dde26254
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_alert.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_calendar.gif b/django/contrib/admin/media/img/admin/icon_calendar.gif
new file mode 100644
index 0000000000..7587b305a4
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_calendar.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_changelink.gif b/django/contrib/admin/media/img/admin/icon_changelink.gif
new file mode 100644
index 0000000000..e1b9afde65
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_changelink.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_clock.gif b/django/contrib/admin/media/img/admin/icon_clock.gif
new file mode 100644
index 0000000000..ff2d57e0a3
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_clock.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_deletelink.gif b/django/contrib/admin/media/img/admin/icon_deletelink.gif
new file mode 100644
index 0000000000..72523e3a3b
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_deletelink.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_error.gif b/django/contrib/admin/media/img/admin/icon_error.gif
new file mode 100644
index 0000000000..3730a00b26
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_error.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_searchbox.png b/django/contrib/admin/media/img/admin/icon_searchbox.png
new file mode 100644
index 0000000000..8ab579e526
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_searchbox.png
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/icon_success.gif b/django/contrib/admin/media/img/admin/icon_success.gif
new file mode 100644
index 0000000000..5cf90a15aa
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/icon_success.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/nav-bg-grabber.gif b/django/contrib/admin/media/img/admin/nav-bg-grabber.gif
new file mode 100644
index 0000000000..0a784fa769
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/nav-bg-grabber.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/nav-bg-reverse.gif b/django/contrib/admin/media/img/admin/nav-bg-reverse.gif
new file mode 100644
index 0000000000..f11029f90f
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/nav-bg-reverse.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/nav-bg.gif b/django/contrib/admin/media/img/admin/nav-bg.gif
new file mode 100644
index 0000000000..f8402b809d
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/nav-bg.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector-add.gif b/django/contrib/admin/media/img/admin/selector-add.gif
new file mode 100644
index 0000000000..50132d1c43
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector-add.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector-addall.gif b/django/contrib/admin/media/img/admin/selector-addall.gif
new file mode 100644
index 0000000000..d6e7c639bb
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector-addall.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector-remove.gif b/django/contrib/admin/media/img/admin/selector-remove.gif
new file mode 100644
index 0000000000..2b9b0a2ac3
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector-remove.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector-removeall.gif b/django/contrib/admin/media/img/admin/selector-removeall.gif
new file mode 100644
index 0000000000..5a4421926d
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector-removeall.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector-search.gif b/django/contrib/admin/media/img/admin/selector-search.gif
new file mode 100644
index 0000000000..6d5f4c7492
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector-search.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector_stacked-add.gif b/django/contrib/admin/media/img/admin/selector_stacked-add.gif
new file mode 100644
index 0000000000..7426169652
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector_stacked-add.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/selector_stacked-remove.gif b/django/contrib/admin/media/img/admin/selector_stacked-remove.gif
new file mode 100644
index 0000000000..60412cee19
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/selector_stacked-remove.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tool-left.gif b/django/contrib/admin/media/img/admin/tool-left.gif
new file mode 100644
index 0000000000..011490ff3a
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tool-left.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tool-left_over.gif b/django/contrib/admin/media/img/admin/tool-left_over.gif
new file mode 100644
index 0000000000..937e07bb1a
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tool-left_over.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tool-right.gif b/django/contrib/admin/media/img/admin/tool-right.gif
new file mode 100644
index 0000000000..cdc140cc59
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tool-right.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tool-right_over.gif b/django/contrib/admin/media/img/admin/tool-right_over.gif
new file mode 100644
index 0000000000..4db977e838
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tool-right_over.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tooltag-add.gif b/django/contrib/admin/media/img/admin/tooltag-add.gif
new file mode 100644
index 0000000000..8b53d49ae5
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tooltag-add.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tooltag-add_over.gif b/django/contrib/admin/media/img/admin/tooltag-add_over.gif
new file mode 100644
index 0000000000..bfc52f10de
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tooltag-add_over.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tooltag-arrowright.gif b/django/contrib/admin/media/img/admin/tooltag-arrowright.gif
new file mode 100644
index 0000000000..cdaaae77ed
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tooltag-arrowright.gif
Binary files differ
diff --git a/django/contrib/admin/media/img/admin/tooltag-arrowright_over.gif b/django/contrib/admin/media/img/admin/tooltag-arrowright_over.gif
new file mode 100644
index 0000000000..7163189604
--- /dev/null
+++ b/django/contrib/admin/media/img/admin/tooltag-arrowright_over.gif
Binary files differ
diff --git a/django/contrib/admin/media/js/SelectBox.js b/django/contrib/admin/media/js/SelectBox.js
new file mode 100644
index 0000000000..af8de204cb
--- /dev/null
+++ b/django/contrib/admin/media/js/SelectBox.js
@@ -0,0 +1,109 @@
+var SelectBox = {
+ cache: new Object(),
+ init: function(id) {
+ var box = document.getElementById(id);
+ var node;
+ SelectBox.cache[id] = new Array();
+ var cache = SelectBox.cache[id];
+ for (var i = 0; (node = box.options[i]); i++) {
+ cache.push({ value: node.value, text: node.text, displayed: 1 });
+ }
+ },
+ redisplay: function(id) {
+ // Repopulate HTML select box from cache
+ var box = document.getElementById(id);
+ box.options.length = 0; // clear all options
+ for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {
+ var node = SelectBox.cache[id][i];
+ if (node.displayed) {
+ box.options[box.options.length] = new Option(node.text, node.value, false, false);
+ }
+ }
+ },
+ filter: function(id, text) {
+ // Redisplay the HTML select box, displaying only the choices containing ALL
+ // the words in text. (It's an AND search.)
+ var tokens = text.toLowerCase().split(/\s+/);
+ var node, token;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ node.displayed = 1;
+ for (var j = 0; (token = tokens[j]); j++) {
+ if (node.text.toLowerCase().indexOf(token) == -1) {
+ node.displayed = 0;
+ }
+ }
+ }
+ SelectBox.redisplay(id);
+ },
+ delete_from_cache: function(id, value) {
+ var node, delete_index = null;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ if (node.value == value) {
+ delete_index = i;
+ break;
+ }
+ }
+ var j = SelectBox.cache[id].length - 1;
+ for (var i = delete_index; i < j; i++) {
+ SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
+ }
+ SelectBox.cache[id].length--;
+ },
+ add_to_cache: function(id, option) {
+ SelectBox.cache[id].push({ value: option.value, text: option.text, displayed: 1 });
+ },
+ cache_contains: function(id, value) {
+ // Check if an item is contained in the cache
+ var node;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ if (node.value == value) {
+ return true;
+ }
+ }
+ return false;
+ },
+ move: function(from, to) {
+ var from_box = document.getElementById(from);
+ var to_box = document.getElementById(to);
+ var option;
+ for (var i = 0; (option = from_box.options[i]); i++) {
+ if (option.selected && SelectBox.cache_contains(from, option.value)) {
+ SelectBox.add_to_cache(to, { value: option.value, text: option.text, displayed: 1 });
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ move_all: function(from, to) {
+ var from_box = document.getElementById(from);
+ var to_box = document.getElementById(to);
+ var option;
+ for (var i = 0; (option = from_box.options[i]); i++) {
+ SelectBox.add_to_cache(to, { value: option.value, text: option.text, displayed: 1 });
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ sort: function(id) {
+ SelectBox.cache[id].sort( function(a, b) {
+ a = a.text.toLowerCase();
+ b = b.text.toLowerCase();
+ try {
+ if (a > b) return 1;
+ if (a < b) return -1;
+ }
+ catch (e) {
+ // silently fail on IE 'unknown' exception
+ }
+ return 0;
+ } );
+ },
+ select_all: function(id) {
+ var box = document.getElementById(id);
+ for (var i = 0; i < box.options.length; i++) {
+ box.options[i].selected = 'selected';
+ }
+ }
+}
diff --git a/django/contrib/admin/media/js/SelectFilter.js b/django/contrib/admin/media/js/SelectFilter.js
new file mode 100644
index 0000000000..0501920608
--- /dev/null
+++ b/django/contrib/admin/media/js/SelectFilter.js
@@ -0,0 +1,81 @@
+/*
+SelectFilter - Turns a multiple-select box into a filter interface.
+
+Requires SelectBox.js and addevent.js.
+*/
+
+function findForm(node) {
+ // returns the node of the form containing the given node
+ if (node.tagName.toLowerCase() != 'form') {
+ return findForm(node.parentNode);
+ }
+ return node;
+}
+
+var SelectFilter = {
+ init: function(field_id) {
+ var from_box = document.getElementById(field_id);
+ from_box.id += '_from'; // change its ID
+ // Create the INPUT input box
+ var input_box = document.createElement('input');
+ input_box.id = field_id + '_input';
+ input_box.setAttribute('type', 'text');
+ from_box.parentNode.insertBefore(input_box, from_box);
+ from_box.parentNode.insertBefore(document.createElement('br'), input_box.nextSibling);
+ // Create the TO box
+ var to_box = document.createElement('select');
+ to_box.id = field_id + '_to';
+ to_box.setAttribute('multiple', 'multiple');
+ to_box.setAttribute('size', from_box.size);
+ from_box.parentNode.insertBefore(to_box, from_box.nextSibling);
+ to_box.setAttribute('name', from_box.getAttribute('name'));
+ from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
+ // Give the filters a CSS hook
+ from_box.setAttribute('class', 'filtered');
+ to_box.setAttribute('class', 'filtered');
+ // Set up the JavaScript event handlers for the select box filter interface
+ addEvent(input_box, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
+ addEvent(input_box, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
+ addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
+ addEvent(from_box, 'focus', function() { input_box.focus(); });
+ addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
+ addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
+ SelectBox.init(field_id + '_from');
+ SelectBox.init(field_id + '_to');
+ // Move selected from_box options to to_box
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ },
+ filter_key_up: function(event, field_id) {
+ from = document.getElementById(field_id + '_from');
+ // don't submit form if user pressed Enter
+ if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
+ from.selectedIndex = 0;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = 0;
+ return false;
+ }
+ var temp = from.selectedIndex;
+ SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
+ from.selectedIndex = temp;
+ return true;
+ },
+ filter_key_down: function(event, field_id) {
+ from = document.getElementById(field_id + '_from');
+ // right arrow -- move across
+ if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
+ var old_index = from.selectedIndex;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
+ return false;
+ }
+ // down arrow -- wrap around
+ if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
+ from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
+ }
+ // up arrow -- wrap around
+ if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
+ from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
+ }
+ return true;
+ }
+}
diff --git a/django/contrib/admin/media/js/SelectFilter2.js b/django/contrib/admin/media/js/SelectFilter2.js
new file mode 100644
index 0000000000..380ae72c91
--- /dev/null
+++ b/django/contrib/admin/media/js/SelectFilter2.js
@@ -0,0 +1,114 @@
+/*
+SelectFilter2 - Turns a multiple-select box into a filter interface.
+
+Different than SelectFilter because this is coupled to the admin framework.
+
+Requires core.js, SelectBox.js and addevent.js.
+*/
+
+function findForm(node) {
+ // returns the node of the form containing the given node
+ if (node.tagName.toLowerCase() != 'form') {
+ return findForm(node.parentNode);
+ }
+ return node;
+}
+
+var SelectFilter = {
+ init: function(field_id, field_name, is_stacked, admin_media_prefix) {
+ var from_box = document.getElementById(field_id);
+ from_box.id += '_from'; // change its ID
+ from_box.className = 'filtered';
+
+ // Remove <p class="info">, because it just gets in the way.
+ var ps = from_box.parentNode.getElementsByTagName('p');
+ for (var i=0; i<ps.length; i++) {
+ from_box.parentNode.removeChild(ps[i]);
+ }
+
+ // <div class="selector"> or <div class="selector stacked">
+ var selector_div = quickElement('div', from_box.parentNode);
+ selector_div.className = is_stacked ? 'selector stacked' : 'selector';
+
+ // <div class="selector-available">
+ var selector_available = quickElement('div', selector_div, '');
+ selector_available.className = 'selector-available';
+ quickElement('h2', selector_available, 'Available ' + field_name);
+ var filter_p = quickElement('p', selector_available, '');
+ filter_p.className = 'selector-filter';
+ quickElement('img', filter_p, '', 'src', admin_media_prefix + 'img/admin/selector-search.gif');
+ filter_p.appendChild(document.createTextNode(' '));
+ var filter_input = quickElement('input', filter_p, '', 'type', 'text');
+ filter_input.id = field_id + '_input';
+ selector_available.appendChild(from_box);
+ var choose_all = quickElement('a', selector_available, 'Choose all', 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); })()');
+ choose_all.className = 'selector-chooseall';
+
+ // <ul class="selector-chooser">
+ var selector_chooser = quickElement('ul', selector_div, '');
+ selector_chooser.className = 'selector-chooser';
+ var add_link = quickElement('a', quickElement('li', selector_chooser, ''), 'Add', 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to");})()');
+ add_link.className = 'selector-add';
+ var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), 'Remove', 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from");})()');
+ remove_link.className = 'selector-remove';
+
+ // <div class="selector-chosen">
+ var selector_chosen = quickElement('div', selector_div, '');
+ selector_chosen.className = 'selector-chosen';
+ quickElement('h2', selector_chosen, 'Chosen ' + field_name);
+ var selector_filter = quickElement('p', selector_chosen, 'Select your choice(s) and click ');
+ selector_filter.className = 'selector-filter';
+ quickElement('img', selector_filter, '', 'src', admin_media_prefix + 'img/admin/selector-add.gif', 'alt', 'Add');
+ var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
+ to_box.className = 'filtered';
+ var clear_all = quickElement('a', selector_chosen, 'Clear all', 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from");})()');
+ clear_all.className = 'selector-clearall';
+
+ from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
+
+ // Set up the JavaScript event handlers for the select box filter interface
+ addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
+ addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
+ addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
+ addEvent(from_box, 'focus', function() { filter_input.focus(); });
+ addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
+ addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
+ SelectBox.init(field_id + '_from');
+ SelectBox.init(field_id + '_to');
+ // Move selected from_box options to to_box
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ },
+ filter_key_up: function(event, field_id) {
+ from = document.getElementById(field_id + '_from');
+ // don't submit form if user pressed Enter
+ if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
+ from.selectedIndex = 0;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = 0;
+ return false;
+ }
+ var temp = from.selectedIndex;
+ SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
+ from.selectedIndex = temp;
+ return true;
+ },
+ filter_key_down: function(event, field_id) {
+ from = document.getElementById(field_id + '_from');
+ // right arrow -- move across
+ if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
+ var old_index = from.selectedIndex;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
+ return false;
+ }
+ // down arrow -- wrap around
+ if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
+ from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
+ }
+ // up arrow -- wrap around
+ if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
+ from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
+ }
+ return true;
+ }
+}
diff --git a/django/contrib/admin/media/js/admin/CollapsedFieldsets.js b/django/contrib/admin/media/js/admin/CollapsedFieldsets.js
new file mode 100644
index 0000000000..0b3ad9e421
--- /dev/null
+++ b/django/contrib/admin/media/js/admin/CollapsedFieldsets.js
@@ -0,0 +1,85 @@
+// Finds all fieldsets with class="collapse", collapses them, and gives each
+// one a "Show" link that uncollapses it. The "Show" link becomes a "Hide"
+// link when the fieldset is visible.
+
+function findForm(node) {
+ // returns the node of the form containing the given node
+ if (node.tagName.toLowerCase() != 'form') {
+ return findForm(node.parentNode);
+ }
+ return node;
+}
+
+var CollapsedFieldsets = {
+ collapse_re: /\bcollapse\b/, // Class of fieldsets that should be dealt with.
+ collapsed_re: /\bcollapsed\b/, // Class that fieldsets get when they're hidden.
+ collapsed_class: 'collapsed',
+ init: function() {
+ var fieldsets = document.getElementsByTagName('fieldset');
+ var collapsed_seen = false;
+ for (var i = 0, fs; fs = fieldsets[i]; i++) {
+ // Collapse this fieldset if it has the correct class, and if it
+ // doesn't have any errors. (Collapsing shouldn't apply in the case
+ // of error messages.)
+ if (fs.className.match(CollapsedFieldsets.collapse_re) && !CollapsedFieldsets.fieldset_has_errors(fs)) {
+ collapsed_seen = true;
+ // Give it an additional class, used by CSS to hide it.
+ fs.className += ' ' + CollapsedFieldsets.collapsed_class;
+ // (<a id="fieldsetcollapser3" class="collapse-toggle" href="#">Show</a>)
+ var collapse_link = document.createElement('a');
+ collapse_link.className = 'collapse-toggle';
+ collapse_link.id = 'fieldsetcollapser' + i;
+ collapse_link.onclick = new Function('CollapsedFieldsets.show('+i+'); return false;');
+ collapse_link.href = '#';
+ collapse_link.innerHTML = 'Show';
+ var h2 = fs.getElementsByTagName('h2')[0];
+ h2.appendChild(document.createTextNode(' ('));
+ h2.appendChild(collapse_link);
+ h2.appendChild(document.createTextNode(')'));
+ }
+ }
+ if (collapsed_seen) {
+ // Expand all collapsed fieldsets when form is submitted.
+ addEvent(findForm(document.getElementsByTagName('fieldset')[0]), 'submit', function() { CollapsedFieldsets.uncollapse_all(); });
+ }
+ },
+ fieldset_has_errors: function(fs) {
+ // Returns true if any fields in the fieldset have validation errors.
+ var divs = fs.getElementsByTagName('div');
+ for (var i=0; i<divs.length; i++) {
+ if (divs[i].className.match(/\berror\b/)) {
+ return true;
+ }
+ }
+ return false;
+ },
+ show: function(fieldset_index) {
+ var fs = document.getElementsByTagName('fieldset')[fieldset_index];
+ // Remove the class name that causes the "display: none".
+ fs.className = fs.className.replace(CollapsedFieldsets.collapsed_re, '');
+ // Toggle the "Show" link to a "Hide" link
+ var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
+ collapse_link.onclick = new Function('CollapsedFieldsets.hide('+fieldset_index+'); return false;');
+ collapse_link.innerHTML = 'Hide';
+ },
+ hide: function(fieldset_index) {
+ var fs = document.getElementsByTagName('fieldset')[fieldset_index];
+ // Add the class name that causes the "display: none".
+ fs.className += ' ' + CollapsedFieldsets.collapsed_class;
+ // Toggle the "Hide" link to a "Show" link
+ var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
+ collapse_link.onclick = new Function('CollapsedFieldsets.show('+fieldset_index+'); return false;');
+ collapse_link.innerHTML = 'Show';
+ },
+
+ uncollapse_all: function() {
+ var fieldsets = document.getElementsByTagName('fieldset');
+ for (var i=0; i<fieldsets.length; i++) {
+ if (fieldsets[i].className.match(CollapsedFieldsets.collapsed_re)) {
+ CollapsedFieldsets.show(i);
+ }
+ }
+ }
+}
+
+addEvent(window, 'load', CollapsedFieldsets.init);
diff --git a/django/contrib/admin/media/js/admin/DateTimeShortcuts.js b/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
new file mode 100644
index 0000000000..5da6f2f48e
--- /dev/null
+++ b/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
@@ -0,0 +1,201 @@
+// Inserts shortcut buttons after all of the following:
+// <input type="text" class="vDateField">
+// <input type="text" class="vTimeField">
+
+var DateTimeShortcuts = {
+ calendars: [],
+ calendarInputs: [],
+ clockInputs: [],
+ calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
+ calendarDivName2: 'calendarin', // name of <div> that contains calendar
+ clockDivName: 'clockbox', // name of clock <div> that gets toggled
+ admin_media_prefix: '',
+ init: function() {
+ // Deduce admin_media_prefix by looking at the <script>s in the
+ // current document and finding the URL of *this* module.
+ var scripts = document.getElementsByTagName('script');
+ for (var i=0; i<scripts.length; i++) {
+ if (scripts[i].src.match(/DateTimeShortcuts/)) {
+ var idx = scripts[i].src.indexOf('js/admin/DateTimeShortcuts');
+ DateTimeShortcuts.admin_media_prefix = scripts[i].src.substring(0, idx);
+ break;
+ }
+ }
+
+ var inputs = document.getElementsByTagName('input');
+ for (i=0; i<inputs.length; i++) {
+ var inp = inputs[i];
+ if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
+ DateTimeShortcuts.addClock(inp);
+ }
+ else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
+ DateTimeShortcuts.addCalendar(inp);
+ }
+ }
+ },
+ // Add clock widget to a given field
+ addClock: function(inp) {
+ var num = DateTimeShortcuts.clockInputs.length;
+ DateTimeShortcuts.clockInputs[num] = inp;
+
+ // Shortcut links (clock icon and "Now" link)
+ var shortcuts_span = document.createElement('span');
+ inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
+ var now_link = document.createElement('a');
+ now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());");
+ now_link.appendChild(document.createTextNode('Now'));
+ var clock_link = document.createElement('a');
+ clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
+ quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_clock.gif', 'alt', 'Clock');
+ shortcuts_span.appendChild(document.createTextNode('\240'));
+ shortcuts_span.appendChild(now_link);
+ shortcuts_span.appendChild(document.createTextNode('\240|\240'));
+ shortcuts_span.appendChild(clock_link);
+
+ // Create clock link div
+ //
+ // Markup looks like:
+ // <div id="clockbox1" class="clockbox module">
+ // <h2>Choose a time</h2>
+ // <ul class="timelist">
+ // <li><a href="#">Now</a></li>
+ // <li><a href="#">Midnight</a></li>
+ // <li><a href="#">6 a.m.</a></li>
+ // <li><a href="#">Noon</a></li>
+ // </ul>
+ // <p class="calendar-cancel"><a href="#">Cancel</a></p>
+ // </div>
+
+ var clock_box = document.createElement('div');
+ clock_box.style.display = 'none';
+ clock_box.style.position = 'absolute';
+ clock_box.style.left = findPosX(clock_link) + 17 + 'px';
+ clock_box.style.top = findPosY(clock_link) - 30 + 'px';
+ clock_box.className = 'clockbox module';
+ clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
+ document.body.appendChild(clock_box);
+ addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);
+
+ quickElement('h2', clock_box, 'Choose a time');
+ time_list = quickElement('ul', clock_box, '', 'class', 'timelist');
+ quickElement("a", quickElement("li", time_list, ""), "Now", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());")
+ quickElement("a", quickElement("li", time_list, ""), "Midnight", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00');")
+ quickElement("a", quickElement("li", time_list, ""), "6 a.m.", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00');")
+ quickElement("a", quickElement("li", time_list, ""), "Noon", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00');")
+
+ cancel_p = quickElement('p', clock_box, '', 'class', 'calendar-cancel');
+ quickElement('a', cancel_p, 'Cancel', 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
+ },
+ openClock: function(num) {
+ document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'block';
+ addEvent(window, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
+ },
+ dismissClock: function(num) {
+ document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
+ window.onclick = null;
+ },
+ handleClockQuicklink: function(num, val) {
+ DateTimeShortcuts.clockInputs[num].value = val;
+ DateTimeShortcuts.dismissClock(num);
+ },
+ // Add calendar widget to a given field.
+ addCalendar: function(inp) {
+ var num = DateTimeShortcuts.calendars.length;
+
+ DateTimeShortcuts.calendarInputs[num] = inp;
+
+ // Shortcut links (calendar icon and "Today" link)
+ var shortcuts_span = document.createElement('span');
+ inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
+ var today_link = document.createElement('a');
+ today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
+ today_link.appendChild(document.createTextNode('Today'));
+ var cal_link = document.createElement('a');
+ cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
+ quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', 'Calendar');
+ shortcuts_span.appendChild(document.createTextNode('\240'));
+ shortcuts_span.appendChild(today_link);
+ shortcuts_span.appendChild(document.createTextNode('\240|\240'));
+ shortcuts_span.appendChild(cal_link);
+
+ // Create calendarbox div.
+ //
+ // Markup looks like:
+ //
+ // <div id="calendarbox3" class="calendarbox module">
+ // <h2>
+ // <a href="#" class="link-previous">&lsaquo;</a>
+ // <a href="#" class="link-next">&rsaquo;</a> February 2003
+ // </h2>
+ // <div class="calendar" id="calendarin3">
+ // <!-- (cal) -->
+ // </div>
+ // <div class="calendar-shortcuts">
+ // <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
+ // </div>
+ // <p class="calendar-cancel"><a href="#">Cancel</a></p>
+ // </div>
+ var cal_box = document.createElement('div');
+ cal_box.style.display = 'none';
+ cal_box.style.position = 'absolute';
+ cal_box.style.left = findPosX(cal_link) + 17 + 'px';
+ cal_box.style.top = findPosY(cal_link) - 75 + 'px';
+ cal_box.className = 'calendarbox module';
+ cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
+ document.body.appendChild(cal_box);
+ addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);
+
+ // next-prev links
+ var cal_nav = quickElement('div', cal_box, '');
+ quickElement('a', cal_nav, '<', 'class', 'calendarnav-previous', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
+ quickElement('a', cal_nav, '>', 'class', 'calendarnav-next', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
+ cal_box.appendChild(cal_nav);
+
+ // main box
+ var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
+ cal_main.className = 'calendar';
+ DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
+ DateTimeShortcuts.calendars[num].drawCurrent();
+
+ // calendar shortcuts
+ var shortcuts = quickElement('div', cal_box, '', 'class', 'calendar-shortcuts');
+ quickElement('a', shortcuts, 'Yesterday', 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
+ shortcuts.appendChild(document.createTextNode('\240|\240'));
+ quickElement('a', shortcuts, 'Today', 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
+ shortcuts.appendChild(document.createTextNode('\240|\240'));
+ quickElement('a', shortcuts, 'Tomorrow', 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');
+
+ // cancel bar
+ var cancel_p = quickElement('p', cal_box, '', 'class', 'calendar-cancel');
+ quickElement('a', cancel_p, 'Cancel', 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
+ },
+ openCalendar: function(num) {
+ document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'block';
+ addEvent(window, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
+ },
+ dismissCalendar: function(num) {
+ document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
+ },
+ drawPrev: function(num) {
+ DateTimeShortcuts.calendars[num].drawPreviousMonth();
+ },
+ drawNext: function(num) {
+ DateTimeShortcuts.calendars[num].drawNextMonth();
+ },
+ handleCalendarCallback: function(num) {
+ return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = y+'-'+m+'-'+d; document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
+ },
+ handleCalendarQuickLink: function(num, offset) {
+ var d = new Date();
+ d.setDate(d.getDate() + offset)
+ DateTimeShortcuts.calendarInputs[num].value = d.getISODate();
+ DateTimeShortcuts.dismissCalendar(num);
+ },
+ cancelEventPropagation: function(e) {
+ if (!e) var e = window.event;
+ e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+ }
+}
+
+addEvent(window, 'load', DateTimeShortcuts.init);
diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
new file mode 100644
index 0000000000..3788e048b2
--- /dev/null
+++ b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
@@ -0,0 +1,40 @@
+// Handles related-objects functionality: lookup link for raw_id_admin=True
+// and Add Another links.
+
+function showRelatedObjectLookupPopup(triggeringLink) {
+ var name = triggeringLink.id.replace(/^lookup_/, '');
+ var win = window.open(triggeringLink.href + '?pop=1', name, 'height=500,width=740,resizable=yes,scrollbars=yes');
+ win.focus();
+ return false;
+}
+
+function dismissRelatedLookupPopup(win, chosenId) {
+ var elem = document.getElementById(win.name);
+ if (elem.className.indexOf('vCommaSeparatedIntegerField') != -1 && elem.value) {
+ elem.value += ',' + chosenId;
+ } else {
+ document.getElementById(win.name).value = chosenId;
+ }
+ win.close();
+}
+
+function showAddAnotherPopup(triggeringLink) {
+ var name = triggeringLink.id.replace(/^add_/, '');
+ name = name.replace(/\./g, '___');
+ var win = window.open(triggeringLink.href + '?_popup=1', name, 'height=500,width=800,resizable=yes,scrollbars=yes');
+ win.focus();
+ return false;
+}
+
+function dismissAddAnotherPopup(win, newId, newRepr) {
+ var name = win.name.replace(/___/g, '.')
+ var elem = document.getElementById(name);
+ if (elem.nodeName == 'SELECT') {
+ var o = new Option(newRepr, newId);
+ elem.options[elem.options.length] = o
+ elem.selectedIndex = elem.length - 1;
+ } else if (elem.nodeName == 'INPUT') {
+ elem.value = newId;
+ }
+ win.close();
+}
diff --git a/django/contrib/admin/media/js/admin/ordering.js b/django/contrib/admin/media/js/admin/ordering.js
new file mode 100644
index 0000000000..fb0f5b007e
--- /dev/null
+++ b/django/contrib/admin/media/js/admin/ordering.js
@@ -0,0 +1,137 @@
+addEvent(window, 'load', reorder_init);
+
+var lis;
+var top = 90;
+var left = 545;
+var height = 30;
+
+function reorder_init() {
+ lis = document.getElementsBySelector('ul#orderthese li');
+ var input = document.getElementsBySelector('input[name=order_]')[0];
+ setOrder(input.value.split(','));
+ input.disabled = true;
+ draw();
+ // Now initialise the dragging behaviour
+ var limit = (lis.length - 1) * height;
+ for (var i = 0; i < lis.length; i++) {
+ var li = lis[i];
+ var img = document.getElementById('handle'+li.id);
+ li.style.zIndex = 1;
+ Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit);
+ li.onDragStart = startDrag;
+ li.onDragEnd = endDrag;
+ img.style.cursor = 'move';
+ }
+}
+
+function submitOrderForm() {
+ var inputOrder = document.getElementsBySelector('input[name=order_]')[0];
+ inputOrder.value = getOrder();
+ inputOrder.disabled=false;
+}
+
+function startDrag() {
+ this.style.zIndex = '10';
+ this.className = 'dragging';
+}
+
+function endDrag(x, y) {
+ this.style.zIndex = '1';
+ this.className = '';
+ // Work out how far along it has been dropped, using x co-ordinate
+ var oldIndex = this.index;
+ var newIndex = Math.round((y - 10 - top) / height);
+ // 'Snap' to the correct position
+ this.style.top = (10 + top + newIndex * height) + 'px';
+ this.index = newIndex;
+ moveItem(oldIndex, newIndex);
+}
+
+function moveItem(oldIndex, newIndex) {
+ // Swaps two items, adjusts the index and left co-ord for all others
+ if (oldIndex == newIndex) {
+ return; // Nothing to swap;
+ }
+ var direction, lo, hi;
+ if (newIndex > oldIndex) {
+ lo = oldIndex;
+ hi = newIndex;
+ direction = -1;
+ } else {
+ direction = 1;
+ hi = oldIndex;
+ lo = newIndex;
+ }
+ var lis2 = new Array(); // We will build the new order in this array
+ for (var i = 0; i < lis.length; i++) {
+ if (i < lo || i > hi) {
+ // Position of items not between the indexes is unaffected
+ lis2[i] = lis[i];
+ continue;
+ } else if (i == newIndex) {
+ lis2[i] = lis[oldIndex];
+ continue;
+ } else {
+ // Item is between the two indexes - move it along 1
+ lis2[i] = lis[i - direction];
+ }
+ }
+ // Re-index everything
+ reIndex(lis2);
+ lis = lis2;
+ draw();
+// document.getElementById('hiddenOrder').value = getOrder();
+ document.getElementsBySelector('input[name=order_]')[0].value = getOrder();
+}
+
+function reIndex(lis) {
+ for (var i = 0; i < lis.length; i++) {
+ lis[i].index = i;
+ }
+}
+
+function draw() {
+ for (var i = 0; i < lis.length; i++) {
+ var li = lis[i];
+ li.index = i;
+ li.style.position = 'absolute';
+ li.style.left = (10 + left) + 'px';
+ li.style.top = (10 + top + (i * height)) + 'px';
+ }
+}
+
+function getOrder() {
+ var order = new Array(lis.length);
+ for (var i = 0; i < lis.length; i++) {
+ order[i] = lis[i].id.substring(1, 100);
+ }
+ return order.join(',');
+}
+
+function setOrder(id_list) {
+ /* Set the current order to match the lsit of IDs */
+ var temp_lis = new Array();
+ for (var i = 0; i < id_list.length; i++) {
+ var id = 'p' + id_list[i];
+ temp_lis[temp_lis.length] = document.getElementById(id);
+ }
+ reIndex(temp_lis);
+ lis = temp_lis;
+ draw();
+}
+
+function addEvent(elm, evType, fn, useCapture)
+// addEvent and removeEvent
+// cross-browser event handling for IE5+, NS6 and Mozilla
+// By Scott Andrew
+{
+ if (elm.addEventListener){
+ elm.addEventListener(evType, fn, useCapture);
+ return true;
+ } else if (elm.attachEvent){
+ var r = elm.attachEvent("on"+evType, fn);
+ return r;
+ } else {
+ elm['on'+evType] = fn;
+ }
+}
diff --git a/django/contrib/admin/media/js/calendar.js b/django/contrib/admin/media/js/calendar.js
new file mode 100644
index 0000000000..ad1f0a9734
--- /dev/null
+++ b/django/contrib/admin/media/js/calendar.js
@@ -0,0 +1,139 @@
+/*
+calendar.js - Calendar functions by Adrian Holovaty
+*/
+
+function removeChildren(a) { // "a" is reference to an object
+ while (a.hasChildNodes()) a.removeChild(a.lastChild);
+}
+
+// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);
+function quickElement() {
+ var obj = document.createElement(arguments[0]);
+ if (arguments[2] != '' && arguments[2] != null) {
+ var textNode = document.createTextNode(arguments[2]);
+ obj.appendChild(textNode);
+ }
+ var len = arguments.length;
+ for (var i = 3; i < len; i += 2) {
+ obj.setAttribute(arguments[i], arguments[i+1]);
+ }
+ arguments[1].appendChild(obj);
+ return obj;
+}
+
+// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
+var CalendarNamespace = {
+ monthsOfYear: 'January February March April May June July August September October November December'.split(' '),
+ daysOfWeek: 'S M T W T F S'.split(' '),
+ isLeapYear: function(year) {
+ return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
+ },
+ getDaysInMonth: function(month,year) {
+ var days;
+ if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) {
+ days = 31;
+ }
+ else if (month==4 || month==6 || month==9 || month==11) {
+ days = 30;
+ }
+ else if (month==2 && CalendarNamespace.isLeapYear(year)) {
+ days = 29;
+ }
+ else {
+ days = 28;
+ }
+ return days;
+ },
+ draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999
+ month = parseInt(month);
+ year = parseInt(year);
+ var calDiv = document.getElementById(div_id);
+ removeChildren(calDiv);
+ var calTable = document.createElement('table');
+ quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year);
+ var tableBody = quickElement('tbody', calTable);
+
+ // Draw days-of-week header
+ var tableRow = quickElement('tr', tableBody);
+ for (var i = 0; i < 7; i++) {
+ quickElement('th', tableRow, CalendarNamespace.daysOfWeek[i]);
+ }
+
+ var startingPos = new Date(year, month-1, 1).getDay();
+ var days = CalendarNamespace.getDaysInMonth(month, year);
+
+ // Draw blanks before first of month
+ tableRow = quickElement('tr', tableBody);
+ for (var i = 0; i < startingPos; i++) {
+ quickElement('td', tableRow, ' ', 'bgcolor','#f3f3f3');
+ }
+
+ // Draw days of month
+ var currentDay = 1;
+ for (var i = startingPos; currentDay <= days; i++) {
+ if (i%7 == 0 && currentDay != 1) {
+ tableRow = quickElement('tr', tableBody);
+ }
+ var cell = quickElement('td', tableRow, '');
+ quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
+ currentDay++;
+ }
+
+ // Draw blanks after end of month (optional, but makes for valid code)
+ while (tableRow.childNodes.length < 7) {
+ quickElement('td', tableRow, ' ', 'bgcolor','#f3f3f3');
+ }
+
+ calDiv.appendChild(calTable);
+ }
+}
+
+// Calendar -- A calendar instance
+function Calendar(div_id, callback) {
+ // div_id (string) is the ID of the element in which the calendar will
+ // be displayed
+ // callback (string) is the name of a JavaScript function that will be
+ // called with the parameters (year, month, day) when a day in the
+ // calendar is clicked
+ this.div_id = div_id;
+ this.callback = callback;
+ this.today = new Date();
+ this.currentMonth = this.today.getMonth() + 1;
+ this.currentYear = this.today.getFullYear();
+ this.drawCurrent = function() {
+ CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback);
+ }
+ this.drawDate = function(month, year) {
+ this.currentMonth = month;
+ this.currentYear = year;
+ this.drawCurrent();
+ }
+ this.drawPreviousMonth = function() {
+ if (this.currentMonth == 1) {
+ this.currentMonth = 12;
+ this.currentYear--;
+ }
+ else {
+ this.currentMonth--;
+ }
+ this.drawCurrent();
+ }
+ this.drawNextMonth = function() {
+ if (this.currentMonth == 12) {
+ this.currentMonth = 1;
+ this.currentYear++;
+ }
+ else {
+ this.currentMonth++;
+ }
+ this.drawCurrent();
+ }
+ this.drawPreviousYear = function() {
+ this.currentYear--;
+ this.drawCurrent();
+ }
+ this.drawNextYear = function() {
+ this.currentYear++;
+ this.drawCurrent();
+ }
+}
diff --git a/django/contrib/admin/media/js/core.js b/django/contrib/admin/media/js/core.js
new file mode 100644
index 0000000000..62d74211de
--- /dev/null
+++ b/django/contrib/admin/media/js/core.js
@@ -0,0 +1,138 @@
+// Core javascript helper functions
+
+// Cross-browser event handlers.
+function addEvent(obj, evType, fn) {
+ if (obj.addEventListener) {
+ obj.addEventListener(evType, fn, false);
+ return true;
+ } else if (obj.attachEvent) {
+ var r = obj.attachEvent("on" + evType, fn);
+ return r;
+ } else {
+ return false;
+ }
+}
+
+function removeEvent(obj, evType, fn) {
+ if (obj.removeEventListener) {
+ obj.removeEventListener(evType, fn, false);
+ return true;
+ } else if (obj.detachEvent) {
+ obj.detachEvent("on" + evType, fn);
+ } else {
+ return false;
+ }
+}
+
+// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);
+function quickElement() {
+ var obj = document.createElement(arguments[0]);
+ if (arguments[2] != '' && arguments[2] != null) {
+ var textNode = document.createTextNode(arguments[2]);
+ obj.appendChild(textNode);
+ }
+ var len = arguments.length;
+ for (var i = 3; i < len; i += 2) {
+ obj.setAttribute(arguments[i], arguments[i+1]);
+ }
+ arguments[1].appendChild(obj);
+ return obj;
+}
+
+// ----------------------------------------------------------------------------
+// Cross-browser xmlhttp object
+// from http://jibbering.com/2002/4/httprequest.html
+// ----------------------------------------------------------------------------
+var xmlhttp;
+/*@cc_on @*/
+/*@if (@_jscript_version >= 5)
+ try {
+ xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (E) {
+ xmlhttp = false;
+ }
+ }
+@else
+ xmlhttp = false;
+@end @*/
+if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
+ xmlhttp = new XMLHttpRequest();
+}
+
+// ----------------------------------------------------------------------------
+// Find-position functions by PPK
+// See http://www.quirksmode.org/js/findpos.html
+// ----------------------------------------------------------------------------
+function findPosX(obj) {
+ var curleft = 0;
+ if (obj.offsetParent) {
+ while (obj.offsetParent) {
+ curleft += obj.offsetLeft
+ obj = obj.offsetParent;
+ }
+ } else if (obj.x) {
+ curleft += obj.x;
+ }
+ return curleft;
+}
+
+function findPosY(obj) {
+ var curtop = 0;
+ if (obj.offsetParent) {
+ while (obj.offsetParent) {
+ curtop += obj.offsetTop
+ obj = obj.offsetParent;
+ }
+ } else if (obj.y) {
+ curtop += obj.y;
+ }
+ return curtop;
+}
+
+//-----------------------------------------------------------------------------
+// Date object extensions
+// ----------------------------------------------------------------------------
+Date.prototype.getCorrectYear = function() {
+ // Date.getYear() is unreliable --
+ // see http://www.quirksmode.org/js/introdate.html#year
+ var y = this.getYear() % 100;
+ return (y < 38) ? y + 2000 : y + 1900;
+}
+
+Date.prototype.getTwoDigitMonth = function() {
+ return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
+}
+
+Date.prototype.getTwoDigitDate = function() {
+ return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
+}
+
+Date.prototype.getTwoDigitHour = function() {
+ return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
+}
+
+Date.prototype.getTwoDigitMinute = function() {
+ return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
+}
+
+Date.prototype.getISODate = function() {
+ return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate();
+}
+
+Date.prototype.getHourMinute = function() {
+ return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
+}
+
+// ----------------------------------------------------------------------------
+// String object extensions
+// ----------------------------------------------------------------------------
+String.prototype.pad_left = function(pad_length, pad_string) {
+ new_string = this;
+ for (var i = 0; new_string.length < pad_length; i++) {
+ new_string = pad_string + new_string;
+ }
+ return new_string;
+} \ No newline at end of file
diff --git a/django/contrib/admin/media/js/dateparse.js b/django/contrib/admin/media/js/dateparse.js
new file mode 100644
index 0000000000..cec36e41e0
--- /dev/null
+++ b/django/contrib/admin/media/js/dateparse.js
@@ -0,0 +1,227 @@
+/* 'Magic' date parsing, by Simon Willison (6th October 2003)
+ http://simon.incutio.com/archive/2003/10/06/betterDateInput
+ Adapted for 6newslawrence.com, 28th January 2004
+*/
+
+/* Finds the index of the first occurence of item in the array, or -1 if not found */
+Array.prototype.indexOf = function(item) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] == item) {
+ return i;
+ }
+ }
+ return -1;
+};
+/* Returns an array of items judged 'true' by the passed in test function */
+Array.prototype.filter = function(test) {
+ var matches = [];
+ for (var i = 0; i < this.length; i++) {
+ if (test(this[i])) {
+ matches[matches.length] = this[i];
+ }
+ }
+ return matches;
+};
+
+var monthNames = "January February March April May June July August September October November December".split(" ");
+var weekdayNames = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" ");
+
+/* Takes a string, returns the index of the month matching that string, throws
+ an error if 0 or more than 1 matches
+*/
+function parseMonth(month) {
+ var matches = monthNames.filter(function(item) {
+ return new RegExp("^" + month, "i").test(item);
+ });
+ if (matches.length == 0) {
+ throw new Error("Invalid month string");
+ }
+ if (matches.length > 1) {
+ throw new Error("Ambiguous month");
+ }
+ return monthNames.indexOf(matches[0]);
+}
+/* Same as parseMonth but for days of the week */
+function parseWeekday(weekday) {
+ var matches = weekdayNames.filter(function(item) {
+ return new RegExp("^" + weekday, "i").test(item);
+ });
+ if (matches.length == 0) {
+ throw new Error("Invalid day string");
+ }
+ if (matches.length > 1) {
+ throw new Error("Ambiguous weekday");
+ }
+ return weekdayNames.indexOf(matches[0]);
+}
+
+/* Array of objects, each has 're', a regular expression and 'handler', a
+ function for creating a date from something that matches the regular
+ expression. Handlers may throw errors if string is unparseable.
+*/
+var dateParsePatterns = [
+ // Today
+ { re: /^tod/i,
+ handler: function() {
+ return new Date();
+ }
+ },
+ // Tomorrow
+ { re: /^tom/i,
+ handler: function() {
+ var d = new Date();
+ d.setDate(d.getDate() + 1);
+ return d;
+ }
+ },
+ // Yesterday
+ { re: /^yes/i,
+ handler: function() {
+ var d = new Date();
+ d.setDate(d.getDate() - 1);
+ return d;
+ }
+ },
+ // 4th
+ { re: /^(\d{1,2})(st|nd|rd|th)?$/i,
+ handler: function(bits) {
+ var d = new Date();
+ d.setDate(parseInt(bits[1], 10));
+ return d;
+ }
+ },
+ // 4th Jan
+ { re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
+ handler: function(bits) {
+ var d = new Date();
+ d.setDate(parseInt(bits[1], 10));
+ d.setMonth(parseMonth(bits[2]));
+ return d;
+ }
+ },
+ // 4th Jan 2003
+ { re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
+ handler: function(bits) {
+ var d = new Date();
+ d.setDate(parseInt(bits[1], 10));
+ d.setMonth(parseMonth(bits[2]));
+ d.setYear(bits[3]);
+ return d;
+ }
+ },
+ // Jan 4th
+ { re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?$/i,
+ handler: function(bits) {
+ var d = new Date();
+ d.setDate(parseInt(bits[2], 10));
+ d.setMonth(parseMonth(bits[1]));
+ return d;
+ }
+ },
+ // Jan 4th 2003
+ { re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
+ handler: function(bits) {
+ var d = new Date();
+ d.setDate(parseInt(bits[2], 10));
+ d.setMonth(parseMonth(bits[1]));
+ d.setYear(bits[3]);
+ return d;
+ }
+ },
+ // next Tuesday - this is suspect due to weird meaning of "next"
+ { re: /^next (\w+)$/i,
+ handler: function(bits) {
+ var d = new Date();
+ var day = d.getDay();
+ var newDay = parseWeekday(bits[1]);
+ var addDays = newDay - day;
+ if (newDay <= day) {
+ addDays += 7;
+ }
+ d.setDate(d.getDate() + addDays);
+ return d;
+ }
+ },
+ // last Tuesday
+ { re: /^last (\w+)$/i,
+ handler: function(bits) {
+ throw new Error("Not yet implemented");
+ }
+ },
+ // mm/dd/yyyy (American style)
+ { re: /(\d{1,2})\/(\d{1,2})\/(\d{4})/,
+ handler: function(bits) {
+ var d = new Date();
+ d.setYear(bits[3]);
+ d.setDate(parseInt(bits[2], 10));
+ d.setMonth(parseInt(bits[1], 10) - 1); // Because months indexed from 0
+ return d;
+ }
+ },
+ // yyyy-mm-dd (ISO style)
+ { re: /(\d{4})-(\d{1,2})-(\d{1,2})/,
+ handler: function(bits) {
+ var d = new Date();
+ d.setYear(parseInt(bits[1]));
+ d.setDate(parseInt(bits[3], 10));
+ d.setMonth(parseInt(bits[2], 10) - 1);
+ return d;
+ }
+ },
+];
+
+function parseDateString(s) {
+ for (var i = 0; i < dateParsePatterns.length; i++) {
+ var re = dateParsePatterns[i].re;
+ var handler = dateParsePatterns[i].handler;
+ var bits = re.exec(s);
+ if (bits) {
+ return handler(bits);
+ }
+ }
+ throw new Error("Invalid date string");
+}
+
+function fmt00(x) {
+ // fmt00: Tags leading zero onto numbers 0 - 9.
+ // Particularly useful for displaying results from Date methods.
+ //
+ if (Math.abs(parseInt(x)) < 10){
+ x = "0"+ Math.abs(x);
+ }
+ return x;
+}
+
+function parseDateStringISO(s) {
+ try {
+ var d = parseDateString(s);
+ return d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' + fmt00(d.getDate())
+ }
+ catch (e) { return s; }
+}
+function magicDate(input) {
+ var messagespan = input.id + 'Msg';
+ try {
+ var d = parseDateString(input.value);
+ input.value = d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' +
+ fmt00(d.getDate());
+ input.className = '';
+ // Human readable date
+ if (document.getElementById(messagespan)) {
+ document.getElementById(messagespan).firstChild.nodeValue = d.toDateString();
+ document.getElementById(messagespan).className = 'normal';
+ }
+ }
+ catch (e) {
+ input.className = 'error';
+ var message = e.message;
+ // Fix for IE6 bug
+ if (message.indexOf('is null or not an object') > -1) {
+ message = 'Invalid date string';
+ }
+ if (document.getElementById(messagespan)) {
+ document.getElementById(messagespan).firstChild.nodeValue = message;
+ document.getElementById(messagespan).className = 'error';
+ }
+ }
+}
diff --git a/django/contrib/admin/media/js/getElementsBySelector.js b/django/contrib/admin/media/js/getElementsBySelector.js
new file mode 100644
index 0000000000..ae6d387a91
--- /dev/null
+++ b/django/contrib/admin/media/js/getElementsBySelector.js
@@ -0,0 +1,167 @@
+/* document.getElementsBySelector(selector)
+ - returns an array of element objects from the current document
+ matching the CSS selector. Selectors can contain element names,
+ class names and ids and can be nested. For example:
+
+ elements = document.getElementsBySelect('div#main p a.external')
+
+ Will return an array of all 'a' elements with 'external' in their
+ class attribute that are contained inside 'p' elements that are
+ contained inside the 'div' element which has id="main"
+
+ New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
+ See http://www.w3.org/TR/css3-selectors/#attribute-selectors
+
+ Version 0.4 - Simon Willison, March 25th 2003
+ -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
+ -- Opera 7 fails
+*/
+
+function getAllChildren(e) {
+ // Returns all children of element. Workaround required for IE5/Windows. Ugh.
+ return e.all ? e.all : e.getElementsByTagName('*');
+}
+
+document.getElementsBySelector = function(selector) {
+ // Attempt to fail gracefully in lesser browsers
+ if (!document.getElementsByTagName) {
+ return new Array();
+ }
+ // Split selector in to tokens
+ var tokens = selector.split(' ');
+ var currentContext = new Array(document);
+ for (var i = 0; i < tokens.length; i++) {
+ token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
+ if (token.indexOf('#') > -1) {
+ // Token is an ID selector
+ var bits = token.split('#');
+ var tagName = bits[0];
+ var id = bits[1];
+ var element = document.getElementById(id);
+ if (tagName && element.nodeName.toLowerCase() != tagName) {
+ // tag with that ID not found, return false
+ return new Array();
+ }
+ // Set currentContext to contain just this element
+ currentContext = new Array(element);
+ continue; // Skip to next token
+ }
+ if (token.indexOf('.') > -1) {
+ // Token contains a class selector
+ var bits = token.split('.');
+ var tagName = bits[0];
+ var className = bits[1];
+ if (!tagName) {
+ tagName = '*';
+ }
+ // Get elements matching tag, filter them for class selector
+ var found = new Array;
+ var foundCount = 0;
+ for (var h = 0; h < currentContext.length; h++) {
+ var elements;
+ if (tagName == '*') {
+ elements = getAllChildren(currentContext[h]);
+ } else {
+ try {
+ elements = currentContext[h].getElementsByTagName(tagName);
+ }
+ catch(e) {
+ elements = [];
+ }
+ }
+ for (var j = 0; j < elements.length; j++) {
+ found[foundCount++] = elements[j];
+ }
+ }
+ currentContext = new Array;
+ var currentContextIndex = 0;
+ for (var k = 0; k < found.length; k++) {
+ if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
+ currentContext[currentContextIndex++] = found[k];
+ }
+ }
+ continue; // Skip to next token
+ }
+ // Code to deal with attribute selectors
+ if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
+ var tagName = RegExp.$1;
+ var attrName = RegExp.$2;
+ var attrOperator = RegExp.$3;
+ var attrValue = RegExp.$4;
+ if (!tagName) {
+ tagName = '*';
+ }
+ // Grab all of the tagName elements within current context
+ var found = new Array;
+ var foundCount = 0;
+ for (var h = 0; h < currentContext.length; h++) {
+ var elements;
+ if (tagName == '*') {
+ elements = getAllChildren(currentContext[h]);
+ } else {
+ elements = currentContext[h].getElementsByTagName(tagName);
+ }
+ for (var j = 0; j < elements.length; j++) {
+ found[foundCount++] = elements[j];
+ }
+ }
+ currentContext = new Array;
+ var currentContextIndex = 0;
+ var checkFunction; // This function will be used to filter the elements
+ switch (attrOperator) {
+ case '=': // Equality
+ checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
+ break;
+ case '~': // Match one of space seperated words
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
+ break;
+ case '|': // Match start with value followed by optional hyphen
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
+ break;
+ case '^': // Match starts with value
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
+ break;
+ case '$': // Match ends with value - fails with "Warning" in Opera 7
+ checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
+ break;
+ case '*': // Match ends with value
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
+ break;
+ default :
+ // Just test for existence of attribute
+ checkFunction = function(e) { return e.getAttribute(attrName); };
+ }
+ currentContext = new Array;
+ var currentContextIndex = 0;
+ for (var k = 0; k < found.length; k++) {
+ if (checkFunction(found[k])) {
+ currentContext[currentContextIndex++] = found[k];
+ }
+ }
+ // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
+ continue; // Skip to next token
+ }
+ // If we get here, token is JUST an element (not a class or ID selector)
+ tagName = token;
+ var found = new Array;
+ var foundCount = 0;
+ for (var h = 0; h < currentContext.length; h++) {
+ var elements = currentContext[h].getElementsByTagName(tagName);
+ for (var j = 0; j < elements.length; j++) {
+ found[foundCount++] = elements[j];
+ }
+ }
+ currentContext = found;
+ }
+ return currentContext;
+}
+
+/* That revolting regular expression explained
+/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
+ \---/ \---/\-------------/ \-------/
+ | | | |
+ | | | The value
+ | | ~,|,^,$,* or =
+ | Attribute
+ Tag
+*/
diff --git a/django/contrib/admin/media/js/timeparse.js b/django/contrib/admin/media/js/timeparse.js
new file mode 100644
index 0000000000..882f41d56e
--- /dev/null
+++ b/django/contrib/admin/media/js/timeparse.js
@@ -0,0 +1,94 @@
+var timeParsePatterns = [
+ // 9
+ { re: /^\d{1,2}$/i,
+ handler: function(bits) {
+ if (bits[0].length == 1) {
+ return '0' + bits[0] + ':00';
+ } else {
+ return bits[0] + ':00';
+ }
+ }
+ },
+ // 13:00
+ { re: /^\d{2}[:.]\d{2}$/i,
+ handler: function(bits) {
+ return bits[0].replace('.', ':');
+ }
+ },
+ // 9:00
+ { re: /^\d[:.]\d{2}$/i,
+ handler: function(bits) {
+ return '0' + bits[0].replace('.', ':');
+ }
+ },
+ // 3 am / 3 a.m. / 3am
+ { re: /^(\d+)\s*([ap])(?:.?m.?)?$/i,
+ handler: function(bits) {
+ var hour = parseInt(bits[1]);
+ if (hour == 12) {
+ hour = 0;
+ }
+ if (bits[2].toLowerCase() == 'p') {
+ if (hour == 12) {
+ hour = 0;
+ }
+ return (hour + 12) + ':00';
+ } else {
+ if (hour < 10) {
+ return '0' + hour + ':00';
+ } else {
+ return hour + ':00';
+ }
+ }
+ }
+ },
+ // 3.30 am / 3:15 a.m. / 3.00am
+ { re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i,
+ handler: function(bits) {
+ var hour = parseInt(bits[1]);
+ var mins = parseInt(bits[2]);
+ if (mins < 10) {
+ mins = '0' + mins;
+ }
+ if (hour == 12) {
+ hour = 0;
+ }
+ if (bits[3].toLowerCase() == 'p') {
+ if (hour == 12) {
+ hour = 0;
+ }
+ return (hour + 12) + ':' + mins;
+ } else {
+ if (hour < 10) {
+ return '0' + hour + ':' + mins;
+ } else {
+ return hour + ':' + mins;
+ }
+ }
+ }
+ },
+ // noon
+ { re: /^no/i,
+ handler: function(bits) {
+ return '12:00';
+ }
+ },
+ // midnight
+ { re: /^mid/i,
+ handler: function(bits) {
+ return '00:00';
+ }
+ }
+];
+
+function parseTimeString(s) {
+ for (var i = 0; i < timeParsePatterns.length; i++) {
+ var re = timeParsePatterns[i].re;
+ var handler = timeParsePatterns[i].handler;
+ var bits = re.exec(s);
+ if (bits) {
+ return handler(bits);
+ }
+ }
+ return s;
+}
diff --git a/django/contrib/admin/media/js/urlify.js b/django/contrib/admin/media/js/urlify.js
new file mode 100644
index 0000000000..412130ad6f
--- /dev/null
+++ b/django/contrib/admin/media/js/urlify.js
@@ -0,0 +1,16 @@
+function URLify(s, num_chars) {
+ // changes, e.g., "Petty theft" to "petty_theft"
+
+ // remove all these words from the string before urlifying
+ removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from",
+ "is", "in", "into", "like", "of", "off", "on", "onto", "per",
+ "since", "than", "the", "this", "that", "to", "up", "via",
+ "with"];
+ r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
+ s = s.replace(r, '');
+ s = s.replace(/[^\w\s]/g, ''); // remove unneeded chars
+ s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
+ s = s.replace(/\s+/g, '_'); // convert spaces to underscores
+ s = s.toLowerCase(); // convert to lowercase
+ return s.substring(0, num_chars);// trim to first num_chars chars
+} \ No newline at end of file
diff --git a/django/contrib/admin/templates/admin/404.html b/django/contrib/admin/templates/admin/404.html
new file mode 100644
index 0000000000..9d7876ecbf
--- /dev/null
+++ b/django/contrib/admin/templates/admin/404.html
@@ -0,0 +1,11 @@
+{% extends "admin/base_site" %}
+
+{% block title %}Page not found{% endblock %}
+
+{% block content %}
+
+<h2>Page not found</h2>
+
+<p>We're sorry, but the requested page could not be found.</p>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/500.html b/django/contrib/admin/templates/admin/500.html
new file mode 100644
index 0000000000..34a28ff0f5
--- /dev/null
+++ b/django/contrib/admin/templates/admin/500.html
@@ -0,0 +1,11 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="/">Home</a> &rsaquo; Server error</div>{% endblock %}
+
+{% block title %}Server error (500){% endblock %}
+
+{% block content %}
+<h1>Server Error <em>(500)</em></h1>
+<p>There's been an error. It's been reported to the site administrators via e-mail and should be fixed shortly. Thanks for your patience.</p>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
new file mode 100644
index 0000000000..64c80b934c
--- /dev/null
+++ b/django/contrib/admin/templates/admin/base.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<title>{% block title %}{% endblock %}</title>
+<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% load adminmedia %}{% admin_media_prefix %}css/base.css{% endblock %}" />
+{% block extrastyle %}{% endblock %}
+{% block extrahead %}{% endblock %}
+</head>
+
+<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}">
+
+<!-- Container -->
+<div id="container">
+
+ {% if not is_popup %}
+ <!-- Header -->
+ <div id="header">
+ <div id="branding">
+ {% block branding %}{% endblock %}
+ </div>
+ {% if not user.is_anonymous %}
+ <div id="user-tools">Welcome, <strong>{% if user.first_name %}{{ user.first_name }}{% else %}{{ user.username }}{% endif %}</strong>. <br />{% block userlinks %}<a href="/admin/password_change/">Change password</a> / <a href="/admin/logout/">Log out</a>{% endblock %}</div>
+ {% endif %}
+ {% block nav-global %}{% endblock %}
+ <br class="clear" />
+ </div>
+ <!-- END Header -->
+ {% block breadcrumbs %}<div class="breadcrumbs"><a href="/">Home</a>{% if title %} &rsaquo; {{ title }}{% endif %}</div>{% endblock %}
+ {% endif %}
+
+ {% if messages %}
+ <ul class="messagelist">{% for message in messages %}<li>{{ message }}</li>{% endfor %}</ul>
+ {% endif %}
+
+ <!-- Content -->
+ <div id="content" class="{% block coltype %}colM{% endblock %}">
+ {% block pretitle %}{% endblock %}
+ {% if title %}<h1>{{ title }}</h1>{% endif %}
+ {% block content %}{{ content }}{% endblock %}
+ {% block sidebar %}{% endblock %}
+ <br class="clear" />
+ </div>
+ <!-- END Content -->
+
+ <div id="footer"></div>
+</div>
+<!-- END Container -->
+
+</body>
+</html>
diff --git a/django/contrib/admin/templates/admin/base_site.html b/django/contrib/admin/templates/admin/base_site.html
new file mode 100644
index 0000000000..7113c06d6c
--- /dev/null
+++ b/django/contrib/admin/templates/admin/base_site.html
@@ -0,0 +1,10 @@
+{% extends "admin/base" %}
+
+{% block title %}{{ title }} | Django site admin{% endblock %}
+
+{% block branding %}
+<h1 id="site-name">Django administration</h1>
+<h2 id="site-url"><a href="http://www.example.com/">example.com</a></h2>
+{% endblock %}
+
+{% block nav-global %}{% endblock %}
diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html
new file mode 100644
index 0000000000..99b1cdce7a
--- /dev/null
+++ b/django/contrib/admin/templates/admin/delete_confirmation.html
@@ -0,0 +1,21 @@
+{% extends "admin/base_site" %}
+
+{% block content %}
+
+{% if perms_lacking %}
+ <p>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:</p>
+ <ul>
+ {% for obj in perms_lacking %}
+ <li>{{ obj }}</li>
+ {% endfor %}
+ </ul>
+{% else %}
+ <p>Are you sure you want to delete the {{ object_name }} "{{ object }}"? All of the following related items will be deleted:</p>
+ <ul>{{ deleted_objects|unordered_list }}</ul>
+ <form action="" method="post">
+ <input type="hidden" name="post" value="yes" />
+ <input type="submit" value="Yes, I'm sure" />
+ </form>
+{% endif %}
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/index.html b/django/contrib/admin/templates/admin/index.html
new file mode 100644
index 0000000000..02c5bd8439
--- /dev/null
+++ b/django/contrib/admin/templates/admin/index.html
@@ -0,0 +1,65 @@
+{% extends "admin/base_site" %}
+
+{% block coltype %}colMS{% endblock %}
+{% block bodyclass %}dashboard{% endblock %}
+{% block breadcrumbs %}{% endblock %}
+{% block content %}
+<div id="content-main">
+
+{% load adminapplist %}
+
+{% get_admin_app_list as app_list %}
+{% if app_list %}
+ {% for app in app_list %}
+ <div class="module">
+ <h2>{{ app.name }}</h2>
+ <table>
+ {% for model in app.models %}
+ <tr>
+ {% if model.perms.change %}
+ <th><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
+ {% else %}
+ <th>{{ model.name }}</th>
+ {% endif %}
+
+ {% if model.perms.add %}
+ <td class="x50"><a href="{{ model.admin_url }}add/" class="addlink">Add</a></td>
+ {% else %}
+ <td class="x50">&nbsp;</td>
+ {% endif %}
+
+ {% if model.perms.change %}
+ <td class="x75"><a href="{{ model.admin_url }}" class="changelink">Change</a></td>
+ {% else %}
+ <td class="x75">&nbsp;</td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ {% endfor %}
+{% else %}
+ <p>You don't have permission to edit anything.</p>
+{% endif %}
+</div>
+{% endblock %}
+
+{% block sidebar %}
+<div id="content-related">
+ <div class="module" id="recent-actions-module">
+ <h2>Recent Actions</h2>
+ <h3>My Actions</h3>
+ {% load auth.log %}
+ {% get_admin_log 10 as admin_log for_user user %}
+ {% if not admin_log %}
+ <p>None available</p>
+ {% 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 }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{{ entry.get_content_type.name|capfirst }}</span></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+</div>
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/login.html b/django/contrib/admin/templates/admin/login.html
new file mode 100644
index 0000000000..ef4aa5aae2
--- /dev/null
+++ b/django/contrib/admin/templates/admin/login.html
@@ -0,0 +1,31 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}{% endblock %}
+
+{% block content %}
+
+{% if error_message %}
+<p class="errornote">{{ error_message }}</p>
+{% endif %}
+<div id="content-main">
+<form action="{{ app_path }}" method="post">
+
+<p class="aligned">
+<label for="id_username">Username:</label> <input type="text" name="username" id="id_username" />
+</p>
+<p class="aligned">
+<label for="id_password">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">Have you <a href="/password_reset/">forgotten your password</a>?</span>{% endcomment %}
+</p>
+
+<div class="aligned ">
+<label>&nbsp;</label><input type="submit" value="Log in" />
+</div>
+</form>
+
+<script type="text/javascript">
+document.getElementById('id_username').focus()
+</script>
+</div>
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html
new file mode 100644
index 0000000000..d50936665a
--- /dev/null
+++ b/django/contrib/admin/templates/admin/object_history.html
@@ -0,0 +1,42 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs"><a href="../../../../">Home</a> &rsaquo; <a href="../../">{{ module_name }}</a> &rsaquo; <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo; History</div>
+{% endblock %}
+
+{% block content %}
+
+<div id="content-main">
+<div class="module">
+
+{% if action_list %}
+
+ <table id="change-history">
+ <thead>
+ <tr>
+ <th>Date/time</th>
+ <th>User</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for action in action_list %}
+ <tr>
+ <th>{{ action.action_time|date:"N j, Y, P" }}</th>
+ <td>{{ action.get_user.username }}{% if action.get_user.first_name %} ({{ action.get_user.first_name }} {{ action.get_user.last_name }}){% endif %}</td>
+ <td>{{ action.change_message}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+{% else %}
+
+ <p>This object doesn't have a change history. It probably wasn't added via this admin site.</p>
+
+{% endif %}
+
+</div>
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin/template_validator.html b/django/contrib/admin/templates/admin/template_validator.html
new file mode 100644
index 0000000000..f9ac09a77d
--- /dev/null
+++ b/django/contrib/admin/templates/admin/template_validator.html
@@ -0,0 +1,31 @@
+{% extends "admin/base_site" %}
+
+{% block content %}
+
+<div id="content-main">
+
+<form action="" method="post">
+
+{% if form.error_dict %}
+<p class="errornote">Your template had {{ form.error_dict.items|length }} error{{ form.error_dict.items|pluralize }}:</p>
+{% endif %}
+
+<fieldset class="module aligned">
+<div class="form-row{% if form.site.errors %} error{% endif %} required">
+ {% if form.site.errors %}{{ form.site.html_error_list }}{% endif %}
+ <h4><label for="id_site">Site:</label> {{ form.site }}</h4>
+</div>
+<div class="form-row{% if form.template.errors %} error{% endif %} required">
+ {% if form.template.errors %}{{ form.template.html_error_list }}{% endif %}
+ <h4><label for="id_template">Template:</label> {{ form.template }}</h4>
+</div>
+</fieldset>
+
+<div class="submit-row">
+ <input type="submit" value="Check for errors" class="default" />
+</div>
+
+</form>
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/bookmarklets.html b/django/contrib/admin/templates/admin_doc/bookmarklets.html
new file mode 100644
index 0000000000..d396ec53b5
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/bookmarklets.html
@@ -0,0 +1,30 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Bookmarklets</div>{% endblock %}
+
+{% block title %}Documentation bookmarklets{% endblock %}
+
+{% block content %}
+
+<p class="help">To install bookmarklets, drag the link to your bookmarks
+toolbar, or right-click the link and add it to your bookmarks. Now you can
+select the bookmarklet from any page in the site. Note that some of these
+bookmarklets require you to be viewing the site from a computer designated
+as "internal" (talk to your system administrator if you aren't sure if
+your computer is "internal").</p>
+
+<div id="content-main">
+ <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=="undefined"){alert("No view found for this page");}document.location='{{ admin_url }}/doc/views/'+view+'/';})()">Documentation for this page</a></h3>
+ <p>Jumps you from any page to the documentation for the view that generates that page.</p>
+
+ <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">Show object ID</a></h3>
+ <p>Shows the content-type and unique ID for pages that represent a single object.</p>
+
+ <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}document.location='{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/';})()">Edit this object (current window)</a></h3>
+ <p>Jumps to the admin page for pages that represent a single object.</p>
+
+ <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}window.open('{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/');})()">Edit this object (new window)</a></h3>
+ <p>As above, but opens the admin page in a new window.</p>
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/index.html b/django/contrib/admin/templates/admin_doc/index.html
new file mode 100644
index 0000000000..77d2a96bf2
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/index.html
@@ -0,0 +1,28 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
+
+{% block title %}Documentation{% endblock %}
+
+{% block content %}
+
+<h1>Documentation</h1>
+
+<div id="content-main">
+ <h3><a href="tags/">Tags</a></h3>
+ <p>List of all the template tags and their functions.</p>
+
+ <h3><a href="filters/">Filters</a></h3>
+ <p>Filters are actions which can be applied to variables in a template to alter the output.</p>
+
+ <h3><a href="models/">Models</a></h3>
+ <p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
+
+ <h3><a href="views/">Views</a></h3>
+ <p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
+
+ <h3><a href="bookmarklets/">Bookmarklets</a></h3>
+ <p>Tools for your browser to quickly access admin functionality.</p>
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/missing_docutils.html b/django/contrib/admin/templates/admin_doc/missing_docutils.html
new file mode 100644
index 0000000000..40004c41ab
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/missing_docutils.html
@@ -0,0 +1,17 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
+
+{% block title %}Please install docutils{% endblock %}
+
+{% block content %}
+
+<h1>Documentation</h1>
+
+<div id="content-main">
+ <h3>The admin documentation system requires Python's <a href="http://docutils.sf.net/">docutils</a> library.</h3>
+
+ <p>Please ask your administrators to install <a href="http://docutils.sf.net/">docutils</a>.</p>
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/model_detail.html b/django/contrib/admin/templates/admin_doc/model_detail.html
new file mode 100644
index 0000000000..d3621b7434
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/model_detail.html
@@ -0,0 +1,46 @@
+{% extends "admin/base_site" %}
+
+{% block extrahead %}
+
+{{ block.super }}
+
+<style type="text/css">
+
+.module table { width:100%; }
+
+</style>
+
+{% endblock %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name }}</div>{% endblock %}
+
+{% block title %}Model: {{ name }}{% endblock %}
+
+{% block content %}
+<div id="content-main">
+<h1>{{ summary }}</h1>
+
+<div class="module">
+<table class="model">
+<thead>
+<tr>
+ <th>Field</th>
+ <th>Type</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+{% for field in fields|dictsort:"name" %}
+<tr>
+ <td>{{ field.name }}</td>
+ <td>{{ field.data_type }}</td>
+ <td>{{ field.verbose|default:"" }}{% if field.help_text %} - {{ field.help_text }}{% endif %}</td>
+</tr>
+{% endfor %}
+</tbody>
+</table>
+</div>
+
+<p class="small"><a href="../">&lsaquo; Back to Models Documentation</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
new file mode 100644
index 0000000000..5896ee136e
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/model_index.html
@@ -0,0 +1,44 @@
+{% extends "admin/base_site" %}
+
+{% block coltype %}colSM{% endblock %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Models</div>{% endblock %}
+
+{% block title %}Models{% endblock %}
+
+{% block content %}
+
+<h1>Models Documentation</h1>
+
+<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>
+
+<table class="xfull">
+{% for model in group.list %}
+<tr>
+<th><a href="{{ model.name }}/">{{ model.class }}</a></th>
+</tr>
+{% endfor %}
+</table>
+</div>
+{% endfor %}
+
+</div>
+{% endblock %}
+
+{% block sidebar %}
+<div id="content-related" class="sidebar">
+<div class="module">
+<h2>Model Groups Quick List</h2>
+<ul>
+{% regroup models|dictsort:"module" by module as grouped_models %}
+{% for group in grouped_models %}
+ <li><a href="#{{ group.grouper }}">{{ group.grouper }}</a></li>
+{% endfor %}
+</ul>
+</div>
+</div>
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/template_detail.html b/django/contrib/admin/templates/admin_doc/template_detail.html
new file mode 100644
index 0000000000..448ff212ba
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/template_detail.html
@@ -0,0 +1,21 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; Templates &rsaquo; {{ name }}</div>{% endblock %}
+
+{% block title %}Template: {{ name }}{% endblock %}
+
+{% block content %}
+<h1>Template: "{{ name }}"</h1>
+
+{% regroup templates|dictsort:"site_id" by site as templates_by_site %}
+{% for group in templates_by_site %}
+ <h2>Search path for template "{{ name }}" on {{ group.grouper }}:</h2>
+ <ol>
+ {% for template in group.list|dictsort:"order" %}
+ <li><code>{{ template.file }}</code>{% if not template.exists %} <em>(does not exist)</em>{% endif %}</li>
+ {% endfor %}
+ </ol>
+{% endfor %}
+
+<p class="small"><a href="../../">&lsaquo; Back to Documentation</a></p>
+{% 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
new file mode 100644
index 0000000000..23f0862457
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/template_filter_index.html
@@ -0,0 +1,50 @@
+{% extends "admin/base_site" %}
+
+{% block coltype %}colSM{% endblock %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; filters</div>{% endblock %}
+
+{% block title %}Template filters{% endblock %}
+
+{% block content %}
+
+<h1>Template filter documentation</h1>
+
+<div id="content-main">
+{% regroup filters|dictsort:"library" by library as filter_libraries %}
+{% for library in filter_libraries %}
+<div class="module">
+ <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in filters{% endif %}</h2>
+ {% if library.grouper %}<p class="small quiet">To use these filters, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the filter.</p><hr>{% endif %}
+ {% for filter in library.list|dictsort:"name" %}
+ <h3 id="{{ filter.name }}">{{ filter.name }}</h3>
+ <p>{{ filter.title }}</p>
+ <p><strong>Usage:</strong> <code>{% templatetag openvariable %} variable|{{ filter.name }}{% if filter.meta.AcceptsArgument %}:"arg"{% endif %} {% templatetag closevariable %}</code></p>
+ <p>{{ filter.body }}</p>
+ {% if not forloop.last %}<hr />{% endif %}
+ {% endfor %}
+</div>
+{% endfor %}
+</div>
+
+{% endblock %}
+
+{% block sidebar %}
+
+<div id="content-related">
+
+{% regroup filters|dictsort:"library" by library as filter_libraries %}
+{% for library in filter_libraries %}
+<div class="module">
+ <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in filters{% endif %}</h2>
+ <ul>
+ {% for filter in library.list|dictsort:"name" %}
+ <li><a href="#{{ filter.name }}">{{ filter.name }}</a></li>
+ {% endfor %}
+ </ul>
+</div>
+{% endfor %}
+
+</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
new file mode 100644
index 0000000000..83b2572322
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/template_tag_index.html
@@ -0,0 +1,49 @@
+{% extends "admin/base_site" %}
+
+{% block coltype %}colSM{% endblock %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Tags</div>{% endblock %}
+
+{% block title %}Template tags{% endblock %}
+
+{% block content %}
+
+<h1>Template tag documentation</h1>
+
+<div id="content-main">
+{% regroup tags|dictsort:"library" by library as tag_libraries %}
+{% for library in tag_libraries %}
+<div class="module">
+ <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in tags{% endif %}</h2>
+ {% if library.grouper %}<p class="small quiet">To use these tags, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the tag.</p><hr>{% endif %}
+ {% for tag in library.list|dictsort:"name" %}
+ <h3 id="{{ tag.name }}">{{ tag.name }}</h3>
+ <h4>{{ tag.title }}</h4>
+ <p>{{ tag.body }}</p>
+ {% if not forloop.last %}<hr />{% endif %}
+ {% endfor %}
+</div>
+{% endfor %}
+</div>
+
+{% endblock %}
+
+{% block sidebar %}
+
+<div id="content-related">
+
+{% regroup tags|dictsort:"library" by library as tag_libraries %}
+{% for library in tag_libraries %}
+<div class="module">
+ <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in tags{% endif %}</h2>
+ <ul>
+ {% for tag in library.list|dictsort:"name" %}
+ <li><a href="#{{ tag.name }}">{{ tag.name }}</a></li>
+ {% endfor %}
+ </ul>
+</div>
+{% endfor %}
+
+</div>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/view_detail.html b/django/contrib/admin/templates/admin_doc/view_detail.html
new file mode 100644
index 0000000000..9fb06e7eb7
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/view_detail.html
@@ -0,0 +1,26 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Views</a> &rsaquo; {{ name }}</div>{% endblock %}
+
+{% block title %}View: {{ name }}{% endblock %}
+
+{% block content %}
+
+<h1>{{ name }}</h1>
+
+<h2 class="subhead">{{ summary }}</h2>
+
+<p>{{ body }}</p>
+
+{% if meta.Context %}
+<h3>Context:</h3>
+<p>{{ meta.Context }}</p>
+{% endif %}
+
+{% if meta.Templates %}
+<h3>Templates:</h3>
+<p>{{ meta.Templates }}</p>
+{% endif %}
+
+<p class="small"><a href="../">&lsaquo; Back to Views Documentation</a></p>
+{% endblock %}
diff --git a/django/contrib/admin/templates/admin_doc/view_index.html b/django/contrib/admin/templates/admin_doc/view_index.html
new file mode 100644
index 0000000000..90ef5b1c90
--- /dev/null
+++ b/django/contrib/admin/templates/admin_doc/view_index.html
@@ -0,0 +1,44 @@
+{% extends "admin/base_site" %}
+
+{% block coltype %}colSM{% endblock %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
+
+{% block title %}Views{% endblock %}
+
+{% block content %}
+
+<h1>View documentation</h1>
+
+{% regroup views|dictsort:"site_id" by site as views_by_site %}
+
+<div id="content-related" class="sidebar">
+<div class="module">
+<h2>Jump to site</h2>
+<ul>
+ {% for site_views in views_by_site %}
+ <li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
+ {% endfor %}
+</ul>
+</div>
+</div>
+
+<div id="content-main">
+
+{% for site_views in views_by_site %}
+<div class="module">
+<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
+
+{% for view in site_views.list|dictsort:"url" %}
+{% ifchanged %}
+<h3><a href="{{ view.module }}.{{ view.name }}/"/>{{ view.url|escape }}</a></h3>
+<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
+<p>{{ view.title }}</p>
+<hr>
+{% endifchanged %}
+{% endfor %}
+</div>
+{% endfor %}
+</div>
+{% endblock %}
+
diff --git a/django/contrib/admin/templates/registration/logged_out.html b/django/contrib/admin/templates/registration/logged_out.html
new file mode 100644
index 0000000000..1f4bd29790
--- /dev/null
+++ b/django/contrib/admin/templates/registration/logged_out.html
@@ -0,0 +1,11 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a></div>{% endblock %}
+
+{% block content %}
+
+<p>Thanks for spending some quality time with the Web site today.</p>
+
+<p><a href="../">Log in again</a></p>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_done.html b/django/contrib/admin/templates/registration/password_change_done.html
new file mode 100644
index 0000000000..4345b9bcaa
--- /dev/null
+++ b/django/contrib/admin/templates/registration/password_change_done.html
@@ -0,0 +1,13 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Password change</div>{% endblock %}
+
+{% block title %}Password change successful{% endblock %}
+
+{% block content %}
+
+<h1>Password change successful</h1>
+
+<p>Your password was changed.</p>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html
new file mode 100644
index 0000000000..104249991c
--- /dev/null
+++ b/django/contrib/admin/templates/registration/password_change_form.html
@@ -0,0 +1,25 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Password change</div>{% endblock %}
+
+{% block title %}Password change{% endblock %}
+
+{% block content %}
+
+<h1>Password change</h1>
+
+<p>Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly.</p>
+
+<form action="" method="post">
+
+{% if form.old_password.errors %}{{ form.old_password.html_error_list }}{% endif %}
+<p class="aligned wide"><label for="id_old_password">Old password:</label>{{ form.old_password }}</p>
+{% if form.new_password1.errors %}{{ form.new_password1.html_error_list }}{% endif %}
+<p class="aligned wide"><label for="id_new_password1">New password:</label>{{ form.new_password1 }}</p>
+{% if form.new_password2.errors %}{{ form.new_password2.html_error_list }}{% endif %}
+<p class="aligned wide"><label for="id_new_password2">Confirm password:</label>{{ form.new_password2 }}</p>
+
+<p><input type="submit" value="Change my password" /></p>
+</form>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_done.html b/django/contrib/admin/templates/registration/password_reset_done.html
new file mode 100644
index 0000000000..a8573e8e0e
--- /dev/null
+++ b/django/contrib/admin/templates/registration/password_reset_done.html
@@ -0,0 +1,13 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Password reset</div>{% endblock %}
+
+{% block title %}Password reset successful{% endblock %}
+
+{% block content %}
+
+<h1>Password reset successful</h1>
+
+<p>We've e-mailed a new password to the e-mail address you submitted. You should be receiving it shortly.</p>
+
+{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_email.html b/django/contrib/admin/templates/registration/password_reset_email.html
new file mode 100644
index 0000000000..4a5bb920d6
--- /dev/null
+++ b/django/contrib/admin/templates/registration/password_reset_email.html
@@ -0,0 +1,14 @@
+You're receiving this e-mail because you requested a password reset
+for your user account at {{ site_name }}.
+
+Your new password is: {{ new_password }}
+
+Feel free to change this password by going to this page:
+
+http://{{ domain }}/password_change/
+
+Your username, in case you've forgotten, is {{ user.username }}
+
+Thanks for using our site!
+
+The {{ site_name }} team
diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html
new file mode 100644
index 0000000000..4f9bcb5fc5
--- /dev/null
+++ b/django/contrib/admin/templates/registration/password_reset_form.html
@@ -0,0 +1,18 @@
+{% extends "admin/base_site" %}
+
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Password reset</div>{% endblock %}
+
+{% block title %}Password reset{% endblock %}
+
+{% block content %}
+
+<h1>Password reset</h1>
+
+<p>Forgotten your password? Enter your e-mail address below, and we'll reset your password and e-mail the new one to you.</p>
+
+<form action="" method="post">
+{% if form.email.errors %}{{ form.email.html_error_list }}{% endif %}
+<p><label for="id_email">E-mail address:</label> {{ form.email }} <input type="submit" value="Reset my password" /></p>
+</form>
+
+{% endblock %}
diff --git a/django/contrib/admin/templatetags/adminapplist.py b/django/contrib/admin/templatetags/adminapplist.py
new file mode 100644
index 0000000000..92e1bd3ccb
--- /dev/null
+++ b/django/contrib/admin/templatetags/adminapplist.py
@@ -0,0 +1,57 @@
+from django.core import template
+
+class AdminApplistNode(template.Node):
+ def __init__(self, varname):
+ self.varname = varname
+
+ def render(self, context):
+ from django.core import meta
+ 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:]
+ has_module_perms = user.has_module_perms(app_label)
+
+ if has_module_perms:
+ model_list = []
+ 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())),
+ 'delete': user.has_perm("%s.%s" % (app_label, m._meta.get_delete_permission())),
+ }
+
+ # Check whether user has any perm for this module.
+ # If so, add the module to the model_list.
+ if True in perms.values():
+ model_list.append({
+ 'name': capfirst(m._meta.verbose_name_plural),
+ 'admin_url': '%s/%s/' % (app_label, m._meta.module_name),
+ 'perms': perms,
+ })
+
+ if model_list:
+ app_list.append({
+ 'name': app_label.title(),
+ 'has_module_perms': has_module_perms,
+ 'models': model_list,
+ })
+ context[self.varname] = app_list
+ return ''
+
+def get_admin_app_list(parser, token):
+ """
+ {% get_admin_app_list as app_list %}
+ """
+ tokens = token.contents.split()
+ if len(tokens) < 3:
+ raise template.TemplateSyntaxError, "'%s' tag requires two arguments" % tokens[0]
+ if tokens[1] != 'as':
+ raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
+ return AdminApplistNode(tokens[2])
+
+template.register_tag('get_admin_app_list', get_admin_app_list)
diff --git a/django/contrib/admin/templatetags/adminmedia.py b/django/contrib/admin/templatetags/adminmedia.py
new file mode 100644
index 0000000000..2b18252d7d
--- /dev/null
+++ b/django/contrib/admin/templatetags/adminmedia.py
@@ -0,0 +1,17 @@
+from django.core import template
+
+class AdminMediaPrefixNode(template.Node):
+ def render(self, context):
+ try:
+ from django.conf.settings import ADMIN_MEDIA_PREFIX
+ except ImportError:
+ return ''
+ return ADMIN_MEDIA_PREFIX
+
+def admin_media_prefix(parser, token):
+ """
+ {% admin_media_prefix %}
+ """
+ return AdminMediaPrefixNode()
+
+template.register_tag('admin_media_prefix', admin_media_prefix)
diff --git a/django/contrib/admin/templatetags/log.py b/django/contrib/admin/templatetags/log.py
new file mode 100644
index 0000000000..b24f7c1dad
--- /dev/null
+++ b/django/contrib/admin/templatetags/log.py
@@ -0,0 +1,51 @@
+from django.models.admin import log
+from django.core import template
+
+class AdminLogNode(template.Node):
+ def __init__(self, limit, varname, user):
+ self.limit, self.varname, self.user = limit, varname, user
+
+ def __repr__(self):
+ return "<GetAdminLog 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)
+ return ''
+
+class DoGetAdminLog:
+ """
+ Populates a template variable with the admin log for the given criteria.
+
+ Usage::
+
+ {% get_admin_log [limit] as [varname] for_user [context_var_containing_user_obj] %}
+
+ Examples::
+
+ {% get_admin_log 10 as admin_log for_user 23 %}
+ {% get_admin_log 10 as admin_log for_user user %}
+ {% get_admin_log 10 as admin_log %}
+
+ Note that ``context_var_containing_user_obj`` can be a hard-coded integer
+ (user ID) or the name of a template context variable containing the user
+ object whose ID you want.
+ """
+ def __init__(self, tag_name):
+ self.tag_name = tag_name
+
+ def __call__(self, parser, token):
+ tokens = token.contents.split()
+ if len(tokens) < 4:
+ raise template.TemplateSyntaxError, "'%s' statements require two arguments" % self.tag_name
+ if not tokens[1].isdigit():
+ raise template.TemplateSyntaxError, "First argument in '%s' must be an integer" % self.tag_name
+ if tokens[2] != 'as':
+ raise template.TemplateSyntaxError, "Second argument in '%s' must be 'as'" % self.tag_name
+ if len(tokens) > 4:
+ if tokens[4] != 'for_user':
+ raise template.TemplateSyntaxError, "Fourth argument in '%s' must be 'for_user'" % self.tag_name
+ return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
+
+template.register_tag('get_admin_log', DoGetAdminLog('get_admin_log'))
diff --git a/django/contrib/admin/urls/admin.py b/django/contrib/admin/urls/admin.py
new file mode 100644
index 0000000000..d03b88ceb4
--- /dev/null
+++ b/django/contrib/admin/urls/admin.py
@@ -0,0 +1,57 @@
+from django.conf.urls.defaults import *
+from django.conf.settings import INSTALLED_APPS
+
+urlpatterns = (
+ ('^$', 'django.contrib.admin.views.main.index'),
+ ('^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/views/main.py b/django/contrib/admin/views/main.py
index 685f3b6cba..8ecf0a2c97 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -6,7 +6,7 @@ from django.core.template import loader
from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied
from django.core.extensions import DjangoContext as Context
from django.core.extensions import get_object_or_404, render_to_response
-from django.models.auth import log
+from django.models.admin import log
from django.utils.html import strip_tags
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
from django.utils.text import capfirst, get_text_list
@@ -49,7 +49,7 @@ def get_query_string(original_params, new_params={}, remove=[]):
return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
def index(request):
- return render_to_response('index', {'title': 'Site administration'}, context_instance=Context(request))
+ return render_to_response('admin/index', {'title': 'Site administration'}, context_instance=Context(request))
index = staff_member_required(index)
def change_list(request, app_label, module_name):
@@ -266,7 +266,7 @@ def change_list(request, app_label, module_name):
else:
pass # Invalid argument to "list_filter"
- raw_template = ['{% extends "base_site" %}\n']
+ raw_template = ['{% extends "admin/base_site" %}\n']
raw_template.append('{% block bodyclass %}change-list{% endblock %}\n')
if not is_popup:
raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; %s</div>{%% endblock %%}\n' % capfirst(opts.verbose_name_plural))
@@ -538,7 +538,7 @@ def _get_template(opts, app_label, add=False, change=False, show_delete=False, f
admin_field_objs = opts.admin.get_field_objs(opts)
ordered_objects = opts.get_ordered_objects()[:]
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
- t = ['{% extends "base_site" %}\n']
+ t = ['{% extends "admin/base_site" %}\n']
t.append('{% block extrahead %}')
# Put in any necessary JavaScript imports.
@@ -1087,7 +1087,7 @@ def delete_stage(request, app_label, module_name, object_id):
log.log_action(request.user.id, opts.get_content_type_id(), object_id, obj_repr, log.DELETION)
request.user.add_message('The %s "%s" was deleted successfully.' % (opts.verbose_name, obj_repr))
return HttpResponseRedirect("../../")
- return render_to_response('delete_confirmation_generic', {
+ return render_to_response('admin/delete_confirmation', {
"title": "Are you sure?",
"object_name": opts.verbose_name,
"object": obj,
@@ -1102,7 +1102,7 @@ def history(request, app_label, module_name, object_id):
order_by=("action_time",), select_related=True)
# 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', {
+ return render_to_response('admin/object_history', {
'title': 'Change history: %r' % obj,
'action_list': action_list,
'module_name': capfirst(opts.verbose_name_plural),
diff --git a/django/contrib/admin/views/template.py b/django/contrib/admin/views/template.py
index 97f67e04c1..fbac6d4f12 100644
--- a/django/contrib/admin/views/template.py
+++ b/django/contrib/admin/views/template.py
@@ -23,7 +23,7 @@ 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('template_validator', {
+ return render_to_response('admin/template_validator', {
'title': 'Template validator',
'form': formfields.FormWrapper(manipulator, new_data, errors),
}, context_instance=DjangoContext(request))