app/django/contrib/admin/util.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 from django.core.exceptions import ObjectDoesNotExist
       
     2 from django.db import models
       
     3 from django.utils.html import escape
       
     4 from django.utils.safestring import mark_safe
       
     5 from django.utils.text import capfirst
       
     6 from django.utils.encoding import force_unicode
       
     7 from django.utils.translation import ugettext as _
       
     8 
       
     9 
       
    10 def quote(s):
       
    11     """
       
    12     Ensure that primary key values do not confuse the admin URLs by escaping
       
    13     any '/', '_' and ':' characters. Similar to urllib.quote, except that the
       
    14     quoting is slightly different so that it doesn't get automatically
       
    15     unquoted by the Web browser.
       
    16     """
       
    17     if not isinstance(s, basestring):
       
    18         return s
       
    19     res = list(s)
       
    20     for i in range(len(res)):
       
    21         c = res[i]
       
    22         if c in """:/_#?;@&=+$,"<>%\\""":
       
    23             res[i] = '_%02X' % ord(c)
       
    24     return ''.join(res)
       
    25 
       
    26 def unquote(s):
       
    27     """
       
    28     Undo the effects of quote(). Based heavily on urllib.unquote().
       
    29     """
       
    30     mychr = chr
       
    31     myatoi = int
       
    32     list = s.split('_')
       
    33     res = [list[0]]
       
    34     myappend = res.append
       
    35     del list[0]
       
    36     for item in list:
       
    37         if item[1:2]:
       
    38             try:
       
    39                 myappend(mychr(myatoi(item[:2], 16)) + item[2:])
       
    40             except ValueError:
       
    41                 myappend('_' + item)
       
    42         else:
       
    43             myappend('_' + item)
       
    44     return "".join(res)
       
    45 
       
    46 def flatten_fieldsets(fieldsets):
       
    47     """Returns a list of field names from an admin fieldsets structure."""
       
    48     field_names = []
       
    49     for name, opts in fieldsets:
       
    50         for field in opts['fields']:
       
    51             # type checking feels dirty, but it seems like the best way here
       
    52             if type(field) == tuple:
       
    53                 field_names.extend(field)
       
    54             else:
       
    55                 field_names.append(field)
       
    56     return field_names
       
    57 
       
    58 def _nest_help(obj, depth, val):
       
    59     current = obj
       
    60     for i in range(depth):
       
    61         current = current[-1]
       
    62     current.append(val)
       
    63 
       
    64 def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth, admin_site):
       
    65     "Helper function that recursively populates deleted_objects."
       
    66     nh = _nest_help # Bind to local variable for performance
       
    67     if current_depth > 16:
       
    68         return # Avoid recursing too deep.
       
    69     opts_seen = []
       
    70     for related in opts.get_all_related_objects():
       
    71         has_admin = related.model in admin_site._registry
       
    72         if related.opts in opts_seen:
       
    73             continue
       
    74         opts_seen.append(related.opts)
       
    75         rel_opts_name = related.get_accessor_name()
       
    76         if isinstance(related.field.rel, models.OneToOneRel):
       
    77             try:
       
    78                 sub_obj = getattr(obj, rel_opts_name)
       
    79             except ObjectDoesNotExist:
       
    80                 pass
       
    81             else:
       
    82                 if has_admin:
       
    83                     p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
       
    84                     if not user.has_perm(p):
       
    85                         perms_needed.add(related.opts.verbose_name)
       
    86                         # We don't care about populating deleted_objects now.
       
    87                         continue
       
    88                 if not has_admin:
       
    89                     # Don't display link to edit, because it either has no
       
    90                     # admin or is edited inline.
       
    91                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []])
       
    92                 else:
       
    93                     # Display a link to the admin page.
       
    94                     nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
       
    95                         (escape(force_unicode(capfirst(related.opts.verbose_name))),
       
    96                             related.opts.app_label,
       
    97                             related.opts.object_name.lower(),
       
    98                             sub_obj._get_pk_val(), sub_obj)), []])
       
    99                 get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
       
   100         else:
       
   101             has_related_objs = False
       
   102             for sub_obj in getattr(obj, rel_opts_name).all():
       
   103                 has_related_objs = True
       
   104                 if not has_admin:
       
   105                     # Don't display link to edit, because it either has no
       
   106                     # admin or is edited inline.
       
   107                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []])
       
   108                 else:
       
   109                     # Display a link to the admin page.
       
   110                     nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
       
   111                         (escape(force_unicode(capfirst(related.opts.verbose_name))), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj))), []])
       
   112                 get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
       
   113             # If there were related objects, and the user doesn't have
       
   114             # permission to delete them, add the missing perm to perms_needed.
       
   115             if has_admin and has_related_objs:
       
   116                 p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
       
   117                 if not user.has_perm(p):
       
   118                     perms_needed.add(related.opts.verbose_name)
       
   119     for related in opts.get_all_related_many_to_many_objects():
       
   120         has_admin = related.model in admin_site._registry
       
   121         if related.opts in opts_seen:
       
   122             continue
       
   123         opts_seen.append(related.opts)
       
   124         rel_opts_name = related.get_accessor_name()
       
   125         has_related_objs = False
       
   126 
       
   127         # related.get_accessor_name() could return None for symmetrical relationships
       
   128         if rel_opts_name:
       
   129             rel_objs = getattr(obj, rel_opts_name, None)
       
   130             if rel_objs:
       
   131                 has_related_objs = True
       
   132 
       
   133         if has_related_objs:
       
   134             for sub_obj in rel_objs.all():
       
   135                 if not has_admin:
       
   136                     # Don't display link to edit, because it either has no
       
   137                     # admin or is edited inline.
       
   138                     nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
       
   139                         {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []])
       
   140                 else:
       
   141                     # Display a link to the admin page.
       
   142                     nh(deleted_objects, current_depth, [
       
   143                         mark_safe((_('One or more %(fieldname)s in %(name)s:') % {'fieldname': escape(force_unicode(related.field.verbose_name)), 'name': escape(force_unicode(related.opts.verbose_name))}) + \
       
   144                         (u' <a href="../../../../%s/%s/%s/">%s</a>' % \
       
   145                             (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []])
       
   146         # If there were related objects, and the user doesn't have
       
   147         # permission to change them, add the missing perm to perms_needed.
       
   148         if has_admin and has_related_objs:
       
   149             p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
       
   150             if not user.has_perm(p):
       
   151                 perms_needed.add(related.opts.verbose_name)