Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

MERGED NEW-ADMIN BRANCH (except for po/mo files, which will come in a…

… separate commit)

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1434 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 9dda4abee1225db7a7b195b84c915fdd141a7260 1 parent 4fe5c9b
Adrian Holovaty authored November 25, 2005

Showing 30 changed files with 2,062 additions and 1,125 deletions. Show diff stats Hide diff stats

  1. 16  django/bin/validate.py
  2. 152  django/contrib/admin/filterspecs.py
  3. 75  django/contrib/admin/templates/admin/change_form.html
  4. 20  django/contrib/admin/templates/admin/change_list.html
  5. 13  django/contrib/admin/templates/admin/change_list_results.html
  6. 10  django/contrib/admin/templates/admin/date_hierarchy.html
  7. 15  django/contrib/admin/templates/admin/edit_inline_stacked.html
  8. 42  django/contrib/admin/templates/admin/edit_inline_tabular.html
  9. 21  django/contrib/admin/templates/admin/field_line.html
  10. 7  django/contrib/admin/templates/admin/filter.html
  11. 5  django/contrib/admin/templates/admin/filters.html
  12. 9  django/contrib/admin/templates/admin/pagination.html
  13. 14  django/contrib/admin/templates/admin/search_form.html
  14. 8  django/contrib/admin/templates/admin/submit_line.html
  15. 22  django/contrib/admin/templates/admin_doc/bookmarklets.html
  16. 4  django/contrib/admin/templates/widget/date_time.html
  17. 1  django/contrib/admin/templates/widget/default.html
  18. 4  django/contrib/admin/templates/widget/file.html
  19. 7  django/contrib/admin/templates/widget/foreign.html
  20. 1  django/contrib/admin/templates/widget/many_to_many.html
  21. 278  django/contrib/admin/templatetags/admin_list.py
  22. 258  django/contrib/admin/templatetags/admin_modify.py
  23. 1,307  django/contrib/admin/views/main.py
  24. 154  django/core/formfields.py
  25. 6  django/core/management.py
  26. 452  django/core/meta/__init__.py
  27. 213  django/core/meta/fields.py
  28. 55  django/models/__init__.py
  29. 17  django/views/generic/create_update.py
  30. 1  setup.py
16  django/bin/validate.py
@@ -16,19 +16,19 @@ def validate_class(klass):
16 16
             assert isinstance(f.rel, meta.ManyToMany), \
17 17
                 "ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name
18 18
     # Inline related objects.
19  
-    for rel_opts, rel_field in opts.get_inline_related_objects():
20  
-        assert len([f for f in rel_opts.fields if f.core]) > 0, \
  19
+    for related in opts.get_followed_related_objects():
  20
+        assert len([f for f in related.opts.fields if f.core]) > 0, \
21 21
             "At least one field in %s should have core=True, because it's being edited inline by %s." % \
22  
-            (rel_opts.object_name, opts.object_name)
  22
+            (related.opts.object_name, opts.object_name)
23 23
     # All related objects.
24 24
     related_apps_seen = []
25  
-    for rel_opts, rel_field in opts.get_all_related_objects():
26  
-        if rel_opts in related_apps_seen:
27  
-            assert rel_field.rel.related_name is not None, \
  25
+    for related in opts.get_all_related_objects():
  26
+        if related.opts in related_apps_seen:
  27
+            assert related.field.rel.related_name is not None, \
28 28
                 "Relationship in field %s.%s needs to set 'related_name' because more than one" \
29 29
                 " %s object is referenced in %s." % \
30  
-                (rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name)
31  
-        related_apps_seen.append(rel_opts)
  30
+                (related.opts.object_name, related.field.name, opts.object_name, rel_opts.object_name)
  31
+        related_apps_seen.append(related.opts)
32 32
     # Etc.
33 33
     if opts.admin is not None:
34 34
         assert opts.admin.ordering or opts.ordering, \
152  django/contrib/admin/filterspecs.py
... ...
@@ -0,0 +1,152 @@
  1
+"""
  2
+FilterSpec encapsulates the logic for displaying filters in the Django admin.
  3
+Filters are specified in models with the "list_filter" option.
  4
+
  5
+Each filter subclass knows how to display a filter for a field that passes a
  6
+certain test -- e.g. being a DateField or ForeignKey.
  7
+"""
  8
+
  9
+from django.core import meta
  10
+import datetime
  11
+
  12
+class FilterSpec(object):
  13
+    filter_specs = []
  14
+    def __init__(self, f, request, params):
  15
+        self.field = f
  16
+        self.params = params
  17
+
  18
+    def register(cls, test, factory):
  19
+        cls.filter_specs.append( (test, factory) )
  20
+    register = classmethod(register)
  21
+
  22
+    def create(cls, f, request, params):
  23
+        for test, factory in cls.filter_specs:
  24
+            if test(f):
  25
+                return factory(f, request, params)
  26
+    create = classmethod(create)
  27
+
  28
+    def has_output(self):
  29
+        return True
  30
+
  31
+    def choices(self, cl):
  32
+        raise NotImplementedError()
  33
+
  34
+    def title(self):
  35
+        return self.field.verbose_name
  36
+
  37
+    def output(self, cl):
  38
+        t = []
  39
+        if self.has_output():
  40
+            t.append(_('<h3>By %s:</h3>\n<ul>\n') % self.title())
  41
+
  42
+            for choice in self.choices(cl):
  43
+                t.append('<li%s><a href="%s">%s</a></li>\n' % \
  44
+                    ((choice['selected'] and ' class="selected"' or ''),
  45
+                     choice['query_string'] ,
  46
+                     choice['display']))
  47
+            t.append('</ul>\n\n')
  48
+        return "".join(t)
  49
+
  50
+class RelatedFilterSpec(FilterSpec):
  51
+    def __init__(self, f, request, params):
  52
+        super(RelatedFilterSpec, self).__init__(f, request, params)
  53
+        if isinstance(f, meta.ManyToManyField):
  54
+            self.lookup_title = f.rel.to.verbose_name
  55
+        else:
  56
+            self.lookup_title = f.verbose_name
  57
+        self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
  58
+        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  59
+        self.lookup_choices = f.rel.to.get_model_module().get_list()
  60
+
  61
+    def has_output(self):
  62
+        return len(self.lookup_choices) > 1
  63
+
  64
+    def title(self):
  65
+        return self.lookup_title
  66
+
  67
+    def choices(self, cl):
  68
+        yield {'selected': self.lookup_val is None,
  69
+               'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
  70
+               'display': _('All')}
  71
+        for val in self.lookup_choices:
  72
+            pk_val = getattr(val, self.field.rel.to.pk.attname)
  73
+            yield {'selected': self.lookup_val == str(pk_val),
  74
+                   'query_string': cl.get_query_string( {self.lookup_kwarg: pk_val}),
  75
+                   'display': val}
  76
+
  77
+FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
  78
+
  79
+class ChoicesFilterSpec(FilterSpec):
  80
+    def __init__(self, f, request, params):
  81
+        super(ChoicesFilterSpec, self).__init__(f, request, params)
  82
+        self.lookup_kwarg = '%s__exact' % f.name
  83
+        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  84
+
  85
+    def choices(self, cl):
  86
+        yield {'selected': self.lookup_val is None,
  87
+               'query_string': cl.get_query_string( {}, [self.lookup_kwarg]),
  88
+               'display': _('All')}
  89
+        for k, v in self.field.choices:
  90
+            yield {'selected': str(k) == self.lookup_val,
  91
+                    'query_string': cl.get_query_string( {self.lookup_kwarg: k}),
  92
+                    'display': v}
  93
+
  94
+FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
  95
+
  96
+class DateFieldFilterSpec(FilterSpec):
  97
+    def __init__(self, f, request, params):
  98
+        super(DateFieldFilterSpec, self).__init__(f, request, params)
  99
+
  100
+        self.field_generic = '%s__' % self.field.name
  101
+
  102
+        self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(self.field_generic)])
  103
+
  104
+        today = datetime.date.today()
  105
+        one_week_ago = today - datetime.timedelta(days=7)
  106
+        today_str = isinstance(self.field, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
  107
+
  108
+        self.links = (
  109
+            (_('Any date'), {}),
  110
+            (_('Today'), {'%s__year' % self.field.name: str(today.year),
  111
+                       '%s__month' % self.field.name: str(today.month),
  112
+                       '%s__day' % self.field.name: str(today.day)}),
  113
+            (_('Past 7 days'), {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'),
  114
+                             '%s__lte' % f.name: today_str}),
  115
+            (_('This month'), {'%s__year' % self.field.name: str(today.year),
  116
+                             '%s__month' % f.name: str(today.month)}),
  117
+            (_('This year'), {'%s__year' % self.field.name: str(today.year)})
  118
+        )
  119
+
  120
+    def title(self):
  121
+        return self.field.verbose_name
  122
+
  123
+    def choices(self, cl):
  124
+        for title, param_dict in self.links:
  125
+            yield {'selected': self.date_params == param_dict,
  126
+                   'query_string': cl.get_query_string( param_dict, self.field_generic),
  127
+                   'display': title}
  128
+
  129
+FilterSpec.register(lambda f: isinstance(f, meta.DateField), DateFieldFilterSpec)
  130
+
  131
+class BooleanFieldFilterSpec(FilterSpec):
  132
+    def __init__(self, f, request, params):
  133
+        super(BooleanFieldFilterSpec, self).__init__(f, request, params)
  134
+        self.lookup_kwarg = '%s__exact' % f.name
  135
+        self.lookup_kwarg2 = '%s__isnull' % f.name
  136
+        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  137
+        self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
  138
+
  139
+    def title(self):
  140
+        return self.field.verbose_name
  141
+
  142
+    def choices(self, cl):
  143
+        for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
  144
+            yield {'selected': self.lookup_val == v and not self.lookup_val2,
  145
+                   'query_string': cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]),
  146
+                   'display': k}
  147
+        if isinstance(self.field, meta.NullBooleanField):
  148
+            yield {'selected': self.lookup_val2 == 'True',
  149
+                   'query_string': cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
  150
+                   'display': _('Unknown')}
  151
+
  152
+FilterSpec.register(lambda f: isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField), BooleanFieldFilterSpec)
75  django/contrib/admin/templates/admin/change_form.html
... ...
@@ -0,0 +1,75 @@
  1
+{% extends "admin/base_site" %}
  2
+{% load i18n %}
  3
+{% load admin_modify %}
  4
+{% load adminmedia %}
  5
+{% block extrahead %}
  6
+{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
  7
+{% endblock %}
  8
+{% block coltype %}{{ bound_manipulator.coltype }}{% endblock %}
  9
+{% block bodyclass %}{{ app_label }}-{{ bound_manipulator.object_name.lower }} change-form{% endblock %}
  10
+{% block breadcrumbs %}{% if not is_popup %}
  11
+<div class="breadcrumbs">
  12
+     <a href="../../../">{% trans "Home" %}</a> &rsaquo;
  13
+     <a href="../">{{ bound_manipulator.verbose_name_plural|capfirst }}</a> &rsaquo;
  14
+     {% if add %}{% trans "Add" %} {{ bound_manipulator.verbose_name }}{% else %}{{ bound_manipulator.original|striptags|truncatewords:"18" }}{% endif %}
  15
+</div>
  16
+{% endif %}{% endblock %}
  17
+{% block content %}<div id="content-main">
  18
+{% if change %}{% if not is_popup %}
  19
+  <ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
  20
+  {% if bound_manipulator.has_absolute_url %}<li><a href="/r/{{ bound_manipulator.content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
  21
+  </ul>
  22
+{% endif %}{% endif %}
  23
+<form {{ bound_manipulator.form_enc_attrib }} action='{{ form_url }}' method="post">{% block form_top %}{% endblock %}
  24
+{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
  25
+{% if bound_manipulator.save_on_top %}{% submit_row bound_manipulator %}{% endif %}
  26
+{% if form.error_dict %}
  27
+    <p class="errornote">
  28
+    {% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
  29
+    </p>
  30
+{% endif %}
  31
+{% for bound_field_set in bound_manipulator.bound_field_sets %}
  32
+   <fieldset class="module aligned {{ bound_field_set.classes }}">
  33
+    {% if bound_field_set.name %}<h2>{{ bound_field_set.name }}</h2>{% endif %}
  34
+    {% for bound_field_line in bound_field_set %}
  35
+        {% admin_field_line bound_field_line %}
  36
+        {% for bound_field in bound_field_line %}
  37
+            {% filter_interface_script_maybe bound_field %}
  38
+        {% endfor %}
  39
+    {% endfor %}
  40
+   </fieldset>
  41
+{% endfor %}
  42
+{% block after_field_sets %}{% endblock %}
  43
+{% if change %}
  44
+   {% if bound_manipulator.ordered_objects %}
  45
+   <fieldset class="module"><h2>{% trans "Ordering" %}</h2>
  46
+   <div class="form-row{% if form.order_.errors %} error{% endif %} ">
  47
+   {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
  48
+   <p><label for="id_order_">{% trans "Order:" %}</label> {{ form.order_ }}</p>
  49
+   </div></fieldset>
  50
+   {% endif %}
  51
+{% endif %}
  52
+{% for related_object in bound_manipulator.inline_related_objects %}{% edit_inline related_object %}{% endfor %}
  53
+{% block after_related_objects %}{% endblock %}
  54
+{% submit_row bound_manipulator %}
  55
+{% if add %}
  56
+   <script type="text/javascript">document.getElementById("{{ bound_manipulator.first_form_field_id }}").focus();</script>
  57
+{% endif %}
  58
+{% if bound_manipulator.auto_populated_fields %}
  59
+   <script type="text/javascript">
  60
+   {% auto_populated_field_script bound_manipulator.auto_populated_fields change %}
  61
+   </script>
  62
+{% endif %}
  63
+{% if change %}
  64
+   {% if bound_manipulator.ordered_objects %}
  65
+      {% if form.order_objects %}<ul id="orderthese">
  66
+          {% for object in form.order_objects %}
  67
+             <li id="p{% object_pk bound_manipulator object %}">
  68
+             <span id="handlep{% object_pk bound_manipulator object %}">{{ object|truncatewords:"5" }}</span>
  69
+             </li>
  70
+             {% endfor %}
  71
+      </ul>{% endif %}
  72
+   {% endif %}
  73
+{% endif %}
  74
+</form></div>
  75
+{% endblock %}
20  django/contrib/admin/templates/admin/change_list.html
... ...
@@ -0,0 +1,20 @@
  1
+{% load admin_list %}
  2
+{% load i18n %}
  3
+{% extends "admin/base_site" %}
  4
+{% block bodyclass %}change-list{% endblock %}
  5
+{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
  6
+{% block coltype %}flex{% endblock %}
  7
+{% block content %}
  8
+<div id="content-main">
  9
+{% if has_add_permission %}
  10
+<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
  11
+{% endif %}
  12
+<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
  13
+{% search_form cl %}
  14
+{% date_hierarchy cl %}
  15
+{% filters cl %}
  16
+{% result_list cl %}
  17
+{% pagination cl %}
  18
+</div>
  19
+</div>
  20
+{% endblock %}
13  django/contrib/admin/templates/admin/change_list_results.html
... ...
@@ -0,0 +1,13 @@
  1
+<table cellspacing="0">
  2
+<thead>
  3
+<tr>
  4
+{% for header in result_headers %}<th{{ header.class_attrib }}>
  5
+{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
  6
+{{ header.text|capfirst }}
  7
+{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
  8
+</tr>
  9
+</thead>
  10
+{% for result in results %}
  11
+<tr class="{% cycle row1,row2 %}">{% for item in result %}{{ item }}{% endfor %}</tr>
  12
+{% endfor %}
  13
+</table>
10  django/contrib/admin/templates/admin/date_hierarchy.html
... ...
@@ -0,0 +1,10 @@
  1
+{% if show %}
  2
+<div class="xfull">
  3
+<ul class="toplinks">
  4
+{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %}
  5
+{% for choice in choices %}
  6
+<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
  7
+{% endfor %}
  8
+</ul><br class="clear" />
  9
+</div>
  10
+{% endif %}
15  django/contrib/admin/templates/admin/edit_inline_stacked.html
... ...
@@ -0,0 +1,15 @@
  1
+<fieldset class="module aligned">
  2
+   {% for fcw in bound_related_object.form_field_collection_wrappers %}
  3
+      <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
  4
+      {% if bound_related_object.show_url %}{% if fcw.obj.original %}
  5
+      <p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p>
  6
+      {% endif %}{% endif %}
  7
+      {% for bound_field in fcw.bound_fields %}
  8
+         {% if bound_field.hidden %}
  9
+            {% field_widget bound_field %}
  10
+         {% else %}
  11
+            {% admin_field_line bound_field %}
  12
+         {% endif %}
  13
+      {% endfor %}
  14
+    {% endfor %}
  15
+</fieldset>
42  django/contrib/admin/templates/admin/edit_inline_tabular.html
... ...
@@ -0,0 +1,42 @@
  1
+<fieldset class="module">
  2
+   <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
  3
+   <thead><tr>
  4
+   {% for fw in bound_related_object.field_wrapper_list %}
  5
+      {% if fw.needs_header %}
  6
+         <th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
  7
+      {% endif %}
  8
+   {% endfor %}
  9
+   {% for fcw in bound_related_object.form_field_collection_wrappers %}
  10
+      {% if change %}{% if original_row_needed %}
  11
+         {% if fcw.obj.original %}
  12
+            <tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
  13
+         {% endif %}
  14
+      {% endif %}{% endif %}
  15
+      {% if fcw.obj.errors %}
  16
+         <tr class="errorlist"><td colspan="{{ num_headers }}">
  17
+            {{ fcw.obj.html_combined_error_list }}
  18
+         </tr>
  19
+      {% endif %}
  20
+      <tr class="{% cycle row1,row2 %}">
  21
+      {% for bound_field in fcw.bound_fields %}
  22
+         {% if not bound_field.hidden %}
  23
+         <td {{ bound_field.cell_class_attribute }}>
  24
+            {% field_widget bound_field %}
  25
+         </td>
  26
+         {% endif %}
  27
+      {% endfor %}
  28
+      {% if bound_related_object.show_url %}<td>
  29
+         {% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
  30
+      </td>{% endif %}
  31
+      </tr>
  32
+
  33
+   {% endfor %} </table>
  34
+
  35
+   {% for fcw in bound_related_object.form_field_collection_wrappers %}
  36
+      {% for bound_field in fcw.bound_fields %}
  37
+         {% if bound_field.hidden %}
  38
+            {% field_widget bound_field %}
  39
+         {% endif %}
  40
+      {% endfor %}
  41
+   {% endfor %}
  42
+</fieldset>
21  django/contrib/admin/templates/admin/field_line.html
... ...
@@ -0,0 +1,21 @@
  1
+<div class="{{ class_names }}" >
  2
+{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
  3
+{% for bound_field in bound_fields %}
  4
+  {% if bound_field.has_label_first %}
  5
+    {% field_label bound_field %}
  6
+  {% endif %}
  7
+  {% field_widget bound_field %}
  8
+  {% if not bound_field.has_label_first %}
  9
+    {% field_label bound_field %}
  10
+  {% endif %}
  11
+  {% if change %}
  12
+    {% if bound_field.field.primary_key %}
  13
+      {{ bound_field.original_value }}
  14
+    {% endif %}
  15
+    {% if bound_field.raw_id_admin %}
  16
+      {% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}
  17
+    {% endif %}
  18
+  {% endif %}
  19
+  {% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %}
  20
+{% endfor %}
  21
+</div>
7  django/contrib/admin/templates/admin/filter.html
... ...
@@ -0,0 +1,7 @@
  1
+<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
  2
+<ul>
  3
+{% for choice in choices %}
  4
+    <li{% if choice.selected %} class="selected"{% endif %}>
  5
+    <a href="{{ choice.query_string }}">{{ choice.display }}</a></li>
  6
+{% endfor %}
  7
+</ul>
5  django/contrib/admin/templates/admin/filters.html
... ...
@@ -0,0 +1,5 @@
  1
+{% if cl.has_filters %}<div id="changelist-filter">
  2
+<h2>Filter</h2>
  3
+{% for spec in cl.filter_specs %}
  4
+   {% filter cl spec %}
  5
+{% endfor %}</div>{% endif %}
9  django/contrib/admin/templates/admin/pagination.html
... ...
@@ -0,0 +1,9 @@
  1
+<p class="paginator">
  2
+{% if pagination_required %}
  3
+{% for i in page_range %}
  4
+    {% paginator_number cl i %}
  5
+{% endfor %}
  6
+{% endif %}
  7
+{{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %}
  8
+{% if show_all_url %}&nbsp;&nbsp;<a href="{{ show_all_url }}" class="showall">Show all</a>{% endif %}
  9
+</p>
14  django/contrib/admin/templates/admin/search_form.html
... ...
@@ -0,0 +1,14 @@
  1
+{% if cl.lookup_opts.admin.search_fields %}
  2
+<div id="toolbar"><form id="changelist-search" action="" method="get">
  3
+<label><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" /></label>
  4
+<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" />
  5
+<input type="submit" value="Go" />
  6
+{% if show_result_count %}
  7
+    <span class="small quiet">{{ cl.result_count }} result{{ cl.result_count|pluralize }} (<a href="?">{{ cl.full_result_count }} total</a>)</span>
  8
+{% endif %}
  9
+{% for pair in cl.params.items %}
  10
+    {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %}
  11
+{% endfor %}
  12
+</form></div>
  13
+<script type="text/javascript">document.getElementById("searchbar").focus();</script>
  14
+{% endif %}
8  django/contrib/admin/templates/admin/submit_line.html
... ...
@@ -0,0 +1,8 @@
  1
+{% load i18n %}
  2
+<div class="submit-row">
  3
+{% if show_delete_link %}<p class="float-left"><a href="delete/" class="deletelink">{% trans "Delete" %}</a></p>{% endif %}
  4
+{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
  5
+{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }} />{% endif %}
  6
+{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}
  7
+{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" {{ onclick_attrib }}/>{% endif %}
  8
+</div>
22  django/contrib/admin/templates/admin_doc/bookmarklets.html
... ...
@@ -1,30 +1,32 @@
1 1
 {% extends "admin/base_site" %}
2 2
 
3  
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Bookmarklets</div>{% endblock %}
  3
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
4 4
 
5  
-{% block title %}Documentation bookmarklets{% endblock %}
  5
+{% block title %}{% trans "Documentation bookmarklets" %}{% endblock %}
6 6
 
7 7
 {% block content %}
8 8
 
  9
+{% blocktrans %}
9 10
 <p class="help">To install bookmarklets, drag the link to your bookmarks
10 11
 toolbar, or right-click the link and add it to your bookmarks. Now you can
11 12
 select the bookmarklet from any page in the site.  Note that some of these
12 13
 bookmarklets require you to be viewing the site from a computer designated
13 14
 as "internal" (talk to your system administrator if you aren't sure if
14 15
 your computer is "internal").</p>
  16
+{% endblocktrans %}
15 17
 
16 18
 <div id="content-main">
17  
-    <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>
18  
-    <p>Jumps you from any page to the documentation for the view that generates that page.</p>
  19
+    <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+'/';})()">{% trans "Documentation for this page" %}</a></h3>
  20
+    <p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p>
19 21
 
20  
-    <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>
21  
-    <p>Shows the content-type and unique ID for pages that represent a single object.</p>
  22
+    <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);})();">{% trans "Show object ID" %}</a></h3>
  23
+    <p>{% trans "Shows the content-type and unique ID for pages that represent a single object." %}</p>
22 24
 
23  
-    <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>
24  
-    <p>Jumps to the admin page for pages that represent a single object.</p>
  25
+    <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+'/';})()">{% trans "Edit this object (current window)" %}</a></h3>
  26
+    <p>{% trans "Jumps to the admin page for pages that represent a single object." %}</p>
25 27
 
26  
-    <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>
27  
-    <p>As above, but opens the admin page in a new window.</p>
  28
+    <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+'/');})()">{% trans "Edit this object (new window)" %}</a></h3>
  29
+    <p>{% trans "As above, but opens the admin page in a new window." %}</p>
28 30
 </div>
29 31
 
30 32
 {% endblock %}
4  django/contrib/admin/templates/widget/date_time.html
... ...
@@ -0,0 +1,4 @@
  1
+<p class="datetime"> 
  2
+   Date: {{ bound_field.form_fields.0 }}<br />
  3
+   Time: {{ bound_field.form_fields.1 }}
  4
+</p>
1  django/contrib/admin/templates/widget/default.html
... ...
@@ -0,0 +1 @@
  1
+{% output_all bound_field.form_fields %}
4  django/contrib/admin/templates/widget/file.html
... ...
@@ -0,0 +1,4 @@
  1
+{% if bound_field.original_value %}
  2
+Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
  3
+Change: {% output_all bound_field.form_fields %}
  4
+{% else %} {% output_all bound_field.form_fields %} {% endif %}
7  django/contrib/admin/templates/widget/foreign.html
... ...
@@ -0,0 +1,7 @@
  1
+{% output_all bound_field.form_fields %}
  2
+{% if bound_field.raw_id_admin %}
  3
+            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
  4
+{% else %}
  5
+{% if bound_field.needs_add_label %}
  6
+            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.element_id}}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>
  7
+{% endif %} {% endif %}
1  django/contrib/admin/templates/widget/many_to_many.html
... ...
@@ -0,0 +1 @@
  1
+{% include "widget/foreign" %}
278  django/contrib/admin/templatetags/admin_list.py
... ...
@@ -0,0 +1,278 @@
  1
+from django.contrib.admin.views.main import MAX_SHOW_ALL_ALLOWED, DEFAULT_RESULTS_PER_PAGE, ALL_VAR
  2
+from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
  3
+from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
  4
+from django.core import meta, template
  5
+from django.core.exceptions import ObjectDoesNotExist
  6
+from django.core.template.decorators import simple_tag, inclusion_tag
  7
+from django.utils import dateformat
  8
+from django.utils.html import strip_tags, escape
  9
+from django.utils.text import capfirst
  10
+from django.utils.translation import get_date_formats
  11
+from django.conf.settings import ADMIN_MEDIA_PREFIX
  12
+
  13
+DOT = '.'
  14
+
  15
+#@simple_tag
  16
+def paginator_number(cl,i):
  17
+    if i == DOT:
  18
+       return '... '
  19
+    elif i == cl.page_num:
  20
+       return '<span class="this-page">%d</span> ' % (i+1)
  21
+    else:
  22
+       return '<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1)
  23
+paginator_number = simple_tag(paginator_number)
  24
+
  25
+#@inclusion_tag('admin/pagination')
  26
+def pagination(cl):
  27
+    paginator, page_num = cl.paginator, cl.page_num
  28
+
  29
+    pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page
  30
+    if not pagination_required:
  31
+        page_range = []
  32
+    else:
  33
+        ON_EACH_SIDE = 3
  34
+        ON_ENDS = 2
  35
+
  36
+        # If there are 10 or fewer pages, display links to every page.
  37
+        # Otherwise, do some fancy
  38
+        if paginator.pages <= 10:
  39
+            page_range = range(paginator.pages)
  40
+        else:
  41
+            # Insert "smart" pagination links, so that there are always ON_ENDS
  42
+            # links at either end of the list of pages, and there are always
  43
+            # ON_EACH_SIDE links at either end of the "current page" link.
  44
+            page_range = []
  45
+            if page_num > (ON_EACH_SIDE + ON_ENDS):
  46
+                page_range.extend(range(0, ON_EACH_SIDE - 1))
  47
+                page_range.append(DOT)
  48
+                page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
  49
+            else:
  50
+                page_range.extend(range(0, page_num + 1))
  51
+            if page_num < (paginator.pages - ON_EACH_SIDE - ON_ENDS - 1):
  52
+                page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
  53
+                page_range.append(DOT)
  54
+                page_range.extend(range(paginator.pages - ON_ENDS, paginator.pages))
  55
+            else:
  56
+                page_range.extend(range(page_num + 1, paginator.pages))
  57
+
  58
+    need_show_all_link = cl.can_show_all and not cl.show_all and cl.multi_page
  59
+    return {
  60
+        'cl': cl,
  61
+        'pagination_required': pagination_required,
  62
+        'show_all_url': need_show_all_link and cl.get_query_string({ALL_VAR: ''}),
  63
+        'page_range': page_range,
  64
+        'ALL_VAR': ALL_VAR,
  65
+        '1': 1,
  66
+    }
  67
+pagination = inclusion_tag('admin/pagination')(pagination)
  68
+
  69
+def result_headers(cl):
  70
+    lookup_opts = cl.lookup_opts
  71
+
  72
+    for i, field_name in enumerate(lookup_opts.admin.list_display):
  73
+        try:
  74
+            f = lookup_opts.get_field(field_name)
  75
+        except meta.FieldDoesNotExist:
  76
+            # For non-field list_display values, check for the function
  77
+            # attribute "short_description". If that doesn't exist, fall
  78
+            # back to the method name. And __repr__ is a special-case.
  79
+            if field_name == '__repr__':
  80
+                header = lookup_opts.verbose_name
  81
+            else:
  82
+                func = getattr(cl.mod.Klass, field_name) # Let AttributeErrors propogate.
  83
+                try:
  84
+                    header = func.short_description
  85
+                except AttributeError:
  86
+                    header = func.__name__.replace('_', ' ')
  87
+            # Non-field list_display values don't get ordering capability.
  88
+            yield {"text": header}
  89
+        else:
  90
+            if isinstance(f.rel, meta.ManyToOne) and f.null:
  91
+                yield {"text": f.verbose_name}
  92
+            else:
  93
+                th_classes = []
  94
+                new_order_type = 'asc'
  95
+                if field_name == cl.order_field:
  96
+                    th_classes.append('sorted %sending' % cl.order_type.lower())
  97
+                    new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
  98
+
  99
+                yield {"text": f.verbose_name,
  100
+                       "sortable": True,
  101
+                       "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
  102
+                       "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
  103
+
  104
+def items_for_result(cl, result):
  105
+    first = True
  106
+    pk = cl.lookup_opts.pk.attname
  107
+    for field_name in cl.lookup_opts.admin.list_display:
  108
+        row_class = ''
  109
+        try:
  110
+            f = cl.lookup_opts.get_field(field_name)
  111
+        except meta.FieldDoesNotExist:
  112
+            # For non-field list_display values, the value is a method
  113
+            # name. Execute the method.
  114
+            try:
  115
+                func = getattr(result, field_name)
  116
+                result_repr = str(func())
  117
+            except AttributeError, ObjectDoesNotExist:
  118
+                result_repr = EMPTY_CHANGELIST_VALUE
  119
+            else:
  120
+                # Strip HTML tags in the resulting text, except if the
  121
+                # function has an "allow_tags" attribute set to True.
  122
+                if not getattr(func, 'allow_tags', False):
  123
+                    result_repr = strip_tags(result_repr)
  124
+        else:
  125
+            field_val = getattr(result, f.attname)
  126
+
  127
+            if isinstance(f.rel, meta.ManyToOne):
  128
+                if field_val is not None:
  129
+                    result_repr = getattr(result, 'get_%s' % f.name)()
  130
+                else:
  131
+                    result_repr = EMPTY_CHANGELIST_VALUE
  132
+            # Dates and times are special: They're formatted in a certain way.
  133
+            elif isinstance(f, meta.DateField) or isinstance(f, meta.TimeField):
  134
+                if field_val:
  135
+                    (date_format, datetime_format, time_format) = get_date_formats()
  136
+                    if isinstance(f, meta.DateTimeField):
  137
+                        result_repr = capfirst(dateformat.format(field_val, datetime_format))
  138
+                    elif isinstance(f, meta.TimeField):
  139
+                        result_repr = capfirst(dateformat.time_format(field_val, time_format))
  140
+                    else:
  141
+                        result_repr = capfirst(dateformat.format(field_val, date_format))
  142
+                else:
  143
+                    result_repr = EMPTY_CHANGELIST_VALUE
  144
+                row_class = ' class="nowrap"'
  145
+            # Booleans are special: We use images.
  146
+            elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
  147
+                BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
  148
+                result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
  149
+            # ImageFields are special: Use a thumbnail.
  150
+            elif isinstance(f, meta.ImageField):
  151
+                from django.parts.media.photos import get_thumbnail_url
  152
+                result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
  153
+            # FloatFields are special: Zero-pad the decimals.
  154
+            elif isinstance(f, meta.FloatField):
  155
+                if field_val is not None:
  156
+                    result_repr = ('%%.%sf' % f.decimal_places) % field_val
  157
+                else:
  158
+                    result_repr = EMPTY_CHANGELIST_VALUE
  159
+            # Fields with choices are special: Use the representation
  160
+            # of the choice.
  161
+            elif f.choices:
  162
+                result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
  163
+            else:
  164
+                result_repr = strip_tags(str(field_val))
  165
+        if result_repr == '':
  166
+                result_repr = '&nbsp;'
  167
+        if first: # First column is a special case
  168
+            first = False
  169
+            url = cl.url_for_result(result)
  170
+            result_id = getattr(result, pk)
  171
+            yield ('<th%s><a href="%s"%s>%s</a></th>' % \
  172
+                (row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr))
  173
+        else:
  174
+            yield ('<td%s>%s</td>' % (row_class, result_repr))
  175
+
  176
+def results(cl):
  177
+    for res in cl.result_list:
  178
+        yield list(items_for_result(cl,res))
  179
+
  180
+#@inclusion_tag("admin/change_list_results")
  181
+def result_list(cl):
  182
+    res = list(results(cl))
  183
+    return {'cl': cl,
  184
+            'result_headers': list(result_headers(cl)),
  185
+            'results': list(results(cl))}
  186
+result_list = inclusion_tag("admin/change_list_results")(result_list)
  187
+
  188
+#@inclusion_tag("admin/date_hierarchy")
  189
+def date_hierarchy(cl):
  190
+    lookup_opts, params, lookup_params, lookup_mod = \
  191
+      cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
  192
+
  193
+    if lookup_opts.admin.date_hierarchy:
  194
+        field_name = lookup_opts.admin.date_hierarchy
  195
+
  196
+        year_field = '%s__year' % field_name
  197
+        month_field = '%s__month' % field_name
  198
+        day_field = '%s__day' % field_name
  199
+        field_generic = '%s__' % field_name
  200
+        year_lookup = params.get(year_field)
  201
+        month_lookup = params.get(month_field)
  202
+        day_lookup = params.get(day_field)
  203
+
  204
+        def link(d):
  205
+            return cl.get_query_string(d, [field_generic])
  206
+
  207
+        def get_dates(unit, params):
  208
+            return getattr(lookup_mod, 'get_%s_list' % field_name)(unit, **params)
  209
+
  210
+        if year_lookup and month_lookup and day_lookup:
  211
+            month_name = MONTHS[int(month_lookup)]
  212
+            return {
  213
+                'show': True,
  214
+                'back': {
  215
+                    'link': link({year_field: year_lookup, month_field: month_lookup}),
  216
+                    'title': "%s %s" % (month_name, year_lookup)
  217
+                },
  218
+                'choices': [{'title': "%s %s" % (month_name, day_lookup)}]
  219
+            }
  220
+        elif year_lookup and month_lookup:
  221
+            date_lookup_params = lookup_params.copy()
  222
+            date_lookup_params.update({year_field: year_lookup, month_field: month_lookup})
  223
+            days = get_dates('day', date_lookup_params)
  224
+            return {
  225
+                'show': True,
  226
+                'back': {
  227
+                    'link': link({year_field: year_lookup}),
  228
+                    'title': year_lookup
  229
+                },
  230
+                'choices': [{
  231
+                    'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}),
  232
+                    'title': day.strftime('%B %d')
  233
+                } for day in days]
  234
+            }
  235
+        elif year_lookup:
  236
+            date_lookup_params = lookup_params.copy()
  237
+            date_lookup_params.update({year_field: year_lookup})
  238
+            months = get_dates('month', date_lookup_params)
  239
+            return {
  240
+                'show' : True,
  241
+                'back': {
  242
+                    'link' : link({}),
  243
+                    'title': _('All dates')
  244
+                },
  245
+                'choices': [{
  246
+                    'link': link( {year_field: year_lookup, month_field: month.month}),
  247
+                    'title': "%s %s" % (month.strftime('%B') ,  month.year)
  248
+                } for month in months]
  249
+            }
  250
+        else:
  251
+            years = get_dates('year', lookup_params)
  252
+            return {
  253
+                'show': True,
  254
+                'choices': [{
  255
+                    'link': link({year_field: year.year}),
  256
+                    'title': year.year
  257
+                } for year in years ]
  258
+            }
  259
+date_hierarchy = inclusion_tag('admin/date_hierarchy')(date_hierarchy)
  260
+
  261
+#@inclusion_tag('admin/search_form')
  262
+def search_form(cl):
  263
+    return {
  264
+        'cl': cl,
  265
+        'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
  266
+        'search_var': SEARCH_VAR
  267
+    }
  268
+search_form = inclusion_tag('admin/search_form')(search_form)
  269
+
  270
+#@inclusion_tag('admin/filter')
  271
+def filter(cl, spec):
  272
+    return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
  273
+filter = inclusion_tag('admin/filter')(filter)
  274
+
  275
+#@inclusion_tag('admin/filters')
  276
+def filters(cl):
  277
+    return {'cl': cl}
  278
+filters = inclusion_tag('admin/filters')(filters)
258  django/contrib/admin/templatetags/admin_modify.py
... ...
@@ -0,0 +1,258 @@
  1
+from django.core import template, template_loader, meta
  2
+from django.utils.html import escape
  3
+from django.utils.text import capfirst
  4
+from django.utils.functional import curry
  5
+from django.core.template.decorators import simple_tag, inclusion_tag
  6
+from django.contrib.admin.views.main import AdminBoundField
  7
+from django.core.meta.fields import BoundField, Field
  8
+from django.core.meta import BoundRelatedObject, TABULAR, STACKED
  9
+from django.conf.settings import ADMIN_MEDIA_PREFIX
  10
+import re
  11
+
  12
+word_re = re.compile('[A-Z][a-z]+')
  13
+
  14
+def class_name_to_underscored(name):
  15
+    return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
  16
+
  17
+#@simple_tag
  18
+def include_admin_script(script_path):
  19
+    return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
  20
+include_admin_script = simple_tag(include_admin_script)
  21
+
  22
+#@inclusion_tag('admin/submit_line', takes_context=True)
  23
+def submit_row(context, bound_manipulator):
  24
+    change = context['change']
  25
+    add = context['add']
  26
+    show_delete = context['show_delete']
  27
+    has_delete_permission = context['has_delete_permission']
  28
+    is_popup = context['is_popup']
  29
+    return {
  30
+        'onclick_attrib': (bound_manipulator.ordered_objects and change
  31
+                            and 'onclick="submitOrderForm();"' or ''),
  32
+        'show_delete_link': (not is_popup and has_delete_permission
  33
+                              and (change or show_delete)),
  34
+        'show_save_as_new': not is_popup and change and bound_manipulator.save_as,
  35
+        'show_save_and_add_another': not is_popup and (not bound_manipulator.save_as or add),
  36
+        'show_save_and_continue': not is_popup,
  37
+        'show_save': True
  38
+    }
  39
+submit_row = inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
  40
+
  41
+#@simple_tag
  42
+def field_label(bound_field):
  43
+    class_names = []
  44
+    if isinstance(bound_field.field, meta.BooleanField):
  45
+        class_names.append("vCheckboxLabel")
  46
+        colon = ""
  47
+    else:
  48
+        if not bound_field.field.blank:
  49
+            class_names.append('required')
  50
+        if not bound_field.first:
  51
+            class_names.append('inline')
  52
+        colon = ":"
  53
+    class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
  54
+    return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \
  55
+        capfirst(bound_field.field.verbose_name), colon)
  56
+field_label = simple_tag(field_label)
  57
+
  58
+class FieldWidgetNode(template.Node):
  59
+    nodelists = {}
  60
+    default = None
  61
+
  62
+    def __init__(self, bound_field_var):
  63
+        self.bound_field_var = bound_field_var
  64
+
  65
+    def get_nodelist(cls, klass):
  66
+        if not cls.nodelists.has_key(klass):
  67
+            try:
  68
+                field_class_name = klass.__name__
  69
+                template_name = "widget/%s" % \
  70
+                    class_name_to_underscored(field_class_name)
  71
+                nodelist = template_loader.get_template(template_name).nodelist
  72
+            except template.TemplateDoesNotExist:
  73
+                super_klass = bool(klass.__bases__) and klass.__bases__[0] or None
  74
+                if super_klass and super_klass != Field:
  75
+                    nodelist = cls.get_nodelist(super_klass)
  76
+                else:
  77
+                    if not cls.default:
  78
+                        cls.default = template_loader.get_template("widget/default").nodelist
  79
+                    nodelist = cls.default
  80
+
  81
+            cls.nodelists[klass] = nodelist
  82
+            return nodelist
  83
+        else:
  84
+            return cls.nodelists[klass]
  85
+    get_nodelist = classmethod(get_nodelist)
  86
+
  87
+    def render(self, context):
  88
+        bound_field = template.resolve_variable(self.bound_field_var, context)
  89
+
  90
+        context.push()
  91
+        context['bound_field'] = bound_field
  92
+
  93
+        output = self.get_nodelist(bound_field.field.__class__).render(context)
  94
+        context.pop()
  95
+        return output
  96
+
  97
+class FieldWrapper(object):
  98
+    def __init__(self, field ):
  99
+        self.field = field
  100
+
  101
+    def needs_header(self):
  102
+        return not isinstance(self.field, meta.AutoField)
  103
+
  104
+    def header_class_attribute(self):
  105
+        return self.field.blank and ' class="optional"' or ''
  106
+
  107
+    def use_raw_id_admin(self):
  108
+         return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \
  109
+            and self.field.rel.raw_id_admin
  110
+
  111
+class FormFieldCollectionWrapper(object):
  112
+    def __init__(self, field_mapping, fields):
  113
+        self.field_mapping = field_mapping
  114
+        self.fields = fields
  115
+        self.bound_fields = [AdminBoundField(field, self.field_mapping, field_mapping['original'])
  116
+                             for field in self.fields]
  117
+
  118
+class TabularBoundRelatedObject(BoundRelatedObject):
  119
+    def __init__(self, related_object, field_mapping, original):
  120
+        super(TabularBoundRelatedObject, self).__init__(related_object, field_mapping, original)
  121
+        self.field_wrapper_list = [FieldWrapper(field) for field in self.relation.editable_fields()]
  122
+
  123
+        fields = self.relation.editable_fields()
  124
+
  125
+        self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping, fields)
  126
+                                               for field_mapping in self.field_mappings]
  127
+        self.original_row_needed = max([fw.use_raw_id_admin() for fw in self.field_wrapper_list])
  128
+        self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
  129
+
  130
+    def template_name(self):
  131
+        return "admin/edit_inline_tabular"
  132
+
  133
+class StackedBoundRelatedObject(BoundRelatedObject):
  134
+    def __init__(self, related_object, field_mapping, original):
  135
+        super(StackedBoundRelatedObject, self).__init__(related_object, field_mapping, original)
  136
+        fields = self.relation.editable_fields()
  137
+        self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields)
  138
+                                               for field_mapping in self.field_mappings]
  139
+        self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
  140
+
  141
+    def template_name(self):
  142
+        return "admin/edit_inline_stacked"
  143
+
  144
+bound_related_object_overrides = {
  145
+    TABULAR: TabularBoundRelatedObject,
  146
+    STACKED: StackedBoundRelatedObject,
  147
+}
  148
+
  149
+class EditInlineNode(template.Node):
  150
+    def __init__(self, rel_var):
  151
+        self.rel_var = rel_var
  152
+
  153
+    def render(self, context):
  154
+        relation = template.resolve_variable(self.rel_var, context)
  155
+
  156
+        context.push()
  157
+
  158
+        klass = relation.field.rel.edit_inline
  159
+        bound_related_object_class = bound_related_object_overrides.get(klass, klass)
  160
+
  161
+        original = context.get('original', None)
  162
+
  163
+        bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
  164
+        context['bound_related_object'] = bound_related_object
  165
+
  166
+        t = template_loader.get_template(bound_related_object.template_name())
  167
+
  168
+        output = t.render(context)
  169
+
  170
+        context.pop()
  171
+        return output
  172
+
  173
+#@simple_tag
  174
+def output_all(form_fields):
  175
+    return ''.join([str(f) for f in form_fields])
  176
+output_all = simple_tag(output_all)
  177
+
  178
+#@simple_tag
  179
+def auto_populated_field_script(auto_pop_fields, change = False):
  180
+    for field in auto_pop_fields:
  181
+        t = []
  182
+        if change:
  183
+            t.append('document.getElementById("id_%s")._changed = true;' % field.name)
  184
+        else:
  185
+            t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name)
  186
+
  187
+        add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from])
  188
+        for f in field.prepopulate_from:
  189
+            t.append('document.getElementById("id_%s").onkeyup = function() {' \
  190
+                     ' var e = document.getElementById("id_%s");' \
  191
+                     ' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
  192
+                     f, field.name, add_values, field.maxlength))
  193
+    return ''.join(t)
  194
+auto_populated_field_script = simple_tag(auto_populated_field_script)
  195
+
  196
+#@simple_tag
  197
+def filter_interface_script_maybe(bound_field):
  198
+    f = bound_field.field
  199
+    if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
  200
+       return '<script type="text/javascript">addEvent(window, "load", function(e) {' \
  201
+              ' SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (
  202
+              f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
  203
+    else:
  204
+        return ''
  205
+filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)
  206
+
  207
+def do_one_arg_tag(node_factory, parser,token):
  208
+    tokens = token.contents.split()
  209
+    if len(tokens) != 2:
  210
+        raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0])
  211
+    return node_factory(tokens[1])
  212
+
  213
+def register_one_arg_tag(node):
  214
+    tag_name = class_name_to_underscored(node.__name__)
  215
+    parse_func = curry(do_one_arg_tag, node)
  216
+    template.register_tag(tag_name, parse_func)
  217
+
  218
+one_arg_tag_nodes = (
  219
+    FieldWidgetNode,
  220
+    EditInlineNode,
  221
+)
  222
+
  223
+for node in one_arg_tag_nodes:
  224
+    register_one_arg_tag(node)
  225
+
  226
+#@inclusion_tag('admin/field_line', takes_context=True)
  227
+def admin_field_line(context, argument_val):
  228
+    if (isinstance(argument_val, BoundField)):
  229
+        bound_fields = [argument_val]
  230
+    else:
  231
+        bound_fields = [bf for bf in argument_val]
  232
+    add = context['add']
  233
+    change = context['change']
  234
+
  235
+    class_names = ['form-row']
  236
+    for bound_field in bound_fields:
  237
+        for f in bound_field.form_fields:
  238
+            if f.errors():
  239
+                class_names.append('errors')
  240
+                break
  241
+
  242
+    # Assumes BooleanFields won't be stacked next to each other!
  243
+    if isinstance(bound_fields[0].field, meta.BooleanField):
  244
+        class_names.append('checkbox-row')
  245
+
  246
+    return {
  247
+        'add': context['add'],
  248
+        'change': context['change'],
  249
+        'bound_fields':  bound_fields,
  250
+        'class_names': " ".join(class_names),
  251
+    }
  252
+admin_field_line = inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
  253
+
  254
+#@simple_tag
  255
+def object_pk(bound_manip, ordered_obj):
  256
+    return bound_manip.get_ordered_object_pk(ordered_obj)
  257
+
  258
+object_pk = simple_tag(object_pk)
1,307  django/contrib/admin/views/main.py