Django

Code

root/django/branches/newforms-admin/django/contrib/admin/util.py

Revision 7935, 6.7 kB (checked in by brosner, 5 months ago)

newforms-admin: Fixed #5490 -- Properly quote special characters in primary keys in the admin. Added tests to ensure functionality. This also moves quote and unquote to django/contrib/admin/util.py. Thanks jdetaeye and shanx for all your help.

Line 
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 _nest_help(obj, depth, val):
47     current = obj
48     for i in range(depth):
49         current = current[-1]
50     current.append(val)
51
52 def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth, admin_site):
53     "Helper function that recursively populates deleted_objects."
54     nh = _nest_help # Bind to local variable for performance
55     if current_depth > 16:
56         return # Avoid recursing too deep.
57     opts_seen = []
58     for related in opts.get_all_related_objects():
59         has_admin = related.model in admin_site._registry
60         if related.opts in opts_seen:
61             continue
62         opts_seen.append(related.opts)
63         rel_opts_name = related.get_accessor_name()
64         if isinstance(related.field.rel, models.OneToOneRel):
65             try:
66                 sub_obj = getattr(obj, rel_opts_name)
67             except ObjectDoesNotExist:
68                 pass
69             else:
70                 if has_admin:
71                     p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
72                     if not user.has_perm(p):
73                         perms_needed.add(related.opts.verbose_name)
74                         # We don't care about populating deleted_objects now.
75                         continue
76                 if related.field.rel.edit_inline or not has_admin:
77                     # Don't display link to edit, because it either has no
78                     # admin or is edited inline.
79                     nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []])
80                 else:
81                     # Display a link to the admin page.
82                     nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
83                         (escape(force_unicode(capfirst(related.opts.verbose_name))),
84                             related.opts.app_label,
85                             related.opts.object_name.lower(),
86                             sub_obj._get_pk_val(), sub_obj)), []])
87                 get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
88         else:
89             has_related_objs = False
90             for sub_obj in getattr(obj, rel_opts_name).all():
91                 has_related_objs = True
92                 if related.field.rel.edit_inline or not has_admin:
93                     # Don't display link to edit, because it either has no
94                     # admin or is edited inline.
95                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []])
96                 else:
97                     # Display a link to the admin page.
98                     nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
99                         (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))), []])
100                 get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
101             # If there were related objects, and the user doesn't have
102             # permission to delete them, add the missing perm to perms_needed.
103             if has_admin and has_related_objs:
104                 p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
105                 if not user.has_perm(p):
106                     perms_needed.add(related.opts.verbose_name)
107     for related in opts.get_all_related_many_to_many_objects():
108         has_admin = related.model in admin_site._registry
109         if related.opts in opts_seen:
110             continue
111         opts_seen.append(related.opts)
112         rel_opts_name = related.get_accessor_name()
113         has_related_objs = False
114
115         # related.get_accessor_name() could return None for symmetrical relationships
116         if rel_opts_name:
117             rel_objs = getattr(obj, rel_opts_name, None)
118             if rel_objs:
119                 has_related_objs = True
120
121         if has_related_objs:
122             for sub_obj in rel_objs.all():
123                 if related.field.rel.edit_inline or not has_admin:
124                     # Don't display link to edit, because it either has no
125                     # admin or is edited inline.
126                     nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
127                         {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []])
128                 else:
129                     # Display a link to the admin page.
130                     nh(deleted_objects, current_depth, [
131                         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))}) + \
132                         (u' <a href="../../../../%s/%s/%s/">%s</a>' % \
133                             (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []])
134         # If there were related objects, and the user doesn't have
135         # permission to change them, add the missing perm to perms_needed.
136         if has_admin and has_related_objs:
137             p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
138             if not user.has_perm(p):
139                 perms_needed.add(related.opts.verbose_name)
Note: See TracBrowser for help on using the browser.