From b4fd513f9f48f4658258ccae31ecf2e58d550d8b Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Tue, 18 Oct 2005 04:58:34 +0000 Subject: Moved views.admin.main to contrib.admin.views.main. Refs #627. Note that this is still backwards-compatible, assuming people are using django.conf.urls.admin as their admin URLconf git-svn-id: http://code.djangoproject.com/svn/django/trunk@927 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/urls/admin.py | 12 +- django/contrib/admin/views/main.py | 1103 ++++++++++++++++++++++++++++++++++++ django/views/admin/main.py | 1103 ------------------------------------ 3 files changed, 1109 insertions(+), 1109 deletions(-) create mode 100644 django/contrib/admin/views/main.py delete mode 100644 django/views/admin/main.py diff --git a/django/conf/urls/admin.py b/django/conf/urls/admin.py index e33d377575..d03b88ceb4 100644 --- a/django/conf/urls/admin.py +++ b/django/conf/urls/admin.py @@ -2,7 +2,7 @@ from django.conf.urls.defaults import * from django.conf.settings import INSTALLED_APPS urlpatterns = ( - ('^$', 'django.views.admin.main.index'), + ('^$', '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'), @@ -48,10 +48,10 @@ if 'ellington.media' in INSTALLED_APPS: urlpatterns += ( # Metasystem admin pages - ('^(?P[^/]+)/(?P[^/]+)/$', 'django.views.admin.main.change_list'), - ('^(?P[^/]+)/(?P[^/]+)/add/$', 'django.views.admin.main.add_stage'), - ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/history/$', 'django.views.admin.main.history'), - ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/delete/$', 'django.views.admin.main.delete_stage'), - ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/$', 'django.views.admin.main.change_stage'), + ('^(?P[^/]+)/(?P[^/]+)/$', 'django.contrib.admin.views.main.change_list'), + ('^(?P[^/]+)/(?P[^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'), + ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/history/$', 'django.contrib.admin.views.main.history'), + ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/delete/$', 'django.contrib.admin.views.main.delete_stage'), + ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/$', '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 new file mode 100644 index 0000000000..5379c8efb5 --- /dev/null +++ b/django/contrib/admin/views/main.py @@ -0,0 +1,1103 @@ +# Generic admin views, with admin templates created dynamically at runtime. + +from django.core import formfields, meta, template_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.utils.html import strip_tags +from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect +from django.utils.text import capfirst, get_text_list +from django.conf.settings import ADMIN_MEDIA_PREFIX +import operator + +# Text to display within changelist table cells if the value is blank. +EMPTY_CHANGELIST_VALUE = '(None)' + +def _get_mod_opts(app_label, module_name): + "Helper function that returns a tuple of (module, opts), raising Http404 if necessary." + try: + mod = meta.get_module(app_label, module_name) + except ImportError: + raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS. + opts = mod.Klass._meta + if not opts.admin: + raise Http404 # This object is valid but has no admin interface. + return mod, opts + +def get_query_string(original_params, new_params={}, remove=[]): + """ + >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}) + '?first_name=adrian&last_name=smith' + >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}, {'first_name': 'john'}) + '?first_name=john&last_name=smith' + >>> get_query_string({'test': 'yes'}, {'blah': 'no'}, ['te']) + '?blah=no' + """ + p = original_params.copy() + for r in remove: + for k in p.keys(): + if k.startswith(r): + del p[k] + for k, v in new_params.items(): + if p.has_key(k) and v is None: + del p[k] + elif v is not None: + p[k] = v + return '?' + '&'.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)) + +def change_list(request, app_label, module_name): + from django.core import paginator + from django.utils import dateformat + from django.utils.dates import MONTHS + from django.utils.html import escape + import datetime + + # The system will display a "Show all" link only if the total result count + # is less than or equal to this setting. + MAX_SHOW_ALL_ALLOWED = 200 + + DEFAULT_RESULTS_PER_PAGE = 100 + + ALL_VAR = 'all' + ORDER_VAR = 'o' + ORDER_TYPE_VAR = 'ot' + PAGE_VAR = 'p' + SEARCH_VAR = 'q' + IS_POPUP_VAR = 'pop' + + mod, opts = _get_mod_opts(app_label, module_name) + if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): + raise PermissionDenied + + lookup_mod, lookup_opts = mod, opts + + if opts.one_to_one_field: + lookup_mod = opts.one_to_one_field.rel.to.get_model_module() + lookup_opts = lookup_mod.Klass._meta + # If lookup_opts doesn't have admin set, give it the default meta.Admin(). + if not lookup_opts.admin: + lookup_opts.admin = meta.Admin() + + # Get search parameters from the query string. + try: + page_num = int(request.GET.get(PAGE_VAR, 0)) + except ValueError: + page_num = 0 + show_all = request.GET.has_key(ALL_VAR) + is_popup = request.GET.has_key(IS_POPUP_VAR) + params = dict(request.GET.copy()) + if params.has_key(PAGE_VAR): + del params[PAGE_VAR] + # For ordering, first check the "ordering" parameter in the admin options, + # then check the object's default ordering. If neither of those exist, + # order descending by ID by default. Finally, look for manually-specified + # ordering from the query string. + ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] + + # Normalize it to new-style ordering. + ordering = meta.handle_legacy_orderlist(ordering) + + if ordering[0].startswith('-'): + order_field, order_type = ordering[0][1:], 'desc' + else: + order_field, order_type = ordering[0], 'asc' + if params.has_key(ORDER_VAR): + try: + try: + f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) + except meta.FieldDoesNotExist: + pass + else: + if not isinstance(f.rel, meta.ManyToOne) or not f.null: + order_field = f.name + except (IndexError, ValueError): + pass # Invalid ordering specified. Just use the default. + if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'): + order_type = params[ORDER_TYPE_VAR] + query = request.GET.get(SEARCH_VAR, '') + + # Prepare the lookup parameters for the API lookup. + lookup_params = params.copy() + for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): + if lookup_params.has_key(i): + del lookup_params[i] + # If the order-by field is a field with a relationship, order by the value + # in the related table. + lookup_order_field = order_field + if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne): + f = lookup_opts.get_field(order_field) + rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column + lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering) + # Use select_related if one of the list_display options is a field with a + # relationship. + for field_name in lookup_opts.admin.list_display: + try: + f = lookup_opts.get_field(field_name) + except meta.FieldDoesNotExist: + pass + else: + if isinstance(f.rel, meta.ManyToOne): + lookup_params['select_related'] = True + break + lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,) + if lookup_opts.admin.search_fields and query: + or_queries = [] + for bit in query.split(): + or_query = [] + for field_name in lookup_opts.admin.search_fields: + or_query.append(('%s__icontains' % field_name, bit)) + or_queries.append(or_query) + lookup_params['_or'] = or_queries + + if opts.one_to_one_field: + lookup_params.update(opts.one_to_one_field.rel.limit_choices_to) + + # Get the results. + try: + p = paginator.ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE) + # Naked except! Because we don't have any other way of validating "params". + # They might be invalid if the keyword arguments are incorrect, or if the + # values are not in the correct type (which would result in a database + # error). + except: + return HttpResponseRedirect(request.path) + + # Get the total number of objects, with no filters applied. + real_lookup_params = lookup_params.copy() + del real_lookup_params['order_by'] + if real_lookup_params: + full_result_count = lookup_mod.get_count() + else: + full_result_count = p.hits + del real_lookup_params + result_count = p.hits + can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED + multi_page = result_count > DEFAULT_RESULTS_PER_PAGE + + # Get the list of objects to display on this page. + if (show_all and can_show_all) or not multi_page: + result_list = lookup_mod.get_list(**lookup_params) + else: + try: + result_list = p.get_page(page_num) + except paginator.InvalidPage: + result_list = [] + + # Calculate filters first, because a CSS class high in the document depends + # on whether they are available. + filter_template = [] + if lookup_opts.admin.list_filter and not opts.one_to_one_field: + filter_fields = [lookup_opts.get_field(field_name) for field_name in lookup_opts.admin.list_filter] + for f in filter_fields: + # Many-to-many or many-to-one filter. + if f.rel: + if isinstance(f, meta.ManyToManyField): + lookup_title = f.rel.to.verbose_name + else: + lookup_title = f.verbose_name + lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name) + lookup_val = request.GET.get(lookup_kwarg, None) + lookup_choices = f.rel.to.get_model_module().get_list() + if len(lookup_choices) > 1: + filter_template.append('

By %s:

\n
    \n' % lookup_title) + filter_template.append('All\n' % \ + ((lookup_val is None and ' class="selected"' or ''), + get_query_string(params, {}, [lookup_kwarg]))) + for val in lookup_choices: + pk_val = getattr(val, f.rel.to.pk.column) + filter_template.append('%r\n' % \ + ((lookup_val == str(pk_val) and ' class="selected"' or ''), + get_query_string(params, {lookup_kwarg: pk_val}), val)) + filter_template.append('
\n\n') + # Field with choices. + elif f.choices: + lookup_kwarg = '%s__exact' % f.name + lookup_val = request.GET.get(lookup_kwarg, None) + filter_template.append('

By %s:

    \n' % f.verbose_name) + filter_template.append('All\n' % \ + ((lookup_val is None and ' class="selected"' or ''), + get_query_string(params, {}, [lookup_kwarg]))) + for k, v in f.choices: + filter_template.append('%s' % \ + ((str(k) == lookup_val) and ' class="selected"' or '', + get_query_string(params, {lookup_kwarg: k}), v)) + filter_template.append('
\n\n') + # Date filter. + elif isinstance(f, meta.DateField): + today = datetime.date.today() + one_week_ago = today - datetime.timedelta(days=7) + field_generic = '%s__' % f.name + filter_template.append('

By %s:

    \n' % f.verbose_name) + date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)]) + today_str = isinstance(f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d') + for title, param_dict in ( + ('Any date', {}), + ('Today', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month), '%s__day' % f.name: str(today.day)}), + ('Past 7 days', {'%s__gte' % f.name: one_week_ago.strftime('%Y-%m-%d'), '%s__lte' % f.name: today_str}), + ('This month', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month)}), + ('This year', {'%s__year' % f.name: str(today.year)}) + ): + filter_template.append('%s\n' % \ + ((date_params == param_dict) and ' class="selected"' or '', + get_query_string(params, param_dict, field_generic), title)) + filter_template.append('
\n\n') + elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): + lookup_kwarg = '%s__exact' % f.name + lookup_kwarg2 = '%s__isnull' % f.name + lookup_val = request.GET.get(lookup_kwarg, None) + lookup_val2 = request.GET.get(lookup_kwarg2, None) + filter_template.append('

By %s:

    \n' % f.verbose_name) + for k, v in (('All', None), ('Yes', '1'), ('No', '0')): + filter_template.append('%s\n' % \ + (((lookup_val == v and not lookup_val2) and ' class="selected"' or ''), + get_query_string(params, {lookup_kwarg: v}, [lookup_kwarg2]), k)) + if isinstance(f, meta.NullBooleanField): + filter_template.append('%s\n' % \ + (((lookup_val2 == 'True') and ' class="selected"' or ''), + get_query_string(params, {lookup_kwarg2: 'True'}, [lookup_kwarg]), 'Unknown')) + filter_template.append('
\n\n') + else: + pass # Invalid argument to "list_filter" + + raw_template = ['{% extends "base_site" %}\n'] + raw_template.append('{% block bodyclass %}change-list{% endblock %}\n') + if not is_popup: + raw_template.append('{%% block breadcrumbs %%}{%% endblock %%}\n' % capfirst(opts.verbose_name_plural)) + raw_template.append('{% block coltype %}flex{% endblock %}') + raw_template.append('{% block content %}\n') + raw_template.append('
\n') + if request.user.has_perm(app_label + '.' + lookup_opts.get_add_permission()): + raw_template.append('\n' % ((is_popup and '?_popup=1' or ''), opts.verbose_name)) + raw_template.append('
\n' % (filter_template and ' filtered' or '')) + + # Search form. + if lookup_opts.admin.search_fields: + raw_template.append('
\n
\n') + raw_template.append('') + + # Date-based navigation. + if lookup_opts.admin.date_hierarchy: + field_name = lookup_opts.admin.date_hierarchy + + year_field = '%s__year' % field_name + month_field = '%s__month' % field_name + day_field = '%s__day' % field_name + field_generic = '%s__' % field_name + year_lookup = params.get(year_field) + month_lookup = params.get(month_field) + day_lookup = params.get(day_field) + + raw_template.append('
\n
\n
\n') + + # Filters. + if filter_template: + raw_template.append('
\n

Filter

\n') + raw_template.extend(filter_template) + raw_template.append('
') + del filter_template + + # Result table. + if result_list: + # Table headers. + raw_template.append('\n\n\n') + for i, field_name in enumerate(lookup_opts.admin.list_display): + try: + f = lookup_opts.get_field(field_name) + except meta.FieldDoesNotExist: + # For non-field list_display values, check for the function + # attribute "short_description". If that doesn't exist, fall + # back to the method name. And __repr__ is a special-case. + if field_name == '__repr__': + header = lookup_opts.verbose_name + else: + func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate. + try: + header = func.short_description + except AttributeError: + header = func.__name__ + # Non-field list_display values don't get ordering capability. + raw_template.append('' % capfirst(header)) + else: + if isinstance(f.rel, meta.ManyToOne) and f.null: + raw_template.append('' % capfirst(f.verbose_name)) + else: + th_classes = [] + new_order_type = 'asc' + if field_name == order_field: + th_classes.append('sorted %sending' % order_type.lower()) + new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()] + raw_template.append('%s' % \ + ((th_classes and ' class="%s"' % ' '.join(th_classes) or ''), + get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), + capfirst(f.verbose_name))) + raw_template.append('\n\n') + # Result rows. + pk = lookup_opts.pk.name + for i, result in enumerate(result_list): + raw_template.append('\n' % (i % 2 + 1)) + for j, field_name in enumerate(lookup_opts.admin.list_display): + row_class = '' + try: + f = lookup_opts.get_field(field_name) + except meta.FieldDoesNotExist: + # For non-field list_display values, the value is a method + # name. Execute the method. + try: + result_repr = strip_tags(str(getattr(result, field_name)())) + except ObjectDoesNotExist: + result_repr = EMPTY_CHANGELIST_VALUE + else: + field_val = getattr(result, f.column) + # Foreign-key fields are special: Use the repr of the + # related object. + if isinstance(f.rel, meta.ManyToOne): + if field_val is not None: + result_repr = getattr(result, 'get_%s' % f.name)() + else: + result_repr = EMPTY_CHANGELIST_VALUE + # Dates are special: They're formatted in a certain way. + elif isinstance(f, meta.DateField): + if field_val: + if isinstance(f, meta.DateTimeField): + result_repr = dateformat.format(field_val, 'N j, Y, P') + else: + result_repr = dateformat.format(field_val, 'N j, Y') + else: + result_repr = EMPTY_CHANGELIST_VALUE + row_class = ' class="nowrap"' + # Booleans are special: We use images. + elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): + BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} + result_repr = '%s' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val) + # ImageFields are special: Use a thumbnail. + elif isinstance(f, meta.ImageField): + from django.parts.media.photos import get_thumbnail_url + result_repr = '%s' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val) + # FloatFields are special: Zero-pad the decimals. + elif isinstance(f, meta.FloatField): + if field_val is not None: + result_repr = ('%%.%sf' % f.decimal_places) % field_val + else: + result_repr = EMPTY_CHANGELIST_VALUE + # Fields with choices are special: Use the representation + # of the choice. + elif f.choices: + result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE) + else: + result_repr = strip_tags(str(field_val)) + # Some browsers don't like empty ""s. + if result_repr == '': + result_repr = ' ' + if j == 0: # First column is a special case + result_id = getattr(result, pk) + raw_template.append('%s' % \ + (row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr)) + else: + raw_template.append('%s' % (row_class, result_repr)) + raw_template.append('\n') + del result_list # to free memory + raw_template.append('
%s%s
\n') + else: + raw_template.append('

No %s matched your search criteria.

' % opts.verbose_name_plural) + + # Pagination. + raw_template.append('

') + if (show_all and can_show_all) or not multi_page: + pass + else: + raw_template.append('Page › ') + ON_EACH_SIDE = 3 + ON_ENDS = 2 + DOT = '.' + # If there are 10 or fewer pages, display links to every page. + # Otherwise, do some fancy + if p.pages <= 10: + page_range = range(p.pages) + else: + # Insert "smart" pagination links, so that there are always ON_ENDS + # links at either end of the list of pages, and there are always + # ON_EACH_SIDE links at either end of the "current page" link. + page_range = [] + if page_num > (ON_EACH_SIDE + ON_ENDS): + page_range.extend(range(0, ON_EACH_SIDE - 1)) + page_range.append(DOT) + page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1)) + else: + page_range.extend(range(0, page_num + 1)) + if page_num < (p.pages - ON_EACH_SIDE - ON_ENDS - 1): + page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1)) + page_range.append(DOT) + page_range.extend(range(p.pages - ON_ENDS, p.pages)) + else: + page_range.extend(range(page_num + 1, p.pages)) + for i in page_range: + if i == DOT: + raw_template.append('... ') + elif i == page_num: + raw_template.append('%d ' % (i+1)) + else: + raw_template.append('%d ' % \ + (get_query_string(params, {PAGE_VAR: i}), (i == p.pages-1 and ' class="end"' or ''), i+1)) + raw_template.append('%s %s' % (result_count, result_count == 1 and opts.verbose_name or opts.verbose_name_plural)) + if can_show_all and not show_all and multi_page: + raw_template.append('  Show all' % \ + get_query_string(params, {ALL_VAR: ''})) + raw_template.append('

') + + raw_template.append('
\n
') + raw_template.append('{% endblock %}\n') + t = template_loader.get_template_from_string(''.join(raw_template)) + c = Context(request, { + 'title': (is_popup and 'Select %s' % opts.verbose_name or 'Select %s to change' % opts.verbose_name), + 'is_popup': is_popup, + }) + return HttpResponse(t.render(c)) + +def _get_flattened_data(field, val): + """ + Returns a dictionary mapping the field's manipulator field names to its + "flattened" string values for the admin view. "val" is an instance of the + field's value. + """ + if isinstance(field, meta.DateTimeField): + date_field, time_field = field.get_manipulator_field_names('') + return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), + time_field: (val is not None and val.strftime("%H:%M:%S") or '')} + elif isinstance(field, meta.DateField): + return {field.name: (val is not None and val.strftime("%Y-%m-%d") or '')} + elif isinstance(field, meta.TimeField): + return {field.name: (val is not None and val.strftime("%H:%M:%S") or '')} + else: + return {field.name: val} + +use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin + +def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects): + t = ['
'] + if change or show_delete: + t.append('{%% if perms.%s.%s %%}{%% if not is_popup %%}

Delete

{%% endif %%}{%% endif %%}' % \ + (app_label, opts.get_delete_permission())) + if change and opts.admin.save_as: + t.append('{%% if not is_popup %%}{%% endif %%}' % \ + (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) + if not opts.admin.save_as or add: + t.append('{%% if not is_popup %%}{%% endif %%}' % \ + (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) + t.append('{%% if not is_popup %%}{%% endif %%}' % \ + (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) + t.append('' % \ + (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) + t.append('
\n') + return t + +def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): + 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.append('{% block extrahead %}') + + # Put in any necessary JavaScript imports. + javascript_imports = ['%sjs/core.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/RelatedObjectLookups.js' % ADMIN_MEDIA_PREFIX] + if auto_populated_fields: + javascript_imports.append('%sjs/urlify.js' % ADMIN_MEDIA_PREFIX) + if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField): + javascript_imports.extend(['%sjs/calendar.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/DateTimeShortcuts.js' % ADMIN_MEDIA_PREFIX]) + if ordered_objects: + javascript_imports.extend(['%sjs/getElementsBySelector.js' % ADMIN_MEDIA_PREFIX, '%sjs/dom-drag.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/ordering.js' % ADMIN_MEDIA_PREFIX]) + if opts.admin.js: + javascript_imports.extend(opts.admin.js) + seen_collapse = False + for _, options in admin_field_objs: + if not seen_collapse and 'collapse' in options.get('classes', ''): + seen_collapse = True + javascript_imports.append('%sjs/admin/CollapsedFieldsets.js' % ADMIN_MEDIA_PREFIX) + try: + for field_list in options['fields']: + for f in field_list: + if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: + javascript_imports.extend(['%sjs/SelectBox.js' % ADMIN_MEDIA_PREFIX, '%sjs/SelectFilter2.js' % ADMIN_MEDIA_PREFIX]) + raise StopIteration + except StopIteration: + break + for j in javascript_imports: + t.append('' % j) + + t.append('{% endblock %}\n') + if ordered_objects: + coltype = 'colMS' + else: + coltype = 'colM' + t.append('{%% block coltype %%}%s{%% endblock %%}\n' % coltype) + t.append('{%% block bodyclass %%}%s-%s change-form{%% endblock %%}\n' % (app_label, opts.object_name.lower())) + breadcrumb_title = add and "Add %s" % opts.verbose_name or '{{ original|striptags|truncatewords:"18" }}' + t.append('{%% block breadcrumbs %%}{%% if not is_popup %%}{%% endif %%}{%% endblock %%}\n' % \ + (capfirst(opts.verbose_name_plural), breadcrumb_title)) + t.append('{% block content %}
\n') + if change: + t.append('{% if not is_popup %}') + t.append('
  • History
  • ') + if hasattr(opts.get_model_module().Klass, 'get_absolute_url'): + t.append('
  • View on site
  • ' % opts.get_content_type_id()) + t.append('
\n') + t.append('{% endif %}') + t.append('
\n' % form_url) + t.append('{% if is_popup %}{% endif %}') + if opts.admin.save_on_top: + t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) + t.append('{% if form.error_dict %}

Please correct the error{{ form.error_dict.items|pluralize }} below.

{% endif %}\n') + for fieldset_name, options in admin_field_objs: + t.append('
\n\n' % options.get('classes', '')) + if fieldset_name: + t.append('

%s

\n' % fieldset_name) + for field_list in options['fields']: + t.append(_get_admin_field(field_list, 'form.', False, add, change)) + for f in field_list: + if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: + t.append('\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)) + t.append('
\n') + if ordered_objects and change: + t.append('

Ordering

') + t.append('
\n') + t.append('{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}') + t.append('

{{ form.order_ }}

\n') + t.append('
\n') + for rel_obj, rel_field in opts.get_inline_related_objects(): + var_name = rel_obj.object_name.lower() + field_list = [f for f in rel_obj.fields + rel_obj.many_to_many if f.editable and f != rel_field] + t.append('
\n' % ((rel_field.rel.edit_inline != meta.TABULAR) and ' aligned' or '')) + view_on_site = '' + if change and hasattr(rel_obj, 'get_absolute_url'): + view_on_site = '{%% if %s.original %%}View on site{%% endif %%}' % (var_name, var_name, var_name) + if rel_field.rel.edit_inline == meta.TABULAR: + t.append('

%s

\n\n' % capfirst(rel_obj.verbose_name_plural)) + t.append('') + for f in field_list: + if isinstance(f, meta.AutoField): + continue + t.append('%s' % (f.blank and ' class="optional"' or '', capfirst(f.verbose_name))) + t.append('\n') + t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) + if change: + for f in field_list: + if use_raw_id_admin(f): + t.append('{%% if %s.original %%}' % var_name) + t.append('') + t.append('' % (30, var_name)) + t.append('{% endif %}\n') + break + t.append('{%% if %s %%}\n' % ' or '.join(['%s.%s.errors' % (var_name, f.name) for f in field_list])) + t.append('\n{%% endif %%}\n' % \ + (len(field_list), ''.join(['{{ %s.%s.html_error_list }}' % (var_name, f.name) for f in field_list]))) + t.append('\n') + hidden_fields = [] + for f in field_list: + form_widget = _get_admin_field_form_widget(f, var_name+'.', True, add, change) + # Don't put AutoFields within a \n' % (var_name, f.name, form_widget)) + else: + t.append('%s\n' % (var_name, f.name, form_widget)) + else: + hidden_fields.append(form_widget) + if hasattr(rel_obj, 'get_absolute_url'): + t.append('\n' % view_on_site) + t.append('\n') + t.append('{% endfor %}\n
{{ %s.original }}
%s
, because they're hidden. + if not isinstance(f, meta.AutoField): + # Fields with raw_id_admin=True get class="nowrap". + if use_raw_id_admin(f): + t.append('%s%s
\n') + # Write out the hidden fields. We didn't write them out earlier + # because it would've been invalid HTML. + t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) + t.extend(hidden_fields) + t.append('{% endfor %}\n') + else: # edit_inline == STACKED + t.append('{%% for %s in form.%s %%}' % (var_name, rel_obj.module_name)) + t.append('

%s #{{ forloop.counter }}

' % capfirst(rel_obj.verbose_name)) + if view_on_site: + t.append('

%s

' % view_on_site) + for f in field_list: + # Don't put AutoFields within the widget -- just use the field. + if isinstance(f, meta.AutoField): + t.append(_get_admin_field_form_widget(f, var_name+'.', True, add, change)) + else: + t.append(_get_admin_field([f], var_name+'.', True, add, change)) + t.append('{% endfor %}\n') + t.append('
\n') + t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) + if add: + # Add focus to the first field on the form, if this is an "add" form. + t.append('' % \ + admin_field_objs[0][1]['fields'][0][0].get_manipulator_field_names('')[0]) + if auto_populated_fields: + t.append('\n') + if change and ordered_objects: + t.append('{% if form.order_objects %}
    {% for object in form.order_objects %}') + t.append('
  • {{ object|truncatewords:"5" }}
  • ' % \ + {'x': ' '.join(['object.%s' % o.pk.name for o in ordered_objects])}) + t.append('{% endfor %}
{% endif %}\n') + t.append('
\n
\n{% endblock %}') + return ''.join(t) + +def _get_admin_field(field_list, name_prefix, rel, add, change): + "Returns the template code for editing the given list of fields in the admin template." + field_names = [] + for f in field_list: + field_names.extend(f.get_manipulator_field_names(name_prefix)) + div_class_names = ['form-row', '{%% if %s %%} error{%% endif %%}' % ' or '.join(['%s.errors' % n for n in field_names])] + # Assumes BooleanFields won't be stacked next to each other! + if isinstance(field_list[0], meta.BooleanField): + div_class_names.append('checkbox-row') + t = [] + t.append('
\n' % ' '.join(div_class_names)) + for n in field_names: + t.append('{%% if %s.errors %%}{{ %s.html_error_list }}{%% endif %%}\n' % (n, n)) + for i, field in enumerate(field_list): + label_name = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) + # BooleanFields are a special case, because the checkbox widget appears to + # the *left* of the label. + if isinstance(field, meta.BooleanField): + t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) + t.append(' ' % (label_name, capfirst(field.verbose_name))) + else: + class_names = [] + if not field.blank: + class_names.append('required') + if i > 0: + class_names.append('inline') + t.append(' ' % (label_name, class_names and ' class="%s"' % ' '.join(class_names) or '', capfirst(field.verbose_name))) + t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) + if change and field.primary_key: + t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name)) + if change and use_raw_id_admin(field): + if isinstance(field.rel, meta.ManyToOne): + if_bit = '%soriginal.get_%s' % (rel and name_prefix or '', field.name) + obj_repr = if_bit + '|truncatewords:"14"' + elif isinstance(field.rel, meta.ManyToMany): + if_bit = '%soriginal.get_%s_list' % (rel and name_prefix or '', field.name) + obj_repr = if_bit + '|join:", "|truncatewords:"14"' + t.append('{%% if %s %%} {{ %s }}{%% endif %%}' % (if_bit, obj_repr)) + if field.help_text: + t.append('

%s

\n' % field.help_text) + t.append('
\n\n') + return ''.join(t) + +def _get_admin_field_form_widget(field, name_prefix, rel, add, change): + "Returns JUST the formfield widget for the field's admin interface." + field_names = field.get_manipulator_field_names(name_prefix) + if isinstance(field, meta.DateTimeField): + return '

Date: {{ %s }}
Time: {{ %s }}

' % tuple(field_names) + t = ['{{ %s }}' % n for n in field_names] + if change and isinstance(field, meta.FileField): + return '{%% if %soriginal.%s %%}Currently: {{ %soriginal.%s }}
Change: %s{%% else %%}%s{%% endif %%}' % \ + (name_prefix, field.name, name_prefix, field.name, name_prefix, field.name, ''.join(t), ''.join(t)) + field_id = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) + # raw_id_admin fields get the little lookup link next to them + if use_raw_id_admin(field): + t.append(' ' % \ + (field.rel.to.app_label, field.rel.to.module_name, field_id)) + t.append('Lookup' % ADMIN_MEDIA_PREFIX) + # fields with relationships to editable objects get an "add another" link, + # but only if the field doesn't have raw_admin ('cause in that case they get + # the "add" button in the popup) + elif field.rel and (isinstance(field.rel, meta.ManyToOne) or isinstance(field.rel, meta.ManyToMany)) and field.rel.to.admin: + t.append('{%% if perms.%s.%s %%}' % (field.rel.to.app_label, field.rel.to.get_add_permission())) + t.append(' ' % \ + (field.rel.to.app_label, field.rel.to.module_name, field_id)) + t.append('Add Another' % ADMIN_MEDIA_PREFIX) + t.append('{% endif %}') + return ''.join(t) + +def add_stage(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): + mod, opts = _get_mod_opts(app_label, module_name) + if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): + raise PermissionDenied + manipulator = mod.AddManipulator() + if request.POST: + new_data = request.POST.copy() + if opts.has_field_type(meta.FileField): + new_data.update(request.FILES) + errors = manipulator.get_validation_errors(new_data) + if not errors and not request.POST.has_key("_preview"): + for f in opts.many_to_many: + if f.rel.raw_id_admin: + new_data.setlist(f.name, new_data[f.name].split(",")) + manipulator.do_html2python(new_data) + new_object = manipulator.save(new_data) + pk_value = getattr(new_object, opts.pk.column) + log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) + msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) + # Here, we distinguish between different save types by checking for + # the presence of keys in request.POST. + if request.POST.has_key("_continue"): + request.user.add_message("%s You may edit it again below." % msg) + if request.POST.has_key("_popup"): + post_url_continue += "?_popup=1" + return HttpResponseRedirect(post_url_continue % pk_value) + if request.POST.has_key("_popup"): + return HttpResponse('' % \ + (pk_value, repr(new_object).replace('"', '\\"'))) + elif request.POST.has_key("_addanother"): + request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) + return HttpResponseRedirect(request.path) + else: + request.user.add_message(msg) + return HttpResponseRedirect(post_url) + if request.POST.has_key("_preview"): + manipulator.do_html2python(new_data) + else: + new_data = {} + # Add default data. + for f in opts.fields: + if f.has_default(): + new_data.update(_get_flattened_data(f, f.get_default())) + # In required many-to-one fields with only one available choice, + # select that one available choice. Note: We have to check that + # the length of choices is *2*, not 1, because SelectFields always + # have an initial "blank" value. + elif not f.blank and ((isinstance(f.rel, meta.ManyToOne) and not f.rel.raw_id_admin) or f.choices) and len(manipulator[f.name].choices) == 2: + new_data[f.name] = manipulator[f.name].choices[1][0] + # In required many-to-many fields with only one available choice, + # select that one available choice. + for f in opts.many_to_many: + if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1: + new_data[f.name] = [manipulator[f.name].choices[0][0]] + # Add default data for related objects. + for rel_opts, rel_field in opts.get_inline_related_objects(): + var_name = rel_opts.object_name.lower() + for i in range(rel_field.rel.num_in_admin): + for f in rel_opts.fields + rel_opts.many_to_many: + if f.has_default(): + for field_name in f.get_manipulator_field_names(''): + new_data['%s.%d.%s' % (var_name, i, field_name)] = f.get_default() + # Override the defaults with request.GET, if it exists. + new_data.update(request.GET) + errors = {} + + # Populate the FormWrapper. + form = formfields.FormWrapper(manipulator, new_data, errors) + for rel_opts, rel_field in opts.get_inline_related_objects(): + var_name = rel_opts.object_name.lower() + wrapper = [] + for i in range(rel_field.rel.num_in_admin): + collection = {} + for f in rel_opts.fields + rel_opts.many_to_many: + if f.editable and f != rel_field and not isinstance(f, meta.AutoField): + for field_name in f.get_manipulator_field_names(''): + full_field_name = '%s.%d.%s' % (var_name, i, field_name) + collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, ''), errors.get(full_field_name, [])) + wrapper.append(formfields.FormFieldCollection(collection)) + setattr(form, rel_opts.module_name, wrapper) + + c = Context(request, { + 'title': 'Add %s' % opts.verbose_name, + "form": form, + "is_popup": request.REQUEST.has_key("_popup"), + }) + if object_id_override is not None: + c['object_id'] = object_id_override + raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) +# return HttpResponse(raw_template, mimetype='text/plain') + t = template_loader.get_template_from_string(raw_template) + return HttpResponse(t.render(c)) + +def change_stage(request, app_label, module_name, object_id): + mod, opts = _get_mod_opts(app_label, module_name) + if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): + raise PermissionDenied + if request.POST and request.POST.has_key("_saveasnew"): + return add_stage(request, app_label, module_name, form_url='../add/') + try: + manipulator = mod.ChangeManipulator(object_id) + except ObjectDoesNotExist: + raise Http404 + + inline_related_objects = opts.get_inline_related_objects() + if request.POST: + new_data = request.POST.copy() + if opts.has_field_type(meta.FileField): + new_data.update(request.FILES) + + errors = manipulator.get_validation_errors(new_data) + if not errors and not request.POST.has_key("_preview"): + for f in opts.many_to_many: + if f.rel.raw_id_admin: + new_data.setlist(f.name, new_data[f.name].split(",")) + manipulator.do_html2python(new_data) + new_object = manipulator.save(new_data) + pk_value = getattr(new_object, opts.pk.column) + + # Construct the change message. + change_message = [] + if manipulator.fields_added: + change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) + if manipulator.fields_changed: + change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) + if manipulator.fields_deleted: + change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) + change_message = ' '.join(change_message) + if not change_message: + change_message = 'No fields changed.' + + log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) + msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) + if request.POST.has_key("_continue"): + request.user.add_message("%s You may edit it again below." % msg) + if request.REQUEST.has_key('_popup'): + return HttpResponseRedirect(request.path + "?_popup=1") + else: + return HttpResponseRedirect(request.path) + elif request.POST.has_key("_saveasnew"): + request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) + return HttpResponseRedirect("../%s/" % pk_value) + elif request.POST.has_key("_addanother"): + request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) + return HttpResponseRedirect("../add/") + else: + request.user.add_message(msg) + return HttpResponseRedirect("../") + if request.POST.has_key("_preview"): + manipulator.do_html2python(new_data) + else: + # Populate new_data with a "flattened" version of the current data. + new_data = {} + obj = manipulator.original_object + for f in opts.fields: + new_data.update(_get_flattened_data(f, getattr(obj, f.column))) + for f in opts.many_to_many: + get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) + if f.rel.raw_id_admin: + new_data[f.name] = ",".join([str(getattr(i, f.rel.to.pk.column)) for i in get_list_func()]) + elif not f.rel.edit_inline: + new_data[f.name] = [getattr(i, f.rel.to.pk.column) for i in get_list_func()] + for rel_obj, rel_field in inline_related_objects: + var_name = rel_obj.object_name.lower() + for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): + for f in rel_obj.fields: + if f.editable and f != rel_field: + for k, v in _get_flattened_data(f, getattr(rel_instance, f.column)).items(): + new_data['%s.%d.%s' % (var_name, i, k)] = v + for f in rel_obj.many_to_many: + new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] + + # If the object has ordered objects on its admin page, get the existing + # order and flatten it into a comma-separated list of IDs. + id_order_list = [] + for rel_obj in opts.get_ordered_objects(): + id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) + if id_order_list: + new_data['order_'] = ','.join(map(str, id_order_list)) + errors = {} + + # Populate the FormWrapper. + form = formfields.FormWrapper(manipulator, new_data, errors) + form.original = manipulator.original_object + form.order_objects = [] + for rel_opts, rel_field in inline_related_objects: + var_name = rel_opts.object_name.lower() + wrapper = [] + orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() + count = len(orig_list) + rel_field.rel.num_extra_on_change + if rel_field.rel.min_num_in_admin: + count = max(count, rel_field.rel.min_num_in_admin) + if rel_field.rel.max_num_in_admin: + count = min(count, rel_field.rel.max_num_in_admin) + for i in range(count): + collection = {'original': (i < len(orig_list) and orig_list[i] or None)} + for f in rel_opts.fields + rel_opts.many_to_many: + if f.editable and f != rel_field: + for field_name in f.get_manipulator_field_names(''): + full_field_name = '%s.%d.%s' % (var_name, i, field_name) + collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, f.get_default()), errors.get(full_field_name, [])) + wrapper.append(formfields.FormFieldCollection(collection)) + setattr(form, rel_opts.module_name, wrapper) + if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: + form.order_objects.extend(orig_list) + + c = Context(request, { + 'title': 'Change %s' % opts.verbose_name, + "form": form, + 'object_id': object_id, + 'original': manipulator.original_object, + 'is_popup' : request.REQUEST.has_key('_popup'), + }) + raw_template = _get_template(opts, app_label, change=True) +# return HttpResponse(raw_template, mimetype='text/plain') + t = template_loader.get_template_from_string(raw_template) + return HttpResponse(t.render(c)) + +def _nest_help(obj, depth, val): + current = obj + for i in range(depth): + current = current[-1] + current.append(val) + +def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth): + "Helper function that recursively populates deleted_objects." + nh = _nest_help # Bind to local variable for performance + if current_depth > 16: + return # Avoid recursing too deep. + objects_seen = [] + for rel_opts, rel_field in opts.get_all_related_objects(): + if rel_opts in objects_seen: + continue + objects_seen.append(rel_opts) + rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) + if isinstance(rel_field.rel, meta.OneToOne): + try: + sub_obj = getattr(obj, 'get_%s' % rel_opts_name)() + except ObjectDoesNotExist: + pass + else: + if rel_opts.admin: + p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) + if not user.has_perm(p): + perms_needed.add(rel_opts.verbose_name) + # We don't care about populating deleted_objects now. + continue + if rel_field.rel.edit_inline or not rel_opts.admin: + # Don't display link to edit, because it either has no + # admin or is edited inline. + nh(deleted_objects, current_depth, ['%s: %r' % (capfirst(rel_opts.verbose_name), sub_obj), []]) + else: + # Display a link to the admin page. + nh(deleted_objects, current_depth, ['%s: %r' % \ + (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, + getattr(sub_obj, rel_opts.pk.column), sub_obj), []]) + _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) + else: + has_related_objs = False + for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)(): + has_related_objs = True + if rel_field.rel.edit_inline or not rel_opts.admin: + # Don't display link to edit, because it either has no + # admin or is edited inline. + nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(rel_opts.verbose_name), strip_tags(repr(sub_obj))), []]) + else: + # Display a link to the admin page. + nh(deleted_objects, current_depth, ['%s: %s' % \ + (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) + _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) + # If there were related objects, and the user doesn't have + # permission to delete them, add the missing perm to perms_needed. + if rel_opts.admin and has_related_objs: + p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) + if not user.has_perm(p): + perms_needed.add(rel_opts.verbose_name) + for rel_opts, rel_field in opts.get_all_related_many_to_many_objects(): + if rel_opts in objects_seen: + continue + objects_seen.append(rel_opts) + rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) + has_related_objs = False + for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)(): + has_related_objs = True + if rel_field.rel.edit_inline or not rel_opts.admin: + # Don't display link to edit, because it either has no + # admin or is edited inline. + nh(deleted_objects, current_depth, ['One or more %s in %s: %s' % \ + (rel_field.name, rel_opts.verbose_name, strip_tags(repr(sub_obj))), []]) + else: + # Display a link to the admin page. + nh(deleted_objects, current_depth, ['One or more %s in %s: %s' % \ + (rel_field.name, rel_opts.verbose_name, rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) + # If there were related objects, and the user doesn't have + # permission to change them, add the missing perm to perms_needed. + if rel_opts.admin and has_related_objs: + p = '%s.%s' % (rel_opts.app_label, rel_opts.get_change_permission()) + if not user.has_perm(p): + perms_needed.add(rel_opts.verbose_name) + +def delete_stage(request, app_label, module_name, object_id): + import sets + mod, opts = _get_mod_opts(app_label, module_name) + if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()): + raise PermissionDenied + obj = get_object_or_404(mod, pk=object_id) + + # Populate deleted_objects, a data structure of all related objects that + # will also be deleted. + deleted_objects = ['%s: %s' % (capfirst(opts.verbose_name), object_id, strip_tags(repr(obj))), []] + perms_needed = sets.Set() + _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) + + if request.POST: # The user has already confirmed the deletion. + if perms_needed: + raise PermissionDenied + obj_repr = repr(obj) + obj.delete() + 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', { + "title": "Are you sure?", + "object_name": opts.verbose_name, + "object": obj, + "deleted_objects": deleted_objects, + "perms_lacking": perms_needed, + }, context_instance=Context(request)) + +def history(request, app_label, module_name, object_id): + mod, opts = _get_mod_opts(app_label, module_name) + action_list = log.get_list(object_id__exact=object_id, content_type__id__exact=opts.get_content_type_id(), + order_by=("action_time",), select_related=True) + # 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', { + 'title': 'Change history: %r' % obj, + 'action_list': action_list, + 'module_name': capfirst(opts.verbose_name_plural), + 'object': obj, + }, context_instance=Context(request)) diff --git a/django/views/admin/main.py b/django/views/admin/main.py deleted file mode 100644 index 5379c8efb5..0000000000 --- a/django/views/admin/main.py +++ /dev/null @@ -1,1103 +0,0 @@ -# Generic admin views, with admin templates created dynamically at runtime. - -from django.core import formfields, meta, template_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.utils.html import strip_tags -from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect -from django.utils.text import capfirst, get_text_list -from django.conf.settings import ADMIN_MEDIA_PREFIX -import operator - -# Text to display within changelist table cells if the value is blank. -EMPTY_CHANGELIST_VALUE = '(None)' - -def _get_mod_opts(app_label, module_name): - "Helper function that returns a tuple of (module, opts), raising Http404 if necessary." - try: - mod = meta.get_module(app_label, module_name) - except ImportError: - raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS. - opts = mod.Klass._meta - if not opts.admin: - raise Http404 # This object is valid but has no admin interface. - return mod, opts - -def get_query_string(original_params, new_params={}, remove=[]): - """ - >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}) - '?first_name=adrian&last_name=smith' - >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}, {'first_name': 'john'}) - '?first_name=john&last_name=smith' - >>> get_query_string({'test': 'yes'}, {'blah': 'no'}, ['te']) - '?blah=no' - """ - p = original_params.copy() - for r in remove: - for k in p.keys(): - if k.startswith(r): - del p[k] - for k, v in new_params.items(): - if p.has_key(k) and v is None: - del p[k] - elif v is not None: - p[k] = v - return '?' + '&'.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)) - -def change_list(request, app_label, module_name): - from django.core import paginator - from django.utils import dateformat - from django.utils.dates import MONTHS - from django.utils.html import escape - import datetime - - # The system will display a "Show all" link only if the total result count - # is less than or equal to this setting. - MAX_SHOW_ALL_ALLOWED = 200 - - DEFAULT_RESULTS_PER_PAGE = 100 - - ALL_VAR = 'all' - ORDER_VAR = 'o' - ORDER_TYPE_VAR = 'ot' - PAGE_VAR = 'p' - SEARCH_VAR = 'q' - IS_POPUP_VAR = 'pop' - - mod, opts = _get_mod_opts(app_label, module_name) - if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): - raise PermissionDenied - - lookup_mod, lookup_opts = mod, opts - - if opts.one_to_one_field: - lookup_mod = opts.one_to_one_field.rel.to.get_model_module() - lookup_opts = lookup_mod.Klass._meta - # If lookup_opts doesn't have admin set, give it the default meta.Admin(). - if not lookup_opts.admin: - lookup_opts.admin = meta.Admin() - - # Get search parameters from the query string. - try: - page_num = int(request.GET.get(PAGE_VAR, 0)) - except ValueError: - page_num = 0 - show_all = request.GET.has_key(ALL_VAR) - is_popup = request.GET.has_key(IS_POPUP_VAR) - params = dict(request.GET.copy()) - if params.has_key(PAGE_VAR): - del params[PAGE_VAR] - # For ordering, first check the "ordering" parameter in the admin options, - # then check the object's default ordering. If neither of those exist, - # order descending by ID by default. Finally, look for manually-specified - # ordering from the query string. - ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] - - # Normalize it to new-style ordering. - ordering = meta.handle_legacy_orderlist(ordering) - - if ordering[0].startswith('-'): - order_field, order_type = ordering[0][1:], 'desc' - else: - order_field, order_type = ordering[0], 'asc' - if params.has_key(ORDER_VAR): - try: - try: - f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) - except meta.FieldDoesNotExist: - pass - else: - if not isinstance(f.rel, meta.ManyToOne) or not f.null: - order_field = f.name - except (IndexError, ValueError): - pass # Invalid ordering specified. Just use the default. - if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'): - order_type = params[ORDER_TYPE_VAR] - query = request.GET.get(SEARCH_VAR, '') - - # Prepare the lookup parameters for the API lookup. - lookup_params = params.copy() - for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): - if lookup_params.has_key(i): - del lookup_params[i] - # If the order-by field is a field with a relationship, order by the value - # in the related table. - lookup_order_field = order_field - if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne): - f = lookup_opts.get_field(order_field) - rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column - lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering) - # Use select_related if one of the list_display options is a field with a - # relationship. - for field_name in lookup_opts.admin.list_display: - try: - f = lookup_opts.get_field(field_name) - except meta.FieldDoesNotExist: - pass - else: - if isinstance(f.rel, meta.ManyToOne): - lookup_params['select_related'] = True - break - lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,) - if lookup_opts.admin.search_fields and query: - or_queries = [] - for bit in query.split(): - or_query = [] - for field_name in lookup_opts.admin.search_fields: - or_query.append(('%s__icontains' % field_name, bit)) - or_queries.append(or_query) - lookup_params['_or'] = or_queries - - if opts.one_to_one_field: - lookup_params.update(opts.one_to_one_field.rel.limit_choices_to) - - # Get the results. - try: - p = paginator.ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE) - # Naked except! Because we don't have any other way of validating "params". - # They might be invalid if the keyword arguments are incorrect, or if the - # values are not in the correct type (which would result in a database - # error). - except: - return HttpResponseRedirect(request.path) - - # Get the total number of objects, with no filters applied. - real_lookup_params = lookup_params.copy() - del real_lookup_params['order_by'] - if real_lookup_params: - full_result_count = lookup_mod.get_count() - else: - full_result_count = p.hits - del real_lookup_params - result_count = p.hits - can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED - multi_page = result_count > DEFAULT_RESULTS_PER_PAGE - - # Get the list of objects to display on this page. - if (show_all and can_show_all) or not multi_page: - result_list = lookup_mod.get_list(**lookup_params) - else: - try: - result_list = p.get_page(page_num) - except paginator.InvalidPage: - result_list = [] - - # Calculate filters first, because a CSS class high in the document depends - # on whether they are available. - filter_template = [] - if lookup_opts.admin.list_filter and not opts.one_to_one_field: - filter_fields = [lookup_opts.get_field(field_name) for field_name in lookup_opts.admin.list_filter] - for f in filter_fields: - # Many-to-many or many-to-one filter. - if f.rel: - if isinstance(f, meta.ManyToManyField): - lookup_title = f.rel.to.verbose_name - else: - lookup_title = f.verbose_name - lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name) - lookup_val = request.GET.get(lookup_kwarg, None) - lookup_choices = f.rel.to.get_model_module().get_list() - if len(lookup_choices) > 1: - filter_template.append('

By %s:

\n
    \n' % lookup_title) - filter_template.append('All\n' % \ - ((lookup_val is None and ' class="selected"' or ''), - get_query_string(params, {}, [lookup_kwarg]))) - for val in lookup_choices: - pk_val = getattr(val, f.rel.to.pk.column) - filter_template.append('%r\n' % \ - ((lookup_val == str(pk_val) and ' class="selected"' or ''), - get_query_string(params, {lookup_kwarg: pk_val}), val)) - filter_template.append('
\n\n') - # Field with choices. - elif f.choices: - lookup_kwarg = '%s__exact' % f.name - lookup_val = request.GET.get(lookup_kwarg, None) - filter_template.append('

By %s:

    \n' % f.verbose_name) - filter_template.append('All\n' % \ - ((lookup_val is None and ' class="selected"' or ''), - get_query_string(params, {}, [lookup_kwarg]))) - for k, v in f.choices: - filter_template.append('%s' % \ - ((str(k) == lookup_val) and ' class="selected"' or '', - get_query_string(params, {lookup_kwarg: k}), v)) - filter_template.append('
\n\n') - # Date filter. - elif isinstance(f, meta.DateField): - today = datetime.date.today() - one_week_ago = today - datetime.timedelta(days=7) - field_generic = '%s__' % f.name - filter_template.append('

By %s:

    \n' % f.verbose_name) - date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)]) - today_str = isinstance(f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d') - for title, param_dict in ( - ('Any date', {}), - ('Today', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month), '%s__day' % f.name: str(today.day)}), - ('Past 7 days', {'%s__gte' % f.name: one_week_ago.strftime('%Y-%m-%d'), '%s__lte' % f.name: today_str}), - ('This month', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month)}), - ('This year', {'%s__year' % f.name: str(today.year)}) - ): - filter_template.append('%s\n' % \ - ((date_params == param_dict) and ' class="selected"' or '', - get_query_string(params, param_dict, field_generic), title)) - filter_template.append('
\n\n') - elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): - lookup_kwarg = '%s__exact' % f.name - lookup_kwarg2 = '%s__isnull' % f.name - lookup_val = request.GET.get(lookup_kwarg, None) - lookup_val2 = request.GET.get(lookup_kwarg2, None) - filter_template.append('

By %s:

    \n' % f.verbose_name) - for k, v in (('All', None), ('Yes', '1'), ('No', '0')): - filter_template.append('%s\n' % \ - (((lookup_val == v and not lookup_val2) and ' class="selected"' or ''), - get_query_string(params, {lookup_kwarg: v}, [lookup_kwarg2]), k)) - if isinstance(f, meta.NullBooleanField): - filter_template.append('%s\n' % \ - (((lookup_val2 == 'True') and ' class="selected"' or ''), - get_query_string(params, {lookup_kwarg2: 'True'}, [lookup_kwarg]), 'Unknown')) - filter_template.append('
\n\n') - else: - pass # Invalid argument to "list_filter" - - raw_template = ['{% extends "base_site" %}\n'] - raw_template.append('{% block bodyclass %}change-list{% endblock %}\n') - if not is_popup: - raw_template.append('{%% block breadcrumbs %%}{%% endblock %%}\n' % capfirst(opts.verbose_name_plural)) - raw_template.append('{% block coltype %}flex{% endblock %}') - raw_template.append('{% block content %}\n') - raw_template.append('
\n') - if request.user.has_perm(app_label + '.' + lookup_opts.get_add_permission()): - raw_template.append('\n' % ((is_popup and '?_popup=1' or ''), opts.verbose_name)) - raw_template.append('
\n' % (filter_template and ' filtered' or '')) - - # Search form. - if lookup_opts.admin.search_fields: - raw_template.append('
\n
\n') - raw_template.append('') - - # Date-based navigation. - if lookup_opts.admin.date_hierarchy: - field_name = lookup_opts.admin.date_hierarchy - - year_field = '%s__year' % field_name - month_field = '%s__month' % field_name - day_field = '%s__day' % field_name - field_generic = '%s__' % field_name - year_lookup = params.get(year_field) - month_lookup = params.get(month_field) - day_lookup = params.get(day_field) - - raw_template.append('
\n
\n
\n') - - # Filters. - if filter_template: - raw_template.append('
\n

Filter

\n') - raw_template.extend(filter_template) - raw_template.append('
') - del filter_template - - # Result table. - if result_list: - # Table headers. - raw_template.append('\n\n\n') - for i, field_name in enumerate(lookup_opts.admin.list_display): - try: - f = lookup_opts.get_field(field_name) - except meta.FieldDoesNotExist: - # For non-field list_display values, check for the function - # attribute "short_description". If that doesn't exist, fall - # back to the method name. And __repr__ is a special-case. - if field_name == '__repr__': - header = lookup_opts.verbose_name - else: - func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate. - try: - header = func.short_description - except AttributeError: - header = func.__name__ - # Non-field list_display values don't get ordering capability. - raw_template.append('' % capfirst(header)) - else: - if isinstance(f.rel, meta.ManyToOne) and f.null: - raw_template.append('' % capfirst(f.verbose_name)) - else: - th_classes = [] - new_order_type = 'asc' - if field_name == order_field: - th_classes.append('sorted %sending' % order_type.lower()) - new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()] - raw_template.append('%s' % \ - ((th_classes and ' class="%s"' % ' '.join(th_classes) or ''), - get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), - capfirst(f.verbose_name))) - raw_template.append('\n\n') - # Result rows. - pk = lookup_opts.pk.name - for i, result in enumerate(result_list): - raw_template.append('\n' % (i % 2 + 1)) - for j, field_name in enumerate(lookup_opts.admin.list_display): - row_class = '' - try: - f = lookup_opts.get_field(field_name) - except meta.FieldDoesNotExist: - # For non-field list_display values, the value is a method - # name. Execute the method. - try: - result_repr = strip_tags(str(getattr(result, field_name)())) - except ObjectDoesNotExist: - result_repr = EMPTY_CHANGELIST_VALUE - else: - field_val = getattr(result, f.column) - # Foreign-key fields are special: Use the repr of the - # related object. - if isinstance(f.rel, meta.ManyToOne): - if field_val is not None: - result_repr = getattr(result, 'get_%s' % f.name)() - else: - result_repr = EMPTY_CHANGELIST_VALUE - # Dates are special: They're formatted in a certain way. - elif isinstance(f, meta.DateField): - if field_val: - if isinstance(f, meta.DateTimeField): - result_repr = dateformat.format(field_val, 'N j, Y, P') - else: - result_repr = dateformat.format(field_val, 'N j, Y') - else: - result_repr = EMPTY_CHANGELIST_VALUE - row_class = ' class="nowrap"' - # Booleans are special: We use images. - elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): - BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} - result_repr = '%s' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val) - # ImageFields are special: Use a thumbnail. - elif isinstance(f, meta.ImageField): - from django.parts.media.photos import get_thumbnail_url - result_repr = '%s' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val) - # FloatFields are special: Zero-pad the decimals. - elif isinstance(f, meta.FloatField): - if field_val is not None: - result_repr = ('%%.%sf' % f.decimal_places) % field_val - else: - result_repr = EMPTY_CHANGELIST_VALUE - # Fields with choices are special: Use the representation - # of the choice. - elif f.choices: - result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE) - else: - result_repr = strip_tags(str(field_val)) - # Some browsers don't like empty ""s. - if result_repr == '': - result_repr = ' ' - if j == 0: # First column is a special case - result_id = getattr(result, pk) - raw_template.append('%s' % \ - (row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr)) - else: - raw_template.append('%s' % (row_class, result_repr)) - raw_template.append('\n') - del result_list # to free memory - raw_template.append('
%s%s
\n') - else: - raw_template.append('

No %s matched your search criteria.

' % opts.verbose_name_plural) - - # Pagination. - raw_template.append('

') - if (show_all and can_show_all) or not multi_page: - pass - else: - raw_template.append('Page › ') - ON_EACH_SIDE = 3 - ON_ENDS = 2 - DOT = '.' - # If there are 10 or fewer pages, display links to every page. - # Otherwise, do some fancy - if p.pages <= 10: - page_range = range(p.pages) - else: - # Insert "smart" pagination links, so that there are always ON_ENDS - # links at either end of the list of pages, and there are always - # ON_EACH_SIDE links at either end of the "current page" link. - page_range = [] - if page_num > (ON_EACH_SIDE + ON_ENDS): - page_range.extend(range(0, ON_EACH_SIDE - 1)) - page_range.append(DOT) - page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1)) - else: - page_range.extend(range(0, page_num + 1)) - if page_num < (p.pages - ON_EACH_SIDE - ON_ENDS - 1): - page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1)) - page_range.append(DOT) - page_range.extend(range(p.pages - ON_ENDS, p.pages)) - else: - page_range.extend(range(page_num + 1, p.pages)) - for i in page_range: - if i == DOT: - raw_template.append('... ') - elif i == page_num: - raw_template.append('%d ' % (i+1)) - else: - raw_template.append('%d ' % \ - (get_query_string(params, {PAGE_VAR: i}), (i == p.pages-1 and ' class="end"' or ''), i+1)) - raw_template.append('%s %s' % (result_count, result_count == 1 and opts.verbose_name or opts.verbose_name_plural)) - if can_show_all and not show_all and multi_page: - raw_template.append('  Show all' % \ - get_query_string(params, {ALL_VAR: ''})) - raw_template.append('

') - - raw_template.append('
\n
') - raw_template.append('{% endblock %}\n') - t = template_loader.get_template_from_string(''.join(raw_template)) - c = Context(request, { - 'title': (is_popup and 'Select %s' % opts.verbose_name or 'Select %s to change' % opts.verbose_name), - 'is_popup': is_popup, - }) - return HttpResponse(t.render(c)) - -def _get_flattened_data(field, val): - """ - Returns a dictionary mapping the field's manipulator field names to its - "flattened" string values for the admin view. "val" is an instance of the - field's value. - """ - if isinstance(field, meta.DateTimeField): - date_field, time_field = field.get_manipulator_field_names('') - return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), - time_field: (val is not None and val.strftime("%H:%M:%S") or '')} - elif isinstance(field, meta.DateField): - return {field.name: (val is not None and val.strftime("%Y-%m-%d") or '')} - elif isinstance(field, meta.TimeField): - return {field.name: (val is not None and val.strftime("%H:%M:%S") or '')} - else: - return {field.name: val} - -use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin - -def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects): - t = ['
'] - if change or show_delete: - t.append('{%% if perms.%s.%s %%}{%% if not is_popup %%}

Delete

{%% endif %%}{%% endif %%}' % \ - (app_label, opts.get_delete_permission())) - if change and opts.admin.save_as: - t.append('{%% if not is_popup %%}{%% endif %%}' % \ - (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) - if not opts.admin.save_as or add: - t.append('{%% if not is_popup %%}{%% endif %%}' % \ - (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) - t.append('{%% if not is_popup %%}{%% endif %%}' % \ - (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) - t.append('' % \ - (ordered_objects and change and 'onclick="submitOrderForm();"' or '')) - t.append('
\n') - return t - -def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): - 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.append('{% block extrahead %}') - - # Put in any necessary JavaScript imports. - javascript_imports = ['%sjs/core.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/RelatedObjectLookups.js' % ADMIN_MEDIA_PREFIX] - if auto_populated_fields: - javascript_imports.append('%sjs/urlify.js' % ADMIN_MEDIA_PREFIX) - if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField): - javascript_imports.extend(['%sjs/calendar.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/DateTimeShortcuts.js' % ADMIN_MEDIA_PREFIX]) - if ordered_objects: - javascript_imports.extend(['%sjs/getElementsBySelector.js' % ADMIN_MEDIA_PREFIX, '%sjs/dom-drag.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/ordering.js' % ADMIN_MEDIA_PREFIX]) - if opts.admin.js: - javascript_imports.extend(opts.admin.js) - seen_collapse = False - for _, options in admin_field_objs: - if not seen_collapse and 'collapse' in options.get('classes', ''): - seen_collapse = True - javascript_imports.append('%sjs/admin/CollapsedFieldsets.js' % ADMIN_MEDIA_PREFIX) - try: - for field_list in options['fields']: - for f in field_list: - if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: - javascript_imports.extend(['%sjs/SelectBox.js' % ADMIN_MEDIA_PREFIX, '%sjs/SelectFilter2.js' % ADMIN_MEDIA_PREFIX]) - raise StopIteration - except StopIteration: - break - for j in javascript_imports: - t.append('' % j) - - t.append('{% endblock %}\n') - if ordered_objects: - coltype = 'colMS' - else: - coltype = 'colM' - t.append('{%% block coltype %%}%s{%% endblock %%}\n' % coltype) - t.append('{%% block bodyclass %%}%s-%s change-form{%% endblock %%}\n' % (app_label, opts.object_name.lower())) - breadcrumb_title = add and "Add %s" % opts.verbose_name or '{{ original|striptags|truncatewords:"18" }}' - t.append('{%% block breadcrumbs %%}{%% if not is_popup %%}{%% endif %%}{%% endblock %%}\n' % \ - (capfirst(opts.verbose_name_plural), breadcrumb_title)) - t.append('{% block content %}
\n') - if change: - t.append('{% if not is_popup %}') - t.append('
  • History
  • ') - if hasattr(opts.get_model_module().Klass, 'get_absolute_url'): - t.append('
  • View on site
  • ' % opts.get_content_type_id()) - t.append('
\n') - t.append('{% endif %}') - t.append('
\n' % form_url) - t.append('{% if is_popup %}{% endif %}') - if opts.admin.save_on_top: - t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) - t.append('{% if form.error_dict %}

Please correct the error{{ form.error_dict.items|pluralize }} below.

{% endif %}\n') - for fieldset_name, options in admin_field_objs: - t.append('
\n\n' % options.get('classes', '')) - if fieldset_name: - t.append('

%s

\n' % fieldset_name) - for field_list in options['fields']: - t.append(_get_admin_field(field_list, 'form.', False, add, change)) - for f in field_list: - if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: - t.append('\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)) - t.append('
\n') - if ordered_objects and change: - t.append('

Ordering

') - t.append('
\n') - t.append('{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}') - t.append('

{{ form.order_ }}

\n') - t.append('
\n') - for rel_obj, rel_field in opts.get_inline_related_objects(): - var_name = rel_obj.object_name.lower() - field_list = [f for f in rel_obj.fields + rel_obj.many_to_many if f.editable and f != rel_field] - t.append('
\n' % ((rel_field.rel.edit_inline != meta.TABULAR) and ' aligned' or '')) - view_on_site = '' - if change and hasattr(rel_obj, 'get_absolute_url'): - view_on_site = '{%% if %s.original %%}View on site{%% endif %%}' % (var_name, var_name, var_name) - if rel_field.rel.edit_inline == meta.TABULAR: - t.append('

%s

\n\n' % capfirst(rel_obj.verbose_name_plural)) - t.append('') - for f in field_list: - if isinstance(f, meta.AutoField): - continue - t.append('%s' % (f.blank and ' class="optional"' or '', capfirst(f.verbose_name))) - t.append('\n') - t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) - if change: - for f in field_list: - if use_raw_id_admin(f): - t.append('{%% if %s.original %%}' % var_name) - t.append('') - t.append('' % (30, var_name)) - t.append('{% endif %}\n') - break - t.append('{%% if %s %%}\n' % ' or '.join(['%s.%s.errors' % (var_name, f.name) for f in field_list])) - t.append('\n{%% endif %%}\n' % \ - (len(field_list), ''.join(['{{ %s.%s.html_error_list }}' % (var_name, f.name) for f in field_list]))) - t.append('\n') - hidden_fields = [] - for f in field_list: - form_widget = _get_admin_field_form_widget(f, var_name+'.', True, add, change) - # Don't put AutoFields within a \n' % (var_name, f.name, form_widget)) - else: - t.append('%s\n' % (var_name, f.name, form_widget)) - else: - hidden_fields.append(form_widget) - if hasattr(rel_obj, 'get_absolute_url'): - t.append('\n' % view_on_site) - t.append('\n') - t.append('{% endfor %}\n
{{ %s.original }}
%s
, because they're hidden. - if not isinstance(f, meta.AutoField): - # Fields with raw_id_admin=True get class="nowrap". - if use_raw_id_admin(f): - t.append('%s%s
\n') - # Write out the hidden fields. We didn't write them out earlier - # because it would've been invalid HTML. - t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) - t.extend(hidden_fields) - t.append('{% endfor %}\n') - else: # edit_inline == STACKED - t.append('{%% for %s in form.%s %%}' % (var_name, rel_obj.module_name)) - t.append('

%s #{{ forloop.counter }}

' % capfirst(rel_obj.verbose_name)) - if view_on_site: - t.append('

%s

' % view_on_site) - for f in field_list: - # Don't put AutoFields within the widget -- just use the field. - if isinstance(f, meta.AutoField): - t.append(_get_admin_field_form_widget(f, var_name+'.', True, add, change)) - else: - t.append(_get_admin_field([f], var_name+'.', True, add, change)) - t.append('{% endfor %}\n') - t.append('
\n') - t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) - if add: - # Add focus to the first field on the form, if this is an "add" form. - t.append('' % \ - admin_field_objs[0][1]['fields'][0][0].get_manipulator_field_names('')[0]) - if auto_populated_fields: - t.append('\n') - if change and ordered_objects: - t.append('{% if form.order_objects %}
    {% for object in form.order_objects %}') - t.append('
  • {{ object|truncatewords:"5" }}
  • ' % \ - {'x': ' '.join(['object.%s' % o.pk.name for o in ordered_objects])}) - t.append('{% endfor %}
{% endif %}\n') - t.append('
\n
\n{% endblock %}') - return ''.join(t) - -def _get_admin_field(field_list, name_prefix, rel, add, change): - "Returns the template code for editing the given list of fields in the admin template." - field_names = [] - for f in field_list: - field_names.extend(f.get_manipulator_field_names(name_prefix)) - div_class_names = ['form-row', '{%% if %s %%} error{%% endif %%}' % ' or '.join(['%s.errors' % n for n in field_names])] - # Assumes BooleanFields won't be stacked next to each other! - if isinstance(field_list[0], meta.BooleanField): - div_class_names.append('checkbox-row') - t = [] - t.append('
\n' % ' '.join(div_class_names)) - for n in field_names: - t.append('{%% if %s.errors %%}{{ %s.html_error_list }}{%% endif %%}\n' % (n, n)) - for i, field in enumerate(field_list): - label_name = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) - # BooleanFields are a special case, because the checkbox widget appears to - # the *left* of the label. - if isinstance(field, meta.BooleanField): - t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) - t.append(' ' % (label_name, capfirst(field.verbose_name))) - else: - class_names = [] - if not field.blank: - class_names.append('required') - if i > 0: - class_names.append('inline') - t.append(' ' % (label_name, class_names and ' class="%s"' % ' '.join(class_names) or '', capfirst(field.verbose_name))) - t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) - if change and field.primary_key: - t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name)) - if change and use_raw_id_admin(field): - if isinstance(field.rel, meta.ManyToOne): - if_bit = '%soriginal.get_%s' % (rel and name_prefix or '', field.name) - obj_repr = if_bit + '|truncatewords:"14"' - elif isinstance(field.rel, meta.ManyToMany): - if_bit = '%soriginal.get_%s_list' % (rel and name_prefix or '', field.name) - obj_repr = if_bit + '|join:", "|truncatewords:"14"' - t.append('{%% if %s %%} {{ %s }}{%% endif %%}' % (if_bit, obj_repr)) - if field.help_text: - t.append('

%s

\n' % field.help_text) - t.append('
\n\n') - return ''.join(t) - -def _get_admin_field_form_widget(field, name_prefix, rel, add, change): - "Returns JUST the formfield widget for the field's admin interface." - field_names = field.get_manipulator_field_names(name_prefix) - if isinstance(field, meta.DateTimeField): - return '

Date: {{ %s }}
Time: {{ %s }}

' % tuple(field_names) - t = ['{{ %s }}' % n for n in field_names] - if change and isinstance(field, meta.FileField): - return '{%% if %soriginal.%s %%}Currently: {{ %soriginal.%s }}
Change: %s{%% else %%}%s{%% endif %%}' % \ - (name_prefix, field.name, name_prefix, field.name, name_prefix, field.name, ''.join(t), ''.join(t)) - field_id = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) - # raw_id_admin fields get the little lookup link next to them - if use_raw_id_admin(field): - t.append(' ' % \ - (field.rel.to.app_label, field.rel.to.module_name, field_id)) - t.append('Lookup' % ADMIN_MEDIA_PREFIX) - # fields with relationships to editable objects get an "add another" link, - # but only if the field doesn't have raw_admin ('cause in that case they get - # the "add" button in the popup) - elif field.rel and (isinstance(field.rel, meta.ManyToOne) or isinstance(field.rel, meta.ManyToMany)) and field.rel.to.admin: - t.append('{%% if perms.%s.%s %%}' % (field.rel.to.app_label, field.rel.to.get_add_permission())) - t.append(' ' % \ - (field.rel.to.app_label, field.rel.to.module_name, field_id)) - t.append('Add Another' % ADMIN_MEDIA_PREFIX) - t.append('{% endif %}') - return ''.join(t) - -def add_stage(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): - mod, opts = _get_mod_opts(app_label, module_name) - if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): - raise PermissionDenied - manipulator = mod.AddManipulator() - if request.POST: - new_data = request.POST.copy() - if opts.has_field_type(meta.FileField): - new_data.update(request.FILES) - errors = manipulator.get_validation_errors(new_data) - if not errors and not request.POST.has_key("_preview"): - for f in opts.many_to_many: - if f.rel.raw_id_admin: - new_data.setlist(f.name, new_data[f.name].split(",")) - manipulator.do_html2python(new_data) - new_object = manipulator.save(new_data) - pk_value = getattr(new_object, opts.pk.column) - log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) - msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) - # Here, we distinguish between different save types by checking for - # the presence of keys in request.POST. - if request.POST.has_key("_continue"): - request.user.add_message("%s You may edit it again below." % msg) - if request.POST.has_key("_popup"): - post_url_continue += "?_popup=1" - return HttpResponseRedirect(post_url_continue % pk_value) - if request.POST.has_key("_popup"): - return HttpResponse('' % \ - (pk_value, repr(new_object).replace('"', '\\"'))) - elif request.POST.has_key("_addanother"): - request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) - return HttpResponseRedirect(request.path) - else: - request.user.add_message(msg) - return HttpResponseRedirect(post_url) - if request.POST.has_key("_preview"): - manipulator.do_html2python(new_data) - else: - new_data = {} - # Add default data. - for f in opts.fields: - if f.has_default(): - new_data.update(_get_flattened_data(f, f.get_default())) - # In required many-to-one fields with only one available choice, - # select that one available choice. Note: We have to check that - # the length of choices is *2*, not 1, because SelectFields always - # have an initial "blank" value. - elif not f.blank and ((isinstance(f.rel, meta.ManyToOne) and not f.rel.raw_id_admin) or f.choices) and len(manipulator[f.name].choices) == 2: - new_data[f.name] = manipulator[f.name].choices[1][0] - # In required many-to-many fields with only one available choice, - # select that one available choice. - for f in opts.many_to_many: - if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1: - new_data[f.name] = [manipulator[f.name].choices[0][0]] - # Add default data for related objects. - for rel_opts, rel_field in opts.get_inline_related_objects(): - var_name = rel_opts.object_name.lower() - for i in range(rel_field.rel.num_in_admin): - for f in rel_opts.fields + rel_opts.many_to_many: - if f.has_default(): - for field_name in f.get_manipulator_field_names(''): - new_data['%s.%d.%s' % (var_name, i, field_name)] = f.get_default() - # Override the defaults with request.GET, if it exists. - new_data.update(request.GET) - errors = {} - - # Populate the FormWrapper. - form = formfields.FormWrapper(manipulator, new_data, errors) - for rel_opts, rel_field in opts.get_inline_related_objects(): - var_name = rel_opts.object_name.lower() - wrapper = [] - for i in range(rel_field.rel.num_in_admin): - collection = {} - for f in rel_opts.fields + rel_opts.many_to_many: - if f.editable and f != rel_field and not isinstance(f, meta.AutoField): - for field_name in f.get_manipulator_field_names(''): - full_field_name = '%s.%d.%s' % (var_name, i, field_name) - collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, ''), errors.get(full_field_name, [])) - wrapper.append(formfields.FormFieldCollection(collection)) - setattr(form, rel_opts.module_name, wrapper) - - c = Context(request, { - 'title': 'Add %s' % opts.verbose_name, - "form": form, - "is_popup": request.REQUEST.has_key("_popup"), - }) - if object_id_override is not None: - c['object_id'] = object_id_override - raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) -# return HttpResponse(raw_template, mimetype='text/plain') - t = template_loader.get_template_from_string(raw_template) - return HttpResponse(t.render(c)) - -def change_stage(request, app_label, module_name, object_id): - mod, opts = _get_mod_opts(app_label, module_name) - if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): - raise PermissionDenied - if request.POST and request.POST.has_key("_saveasnew"): - return add_stage(request, app_label, module_name, form_url='../add/') - try: - manipulator = mod.ChangeManipulator(object_id) - except ObjectDoesNotExist: - raise Http404 - - inline_related_objects = opts.get_inline_related_objects() - if request.POST: - new_data = request.POST.copy() - if opts.has_field_type(meta.FileField): - new_data.update(request.FILES) - - errors = manipulator.get_validation_errors(new_data) - if not errors and not request.POST.has_key("_preview"): - for f in opts.many_to_many: - if f.rel.raw_id_admin: - new_data.setlist(f.name, new_data[f.name].split(",")) - manipulator.do_html2python(new_data) - new_object = manipulator.save(new_data) - pk_value = getattr(new_object, opts.pk.column) - - # Construct the change message. - change_message = [] - if manipulator.fields_added: - change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) - if manipulator.fields_changed: - change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) - if manipulator.fields_deleted: - change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) - change_message = ' '.join(change_message) - if not change_message: - change_message = 'No fields changed.' - - log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) - msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) - if request.POST.has_key("_continue"): - request.user.add_message("%s You may edit it again below." % msg) - if request.REQUEST.has_key('_popup'): - return HttpResponseRedirect(request.path + "?_popup=1") - else: - return HttpResponseRedirect(request.path) - elif request.POST.has_key("_saveasnew"): - request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) - return HttpResponseRedirect("../%s/" % pk_value) - elif request.POST.has_key("_addanother"): - request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) - return HttpResponseRedirect("../add/") - else: - request.user.add_message(msg) - return HttpResponseRedirect("../") - if request.POST.has_key("_preview"): - manipulator.do_html2python(new_data) - else: - # Populate new_data with a "flattened" version of the current data. - new_data = {} - obj = manipulator.original_object - for f in opts.fields: - new_data.update(_get_flattened_data(f, getattr(obj, f.column))) - for f in opts.many_to_many: - get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) - if f.rel.raw_id_admin: - new_data[f.name] = ",".join([str(getattr(i, f.rel.to.pk.column)) for i in get_list_func()]) - elif not f.rel.edit_inline: - new_data[f.name] = [getattr(i, f.rel.to.pk.column) for i in get_list_func()] - for rel_obj, rel_field in inline_related_objects: - var_name = rel_obj.object_name.lower() - for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): - for f in rel_obj.fields: - if f.editable and f != rel_field: - for k, v in _get_flattened_data(f, getattr(rel_instance, f.column)).items(): - new_data['%s.%d.%s' % (var_name, i, k)] = v - for f in rel_obj.many_to_many: - new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] - - # If the object has ordered objects on its admin page, get the existing - # order and flatten it into a comma-separated list of IDs. - id_order_list = [] - for rel_obj in opts.get_ordered_objects(): - id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) - if id_order_list: - new_data['order_'] = ','.join(map(str, id_order_list)) - errors = {} - - # Populate the FormWrapper. - form = formfields.FormWrapper(manipulator, new_data, errors) - form.original = manipulator.original_object - form.order_objects = [] - for rel_opts, rel_field in inline_related_objects: - var_name = rel_opts.object_name.lower() - wrapper = [] - orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() - count = len(orig_list) + rel_field.rel.num_extra_on_change - if rel_field.rel.min_num_in_admin: - count = max(count, rel_field.rel.min_num_in_admin) - if rel_field.rel.max_num_in_admin: - count = min(count, rel_field.rel.max_num_in_admin) - for i in range(count): - collection = {'original': (i < len(orig_list) and orig_list[i] or None)} - for f in rel_opts.fields + rel_opts.many_to_many: - if f.editable and f != rel_field: - for field_name in f.get_manipulator_field_names(''): - full_field_name = '%s.%d.%s' % (var_name, i, field_name) - collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, f.get_default()), errors.get(full_field_name, [])) - wrapper.append(formfields.FormFieldCollection(collection)) - setattr(form, rel_opts.module_name, wrapper) - if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: - form.order_objects.extend(orig_list) - - c = Context(request, { - 'title': 'Change %s' % opts.verbose_name, - "form": form, - 'object_id': object_id, - 'original': manipulator.original_object, - 'is_popup' : request.REQUEST.has_key('_popup'), - }) - raw_template = _get_template(opts, app_label, change=True) -# return HttpResponse(raw_template, mimetype='text/plain') - t = template_loader.get_template_from_string(raw_template) - return HttpResponse(t.render(c)) - -def _nest_help(obj, depth, val): - current = obj - for i in range(depth): - current = current[-1] - current.append(val) - -def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth): - "Helper function that recursively populates deleted_objects." - nh = _nest_help # Bind to local variable for performance - if current_depth > 16: - return # Avoid recursing too deep. - objects_seen = [] - for rel_opts, rel_field in opts.get_all_related_objects(): - if rel_opts in objects_seen: - continue - objects_seen.append(rel_opts) - rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) - if isinstance(rel_field.rel, meta.OneToOne): - try: - sub_obj = getattr(obj, 'get_%s' % rel_opts_name)() - except ObjectDoesNotExist: - pass - else: - if rel_opts.admin: - p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) - if not user.has_perm(p): - perms_needed.add(rel_opts.verbose_name) - # We don't care about populating deleted_objects now. - continue - if rel_field.rel.edit_inline or not rel_opts.admin: - # Don't display link to edit, because it either has no - # admin or is edited inline. - nh(deleted_objects, current_depth, ['%s: %r' % (capfirst(rel_opts.verbose_name), sub_obj), []]) - else: - # Display a link to the admin page. - nh(deleted_objects, current_depth, ['%s: %r' % \ - (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, - getattr(sub_obj, rel_opts.pk.column), sub_obj), []]) - _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) - else: - has_related_objs = False - for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)(): - has_related_objs = True - if rel_field.rel.edit_inline or not rel_opts.admin: - # Don't display link to edit, because it either has no - # admin or is edited inline. - nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(rel_opts.verbose_name), strip_tags(repr(sub_obj))), []]) - else: - # Display a link to the admin page. - nh(deleted_objects, current_depth, ['%s: %s' % \ - (capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) - _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) - # If there were related objects, and the user doesn't have - # permission to delete them, add the missing perm to perms_needed. - if rel_opts.admin and has_related_objs: - p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) - if not user.has_perm(p): - perms_needed.add(rel_opts.verbose_name) - for rel_opts, rel_field in opts.get_all_related_many_to_many_objects(): - if rel_opts in objects_seen: - continue - objects_seen.append(rel_opts) - rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) - has_related_objs = False - for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)(): - has_related_objs = True - if rel_field.rel.edit_inline or not rel_opts.admin: - # Don't display link to edit, because it either has no - # admin or is edited inline. - nh(deleted_objects, current_depth, ['One or more %s in %s: %s' % \ - (rel_field.name, rel_opts.verbose_name, strip_tags(repr(sub_obj))), []]) - else: - # Display a link to the admin page. - nh(deleted_objects, current_depth, ['One or more %s in %s: %s' % \ - (rel_field.name, rel_opts.verbose_name, rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) - # If there were related objects, and the user doesn't have - # permission to change them, add the missing perm to perms_needed. - if rel_opts.admin and has_related_objs: - p = '%s.%s' % (rel_opts.app_label, rel_opts.get_change_permission()) - if not user.has_perm(p): - perms_needed.add(rel_opts.verbose_name) - -def delete_stage(request, app_label, module_name, object_id): - import sets - mod, opts = _get_mod_opts(app_label, module_name) - if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()): - raise PermissionDenied - obj = get_object_or_404(mod, pk=object_id) - - # Populate deleted_objects, a data structure of all related objects that - # will also be deleted. - deleted_objects = ['%s: %s' % (capfirst(opts.verbose_name), object_id, strip_tags(repr(obj))), []] - perms_needed = sets.Set() - _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) - - if request.POST: # The user has already confirmed the deletion. - if perms_needed: - raise PermissionDenied - obj_repr = repr(obj) - obj.delete() - 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', { - "title": "Are you sure?", - "object_name": opts.verbose_name, - "object": obj, - "deleted_objects": deleted_objects, - "perms_lacking": perms_needed, - }, context_instance=Context(request)) - -def history(request, app_label, module_name, object_id): - mod, opts = _get_mod_opts(app_label, module_name) - action_list = log.get_list(object_id__exact=object_id, content_type__id__exact=opts.get_content_type_id(), - order_by=("action_time",), select_related=True) - # 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', { - 'title': 'Change history: %r' % obj, - 'action_list': action_list, - 'module_name': capfirst(opts.verbose_name_plural), - 'object': obj, - }, context_instance=Context(request)) -- cgit v1.3