Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

newforms-admin: Merged trunk revision [6671].

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6776 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f88babafc58eafece72d3f2f7444336c69196808 1 parent c82b2be
@jkocherhans jkocherhans authored
Showing with 1,184 additions and 310 deletions.
  1. +4 −2 django/contrib/admin/filterspecs.py
  2. +2 −1  django/contrib/admin/models.py
  3. +3 −2 django/contrib/admin/options.py
  4. +2 −1  django/contrib/admin/sites.py
  5. +1 −1  django/contrib/admin/templates/admin/base_site.html
  6. +2 −2 django/contrib/admin/templates/admin/change_form.html
  7. +2 −2 django/contrib/admin/templates/admin/date_hierarchy.html
  8. +2 −2 django/contrib/admin/templates/admin/delete_confirmation.html
  9. +3 −3 django/contrib/admin/templates/admin/index.html
  10. +1 −1  django/contrib/admin/templates/admin/invalid_setup.html
  11. +3 −3 django/contrib/admin/templates/admin/object_history.html
  12. +1 −1  django/contrib/admin/templates/admin/pagination.html
  13. +4 −4 django/contrib/admin/templates/admin_doc/model_detail.html
  14. +9 −8 django/contrib/admin/templatetags/admin_list.py
  15. +11 −8 django/contrib/admin/util.py
  16. +2 −1  django/contrib/admin/views/decorators.py
  17. +1 −1  django/contrib/admin/views/main.py
  18. +2 −1  django/contrib/admindocs/utils.py
  19. +2 −1  django/contrib/admindocs/views.py
  20. +4 −3 django/contrib/csrf/middleware.py
  21. +7 −6 django/contrib/databrowse/datastructures.py
  22. +7 −5 django/contrib/databrowse/plugins/calendars.py
  23. +6 −4 django/contrib/databrowse/plugins/fieldchoices.py
  24. +2 −1  django/contrib/databrowse/sites.py
  25. +8 −0 django/contrib/flatpages/views.py
  26. +4 −0 django/contrib/humanize/templatetags/humanize.py
  27. +7 −3 django/contrib/markup/templatetags/markup.py
  28. +4 −2 django/contrib/markup/tests.py
  29. +2 −1  django/contrib/sitemaps/templates/sitemap.xml
  30. +2 −1  django/contrib/sitemaps/templates/sitemap_index.xml
  31. +10 −5 django/newforms/forms.py
  32. +6 −2 django/newforms/util.py
  33. +26 −14 django/newforms/widgets.py
  34. +22 −21 django/oldforms/__init__.py
  35. +45 −25 django/template/__init__.py
  36. +4 −1 django/template/context.py
  37. +140 −33 django/template/defaultfilters.py
  38. +36 −1 django/template/defaulttags.py
  39. +5 −1 django/utils/encoding.py
  40. +26 −7 django/utils/html.py
  41. +124 −0 django/utils/safestring.py
  42. +15 −15 django/views/debug.py
  43. +148 −1 docs/templates.txt
  44. +134 −8 docs/templates_python.txt
  45. +2 −2 tests/regressiontests/defaultfilters/tests.py
  46. +1 −1  tests/regressiontests/forms/forms.py
  47. +1 −1  tests/regressiontests/forms/tests.py
  48. +2 −1  tests/regressiontests/humanize/tests.py
  49. +220 −0 tests/regressiontests/templates/filters.py
  50. +107 −100 tests/regressiontests/templates/tests.py
View
6 django/contrib/admin/filterspecs.py
@@ -9,6 +9,8 @@
from django.db import models
from django.utils.encoding import smart_unicode, iri_to_uri
from django.utils.translation import ugettext as _
+from django.utils.html import escape
+from django.utils.safestring import mark_safe
import datetime
class FilterSpec(object):
@@ -39,7 +41,7 @@ def title(self):
def output(self, cl):
t = []
if self.has_output():
- t.append(_(u'<h3>By %s:</h3>\n<ul>\n') % self.title())
+ t.append(_(u'<h3>By %s:</h3>\n<ul>\n') % escape(self.title()))
for choice in self.choices(cl):
t.append(u'<li%s><a href="%s">%s</a></li>\n' % \
@@ -47,7 +49,7 @@ def output(self, cl):
iri_to_uri(choice['query_string']),
choice['display']))
t.append('</ul>\n\n')
- return "".join(t)
+ return mark_safe("".join(t))
class RelatedFilterSpec(FilterSpec):
def __init__(self, f, request, params, model, model_admin):
View
3  django/contrib/admin/models.py
@@ -3,6 +3,7 @@
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
+from django.utils.safestring import mark_safe
ADDITION = 1
CHANGE = 2
@@ -49,4 +50,4 @@ def get_admin_url(self):
Returns the admin URL to edit the object represented by this log entry.
This is relative to the Django admin index page.
"""
- return u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id)
+ return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id))
View
5 django/contrib/admin/options.py
@@ -9,6 +9,7 @@
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render_to_response
from django.utils.html import escape
+from django.utils.safestring import mark_safe
from django.utils.text import capfirst, get_text_list
from django.utils.translation import ugettext as _
from django.utils.encoding import force_unicode
@@ -462,7 +463,7 @@ def render_change_form(self, model, context, add=False, change=False, form_url='
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
'has_absolute_url': hasattr(model, 'get_absolute_url'),
'ordered_objects': ordered_objects,
- 'form_url': form_url,
+ 'form_url': mark_safe(form_url),
'opts': opts,
'content_type_id': ContentType.objects.get_for_model(model).id,
'save_on_top': self.save_on_top,
@@ -652,7 +653,7 @@ def delete_view(self, request, object_id):
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
- deleted_objects = [u'%s: <a href="../../%s/">%s</a>' % (force_unicode(capfirst(opts.verbose_name)), object_id, escape(str(obj))), []]
+ deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), force_unicode(object_id), escape(obj))), []]
perms_needed = sets.Set()
get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site)
View
3  django/contrib/admin/sites.py
@@ -3,6 +3,7 @@
from django.contrib.auth import authenticate, login
from django.db.models.base import ModelBase
from django.shortcuts import render_to_response
+from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy, ugettext as _
import base64
@@ -272,7 +273,7 @@ def index(self, request):
if True in perms.values():
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
- 'admin_url': '%s/%s/' % (app_label, model.__name__.lower()),
+ 'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
'perms': perms,
}
if app_label in app_dict:
View
2  django/contrib/admin/templates/admin/base_site.html
@@ -1,7 +1,7 @@
{% extends "admin/base.html" %}
{% load i18n %}
-{% block title %}{{ title|escape }} | {% trans 'Django site admin' %}{% endblock %}
+{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'Django administration' %}</h1>
View
4 django/contrib/admin/templates/admin/change_form.html
@@ -17,8 +17,8 @@
{% block breadcrumbs %}{% if not is_popup %}
<div class="breadcrumbs">
<a href="../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
- {% if add %}{% trans "Add" %} {{ opts.verbose_name|escape }}{% else %}{{ original|truncatewords:"18"|escape }}{% endif %}
+ <a href="../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
+ {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
{% endif %}{% endblock %}
View
4 django/contrib/admin/templates/admin/date_hierarchy.html
@@ -1,9 +1,9 @@
{% if show %}
<div class="xfull">
<ul class="toplinks">
-{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title|escape }}</a></li>{% endif %}
+{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %}
{% for choice in choices %}
-<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title|escape }}{% if choice.link %}</a>{% endif %}</li>
+<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
{% endfor %}
</ul><br class="clear" />
</div>
View
4 django/contrib/admin/templates/admin/delete_confirmation.html
@@ -6,7 +6,7 @@
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="../../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
+ <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
<a href="../">{{ object|escape|truncatewords:"18" }}</a> &rsaquo;
{% trans 'Delete' %}
</div>
@@ -17,7 +17,7 @@
<p>{% blocktrans with object|escape as escaped_object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
<ul>
{% for obj in perms_lacking %}
- <li>{{ obj|escape }}</li>
+ <li>{{ obj }}</li>
{% endfor %}
</ul>
{% else %}
View
6 django/contrib/admin/templates/admin/index.html
@@ -20,9 +20,9 @@
{% for model in app.models %}
<tr>
{% if model.perms.change %}
- <th scope="row"><a href="{{ model.admin_url }}">{{ model.name|escape }}</a></th>
+ <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
- <th scope="row">{{ model.name|escape }}</th>
+ <th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
@@ -59,7 +59,7 @@
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
- <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{% filter capfirst|escape %}{% trans entry.content_type.name %}{% endfilter %}</span></li>
+ <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span></li>
{% endfor %}
</ul>
{% endif %}
View
2  django/contrib/admin/templates/admin/invalid_setup.html
@@ -1,7 +1,7 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title|escape }}</div>{% endblock %}
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %}
{% block content %}
<p>{% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}</p>
View
6 django/contrib/admin/templates/admin/object_history.html
@@ -4,7 +4,7 @@
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}
-<div class="breadcrumbs"><a href="../../../../">{% trans 'Home' %}</a> &rsaquo; <a href="../../">{{ module_name|escape }}</a> &rsaquo; <a href="../">{{ object|escape|truncatewords:"18" }}</a> &rsaquo; {% trans 'History' %}</div>
+<div class="breadcrumbs"><a href="../../../../">{% trans 'Home' %}</a> &rsaquo; <a href="../../">{{ module_name }}</a> &rsaquo; <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo; {% trans 'History' %}</div>
{% endblock %}
{% block content %}
@@ -24,8 +24,8 @@
{% for action in action_list %}
<tr>
<th scope="row">{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th>
- <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name|escape }} {{ action.user.last_name|escape }}){% endif %}</td>
- <td>{{ action.change_message|escape }}</td>
+ <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name }} {{ action.user.last_name }}){% endif %}</td>
+ <td>{{ action.change_message }}</td>
</tr>
{% endfor %}
</tbody>
View
2  django/contrib/admin/templates/admin/pagination.html
@@ -6,6 +6,6 @@
{% paginator_number cl i %}
{% endfor %}
{% endif %}
-{{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name|escape }}{% else %}{{ cl.opts.verbose_name_plural|escape }}{% endifequal %}
+{{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name|escape }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %}
{% if show_all_url %}&nbsp;&nbsp;<a href="{{ show_all_url }}" class="showall">{% trans 'Show all' %}</a>{% endif %}
</p>
View
8 django/contrib/admin/templates/admin_doc/model_detail.html
@@ -9,16 +9,16 @@
</style>
{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name|escape }}</div>{% endblock %}
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name }}</div>{% endblock %}
-{% block title %}Model: {{ name|escape }}{% endblock %}
+{% block title %}Model: {{ name }}{% endblock %}
{% block content %}
<div id="content-main">
-<h1>{{ summary|escape }}</h1>
+<h1>{{ summary }}</h1>
{% if description %}
- <p>{% filter escape|linebreaksbr %}{% trans description %}{% endfilter %}</p>
+ <p>{% filter linebreaksbr %}{% trans description %}{% endfilter %}</p>
{% endif %}
<div class="module">
View
17 django/contrib/admin/templatetags/admin_list.py
@@ -4,8 +4,9 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import dateformat
-from django.utils.html import escape
+from django.utils.html import escape, conditional_escape
from django.utils.text import capfirst
+from django.utils.safestring import mark_safe
from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _
from django.utils.encoding import smart_unicode, smart_str, force_unicode
from django.template import Library
@@ -19,9 +20,9 @@ def paginator_number(cl,i):
if i == DOT:
return u'... '
elif i == cl.page_num:
- return u'<span class="this-page">%d</span> ' % (i+1)
+ return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
else:
- return u'<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1)
+ return mark_safe(u'<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1))
paginator_number = register.simple_tag(paginator_number)
def pagination(cl):
@@ -117,7 +118,7 @@ def result_headers(cl):
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
- return u'<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
+ return mark_safe(u'<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val))
def items_for_result(cl, result):
first = True
@@ -193,10 +194,10 @@ def items_for_result(cl, result):
# Convert the pk to something that can be used in Javascript.
# Problem cases are long ints (23L) and non-ASCII strings.
result_id = repr(force_unicode(getattr(result, pk)))[1:]
- yield (u'<%s%s><a href="%s"%s>%s</a></%s>' % \
- (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), result_repr, table_tag))
+ yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
+ (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
else:
- yield (u'<td%s>%s</td>' % (row_class, result_repr))
+ yield mark_safe(u'<td%s>%s</td>' % (row_class, conditional_escape(result_repr)))
def results(cl):
for res in cl.result_list:
@@ -220,7 +221,7 @@ def date_hierarchy(cl):
day_lookup = cl.params.get(day_field)
year_month_format, month_day_format = get_partial_date_formats()
- link = lambda d: cl.get_query_string(d, [field_generic])
+ link = lambda d: mark_safe(cl.get_query_string(d, [field_generic]))
if year_lookup and month_lookup and day_lookup:
day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
View
19 django/contrib/admin/util.py
@@ -1,6 +1,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.html import escape
+from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.encoding import force_unicode
@@ -37,12 +38,14 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_
if related.field.rel.edit_inline or not has_admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
- nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []])
+ nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []])
else:
# Display a link to the admin page.
- nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
- (force_unicode(capfirst(related.opts.verbose_name)), related.opts.app_label, related.opts.object_name.lower(),
- sub_obj._get_pk_val(), sub_obj), []])
+ nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
+ (escape(force_unicode(capfirst(related.opts.verbose_name))),
+ related.opts.app_label,
+ related.opts.object_name.lower(),
+ sub_obj._get_pk_val(), sub_obj)), []])
get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
else:
has_related_objs = False
@@ -54,8 +57,8 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_
nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []])
else:
# Display a link to the admin page.
- nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
- (force_unicode(capfirst(related.opts.verbose_name)), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj)), []])
+ nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
+ (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))), []])
get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2, admin_site)
# If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed.
@@ -87,9 +90,9 @@ def get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_
else:
# Display a link to the admin page.
nh(deleted_objects, current_depth, [
- (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name)}) + \
+ 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))}) + \
(u' <a href="../../../../%s/%s/%s/">%s</a>' % \
- (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj))), []])
+ (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(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 has_admin and has_related_objs:
View
3  django/contrib/admin/views/decorators.py
@@ -4,6 +4,7 @@
from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response
from django.utils.translation import ugettext_lazy, ugettext as _
+from django.utils.safestring import mark_safe
import base64, datetime, md5
import cPickle as pickle
@@ -22,7 +23,7 @@ def _display_login_form(request, error_message=''):
post_data = _encode_post_data({})
return render_to_response('admin/login.html', {
'title': _('Log in'),
- 'app_path': request.path,
+ 'app_path': mark_safe(request.path),
'post_data': post_data,
'error_message': error_message
}, context_instance=template.RequestContext(request))
View
2  django/contrib/admin/views/main.py
@@ -173,7 +173,7 @@ def get_query_string(self, new_params=None, remove=None):
del p[k]
elif v is not None:
p[k] = v
- return '?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
+ return mark_safe('?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'))
def get_results(self, request):
paginator = ObjectPaginator(self.query_set, self.list_per_page)
View
3  django/contrib/admindocs/utils.py
@@ -3,6 +3,7 @@
import re
from email.Parser import HeaderParser
from email.Errors import HeaderParseError
+from django.utils.safestring import mark_safe
try:
import docutils.core
import docutils.nodes
@@ -66,7 +67,7 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None, link_bas
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
destination_path=None, writer_name='html',
settings_overrides=overrides)
- return parts['fragment']
+ return mark_safe(parts['fragment'])
#
# reST roles
View
3  django/contrib/admindocs/views.py
@@ -10,6 +10,7 @@
from django.contrib.admindocs import utils
from django.contrib.sites.models import Site
from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
import inspect, os, re
# Exclude methods starting with these strings from documentation
@@ -29,7 +30,7 @@ def bookmarklets(request):
# Hack! This couples this view to the URL it lives at.
admin_root = request.path[:-len('doc/bookmarklets/')]
return render_to_response('admin_doc/bookmarklets.html', {
- 'admin_url': "%s://%s%s" % (request.is_secure() and 'https' or 'http', get_host(request), admin_root),
+ 'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
}, context_instance=RequestContext(request))
bookmarklets = staff_member_required(bookmarklets)
View
7 django/contrib/csrf/middleware.py
@@ -7,11 +7,12 @@
"""
from django.conf import settings
from django.http import HttpResponseForbidden
+from django.utils.safestring import mark_safe
import md5
import re
import itertools
-_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
+_ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>')
_POST_FORM_RE = \
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
@@ -82,10 +83,10 @@ def process_response(self, request, response):
itertools.repeat(''))
def add_csrf_field(match):
"""Returns the matched <form> tag plus the added <input> element"""
- return match.group() + "<div style='display:none;'>" + \
+ return mark_safe(match.group() + "<div style='display:none;'>" + \
"<input type='hidden' " + idattributes.next() + \
" name='csrfmiddlewaretoken' value='" + csrf_token + \
- "' /></div>"
+ "' /></div>")
# Modify any POST forms
response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
View
13 django/contrib/databrowse/datastructures.py
@@ -8,6 +8,7 @@
from django.utils.text import capfirst
from django.utils.translation import get_date_formats
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
+from django.utils.safestring import mark_safe
from django.db.models.query import QuerySet
EMPTY_VALUE = '(None)'
@@ -28,7 +29,7 @@ def model_databrowse(self):
return self.site.registry[self.model]
def url(self):
- return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
+ return mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name))
def objects(self, **kwargs):
return self.get_query_set().filter(**kwargs)
@@ -68,9 +69,9 @@ def choices(self):
def url(self):
if self.field.choices:
- return '%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name)
+ return mark_safe('%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name))
elif self.field.rel:
- return '%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)
+ return mark_safe('%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name))
class EasyChoice(object):
def __init__(self, easy_model, field, value, label):
@@ -81,7 +82,7 @@ def __repr__(self):
return smart_str(u'<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def url(self):
- return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value))
+ return mark_safe('%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value)))
class EasyInstance(object):
def __init__(self, easy_model, instance):
@@ -184,14 +185,14 @@ def urls(self):
if self.field.rel.to in self.model.model_list:
lst = []
for value in self.values():
- url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val()))
+ url = mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val())))
lst.append((smart_unicode(value), url))
else:
lst = [(value, None) for value in self.values()]
elif self.field.choices:
lst = []
for value in self.values():
- url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value))
+ url = mark_safe('%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value)))
lst.append((value, url))
elif isinstance(self.field, models.URLField):
val = self.values()[0]
View
12 django/contrib/databrowse/plugins/calendars.py
@@ -5,8 +5,9 @@
from django.shortcuts import render_to_response
from django.utils.text import capfirst
from django.utils.translation import get_date_formats
-from django.views.generic import date_based
from django.utils.encoding import force_unicode
+from django.utils.safestring import mark_safe
+from django.views.generic import date_based
import datetime
import time
@@ -29,16 +30,17 @@ def model_index_html(self, request, model, site):
fields = self.field_dict(model)
if not fields:
return u''
- return u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \
- u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])
+ return mark_safe(u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \
+ u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
def urls(self, plugin_name, easy_instance_field):
if isinstance(easy_instance_field.field, models.DateField):
- return [u'%s%s/%s/%s/%s/%s/' % (easy_instance_field.model.url(),
+ return [mark_safe(u'%s%s/%s/%s/%s/%s/' % (
+ easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name,
easy_instance_field.raw_value.year,
easy_instance_field.raw_value.strftime('%b').lower(),
- easy_instance_field.raw_value.day)]
+ easy_instance_field.raw_value.day))]
def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site
View
10 django/contrib/databrowse/plugins/fieldchoices.py
@@ -5,6 +5,7 @@
from django.shortcuts import render_to_response
from django.utils.text import capfirst
from django.utils.encoding import smart_str, force_unicode
+from django.utils.safestring import mark_safe
from django.views.generic import date_based
import datetime
import time
@@ -32,15 +33,16 @@ def model_index_html(self, request, model, site):
fields = self.field_dict(model)
if not fields:
return u''
- return u'<p class="filter"><strong>View by:</strong> %s</p>' % \
- u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])
+ return mark_safe(u'<p class="filter"><strong>View by:</strong> %s</p>' % \
+ u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
def urls(self, plugin_name, easy_instance_field):
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
field_value = smart_str(easy_instance_field.raw_value)
- return [u'%s%s/%s/%s/' % (easy_instance_field.model.url(),
+ return [mark_safe(u'%s%s/%s/%s/' % (
+ easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name,
- urllib.quote(field_value, safe=''))]
+ urllib.quote(field_value, safe='')))]
def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site
View
3  django/contrib/databrowse/sites.py
@@ -2,6 +2,7 @@
from django.db import models
from django.contrib.databrowse.datastructures import EasyModel, EasyChoice
from django.shortcuts import render_to_response
+from django.utils.safestring import mark_safe
class AlreadyRegistered(Exception):
pass
@@ -60,7 +61,7 @@ def root(self, request, url):
def main_view(self, request):
easy_model = EasyModel(self.site, self.model)
- html_snippets = u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()])
+ html_snippets = mark_safe(u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()]))
return render_to_response('databrowse/model_detail.html', {
'model': easy_model,
'root_url': self.site.root_url,
View
8 django/contrib/flatpages/views.py
@@ -4,6 +4,7 @@
from django.http import HttpResponse
from django.conf import settings
from django.core.xheaders import populate_xheaders
+from django.utils.safestring import mark_safe
DEFAULT_TEMPLATE = 'flatpages/default.html'
@@ -30,6 +31,13 @@ def flatpage(request, url):
t = loader.select_template((f.template_name, DEFAULT_TEMPLATE))
else:
t = loader.get_template(DEFAULT_TEMPLATE)
+
+ # To avoid having to always use the "|safe" filter in flatpage templates,
+ # mark the title and content as already safe (since they are raw HTML
+ # content in the first place).
+ f.title = mark_safe(f.title)
+ f.content = mark_safe(f.content)
+
c = RequestContext(request, {
'flatpage': f,
})
View
4 django/contrib/humanize/templatetags/humanize.py
@@ -21,6 +21,7 @@ def ordinal(value):
if value % 100 in (11, 12, 13): # special case
return u"%d%s" % (value, t[0])
return u'%d%s' % (value, t[value % 10])
+ordinal.is_safe = True
register.filter(ordinal)
def intcomma(value):
@@ -34,6 +35,7 @@ def intcomma(value):
return new
else:
return intcomma(new)
+intcomma.is_safe = True
register.filter(intcomma)
def intword(value):
@@ -55,6 +57,7 @@ def intword(value):
new_value = value / 1000000000000.0
return ungettext('%(value).1f trillion', '%(value).1f trillion', new_value) % {'value': new_value}
return value
+intword.is_safe = False
register.filter(intword)
def apnumber(value):
@@ -69,6 +72,7 @@ def apnumber(value):
if not 0 < value < 10:
return value
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
+apnumber.is_safe = True
register.filter(apnumber)
def naturalday(value, arg=None):
View
10 django/contrib/markup/templatetags/markup.py
@@ -17,6 +17,7 @@
from django import template
from django.conf import settings
from django.utils.encoding import smart_str, force_unicode
+from django.utils.safestring import mark_safe
register = template.Library()
@@ -28,7 +29,8 @@ def textile(value):
raise template.TemplateSyntaxError, "Error in {% textile %} filter: The Python textile library isn't installed."
return force_unicode(value)
else:
- return force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8'))
+ return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
+textile.is_safe = True
def markdown(value):
try:
@@ -38,7 +40,8 @@ def markdown(value):
raise template.TemplateSyntaxError, "Error in {% markdown %} filter: The Python markdown library isn't installed."
return force_unicode(value)
else:
- return force_unicode(markdown.markdown(smart_str(value)))
+ return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
+markdown.is_safe = True
def restructuredtext(value):
try:
@@ -50,7 +53,8 @@ def restructuredtext(value):
else:
docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
parts = publish_parts(source=smart_str(value), writer_name="html4css1", settings_overrides=docutils_settings)
- return force_unicode(parts["fragment"])
+ return mark_safe(force_unicode(parts["fragment"]))
+restructuredtext.is_safe = True
register.filter(textile)
register.filter(markdown)
View
6 django/contrib/markup/tests.py
@@ -1,9 +1,11 @@
# Quick tests for the markup templatetags (django.contrib.markup)
-from django.template import Template, Context, add_to_builtins
import re
import unittest
+from django.template import Template, Context, add_to_builtins
+from django.utils.html import escape
+
add_to_builtins('django.contrib.markup.templatetags.markup')
class Templates(unittest.TestCase):
@@ -24,7 +26,7 @@ def test_textile(self):
<p>Paragraph 2 with &#8220;quotes&#8221; and <code>code</code></p>""")
else:
- self.assertEqual(rendered, textile_content)
+ self.assertEqual(rendered, escape(textile_content))
def test_markdown(self):
try:
View
3  django/contrib/sitemaps/templates/sitemap.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+{% autoescape off %}<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% spaceless %}
{% for url in urlset %}
@@ -11,3 +11,4 @@
{% endfor %}
{% endspaceless %}
</urlset>
+{% endautoescape %}
View
3  django/contrib/sitemaps/templates/sitemap_index.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
+{% autoescape off %}<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %}
</sitemapindex>
+{% endautoescape %}
View
15 django/newforms/forms.py
@@ -7,6 +7,7 @@
from django.utils.datastructures import SortedDict
from django.utils.html import escape
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
+from django.utils.safestring import mark_safe
from fields import Field
from widgets import Media, media_property, TextInput, Textarea
@@ -123,7 +124,8 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
output.append(error_row % force_unicode(bf_errors))
if bf.label:
label = escape(force_unicode(bf.label))
- # Only add the suffix if the label does not end in punctuation.
+ # Only add the suffix if the label does not end in
+ # punctuation.
if self.label_suffix:
if label[-1] not in ':?.!':
label += self.label_suffix
@@ -141,11 +143,14 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
str_hidden = u''.join(hidden_fields)
if output:
last_row = output[-1]
- # Chop off the trailing row_ender (e.g. '</td></tr>') and insert the hidden fields.
+ # Chop off the trailing row_ender (e.g. '</td></tr>') and
+ # insert the hidden fields.
output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
- else: # If there aren't any rows in the output, just append the hidden fields.
+ else:
+ # If there aren't any rows in the output, just append the
+ # hidden fields.
output.append(str_hidden)
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
@@ -343,7 +348,7 @@ def label_tag(self, contents=None, attrs=None):
if id_:
attrs = attrs and flatatt(attrs) or ''
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
- return contents
+ return mark_safe(contents)
def _is_hidden(self):
"Returns True if this BoundField's widget is hidden."
View
8 django/newforms/util.py
@@ -1,6 +1,7 @@
from django.utils.html import escape
from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode
from django.utils.functional import Promise
+from django.utils.safestring import mark_safe
def flatatt(attrs):
"""
@@ -22,7 +23,9 @@ def __unicode__(self):
def as_ul(self):
if not self: return u''
- return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s%s</li>' % (k, force_unicode(v)) for k, v in self.items()])
+ return mark_safe(u'<ul class="errorlist">%s</ul>'
+ % ''.join([u'<li>%s%s</li>' % (k, force_unicode(v))
+ for k, v in self.items()]))
def as_text(self):
return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u' * %s' % force_unicode(i) for i in v])) for k, v in self.items()])
@@ -36,7 +39,8 @@ def __unicode__(self):
def as_ul(self):
if not self: return u''
- return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self])
+ return mark_safe(u'<ul class="errorlist">%s</ul>'
+ % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self]))
def as_text(self):
if not self: return u''
View
40 django/newforms/widgets.py
@@ -14,6 +14,7 @@
from django.utils.html import escape
from django.utils.translation import ugettext
from django.utils.encoding import StrAndUnicode, force_unicode
+from django.utils.safestring import mark_safe
from util import flatatt
from urlparse import urljoin
@@ -188,8 +189,10 @@ class Input(Widget):
def render(self, name, value, attrs=None):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
- if value != '': final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty.
- return u'<input%s />' % flatatt(final_attrs)
+ if value != '':
+ # Only add the 'value' attribute if a value is non-empty.
+ final_attrs['value'] = force_unicode(value)
+ return mark_safe(u'<input%s />' % flatatt(final_attrs))
class TextInput(Input):
input_type = 'text'
@@ -222,7 +225,9 @@ def __init__(self, attrs=None, choices=()):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
- return u'\n'.join([(u'<input%s />' % flatatt(dict(value=force_unicode(v), **final_attrs))) for v in value])
+ return mark_safe(u'\n'.join([(u'<input%s />' %
+ flatatt(dict(value=force_unicode(v), **final_attrs)))
+ for v in value]))
def value_from_datadict(self, data, files, name):
if isinstance(data, MultiValueDict):
@@ -251,7 +256,8 @@ def render(self, name, value, attrs=None):
if value is None: value = ''
value = force_unicode(value)
final_attrs = self.build_attrs(attrs, name=name)
- return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
+ return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
+ escape(value)))
class DateTimeInput(Input):
input_type = 'text'
@@ -285,8 +291,9 @@ def render(self, name, value, attrs=None):
if result:
final_attrs['checked'] = 'checked'
if value not in ('', True, False, None):
- final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty.
- return u'<input%s />' % flatatt(final_attrs)
+ # Only add the 'value' attribute if a value is non-empty.
+ final_attrs['value'] = force_unicode(value)
+ return mark_safe(u'<input%s />' % flatatt(final_attrs))
def value_from_datadict(self, data, files, name):
if name not in data:
@@ -307,13 +314,14 @@ def render(self, name, value, attrs=None, choices=()):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select%s>' % flatatt(final_attrs)]
- str_value = force_unicode(value) # Normalize to string.
+ # Normalize to string.
+ str_value = force_unicode(value)
for option_value, option_label in chain(self.choices, choices):
option_value = force_unicode(option_value)
selected_html = (option_value == str_value) and u' selected="selected"' or ''
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(force_unicode(option_label))))
output.append(u'</select>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
class NullBooleanSelect(Select):
"""
@@ -350,7 +358,7 @@ def render(self, name, value, attrs=None, choices=()):
selected_html = (option_value in str_values) and ' selected="selected"' or ''
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(force_unicode(option_label))))
output.append(u'</select>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
def value_from_datadict(self, data, files, name):
if isinstance(data, MultiValueDict):
@@ -371,7 +379,8 @@ def __init__(self, name, value, attrs, choice, index):
self.index = index
def __unicode__(self):
- return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
+ return mark_safe(u'<label>%s %s</label>' % (self.tag(),
+ self.choice_label))
def is_checked(self):
return self.value == self.choice_value
@@ -382,7 +391,7 @@ def tag(self):
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
if self.is_checked():
final_attrs['checked'] = 'checked'
- return u'<input%s />' % flatatt(final_attrs)
+ return mark_safe(u'<input%s />' % flatatt(final_attrs))
class RadioFieldRenderer(StrAndUnicode):
"""
@@ -406,7 +415,8 @@ def __unicode__(self):
def render(self):
"""Outputs a <ul> for this set of radio fields."""
- return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])
+ return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>'
+ % force_unicode(w) for w in self]))
class RadioSelect(Select):
@@ -443,7 +453,8 @@ def render(self, name, value, attrs=None, choices=()):
has_id = attrs and 'id' in attrs
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>']
- str_values = set([force_unicode(v) for v in value]) # Normalize to strings.
+ # Normalize to strings
+ str_values = set([force_unicode(v) for v in value])
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
# If an ID attribute was given, add a numeric index as a suffix,
# so that the checkboxes don't all have the same ID attribute.
@@ -454,7 +465,7 @@ def render(self, name, value, attrs=None, choices=()):
rendered_cb = cb.render(name, option_value)
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(force_unicode(option_label))))
output.append(u'</ul>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
def id_for_label(self, id_):
# See the comment for RadioSelect.id_for_label()
@@ -560,3 +571,4 @@ def decompress(self, value):
if value:
return [value.date(), value.time().replace(microsecond=0)]
return [None, None]
+
View
43 django/oldforms/__init__.py
@@ -1,6 +1,7 @@
from django.core import validators
from django.core.exceptions import PermissionDenied
from django.utils.html import escape
+from django.utils.safestring import mark_safe
from django.conf import settings
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import smart_unicode, force_unicode
@@ -189,9 +190,9 @@ def errors(self):
def html_error_list(self):
if self.errors():
- return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
+ return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]))
else:
- return ''
+ return mark_safe('')
def get_id(self):
return self.formfield.get_id()
@@ -226,7 +227,7 @@ def has_errors(self):
return bool(len(self.errors()))
def html_combined_error_list(self):
- return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
+ return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]))
class InlineObjectCollection(object):
"An object that acts like a sparse list of form field collections."
@@ -418,9 +419,9 @@ def render(self, data):
max_length = u''
if self.max_length:
max_length = u'maxlength="%s" ' % self.max_length
- return u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
+ return mark_safe(u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
(self.input_type, self.get_id(), self.__class__.__name__, self.is_required and u' required' or '',
- self.field_name, self.length, escape(data), max_length)
+ self.field_name, self.length, escape(data), max_length))
def html2python(data):
return data
@@ -442,9 +443,9 @@ def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_li
def render(self, data):
if data is None:
data = ''
- return u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
+ return mark_safe(u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
(self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'',
- self.field_name, self.rows, self.cols, escape(data))
+ self.field_name, self.rows, self.cols, escape(data)))
class HiddenField(FormField):
def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
@@ -453,8 +454,8 @@ def __init__(self, field_name, is_required=False, validator_list=None, max_lengt
self.validator_list = validator_list[:]
def render(self, data):
- return u'<input type="hidden" id="%s" name="%s" value="%s" />' % \
- (self.get_id(), self.field_name, escape(data))
+ return mark_safe(u'<input type="hidden" id="%s" name="%s" value="%s" />' % \
+ (self.get_id(), self.field_name, escape(data)))
class CheckboxField(FormField):
def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
@@ -468,9 +469,9 @@ def render(self, data):
checked_html = ''
if data or (data is '' and self.checked_by_default):
checked_html = ' checked="checked"'
- return u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
+ return mark_safe(u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
(self.get_id(), self.__class__.__name__,
- self.field_name, checked_html)
+ self.field_name, checked_html))
def html2python(data):
"Convert value from browser ('on' or '') to a Python boolean"
@@ -502,7 +503,7 @@ def render(self, data):
selected_html = u' selected="selected"'
output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(display_name))))
output.append(u' </select>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
def isValidChoice(self, data, form):
str_data = smart_unicode(data)
@@ -556,7 +557,7 @@ def __unicode__(self):
output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')]
output.extend([u'<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
output.append(u'</ul>')
- return u''.join(output)
+ return mark_safe(u''.join(output))
def __iter__(self):
for d in self.datalist:
yield d
@@ -571,11 +572,11 @@ def __len__(self):
datalist.append({
'value': value,
'name': display_name,
- 'field': u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
- (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html),
- 'label': u'<label for="%s">%s</label>' % \
+ 'field': mark_safe(u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
+ (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html)),
+ 'label': mark_safe(u'<label for="%s">%s</label>' % \
(self.get_id() + u'_' + unicode(i), display_name),
- })
+ )})
return RadioFieldRenderer(datalist, self.ul_class)
def isValidChoice(self, data, form):
@@ -614,7 +615,7 @@ def render(self, data):
selected_html = u' selected="selected"'
output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(choice))))
output.append(u' </select>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
def isValidChoice(self, field_data, all_data):
# data is something like ['1', '2', '3']
@@ -667,7 +668,7 @@ def render(self, data):
(self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
self.get_id() + escape(value), choice))
output.append(u'</ul>')
- return u'\n'.join(output)
+ return mark_safe(u'\n'.join(output))
####################
# FILE UPLOADS #
@@ -688,8 +689,8 @@ def isNonEmptyFile(self, field_data, all_data):
raise validators.CriticalValidationError, ugettext("The submitted file is empty.")
def render(self, data):
- return u'<input type="file" id="%s" class="v%s" name="%s" />' % \
- (self.get_id(), self.__class__.__name__, self.field_name)
+ return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \
+ (self.get_id(), self.__class__.__name__, self.field_name))
def html2python(data):
if data is None:
View
70 django/template/__init__.py
@@ -57,6 +57,8 @@
from django.utils.text import smart_split
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.translation import ugettext as _
+from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
+from django.utils.html import escape
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
@@ -595,7 +597,16 @@ def resolve(self, context, ignore_failures=False):
arg_vals.append(arg)
else:
arg_vals.append(arg.resolve(context))
- obj = func(obj, *arg_vals)
+ if getattr(func, 'needs_autoescape', False):
+ new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
+ else:
+ new_obj = func(obj, *arg_vals)
+ if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
+ obj = mark_safe(new_obj)
+ elif isinstance(obj, EscapeData):
+ obj = mark_for_escaping(new_obj)
+ else:
+ obj = new_obj
return obj
def args_check(name, func, provided):
@@ -637,7 +648,7 @@ def resolve_variable(path, context):
"""
Returns the resolved variable, which may contain attribute syntax, within
the given context.
-
+
Deprecated; use the Variable class instead.
"""
return Variable(path).resolve(context)
@@ -647,7 +658,7 @@ class Variable(object):
A template variable, resolvable against a given context. The variable may be
a hard-coded string (if it begins and ends with single or double quote
marks)::
-
+
>>> c = {'article': {'section':'News'}}
>>> Variable('article.section').resolve(c)
u'News'
@@ -662,25 +673,25 @@ class Variable(object):
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
-
+
def __init__(self, var):
self.var = var
self.literal = None
self.lookups = None
-
+
try:
# First try to treat this variable as a number.
#
- # Note that this could cause an OverflowError here that we're not
+ # Note that this could cause an OverflowError here that we're not
# catching. Since this should only happen at compile time, that's
# probably OK.
self.literal = float(var)
-
+
# So it's a float... is it an int? If the original value contained a
# dot or an "e" then it was a float, not an int.
if '.' not in var and 'e' not in var.lower():
self.literal = int(self.literal)
-
+
# "2." is invalid
if var.endswith('.'):
raise ValueError
@@ -691,12 +702,12 @@ def __init__(self, var):
# we're also dealing with a literal.
if var[0] in "\"'" and var[0] == var[-1]:
self.literal = var[1:-1]
-
+
else:
# Otherwise we'll set self.lookups so that resolve() knows we're
# dealing with a bonafide variable
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
-
+
def resolve(self, context):
"""Resolve this variable against a given context."""
if self.lookups is not None:
@@ -705,18 +716,18 @@ def resolve(self, context):
else:
# We're dealing with a literal, so it's already been "resolved"
return self.literal
-
+
def __repr__(self):
return "<%s: %r>" % (self.__class__.__name__, self.var)
-
+
def __str__(self):
return self.var
def _resolve_lookup(self, context):
"""
Performs resolution of a real variable (i.e. not a literal) against the
- given context.
-
+ given context.
+
As indicated by the method's name, this method is an implementation
detail and shouldn't be called by external code. Use Variable.resolve()
instead.
@@ -757,14 +768,7 @@ def _resolve_lookup(self, context):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
-
- if isinstance(current, (basestring, Promise)):
- try:
- current = force_unicode(current)
- except UnicodeDecodeError:
- # Failing to convert to unicode can happen sometimes (e.g. debug
- # tracebacks). So we allow it in this particular instance.
- pass
+
return current
class Node(object):
@@ -838,16 +842,31 @@ def __repr__(self):
return "<Variable Node: %s>" % self.filter_expression
def render(self, context):
- return self.filter_expression.resolve(context)
+ try:
+ output = force_unicode(self.filter_expression.resolve(context))
+ except UnicodeDecodeError:
+ # Unicode conversion can fail sometimes for reasons out of our
+ # control (e.g. exception rendering). In that case, we fail quietly.
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return force_unicode(escape(output))
+ else:
+ return force_unicode(output)
class DebugVariableNode(VariableNode):
def render(self, context):
try:
- return self.filter_expression.resolve(context)
+ output = force_unicode(self.filter_expression.resolve(context))
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):
e.source = self.source
raise
+ except UnicodeDecodeError:
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return escape(output)
+ else:
+ return output
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
@@ -961,7 +980,8 @@ def render(self, context):
else:
t = get_template(file_name)
self.nodelist = t.nodelist
- return self.nodelist.render(context_class(dict))
+ return self.nodelist.render(context_class(dict,
+ autoescape=context.autoescape))
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
compile_func.__doc__ = func.__doc__
View
5 django/template/context.py
@@ -9,9 +9,11 @@ class ContextPopException(Exception):
class Context(object):
"A stack container for variable context"
- def __init__(self, dict_=None):
+
+ def __init__(self, dict_=None, autoescape=True):
dict_ = dict_ or {}
self.dicts = [dict_]
+ self.autoescape = autoescape
def __repr__(self):
return repr(self.dicts)
@@ -97,3 +99,4 @@ def __init__(self, request, dict=None, processors=None):
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
+
View
173 django/template/defaultfilters.py
@@ -7,6 +7,7 @@
from django.conf import settings
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, iri_to_uri
+from django.utils.safestring import mark_safe, SafeData
register = Library()
@@ -29,6 +30,9 @@ def _dec(*args, **kwargs):
# Include a reference to the real function (used to check original
# arguments by the template parser).
_dec._decorated_function = getattr(func, '_decorated_function', func)
+ for attr in ('is_safe', 'needs_autoescape'):
+ if hasattr(func, attr):
+ setattr(_dec, attr, getattr(func, attr))
return _dec
###################
@@ -39,17 +43,20 @@ def _dec(*args, **kwargs):
def addslashes(value):
"""Adds slashes - useful for passing strings to JavaScript, for example."""
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
+addslashes.is_safe = True
addslashes = stringfilter(addslashes)
def capfirst(value):
"""Capitalizes the first character of the value."""
return value and value[0].upper() + value[1:]
+capfirst.is_safe=True
capfirst = stringfilter(capfirst)
def fix_ampersands(value):
"""Replaces ampersands with ``&amp;`` entities."""
from django.utils.html import fix_ampersands
return fix_ampersands(value)
+fix_ampersands.is_safe=True
fix_ampersands = stringfilter(fix_ampersands)
def floatformat(text, arg=-1):
@@ -90,31 +97,39 @@ def floatformat(text, arg=-1):
return force_unicode(f)
m = f - int(f)
if not m and d < 0:
- return u'%d' % int(f)
+ return mark_safe(u'%d' % int(f))
else:
formatstr = u'%%.%df' % abs(d)
- return formatstr % f
+ return mark_safe(formatstr % f)
+floatformat.is_safe = True
def iriencode(value):
"""Escapes an IRI value for use in a URL."""
return force_unicode(iri_to_uri(value))
iriencode = stringfilter(iriencode)
-def linenumbers(value):
+def linenumbers(value, autoescape=None):
"""Displays text with line numbers."""
from django.utils.html import escape
lines = value.split(u'\n')
# Find the maximum width of the line count, for use with zero padding
- # string format command.
+ # string format command
width = unicode(len(unicode(len(lines))))
- for i, line in enumerate(lines):
- lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
- return u'\n'.join(lines)
+ if not autoescape or isinstance(value, SafeData):
+ for i, line in enumerate(lines):
+ lines[i] = (u"%0" + width + u"d. %s") % (i + 1, line)
+ else:
+ for i, line in enumerate(lines):
+ lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
+ return mark_safe(u'\n'.join(lines))
+linenumbers.is_safe = True
+linenumbers.needs_autoescape = True
linenumbers = stringfilter(linenumbers)
def lower(value):
"""Converts a string into all lowercase."""
return value.lower()
+lower.is_safe = True
lower = stringfilter(lower)
def make_list(value):
@@ -125,6 +140,7 @@ def make_list(value):
For a string, it's a list of characters.
"""
return list(value)
+make_list.is_safe = False
make_list = stringfilter(make_list)
def slugify(value):
@@ -135,7 +151,8 @@ def slugify(value):
import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
- return re.sub('[-\s]+', '-', value)
+ return mark_safe(re.sub('[-\s]+', '-', value))
+slugify.is_safe = True
slugify = stringfilter(slugify)
def stringformat(value, arg):
@@ -152,10 +169,12 @@ def stringformat(value, arg):
return (u"%" + unicode(arg)) % value
except (ValueError, TypeError):
return u""
+stringformat.is_safe = True
def title(value):
"""Converts a string into titlecase."""
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
+title.is_safe = True
title = stringfilter(title)
def truncatewords(value, arg):
@@ -170,6 +189,7 @@ def truncatewords(value, arg):
except ValueError: # Invalid literal for int().
return value # Fail silently.
return truncate_words(value, length)
+truncatewords.is_safe = True
truncatewords = stringfilter(truncatewords)
def truncatewords_html(value, arg):
@@ -184,23 +204,28 @@ def truncatewords_html(value, arg):
except ValueError: # invalid literal for int()
return value # Fail silently.
return truncate_html_words(value, length)
+truncatewords_html.is_safe = True
truncatewords_html = stringfilter(truncatewords_html)
def upper(value):
"""Converts a string into all uppercase."""
return value.upper()
+upper.is_safe = False
upper = stringfilter(upper)
def urlencode(value):
"""Escapes a value for use in a URL."""
from django.utils.http import urlquote
return urlquote(value)
+urlencode.is_safe = False
urlencode = stringfilter(urlencode)
-def urlize(value):
+def urlize(value, autoescape=None):
"""Converts URLs in plain text into clickable links."""
from django.utils.html import urlize
- return urlize(value, nofollow=True)
+ return mark_safe(urlize(value, nofollow=True, autoescape=autoescape))
+urlize.is_safe=True
+urlize.needs_autoescape = True
urlize = stringfilter(urlize)
def urlizetrunc(value, limit):
@@ -211,12 +236,14 @@ def urlizetrunc(value, limit):
Argument: Length to truncate URLs to.
"""
from django.utils.html import urlize
- return urlize(value, trim_url_limit=int(limit), nofollow=True)
+ return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True))
+urlizetrunc.is_safe = True
urlizetrunc = stringfilter(urlizetrunc)
def wordcount(value):
"""Returns the number of words."""
return len(value.split())
+wordcount.is_safe = False
wordcount = stringfilter(wordcount)
def wordwrap(value, arg):
@@ -227,6 +254,7 @@ def wordwrap(value, arg):
"""
from django.utils.text import wrap
return wrap(value, int(arg))
+wordwrap.is_safe = True
wordwrap = stringfilter(wordwrap)
def ljust(value, arg):
@@ -236,6 +264,7 @@ def ljust(value, arg):
Argument: field size.
"""
return value.ljust(int(arg))
+ljust.is_safe = True
ljust = stringfilter(ljust)
def rjust(value, arg):
@@ -245,16 +274,24 @@ def rjust(value, arg):
Argument: field size.
"""
return value.rjust(int(arg))
+rjust.is_safe = True
rjust = stringfilter(rjust)
def center(value, arg):
"""Centers the value in a field of a given width."""
return value.center(int(arg))
+center.is_safe = True
center = stringfilter(center)
def cut(value, arg):
- """Removes all values of arg from the given string."""
- return value.replace(arg, u'')
+ """
+ Removes all values of arg from the given string.
+ """
+ safe = isinstance(value, SafeData)
+ value = value.replace(arg, u'')
+ if safe and arg != ';':
+ return mark_safe(value)
+ return value
cut = stringfilter(cut)
###################
@@ -262,29 +299,60 @@ def cut(value, arg):
###################
def escape(value):
- "Escapes a string's HTML"
+ """
+ Marks the value as a string that should not be auto-escaped.
+ """
+ from django.utils.safestring import mark_for_escaping
+ return mark_for_escaping(value)
+escape.is_safe = True
+escape = stringfilter(escape)
+
+def force_escape(value):
+ """
+ Escapes a string's HTML. This returns a new string containing the escaped
+ characters (as opposed to "escape", which marks the content for later
+ possible escaping).
+ """
from django.utils.html import escape
- return escape(value)
+ return mark_safe(escape(value))
escape = stringfilter(escape)
+force_escape.is_safe = True
-def linebreaks(value):
+def linebreaks(value, autoescape=None):
"""
Replaces line breaks in plain text with appropriate HTML; a single
newline becomes an HTML line break (``<br />``) and a new line
followed by a blank line becomes a paragraph break (``</p>``).
"""
from django.utils.html import linebreaks
- return linebreaks(value)
+ autoescape = autoescape and not isinstance(value, SafeData)
+ return mark_safe(linebreaks(value, autoescape))
+linebreaks.is_safe = True
+linebreaks.needs_autoescape = True
linebreaks = stringfilter(linebreaks)
-def linebreaksbr(value):
+def linebreaksbr(value, autoescape=None):
"""
Converts all newlines in a piece of plain text to HTML line breaks
(``<br />``).
"""
- return value.replace('\n', '<br />')
+ if autoescape and not isinstance(value, SafeData):
+ from django.utils.html import escape
+ value = escape(value)
+ return mark_safe(value.replace('\n', '<br />'))
+linebreaksbr.is_safe = True
+linebreaksbr.needs_autoescape = True
linebreaksbr = stringfilter(linebreaksbr)
+def safe(value):
+ """
+ Marks the value as a string that should not be auto-escaped.
+ """
+ from django.utils.safestring import mark_safe
+ return mark_safe(value)
+safe.is_safe = True
+safe = stringfilter(safe)
+
def removetags(value, tags):
"""Removes a space separated list of [X]HTML tags from the output."""
tags = [re.escape(tag) for tag in tags.split()]
@@ -294,12 +362,14 @@ def removetags(value, tags):
value = starttag_re.sub(u'', value)
value = endtag_re.sub(u'', value)
return value
+removetags.is_safe = True
removetags = stringfilter(removetags)
def striptags(value):
"""Strips all [X]HTML tags."""
from django.utils.html import strip_tags
return strip_tags(value)
+striptags.is_safe = True
striptags = stringfilter(striptags)
###################
@@ -315,6 +385,7 @@ def dictsort(value, arg):
decorated = [(var_resolve(item), item) for item in value]
decorated.sort()
return [item[1] for item in decorated]
+dictsort.is_safe = False
def dictsortreversed(value, arg):
"""
@@ -326,6 +397,7 @@ def dictsortreversed(value, arg):
decorated.sort()
decorated.reverse()
return [item[1] for item in decorated]
+dictsortreversed.is_safe = False
def first(value):
"""Returns the first item in a list."""
@@ -333,25 +405,36 @@ def first(value):
return value[0]
except IndexError:
return u''
+first.is_safe = True
def join(value, arg):
"""Joins a list with a string, like Python's ``str.join(list)``."""
try:
- return arg.join(map(force_unicode, value))
+ data = arg.join(map(force_unicode, value))
except AttributeError: # fail silently but nicely
return value
+ safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData),
+ value, True)
+ if safe_args:
+ return mark_safe(data)
+ else:
+ return data
+join.is_safe = True
def length(value):
"""Returns the length of the value - useful for lists."""
return len(value)
+length.is_safe = True
def length_is(value, arg):
"""Returns a boolean of whether the value's length is the argument."""
return len(value) == int(arg)
+length_is.is_safe = True
def random(value):
"""Returns a random item from the list."""
return random_module.choice(value)
+random.is_safe = True
def slice_(value, arg):
"""
@@ -372,8 +455,9 @@ def slice_(value, arg):
except (ValueError, TypeError):
return value # Fail silently.
+slice_.is_safe = True
-def unordered_list(value):
+def unordered_list(value, autoescape=None):
"""
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing <ul> tags.
@@ -394,6 +478,11 @@ def unordered_list(value):
</ul>
</li>
"""
+ if autoescape:
+ from django.utils.html import conditional_escape
+ escaper = conditional_escape
+ else:
+ escaper = lambda x: x
def convert_old_style_list(list_):
"""
Converts old style lists to the new easier to understand format.
@@ -443,12 +532,14 @@ def _helper(list_, tabs=1):
sublist = _helper(sublist_item, tabs+1)
sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
indent, indent)
- output.append('%s<li>%s%s</li>' % (indent, force_unicode(title),
- sublist))
+ output.append('%s<li>%s%s</li>' % (indent,
+ escaper(force_unicode(title)), sublist))
i += 1
return '\n'.join(output)
value, converted = convert_old_style_list(value)
- return _helper(value)
+ return mark_safe(_helper(value))
+unordered_list.is_safe = True
+unordered_list.needs_autoescape = True
###################
# INTEGERS #
@@ -457,6 +548,7 @@ def _helper(list_, tabs=1):
def add(value, arg):
"""Adds the arg to the value."""
return int(value) + int(arg)
+add.is_safe = False
def get_digit(value, arg):
"""
@@ -476,6 +568,7 @@ def get_digit(value, arg):
return int(str(value)[-arg])
except IndexError:
return 0
+get_digit.is_safe = False
###################
# DATES #
@@ -489,6 +582,7 @@ def date(value, arg=None):
if arg is None:
arg = settings.DATE_FORMAT
return format(value, arg)
+date.is_safe = False
def time(value, arg=None):
"""Formats a time according to the given format."""
@@ -498,6 +592,7 @@ def time(value, arg=None):
if arg is None:
arg = settings.TIME_FORMAT
return time_format(value, arg)
+time.is_safe = False
def timesince(value, arg=None):
"""Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
@@ -507,6 +602,7 @@ def timesince(value, arg=None):
if arg:
return timesince(arg, value)
return timesince(value)
+timesince.is_safe = False
def timeuntil(value, arg=None):
"""Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
@@ -517,6 +613,7 @@ def timeuntil(value, arg=None):
if arg:
return timesince(arg, value)
return timesince(datetime.now(), value)
+timeuntil.is_safe = False
###################
# LOGIC #
@@ -525,16 +622,19 @@ def timeuntil(value, arg=None):
def default(value, arg):
"""If value is unavailable, use given default."""
return value or arg
+default.is_safe = False
def default_if_none(value, arg):
"""If value is None, use given default."""
if value is None:
return arg
return value
+default_if_none.is_safe = False
def divisibleby(value, arg):
"""Returns True if the value is devisible by the argument."""
return int(value) % int(arg) == 0
+divisibleby.is_safe = False
def yesno(value, arg=None):
"""
@@ -566,6 +666,7 @@ def yesno(value, arg=None):
if value:
return yes
return no
+yesno.is_safe = False
###################
# MISC #
@@ -588,29 +689,30 @@ def filesizeformat(bytes):
if bytes < 1024 * 1024 * 1024:
return ugettext("%.1f MB") % (bytes / (1024 * 1024))
return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024))
+filesizeformat.is_safe = True
def pluralize(value, arg=u's'):
"""
Returns a plural suffix if the value is not 1. By default, 's' is used as
the suffix:
- * If value is 0, vote{{ value|plurlize }} displays "0 votes".
- * If value is 1, vote{{ value|plurlize }} displays "1 vote".
- * If value is 2, vote{{ value|plurlize }} displays "2 votes".
+ * If value is 0, vote{{ value|pluralize }} displays "0 votes".
+ * If value is 1, vote{{ value|pluralize }} displays "1 vote".
+ * If value is 2, vote{{ value|pluralize }} displays "2 votes".
If an argument is provided, that string is used instead:
- * If value is 0, class{{ value|plurlize:"es" }} displays "0 classes".
- * If value is 1, class{{ value|plurlize:"es" }} displays "1 class".
- * If value is 2, class{{ value|plurlize:"es" }} displays "2 classes".
+ * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
+ * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
+ * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
If the provided argument contains a comma, the text before the comma is
used for the singular case and the text after the comma is used for the
plural case:
- * If value is 0, cand{{ value|plurlize:"y,ies" }} displays "0 candies".
- * If value is 1, cand{{ value|plurlize:"y,ies" }} displays "1 candy".
- * If value is 2, cand{{ value|plurlize:"y,ies" }} displays "2 candies".
+ * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
+ * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
+ * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
"""
if not u',' in arg:
arg = u',' + arg
@@ -631,11 +733,13 @@ def pluralize(value, arg=u's'):
except TypeError: # len() of unsized object.
pass
return singular_suffix
+pluralize.is_safe = False
def phone2numeric(value):
"""Takes a phone number and converts it in to its numerical equivalent."""
from django.utils.text import phone2numeric
return phone2numeric(value)
+phone2numeric.is_safe = True
def pprint(value):
"""A wrapper around pprint.pprint -- for debugging, really."""
@@ -644,6 +748,7 @@ def pprint(value):
return pformat(value)
except Exception, e:
return u"Error in formatting: %s" % force_unicode(e, errors="replace")
+pprint.is_safe = True
# Syntax: register.filter(name of filter, callback)
register.filter(add)
@@ -662,6 +767,7 @@ def pprint(value):
register.filter(first)
register.filter(fix_ampersands)
register.filter(floatformat)
+register.filter(force_escape)
register.filter(get_digit)
register.filter(iriencode)
register.filter(join)
@@ -679,6 +785,7 @@ def pprint(value):
register.filter(removetags)
register.filter(random)
register.filter(rjust)
+register.filter(safe)
register.filter('slice', slice_)
register.filter(slugify)
register.filter(stringformat)
View
37 django/template/defaulttags.py
@@ -14,9 +14,25 @@
from django.conf import settings
from django.utils.encoding import smart_str, smart_unicode
from django.utils.itercompat import groupby
+from