Hello site
[auf_framonde.git] / eggs / Django-1.4.5-py2.7.egg / django / contrib / admin / templatetags / admin_list.py
1 import datetime
2
3 from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
4 from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
5 ORDER_VAR, PAGE_VAR, SEARCH_VAR)
6 from django.contrib.admin.templatetags.admin_static import static
7 from django.core.exceptions import ObjectDoesNotExist
8 from django.db import models
9 from django.utils import formats
10 from django.utils.html import escape, conditional_escape
11 from django.utils.safestring import mark_safe
12 from django.utils.text import capfirst
13 from django.utils.translation import ugettext as _
14 from django.utils.encoding import smart_unicode, force_unicode
15 from django.template import Library
16 from django.template.loader import get_template
17 from django.template.context import Context
18
19 register = Library()
20
21 DOT = '.'
22
23 @register.simple_tag
24 def paginator_number(cl,i):
25 """
26 Generates an individual page index link in a paginated list.
27 """
28 if i == DOT:
29 return u'... '
30 elif i == cl.page_num:
31 return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
32 else:
33 return mark_safe(u'<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
34
35 @register.inclusion_tag('admin/pagination.html')
36 def pagination(cl):
37 """
38 Generates the series of links to the pages in a paginated list.
39 """
40 paginator, page_num = cl.paginator, cl.page_num
41
42 pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page
43 if not pagination_required:
44 page_range = []
45 else:
46 ON_EACH_SIDE = 3
47 ON_ENDS = 2
48
49 # If there are 10 or fewer pages, display links to every page.
50 # Otherwise, do some fancy
51 if paginator.num_pages <= 10:
52 page_range = range(paginator.num_pages)
53 else:
54 # Insert "smart" pagination links, so that there are always ON_ENDS
55 # links at either end of the list of pages, and there are always
56 # ON_EACH_SIDE links at either end of the "current page" link.
57 page_range = []
58 if page_num > (ON_EACH_SIDE + ON_ENDS):
59 page_range.extend(range(0, ON_EACH_SIDE - 1))
60 page_range.append(DOT)
61 page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
62 else:
63 page_range.extend(range(0, page_num + 1))
64 if page_num < (paginator.num_pages - ON_EACH_SIDE - ON_ENDS - 1):
65 page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
66 page_range.append(DOT)
67 page_range.extend(range(paginator.num_pages - ON_ENDS, paginator.num_pages))
68 else:
69 page_range.extend(range(page_num + 1, paginator.num_pages))
70
71 need_show_all_link = cl.can_show_all and not cl.show_all and cl.multi_page
72 return {
73 'cl': cl,
74 'pagination_required': pagination_required,
75 'show_all_url': need_show_all_link and cl.get_query_string({ALL_VAR: ''}),
76 'page_range': page_range,
77 'ALL_VAR': ALL_VAR,
78 '1': 1,
79 }
80
81 def result_headers(cl):
82 """
83 Generates the list column headers.
84 """
85 ordering_field_columns = cl.get_ordering_field_columns()
86 for i, field_name in enumerate(cl.list_display):
87 text, attr = label_for_field(field_name, cl.model,
88 model_admin = cl.model_admin,
89 return_attr = True
90 )
91 if attr:
92 # Potentially not sortable
93
94 # if the field is the action checkbox: no sorting and special class
95 if field_name == 'action_checkbox':
96 yield {
97 "text": text,
98 "class_attrib": mark_safe(' class="action-checkbox-column"'),
99 "sortable": False,
100 }
101 continue
102
103 admin_order_field = getattr(attr, "admin_order_field", None)
104 if not admin_order_field:
105 # Not sortable
106 yield {
107 "text": text,
108 "sortable": False,
109 }
110 continue
111
112 # OK, it is sortable if we got this far
113 th_classes = ['sortable']
114 order_type = ''
115 new_order_type = 'asc'
116 sort_priority = 0
117 sorted = False
118 # Is it currently being sorted on?
119 if i in ordering_field_columns:
120 sorted = True
121 order_type = ordering_field_columns.get(i).lower()
122 sort_priority = ordering_field_columns.keys().index(i) + 1
123 th_classes.append('sorted %sending' % order_type)
124 new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
125
126 # build new ordering param
127 o_list_primary = [] # URL for making this field the primary sort
128 o_list_remove = [] # URL for removing this field from sort
129 o_list_toggle = [] # URL for toggling order type for this field
130 make_qs_param = lambda t, n: ('-' if t == 'desc' else '') + str(n)
131
132 for j, ot in ordering_field_columns.items():
133 if j == i: # Same column
134 param = make_qs_param(new_order_type, j)
135 # We want clicking on this header to bring the ordering to the
136 # front
137 o_list_primary.insert(0, param)
138 o_list_toggle.append(param)
139 # o_list_remove - omit
140 else:
141 param = make_qs_param(ot, j)
142 o_list_primary.append(param)
143 o_list_toggle.append(param)
144 o_list_remove.append(param)
145
146 if i not in ordering_field_columns:
147 o_list_primary.insert(0, make_qs_param(new_order_type, i))
148
149
150 yield {
151 "text": text,
152 "sortable": True,
153 "sorted": sorted,
154 "ascending": order_type == "asc",
155 "sort_priority": sort_priority,
156 "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}),
157 "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
158 "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}),
159 "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
160 }
161
162 def _boolean_icon(field_val):
163 icon_url = static('admin/img/icon-%s.gif' %
164 {True: 'yes', False: 'no', None: 'unknown'}[field_val])
165 return mark_safe(u'<img src="%s" alt="%s" />' % (icon_url, field_val))
166
167 def items_for_result(cl, result, form):
168 """
169 Generates the actual list of data.
170 """
171 first = True
172 pk = cl.lookup_opts.pk.attname
173 for field_name in cl.list_display:
174 row_class = ''
175 try:
176 f, attr, value = lookup_field(field_name, result, cl.model_admin)
177 except (AttributeError, ObjectDoesNotExist):
178 result_repr = EMPTY_CHANGELIST_VALUE
179 else:
180 if f is None:
181 if field_name == u'action_checkbox':
182 row_class = ' class="action-checkbox"'
183 allow_tags = getattr(attr, 'allow_tags', False)
184 boolean = getattr(attr, 'boolean', False)
185 if boolean:
186 allow_tags = True
187 result_repr = _boolean_icon(value)
188 else:
189 result_repr = smart_unicode(value)
190 # Strip HTML tags in the resulting text, except if the
191 # function has an "allow_tags" attribute set to True.
192 if not allow_tags:
193 result_repr = escape(result_repr)
194 else:
195 result_repr = mark_safe(result_repr)
196 else:
197 if isinstance(f.rel, models.ManyToOneRel):
198 field_val = getattr(result, f.name)
199 if field_val is None:
200 result_repr = EMPTY_CHANGELIST_VALUE
201 else:
202 result_repr = escape(field_val)
203 else:
204 result_repr = display_for_field(value, f)
205 if isinstance(f, models.DateField)\
206 or isinstance(f, models.TimeField)\
207 or isinstance(f, models.ForeignKey):
208 row_class = ' class="nowrap"'
209 if force_unicode(result_repr) == '':
210 result_repr = mark_safe('&nbsp;')
211 # If list_display_links not defined, add the link tag to the first field
212 if (first and not cl.list_display_links) or field_name in cl.list_display_links:
213 table_tag = {True:'th', False:'td'}[first]
214 first = False
215 url = cl.url_for_result(result)
216 # Convert the pk to something that can be used in Javascript.
217 # Problem cases are long ints (23L) and non-ASCII strings.
218 if cl.to_field:
219 attr = str(cl.to_field)
220 else:
221 attr = pk
222 value = result.serializable_value(attr)
223 result_id = repr(force_unicode(value))[1:]
224 yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
225 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
226 else:
227 # By default the fields come from ModelAdmin.list_editable, but if we pull
228 # the fields out of the form instead of list_editable custom admins
229 # can provide fields on a per request basis
230 if (form and field_name in form.fields and not (
231 field_name == cl.model._meta.pk.name and
232 form[cl.model._meta.pk.name].is_hidden)):
233 bf = form[field_name]
234 result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
235 else:
236 result_repr = conditional_escape(result_repr)
237 yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
238 if form and not form[cl.model._meta.pk.name].is_hidden:
239 yield mark_safe(u'<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name]))
240
241 class ResultList(list):
242 # Wrapper class used to return items in a list_editable
243 # changelist, annotated with the form object for error
244 # reporting purposes. Needed to maintain backwards
245 # compatibility with existing admin templates.
246 def __init__(self, form, *items):
247 self.form = form
248 super(ResultList, self).__init__(*items)
249
250 def results(cl):
251 if cl.formset:
252 for res, form in zip(cl.result_list, cl.formset.forms):
253 yield ResultList(form, items_for_result(cl, res, form))
254 else:
255 for res in cl.result_list:
256 yield ResultList(None, items_for_result(cl, res, None))
257
258 def result_hidden_fields(cl):
259 if cl.formset:
260 for res, form in zip(cl.result_list, cl.formset.forms):
261 if form[cl.model._meta.pk.name].is_hidden:
262 yield mark_safe(force_unicode(form[cl.model._meta.pk.name]))
263
264 @register.inclusion_tag("admin/change_list_results.html")
265 def result_list(cl):
266 """
267 Displays the headers and data list together
268 """
269 headers = list(result_headers(cl))
270 num_sorted_fields = 0
271 for h in headers:
272 if h['sortable'] and h['sorted']:
273 num_sorted_fields += 1
274 return {'cl': cl,
275 'result_hidden_fields': list(result_hidden_fields(cl)),
276 'result_headers': headers,
277 'num_sorted_fields': num_sorted_fields,
278 'results': list(results(cl))}
279
280 @register.inclusion_tag('admin/date_hierarchy.html')
281 def date_hierarchy(cl):
282 """
283 Displays the date hierarchy for date drill-down functionality.
284 """
285 if cl.date_hierarchy:
286 field_name = cl.date_hierarchy
287 year_field = '%s__year' % field_name
288 month_field = '%s__month' % field_name
289 day_field = '%s__day' % field_name
290 field_generic = '%s__' % field_name
291 year_lookup = cl.params.get(year_field)
292 month_lookup = cl.params.get(month_field)
293 day_lookup = cl.params.get(day_field)
294
295 link = lambda d: cl.get_query_string(d, [field_generic])
296
297 if not (year_lookup or month_lookup or day_lookup):
298 # select appropriate start level
299 date_range = cl.query_set.aggregate(first=models.Min(field_name),
300 last=models.Max(field_name))
301 if date_range['first'] and date_range['last']:
302 if date_range['first'].year == date_range['last'].year:
303 year_lookup = date_range['first'].year
304 if date_range['first'].month == date_range['last'].month:
305 month_lookup = date_range['first'].month
306
307 if year_lookup and month_lookup and day_lookup:
308 day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
309 return {
310 'show': True,
311 'back': {
312 'link': link({year_field: year_lookup, month_field: month_lookup}),
313 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT'))
314 },
315 'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}]
316 }
317 elif year_lookup and month_lookup:
318 days = cl.query_set.filter(**{year_field: year_lookup, month_field: month_lookup}).dates(field_name, 'day')
319 return {
320 'show': True,
321 'back': {
322 'link': link({year_field: year_lookup}),
323 'title': str(year_lookup)
324 },
325 'choices': [{
326 'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}),
327 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))
328 } for day in days]
329 }
330 elif year_lookup:
331 months = cl.query_set.filter(**{year_field: year_lookup}).dates(field_name, 'month')
332 return {
333 'show' : True,
334 'back': {
335 'link' : link({}),
336 'title': _('All dates')
337 },
338 'choices': [{
339 'link': link({year_field: year_lookup, month_field: month.month}),
340 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT'))
341 } for month in months]
342 }
343 else:
344 years = cl.query_set.dates(field_name, 'year')
345 return {
346 'show': True,
347 'choices': [{
348 'link': link({year_field: str(year.year)}),
349 'title': str(year.year),
350 } for year in years]
351 }
352
353 @register.inclusion_tag('admin/search_form.html')
354 def search_form(cl):
355 """
356 Displays a search form for searching the list.
357 """
358 return {
359 'cl': cl,
360 'show_result_count': cl.result_count != cl.full_result_count,
361 'search_var': SEARCH_VAR
362 }
363
364 @register.simple_tag
365 def admin_list_filter(cl, spec):
366 tpl = get_template(spec.template)
367 return tpl.render(Context({
368 'title': spec.title,
369 'choices' : list(spec.choices(cl)),
370 'spec': spec,
371 }))
372
373 @register.inclusion_tag('admin/actions.html', takes_context=True)
374 def admin_actions(context):
375 """
376 Track the number of times the action field has been rendered on the page,
377 so we know which value to use.
378 """
379 context['action_index'] = context.get('action_index', -1) + 1
380 return context