Django

Code

Changeset 6671

Show
Ignore:
Timestamp:
11/14/07 06:58:53 (2 years ago)
Author:
mtredinnick
Message:

Implemented auto-escaping of variable output in templates. Fully controllable by template authors and it's possible to write filters and templates that simulataneously work in both auto-escaped and non-auto-escaped environments if you need to. Fixed #2359

See documentation in templates.txt and templates_python.txt for how everything
works.

Backwards incompatible if you're inserting raw HTML output via template variables.

Based on an original design from Simon Willison and with debugging help from Michael Radziej.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/admin/filterspecs.py

    r5609 r6671  
    1010from django.utils.encoding import smart_unicode, iri_to_uri 
    1111from django.utils.translation import ugettext as _ 
     12from django.utils.html import escape 
     13from django.utils.safestring import mark_safe 
    1214import datetime 
    1315 
     
    4042        t = [] 
    4143        if self.has_output(): 
    42             t.append(_(u'<h3>By %s:</h3>\n<ul>\n') % self.title()) 
     44            t.append(_(u'<h3>By %s:</h3>\n<ul>\n') % escape(self.title())) 
    4345 
    4446            for choice in self.choices(cl): 
     
    4850                     choice['display'])) 
    4951            t.append('</ul>\n\n') 
    50         return "".join(t
     52        return mark_safe("".join(t)
    5153 
    5254class RelatedFilterSpec(FilterSpec): 
  • django/trunk/django/contrib/admin/models.py

    r5803 r6671  
    44from django.utils.translation import ugettext_lazy as _ 
    55from django.utils.encoding import smart_unicode 
     6from django.utils.safestring import mark_safe 
    67 
    78ADDITION = 1 
     
    5051        This is relative to the Django admin index page. 
    5152        """ 
    52         return u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id
     53        return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, self.object_id)
  • django/trunk/django/contrib/admin/templates/admin/base_site.html

    r3349 r6671  
    22{% load i18n %} 
    33 
    4 {% block title %}{{ title|escape }} | {% trans 'Django site admin' %}{% endblock %} 
     4{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %} 
    55 
    66{% block branding %} 
  • django/trunk/django/contrib/admin/templates/admin/change_form.html

    r6391 r6671  
    1111<div class="breadcrumbs"> 
    1212     <a href="../../../">{% trans "Home" %}</a> &rsaquo; 
    13      <a href="../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo; 
    14      {% if add %}{% trans "Add" %} {{ opts.verbose_name|escape }}{% else %}{{ original|truncatewords:"18"|escape }}{% endif %} 
     13     <a href="../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; 
     14     {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %} 
    1515</div> 
    1616{% endif %}{% endblock %} 
  • django/trunk/django/contrib/admin/templates/admin/date_hierarchy.html

    r3349 r6671  
    22<div class="xfull"> 
    33<ul class="toplinks"> 
    4 {% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title|escape }}</a></li>{% endif %} 
     4{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %} 
    55{% for choice in choices %} 
    6 <li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title|escape }}{% if choice.link %}</a>{% endif %}</li> 
     6<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li> 
    77{% endfor %} 
    88</ul><br class="clear" /> 
  • django/trunk/django/contrib/admin/templates/admin/delete_confirmation.html

    r6391 r6671  
    44<div class="breadcrumbs"> 
    55     <a href="../../../../">{% trans "Home" %}</a> &rsaquo; 
    6      <a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo; 
     6     <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; 
    77     <a href="../">{{ object|escape|truncatewords:"18" }}</a> &rsaquo; 
    88     {% trans 'Delete' %} 
     
    1414    <ul> 
    1515    {% for obj in perms_lacking %} 
    16         <li>{{ obj|escape }}</li> 
     16        <li>{{ obj }}</li> 
    1717    {% endfor %} 
    1818    </ul> 
  • django/trunk/django/contrib/admin/templates/admin_doc/model_detail.html

    r6391 r6671  
    99{% endblock %} 
    1010 
    11 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name|escape }}</div>{% endblock %} 
     11{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name }}</div>{% endblock %} 
    1212 
    13 {% block title %}Model: {{ name|escape }}{% endblock %} 
     13{% block title %}Model: {{ name }}{% endblock %} 
    1414 
    1515{% block content %} 
    1616<div id="content-main"> 
    17 <h1>{{ summary|escape }}</h1> 
     17<h1>{{ summary }}</h1> 
    1818 
    1919{% if description %} 
    20   <p>{% filter escape|linebreaksbr %}{% trans description %}{% endfilter %}</p> 
     20  <p>{% filter linebreaksbr %}{% trans description %}{% endfilter %}</p> 
    2121{% endif %} 
    2222 
  • django/trunk/django/contrib/admin/templates/admin/edit_inline_stacked.html

    r3349 r6671  
    22<fieldset class="module aligned"> 
    33   {% for fcw in bound_related_object.form_field_collection_wrappers %} 
    4       <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst|escape }}&nbsp;#{{ forloop.counter }}</h2> 
     4      <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2> 
    55      {% if bound_related_object.show_url %}{% if fcw.obj.original %} 
    66      <p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p> 
  • django/trunk/django/contrib/admin/templates/admin/edit_inline_tabular.html

    r3571 r6671  
    11{% load admin_modify %} 
    22<fieldset class="module"> 
    3    <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst|escape }}</h2><table> 
     3   <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table> 
    44   <thead><tr> 
    55   {% for fw in bound_related_object.field_wrapper_list %} 
    66      {% if fw.needs_header %} 
    7          <th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst|escape }}</th> 
     7         <th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th> 
    88      {% endif %} 
    99   {% endfor %} 
  • django/trunk/django/contrib/admin/templates/admin/index.html

    r5935 r6671  
    2020            <tr> 
    2121            {% if model.perms.change %} 
    22                 <th scope="row"><a href="{{ model.admin_url }}">{{ model.name|escape }}</a></th> 
     22                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th> 
    2323            {% else %} 
    24                 <th scope="row">{{ model.name|escape }}</th> 
     24                <th scope="row">{{ model.name }}</th> 
    2525            {% endif %} 
    2626 
     
    5959            <ul class="actionlist"> 
    6060            {% for entry in admin_log %} 
    61             <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> 
     61            <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> 
    6262            {% endfor %} 
    6363            </ul> 
  • django/trunk/django/contrib/admin/templates/admin/invalid_setup.html

    r3349 r6671  
    22{% load i18n %} 
    33 
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title|escape }}</div>{% endblock %} 
     4{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %} 
    55 
    66{% block content %} 
  • django/trunk/django/contrib/admin/templates/admin/object_history.html

    r6391 r6671  
    22{% load i18n %} 
    33{% block breadcrumbs %} 
    4 <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> 
     4<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> 
    55{% endblock %} 
    66 
     
    2424        <tr> 
    2525            <th scope="row">{{ action.action_time|date:_("DATE_WITH_TIME_FULL") }}</th> 
    26             <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name|escape }} {{ action.user.last_name|escape }}){% endif %}</td> 
    27             <td>{{ action.change_message|escape }}</td> 
     26            <td>{{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name }} {{ action.user.last_name }}){% endif %}</td> 
     27            <td>{{ action.change_message }}</td> 
    2828        </tr> 
    2929        {% endfor %} 
  • django/trunk/django/contrib/admin/templates/admin/pagination.html

    r3349 r6671  
    77{% endfor %} 
    88{% endif %} 
    9 {{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name|escape }}{% else %}{{ cl.opts.verbose_name_plural|escape }}{% endifequal %} 
     9{{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name|escape }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %} 
    1010{% if show_all_url %}&nbsp;&nbsp;<a href="{{ show_all_url }}" class="showall">{% trans 'Show all' %}</a>{% endif %} 
    1111</p> 
  • django/trunk/django/contrib/admin/templates/widget/foreign.html

    r3352 r6671  
    1616    {% endif %} 
    1717    {% if bound_field.raw_id_admin %} 
    18         {% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14"|escape }}</strong>{% endif %} 
     18        {% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %} 
    1919    {% endif %} 
    2020{% endif %} 
  • django/trunk/django/contrib/admin/templates/widget/one_to_one.html

    r3352 r6671  
    11{% if add %}{% include "widget/foreign.html" %}{% endif %} 
    2 {% if change %}{% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14"|escape }}</strong>{% endif %}{% endif %} 
     2{% if change %}{% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}{% endif %} 
  • django/trunk/django/contrib/admin/templatetags/adminapplist.py

    r5609 r6671  
    22from django.db.models import get_models 
    33from django.utils.encoding import force_unicode 
     4from django.utils.safestring import mark_safe 
    45 
    56register = template.Library() 
     
    3940                            model_list.append({ 
    4041                                'name': force_unicode(capfirst(m._meta.verbose_name_plural)), 
    41                                 'admin_url': u'%s/%s/' % (force_unicode(app_label), m.__name__.lower()), 
     42                                'admin_url': mark_safe(u'%s/%s/' % (force_unicode(app_label), m.__name__.lower())), 
    4243                                'perms': perms, 
    4344                            }) 
  • django/trunk/django/contrib/admin/templatetags/admin_list.py

    r5694 r6671  
    55from django.db import models 
    66from django.utils import dateformat 
    7 from django.utils.html import escape 
     7from django.utils.html import escape, conditional_escape 
    88from django.utils.text import capfirst 
     9from django.utils.safestring import mark_safe 
    910from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _ 
    1011from django.utils.encoding import smart_unicode, smart_str, force_unicode 
     
    2021        return u'... ' 
    2122    elif i == cl.page_num: 
    22         return u'<span class="this-page">%d</span> ' % (i+1
     23        return mark_safe(u'<span class="this-page">%d</span> ' % (i+1)
    2324    else: 
    24         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
     25        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)
    2526paginator_number = register.simple_tag(paginator_number) 
    2627 
     
    118119def _boolean_icon(field_val): 
    119120    BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} 
    120     return u'<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val
     121    return mark_safe(u'<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
    121122 
    122123def items_for_result(cl, result): 
     
    194195            # Problem cases are long ints (23L) and non-ASCII strings. 
    195196            result_id = repr(force_unicode(getattr(result, pk)))[1:] 
    196             yield (u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 
    197                 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), result_repr, table_tag)) 
    198         else: 
    199             yield (u'<td%s>%s</td>' % (row_class, result_repr)) 
     197            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ 
     198                (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)) 
     199        else: 
     200            yield mark_safe(u'<td%s>%s</td>' % (row_class, conditional_escape(result_repr))) 
    200201 
    201202def results(cl): 
     
    221222        year_month_format, month_day_format = get_partial_date_formats() 
    222223 
    223         link = lambda d: cl.get_query_string(d, [field_generic]
     224        link = lambda d: mark_safe(cl.get_query_string(d, [field_generic])
    224225 
    225226        if year_lookup and month_lookup and day_lookup: 
  • django/trunk/django/contrib/admin/templatetags/admin_modify.py

    r6399 r6671  
    44from django.utils.text import capfirst 
    55from django.utils.encoding import force_unicode 
     6from django.utils.safestring import mark_safe 
     7from django.utils.html import escape 
    68from django.db import models 
    79from django.db.models.fields import Field 
     
    3335    if not absolute_url_re.match(script_path): 
    3436        script_path = '%s%s' % (settings.ADMIN_MEDIA_PREFIX, script_path) 
    35     return u'<script type="text/javascript" src="%s"></script>' % script_path 
     37    return mark_safe(u'<script type="text/javascript" src="%s"></script>' 
     38            % script_path) 
    3639include_admin_script = register.simple_tag(include_admin_script) 
    3740 
     
    6467        colon = ":" 
    6568    class_str = class_names and u' class="%s"' % u' '.join(class_names) or u'' 
    66     return u'<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \ 
    67         force_unicode(capfirst(bound_field.field.verbose_name)), colon) 
     69    return mark_safe(u'<label for="%s"%s>%s%s</label> ' % 
     70            (bound_field.element_id, class_str, 
     71            escape(force_unicode(capfirst(bound_field.field.verbose_name))), 
     72            colon)) 
    6873field_label = register.simple_tag(field_label) 
    6974 
     
    194199                     ' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % ( 
    195200                     f, field.name, add_values, field.max_length)) 
    196     return u''.join(t
     201    return mark_safe(u''.join(t)
    197202auto_populated_field_script = register.simple_tag(auto_populated_field_script) 
    198203 
     
    200205    f = bound_field.field 
    201206    if f.rel and isinstance(f.rel, models.ManyToManyRel) and f.rel.filter_interface: 
    202         return u'<script type="text/javascript">addEvent(window, "load", function(e) {' \ 
     207        return mark_safe(u'<script type="text/javascript">addEvent(window, "load", function(e) {' \ 
    203208              ' SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % ( 
    204               f.name, f.verbose_name.replace('"', '\\"'), f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX
     209              f.name, escape(f.verbose_name.replace('"', '\\"')), f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX)
    205210    else: 
    206211        return '' 
  • django/trunk/django/contrib/admin/utils.py

    r4265 r6671  
    44from email.Parser import HeaderParser 
    55from email.Errors import HeaderParseError 
     6from django.utils.safestring import mark_safe 
    67try: 
    78    import docutils.core 
     
    6768                destination_path=None, writer_name='html', 
    6869                settings_overrides=overrides) 
    69     return parts['fragment'] 
     70    return mark_safe(parts['fragment']) 
    7071 
    7172# 
  • django/trunk/django/contrib/admin/views/decorators.py

    r5609 r6671  
    55from django.shortcuts import render_to_response 
    66from django.utils.translation import ugettext_lazy, ugettext as _ 
     7from django.utils.safestring import mark_safe 
    78import base64, datetime, md5 
    89import cPickle as pickle 
     
    2324    return render_to_response('admin/login.html', { 
    2425        'title': _('Log in'), 
    25         'app_path': request.path
     26        'app_path': mark_safe(request.path)
    2627        'post_data': post_data, 
    2728        'error_message': error_message 
  • django/trunk/django/contrib/admin/views/doc.py

    r6296 r6671  
    1111from django.contrib.sites.models import Site 
    1212from django.utils.translation import ugettext as _ 
     13from django.utils.safestring import mark_safe 
    1314import inspect, os, re 
    1415 
     
    3031    admin_root = request.path[:-len('doc/bookmarklets/')] 
    3132    return render_to_response('admin_doc/bookmarklets.html', { 
    32         'admin_url': "%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root), 
     33        'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)), 
    3334    }, context_instance=RequestContext(request)) 
    3435bookmarklets = staff_member_required(bookmarklets) 
  • django/trunk/django/contrib/admin/views/main.py

    r6360 r6671  
    1515from django.utils.encoding import force_unicode, smart_str 
    1616from django.utils.translation import ugettext as _ 
     17from django.utils.safestring import mark_safe 
    1718import operator 
    1819 
     
    137138 
    138139        if field.rel: 
    139             self.related_url = u'../../../%s/%s/' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name.lower()) 
     140            self.related_url = mark_safe(u'../../../%s/%s/' 
     141                    % (field.rel.to._meta.app_label, 
     142                        field.rel.to._meta.object_name.lower())) 
    140143 
    141144    def original_value(self): 
     
    217220        'ordered_objects': ordered_objects, 
    218221        'inline_related_objects': inline_related_objects, 
    219         'form_url': form_url
     222        'form_url': mark_safe(form_url)
    220223        'opts': opts, 
    221224        'content_type_id': ContentType.objects.get_for_model(model).id, 
     
    437440                    # Don't display link to edit, because it either has no 
    438441                    # admin or is edited inline. 
    439                     nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj), []]) 
     442                    nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []]) 
    440443                else: 
    441444                    # Display a link to the admin page. 
    442                     nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ 
    443                         (force_unicode(capfirst(related.opts.verbose_name)), related.opts.app_label, related.opts.object_name.lower(), 
    444                         sub_obj._get_pk_val(), sub_obj), []]) 
     445                    nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % 
     446                        (escape(force_unicode(capfirst(related.opts.verbose_name))), 
     447                            related.opts.app_label, 
     448                            related.opts.object_name.lower(), 
     449                            sub_obj._get_pk_val(), sub_obj)), []]) 
    445450                _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2) 
    446451        else: 
     
    454459                else: 
    455460                    # Display a link to the admin page. 
    456                     nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ 
    457                         (force_unicode(capfirst(related.opts.verbose_name)), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj)), []]) 
     461                    nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ 
     462                        (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))), []]) 
    458463                _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2) 
    459464            # If there were related objects, and the user doesn't have 
     
    486491                    # Display a link to the admin page. 
    487492                    nh(deleted_objects, current_depth, [ 
    488                         (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name)}) + \ 
     493                        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))}) + \ 
    489494                        (u' <a href="../../../../%s/%s/%s/">%s</a>' % \ 
    490                             (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj))), []]) 
     495                            (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []]) 
    491496        # If there were related objects, and the user doesn't have 
    492497        # permission to change them, add the missing perm to perms_needed. 
     
    508513    # Populate deleted_objects, a data structure of all related objects that 
    509514    # will also be deleted. 
    510     deleted_objects = [u'%s: <a href="../../%s/">%s</a>' % (force_unicode(capfirst(opts.verbose_name)), force_unicode(object_id), escape(obj)), []] 
     515    deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), force_unicode(object_id), escape(obj))), []] 
    511516    perms_needed = set() 
    512517    _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) 
     
    605610            elif v is not None: 
    606611                p[k] = v 
    607         return '?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'
     612        return mark_safe('?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
    608613 
    609614    def get_results(self, request): 
  • django/trunk/django/contrib/csrf/middleware.py

    r6038 r6671  
    88from django.conf import settings 
    99from django.http import HttpResponseForbidden 
     10from django.utils.safestring import mark_safe 
    1011import md5 
    1112import re 
    1213import itertools 
    1314 
    14 _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>' 
     15_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>') 
    1516 
    1617_POST_FORM_RE = \ 
     
    8384            def add_csrf_field(match): 
    8485                """Returns the matched <form> tag plus the added <input> element""" 
    85                 return match.group() + "<div style='display:none;'>" + \ 
     86                return mark_safe(match.group() + "<div style='display:none;'>" + \ 
    8687                "<input type='hidden' " + idattributes.next() + \ 
    8788                " name='csrfmiddlewaretoken' value='" + csrf_token + \ 
    88                 "' /></div>" 
     89                "' /></div>") 
    8990 
    9091            # Modify any POST forms 
  • django/trunk/django/contrib/databrowse/datastructures.py

    r5947 r6671  
    99from django.utils.translation import get_date_formats 
    1010from django.utils.encoding import smart_unicode, smart_str, iri_to_uri 
     11from django.utils.safestring import mark_safe 
    1112from django.db.models.query import QuerySet 
    1213 
     
    2930 
    3031    def url(self): 
    31         return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name
     32        return mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
    3233 
    3334    def objects(self, **kwargs): 
     
    6970    def url(self): 
    7071        if self.field.choices: 
    71             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
     72            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)
    7273        elif self.field.rel: 
    73             return '%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name
     74            return mark_safe('%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)
    7475 
    7576class EasyChoice(object): 
     
    8283 
    8384    def url(self): 
    84         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)) 
     85        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))) 
    8586 
    8687class EasyInstance(object): 
     
    185186                lst = [] 
    186187                for value in self.values(): 
    187                     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())) 
     188                    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()))) 
    188189                    lst.append((smart_unicode(value), url)) 
    189190            else: 
     
    192193            lst = [] 
    193194            for value in self.values(): 
    194                 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)) 
     195                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))) 
    195196                lst.append((value, url)) 
    196197        elif isinstance(self.field, models.URLField): 
  • django/trunk/django/contrib/databrowse/plugins/calendars.py

    r5947 r6671  
    66from django.utils.text import capfirst 
    77from django.utils.translation import get_date_formats 
     8from django.utils.encoding import force_unicode 
     9from django.utils.safestring import mark_safe 
    810from django.views.generic import date_based 
    9 from django.utils.encoding import force_unicode 
    1011import datetime 
    1112import time 
     
    3031        if not fields: 
    3132            return u'' 
    32         return u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \ 
    33             u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]) 
     33        return mark_safe(u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \ 
     34            u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) 
    3435 
    3536    def urls(self, plugin_name, easy_instance_field): 
    3637        if isinstance(easy_instance_field.field, models.DateField): 
    37             return [u'%s%s/%s/%s/%s/%s/' % (easy_instance_field.model.url(), 
     38            return [mark_safe(u'%s%s/%s/%s/%s/%s/' % ( 
     39                easy_instance_field.model.url(), 
    3840                plugin_name, easy_instance_field.field.name, 
    3941                easy_instance_field.raw_value.year, 
    4042                easy_instance_field.raw_value.strftime('%b').lower(), 
    41                 easy_instance_field.raw_value.day)
     43                easy_instance_field.raw_value.day))
    4244 
    4345    def model_view(self, request, model_databrowse, url): 
  • django/trunk/django/contrib/databrowse/plugins/fieldchoices.py

    r5876 r6671  
    66from django.utils.text import capfirst 
    77from django.utils.encoding import smart_str, force_unicode 
     8from django.utils.safestring import mark_safe 
    89from django.views.generic import date_based 
    910import datetime 
     
    3334        if not fields: 
    3435            return u'' 
    35         return u'<p class="filter"><strong>View by:</strong> %s</p>' % \ 
    36             u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]) 
     36        return mark_safe(u'<p class="filter"><strong>View by:</strong> %s</p>' % \ 
     37            u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) 
    3738 
    3839    def urls(self, plugin_name, easy_instance_field): 
    3940        if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values(): 
    4041            field_value = smart_str(easy_instance_field.raw_value) 
    41             return [u'%s%s/%s/%s/' % (easy_instance_field.model.url(), 
     42            return [mark_safe(u'%s%s/%s/%s/' % ( 
     43                easy_instance_field.model.url(), 
    4244                plugin_name, easy_instance_field.field.name, 
    43                 urllib.quote(field_value, safe=''))
     45                urllib.quote(field_value, safe='')))
    4446 
    4547    def model_view(self, request, model_databrowse, url): 
  • django/trunk/django/contrib/databrowse/sites.py

    r5876 r6671  
    33from django.contrib.databrowse.datastructures import EasyModel, EasyChoice 
    44from django.shortcuts import render_to_response 
     5from django.utils.safestring import mark_safe 
    56 
    67class AlreadyRegistered(Exception): 
     
    6162    def main_view(self, request): 
    6263        easy_model = EasyModel(self.site, self.model) 
    63         html_snippets = u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()]
     64        html_snippets = mark_safe(u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()])
    6465        return render_to_response('databrowse/model_detail.html', { 
    6566            'model': easy_model, 
  • django/trunk/django/contrib/flatpages/views.py

    r4265 r6671  
    55from django.conf import settings 
    66from django.core.xheaders import populate_xheaders 
     7from django.utils.safestring import mark_safe 
    78 
    89DEFAULT_TEMPLATE = 'flatpages/default.html' 
     
    3132    else: 
    3233        t = loader.get_template(DEFAULT_TEMPLATE) 
     34 
     35    # To avoid having to always use the "|safe" filter in flatpage templates, 
     36    # mark the title and content as already safe (since they are raw HTML 
     37    # content in the first place). 
     38    f.title = mark_safe(f.title) 
     39    f.content = mark_safe(f.content) 
     40 
    3341    c = RequestContext(request, { 
    3442        'flatpage': f, 
  • django/trunk/django/contrib/humanize/templatetags/humanize.py

    r5985 r6671  
    2222        return u"%d%s" % (value, t[0]) 
    2323    return u'%d%s' % (value, t[value % 10]) 
     24ordinal.is_safe = True 
    2425register.filter(ordinal) 
    2526 
     
    3536    else: 
    3637        return intcomma(new) 
     38intcomma.is_safe = True 
    3739register.filter(intcomma) 
    3840 
     
    5658        return ungettext('%(value).1f trillion', '%(value).1f trillion', new_value) % {'value': new_value} 
    5759    return value 
     60intword.is_safe = False 
    5861register.filter(intword) 
    5962 
     
    7073        return value 
    7174    return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1] 
     75apnumber.is_safe = True 
    7276register.filter(apnumber) 
    7377 
  • django/trunk/django/contrib/markup/templatetags/markup.py

    r5609 r6671  
    1818from django.conf import settings 
    1919from django.utils.encoding import smart_str, force_unicode 
     20from django.utils.safestring import mark_safe 
    2021 
    2122register = template.Library() 
     
    2930        return force_unicode(value) 
    3031    else: 
    31         return force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')) 
     32        return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8'))) 
     33textile.is_safe = True 
    3234 
    3335def markdown(value): 
     
    3941        return force_unicode(value) 
    4042    else: 
    41         return force_unicode(markdown.markdown(smart_str(value))) 
     43        return mark_safe(force_unicode(markdown.markdown(smart_str(value)))) 
     44markdown.is_safe = True 
    4245 
    4346def restructuredtext(value): 
     
    5154        docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {}) 
    5255        parts = publish_parts(source=smart_str(value), writer_name="html4css1", settings_overrides=docutils_settings) 
    53         return force_unicode(parts["fragment"]) 
     56        return mark_safe(force_unicode(parts["fragment"])) 
     57restructuredtext.is_safe = True 
    5458 
    5559register.filter(textile) 
  • django/trunk/django/contrib/markup/tests.py

    r5876 r6671  
    11# Quick tests for the markup templatetags (django.contrib.markup) 
    22 
    3 from django.template import Template, Context, add_to_builtins 
    43import re 
    54import unittest 
     5 
     6from django.template import Template, Context, add_to_builtins 
     7from django.utils.html import escape 
    68 
    79add_to_builtins('django.contrib.markup.templatetags.markup') 
     
    2527<p>Paragraph 2 with &#8220;quotes&#8221; and <code>code</code></p>""") 
    2628        else: 
    27             self.assertEqual(rendered, textile_content
     29            self.assertEqual(rendered, escape(textile_content)
    2830 
    2931    def test_markdown(self): 
  • django/trunk/django/contrib/sitemaps/templates/sitemap_index.xml

    r4101 r6671  
    1 <?xml version="1.0" encoding="UTF-8"?> 
     1{% autoescape off %}<?xml version="1.0" encoding="UTF-8"?> 
    22<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 
    33{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %} 
    44</sitemapindex> 
     5{% endautoescape %} 
  • django/trunk/django/contrib/sitemaps/templates/sitemap.xml

    r4088 r6671  
    1 <?xml version="1.0" encoding="UTF-8"?> 
     1{% autoescape off %}<?xml version="1.0" encoding="UTF-8"?> 
    22<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 
    33{% spaceless %} 
     
    1212{% endspaceless %} 
    1313</urlset> 
     14{% endautoescape %} 
  • django/trunk/django/newforms/forms.py

    r6668 r6671  
    88from django.utils.html import escape 
    99from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode 
     10from django.utils.safestring import mark_safe 
    1011 
    1112from fields import Field 
     
    119120                if bf.label: 
    120121                    label = escape(force_unicode(bf.label)) 
    121                     # Only add the suffix if the label does not end in punctuation. 
     122                    # Only add the suffix if the label does not end in 
     123                    # punctuation. 
    122124                    if self.label_suffix: 
    123125                        if label[-1] not in ':?.!': 
     
    137139            if output: 
    138140                last_row = output[-1] 
    139                 # Chop off the trailing row_ender (e.g. '</td></tr>') and insert the hidden fields. 
     141                # Chop off the trailing row_ender (e.g. '</td></tr>') and 
     142                # insert the hidden fields. 
    140143                output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender 
    141             else: # If there aren't any rows in the output, just append the hidden fields. 
     144            else: 
     145                # If there aren't any rows in the output, just append the 
     146                # hidden fields. 
    142147                output.append(str_hidden) 
    143         return u'\n'.join(output
     148        return mark_safe(u'\n'.join(output)
    144149 
    145150    def as_table(self): 
     
    304309            attrs = attrs and flatatt(attrs) or '' 
    305310            contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents) 
    306         return contents 
     311        return mark_safe(contents) 
    307312 
    308313    def _is_hidden(self): 
  • django/trunk/django/newforms/util.py

    r6625 r6671  
    22from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode 
    33from django.utils.functional import Promise 
     4from django.utils.safestring import mark_safe 
    45 
    56def flatatt(attrs): 
     
    2324    def as_ul(self): 
    2425        if not self: return u'' 
    25         return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s%s</li>' % (k, force_unicode(v)) for k, v in self.items()]) 
     26        return mark_safe(u'<ul class="errorlist">%s</ul>' 
     27                % ''.join([u'<li>%s%s</li>' % (k, force_unicode(v)) 
     28                    for k, v in self.items()])) 
    2629 
    2730    def as_text(self): 
     
    3740    def as_ul(self): 
    3841        if not self: return u'' 
    39         return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self]) 
     42        return mark_safe(u'<ul class="errorlist">%s</ul>' 
     43                % ''.join([u'<li>%s</li>' % force_unicode(e) for e in self])) 
    4044 
    4145    def as_text(self): 
  • django/trunk/django/newforms/widgets.py

    r6594 r6671  
    1515from django.utils.translation import ugettext 
    1616from django.utils.encoding import StrAndUnicode, force_unicode 
     17from django.utils.safestring import mark_safe 
    1718from util import flatatt 
    1819 
     
    8788        if value is None: value = '' 
    8889        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 
    89         if value != '': final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty. 
    90         return u'<input%s />' % flatatt(final_attrs) 
     90        if value != '': 
     91            # Only add the 'value' attribute if a value is non-empty. 
     92            final_attrs['value'] = force_unicode(value) 
     93        return mark_safe(u'<input%s />' % flatatt(final_attrs)) 
    9194 
    9295class TextInput(Input): 
     
    121124        if value is None: value = [] 
    122125        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 
    123         return u'\n'.join([(u'<input%s />' % flatatt(dict(value=force_unicode(v), **final_attrs))) for v in value]) 
     126        return mark_safe(u'\n'.join([(u'<input%s />' % 
     127            flatatt(dict(value=force_unicode(v), **final_attrs))) 
     128            for v in value])) 
    124129 
    125130    def value_from_datadict(self, data, files, name): 
     
    150155        value = force_unicode(value) 
    151156        final_attrs = self.build_attrs(attrs, name=name) 
    152         return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value)) 
     157        return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), 
     158                escape(value))) 
    153159 
    154160class DateTimeInput(Input): 
     
    184190            final_attrs['checked'] = 'checked' 
    185191        if value not in ('', True, False, None): 
    186             final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty. 
    187         return u'<input%s />' % flatatt(final_attrs) 
     192            # Only add the 'value' attribute if a value is non-empty. 
     193            final_attrs['value'] = force_unicode(value) 
     194        return mark_safe(u'<input%s />' % flatatt(final_attrs)) 
    188195 
    189196    def value_from_datadict(self, data, files, name): 
     
    206213        final_attrs = self.build_attrs(attrs, name=name) 
    207214        output = [u'<select%s>' % flatatt(final_attrs)] 
    208         str_value = force_unicode(value) # Normalize to string. 
     215        # Normalize to string. 
     216        str_value = force_unicode(value) 
    209217        for option_value, option_label in chain(self.choices, choices): 
    210218            option_value = force_unicode(option_value) 
     
    212220            output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(force_unicode(option_label)))) 
    213221        output.append(u'</select>') 
    214         return u'\n'.join(output
     222        return mark_safe(u'\n'.join(output)
    215223 
    216224class NullBooleanSelect(Select): 
     
    249257            output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(force_unicode(option_label)))) 
    250258        output.append(u'</select>') 
    251         return u'\n'.join(output
     259        return mark_safe(u'\n'.join(output)
    252260 
    253261    def value_from_datadict(self, data, files, name): 
     
    270278 
    271279    def __unicode__(self): 
    272         return u'<label>%s %s</label>' % (self.tag(), self.choice_label) 
     280        return mark_safe(u'<label>%s %s</label>' % (self.tag(), 
     281                self.choice_label)) 
    273282 
    274283    def is_checked(self): 
     
    281290        if self.is_checked(): 
    282291            final_attrs['checked'] = 'checked' 
    283         return u'<input%s />' % flatatt(final_attrs
     292        return mark_safe(u'<input%s />' % flatatt(final_attrs)
    284293 
    285294class RadioFieldRenderer(StrAndUnicode): 
     
    305314    def render(self): 
    306315        """Outputs a <ul> for this set of radio fields.""" 
    307         return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]) 
     316        return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' 
     317                % force_unicode(w) for w in self])) 
    308318 
    309319class RadioSelect(Select): 
     
    342352        final_attrs = self.build_attrs(attrs, name=name) 
    343353        output = [u'<ul>'] 
    344         str_values = set([force_unicode(v) for v in value]) # Normalize to strings. 
     354        # Normalize to strings 
     355        str_values = set([force_unicode(v) for v in value]) 
    345356        for i, (option_value, option_label) in enumerate(chain(self.choices, choices)): 
    346357            # If an ID attribute was given, add a numeric index as a suffix, 
     
    353364            output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(force_unicode(option_label)))) 
    354365        output.append(u'</ul>') 
    355         return u'\n'.join(output
     366        return mark_safe(u'\n'.join(output)
    356367 
    357368    def id_for_label(self, id_): 
     
    451462            return [value.date(), value.time().replace(microsecond=0)] 
    452463        return [None, None] 
     464 
  • django/trunk/django/oldforms/__init__.py

    r6452 r6671  
    22from django.core.exceptions import PermissionDenied 
    33from django.utils.html import escape 
     4from django.utils.safestring import mark_safe 
    45from django.conf import settings 
    56from django.utils.translation import ugettext, ungettext 
     
    190191    def html_error_list(self): 
    191192        if self.errors(): 
    192             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]
     193            return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
    193194        else: 
    194             return '' 
     195            return mark_safe('') 
    195196 
    196197    def get_id(self): 
     
    227228 
    228229    def html_combined_error_list(self): 
    229         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]
     230        return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
    230231 
    231232class InlineObjectCollection(object): 
     
    419420        if self.max_length: 
    420421            max_length = u'maxlength="%s" ' % self.max_length 
    421         return u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
     422        return mark_safe(u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    422423            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and u' required' or '', 
    423             self.field_name, self.length, escape(data), max_length) 
     424            self.field_name, self.length, escape(data), max_length)) 
    424425 
    425426    def html2python(data): 
     
    443444        if data is None: 
    444445            data = '' 
    445         return u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
     446        return mark_safe(u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
    446447            (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'', 
    447             self.field_name, self.rows, self.cols, escape(data)) 
     448            self.field_name, self.rows, self.cols, escape(data))) 
    448449 
    449450class HiddenField(FormField): 
     
    454455 
    455456    def render(self, data): 
    456         return u'<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
    457             (self.get_id(), self.field_name, escape(data)) 
     457        return mark_safe(u'<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
     458            (self.get_id(), self.field_name, escape(data))) 
    458459 
    459460class CheckboxField(FormField): 
     
    469470        if data or (data is '' and self.checked_by_default): 
    470471            checked_html = ' checked="checked"' 
    471         return u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
     472        return mark_safe(u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
    472473            (self.get_id(), self.__class__.__name__, 
    473             self.field_name, checked_html) 
     474            self.field_name, checked_html)) 
    474475 
    475476    def html2python(data): 
     
    503504            output.append(u'    <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(display_name)))) 
    504505        output.append(u'  </select>') 
    505         return u'\n'.join(output
     506        return mark_safe(u'\n'.join(output)
    506507 
    507508    def isValidChoice(self, data, form): 
     
    557558                output.extend([u'<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 
    558559                output.append(u'</ul>') 
    559                 return u''.join(output
     560                return mark_safe(u''.join(output)
    560561            def __iter__(self): 
    561562                for d in self.datalist: 
     
    572573                'value': value, 
    573574                'name': display_name, 
    574                 'field': u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
    575                     (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html)
    576                 'label': u'<label for="%s">%s</label>' % \ 
     575                'field': mark_safe(u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
     576                    (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html))
     577                'label': mark_safe(u'<label for="%s">%s</label>' % \ 
    577578                    (self.get_id() + u'_' + unicode(i), display_name), 
    578             }) 
     579            )}) 
    579580        return RadioFieldRenderer(datalist, self.ul_class) 
    580581 
     
    615616            output.append(u'    <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(choice)))) 
    616617        output.append(u'  </select>') 
    617         return u'\n'.join(output
     618        return mark_safe(u'\n'.join(output)
    618619 
    619620    def isValidChoice(self, field_data, all_data): 
     
    668669                self.get_id() + escape(value), choice)) 
    669670        output.append(u'</ul>') 
    670         return u'\n'.join(output
     671        return mark_safe(u'\n'.join(output)
    671672 
    672673#################### 
     
    689690 
    690691    def render(self, data): 
    691         return u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    692             (self.get_id(), self.__class__.__name__, self.field_name) 
     692        return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 
     693            (self.get_id(), self.__class__.__name__, self.field_name)) 
    693694 
    694695    def html2python(data): 
  • django/trunk/django/template/context.py

    r6098 r6671  
    1010class Context(object): 
    1111    "A stack container for variable context" 
    12     def __init__(self, dict_=None): 
     12 
     13    def __init__(self, dict_=None, autoescape=True): 
    1314        dict_ = dict_ or {} 
    1415        self.dicts = [dict_] 
     16        self.autoescape = autoescape 
    1517 
    1618    def __repr__(self): 
     
    98100        for processor in get_standard_processors() + processors: 
    99101            self.update(processor(request)) 
     102 
  • django/trunk/django/template/defaultfilters.py

    r6646 r6671  
    88from django.utils.translation import ugettext, ungettext 
    99from django.utils.encoding import force_unicode, iri_to_uri 
     10from django.utils.safestring import mark_safe, SafeData 
    1011 
    1112register = Library() 
     
    3031    # arguments by the template parser). 
    3132    _dec._decorated_function = getattr(func, '_decorated_function', func) 
     33    for attr in ('is_safe', 'needs_autoescape'): 
     34        if hasattr(func, attr): 
     35            setattr(_dec, attr, getattr(func, attr)) 
    3236    return _dec 
    3337 
     
    4044    """Adds slashes - useful for passing strings to JavaScript, for example.""" 
    4145    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 
     46addslashes.is_safe = True 
    4247addslashes = stringfilter(addslashes) 
    4348 
     
    4550    """Capitalizes the first character of the value.""" 
    4651    return value and value[0].upper() + value[1:] 
     52capfirst.is_safe=True 
    4753capfirst = stringfilter(capfirst) 
    4854 
     
    5157    from django.utils.html import fix_ampersands 
    5258    return fix_ampersands(value) 
     59fix_ampersands.is_safe=True 
    5360fix_ampersands = stringfilter(fix_ampersands) 
    5461 
     
    9198    m = f - int(f) 
    9299    if not m and d < 0: 
    93         return u'%d' % int(f
     100        return mark_safe(u'%d' % int(f)
    94101    else: 
    95102        formatstr = u'%%.%df' % abs(d) 
    96         return formatstr % f 
     103        return mark_safe(formatstr % f) 
     104floatformat.is_safe = True 
    97105 
    98106def iriencode(value): 
     
    101109iriencode = stringfilter(iriencode) 
    102110 
    103 def linenumbers(value): 
     111def linenumbers(value, autoescape=None): 
    104112    """Displays text with line numbers.""" 
    105113    from django.utils.html import escape 
    106114    lines = value.split(u'\n') 
    107115    # Find the maximum width of the line count, for use with zero padding 
    108     # string format command. 
     116    # string format command 
    109117    width = unicode(len(unicode(len(lines)))) 
    110     for i, line in enumerate(lines): 
    111         lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line)) 
    112     return u'\n'.join(lines) 
     118    if not autoescape or isinstance(value, SafeData): 
     119        for i, line in enumerate(lines): 
     120            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, line) 
     121    else: 
     122        for i, line in enumerate(lines): 
     123            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line)) 
     124    return mark_safe(u'\n'.join(lines)) 
     125linenumbers.is_safe = True 
     126linenumbers.needs_autoescape = True 
    113127linenumbers = stringfilter(linenumbers) 
    114128 
     
    116130    """Converts a string into all lowercase.""" 
    117131    return value.lower() 
     132lower.is_safe = True 
    118133lower = stringfilter(lower) 
    119134 
     
    126141    """ 
    127142    return list(value) 
     143make_list.is_safe = False 
    128144make_list = stringfilter(make_list) 
    129145 
     
    136152    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') 
    137153    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) 
    138     return re.sub('[-\s]+', '-', value) 
     154    return mark_safe(re.sub('[-\s]+', '-', value)) 
     155slugify.is_safe = True 
    139156slugify = stringfilter(slugify) 
    140157 
     
    153170    except (ValueError, TypeError): 
    154171        return u"" 
     172stringformat.is_safe = True 
    155173 
    156174def title(value): 
    157175    """Converts a string into titlecase.""" 
    158176    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     177title.is_safe = True 
    159178title = stringfilter(title) 
    160179 
     
    171190        return value # Fail silently. 
    172191    return truncate_words(value, length) 
     192truncatewords.is_safe = True 
    173193truncatewords = stringfilter(truncatewords) 
    174194 
     
    185205        return value # Fail silently. 
    186206    return truncate_html_words(value, length) 
     207truncatewords_html.is_safe = True 
    187208truncatewords_html = stringfilter(truncatewords_html) 
    188209 
     
    190211    """Converts a string into all uppercase.""" 
    191212    return value.upper() 
     213upper.is_safe = False 
    192214upper = stringfilter(upper) 
    193215 
     
    196218    from django.utils.http import urlquote 
    197219    return urlquote(value) 
     220urlencode.is_safe = False 
    198221urlencode = stringfilter(urlencode) 
    199222 
    200 def urlize(value): 
     223def urlize(value, autoescape=None): 
    201224    """Converts URLs in plain text into clickable links.""" 
    202225    from django.utils.html import urlize 
    203     return urlize(value, nofollow=True) 
     226    return mark_safe(urlize(value, nofollow=True, autoescape=autoescape)) 
     227urlize.is_safe=True 
     228urlize.needs_autoescape = True 
    204229urlize = stringfilter(urlize) 
    205230 
     
    212237    """ 
    213238    from django.utils.html import urlize 
    214     return urlize(value, trim_url_limit=int(limit), nofollow=True) 
     239    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 
     240urlizetrunc.is_safe = True 
    215241urlizetrunc = stringfilter(urlizetrunc) 
    216242 
     
    218244    """Returns the number of words.""" 
    219245    return len(value.split()) 
     246wordcount.is_safe = False 
    220247wordcount = stringfilter(wordcount) 
    221248 
     
    228255    from django.utils.text import wrap 
    229256    return wrap(value, int(arg)) 
     257wordwrap.is_safe = True 
    230258wordwrap = stringfilter(wordwrap) 
    231259 
     
    237265    """ 
    238266    return value.ljust(int(arg)) 
     267ljust.is_safe = True 
    239268ljust = stringfilter(ljust) 
    240269 
     
    246275    """ 
    247276    return value.rjust(int(arg)) 
     277rjust.is_safe = True 
    248278rjust = stringfilter(rjust) 
    249279 
     
    251281    """Centers the value in a field of a given width.""" 
    252282    return value.center(int(arg)) 
     283center.is_safe = True 
    253284center = stringfilter(center) 
    254285 
    255286def cut(value, arg): 
    256     """Removes all values of arg from the given string.""" 
    257     return value.replace(arg, u'') 
     287    """ 
     288    Removes all values of arg from the given string. 
     289    """ 
     290    safe = isinstance(value, SafeData) 
     291    value = value.replace(arg, u'') 
     292    if safe and arg != ';': 
     293        return mark_safe(value) 
     294    return value 
    258295cut = stringfilter(cut) 
    259296 
     
    263300 
    264301def escape(value): 
    265     "Escapes a string's HTML" 
     302    """ 
     303    Marks the value as a string that should not be auto-escaped. 
     304    """ 
     305    from django.utils.safestring import mark_for_escaping 
     306    return mark_for_escaping(value) 
     307escape.is_safe = True 
     308escape = stringfilter(escape) 
     309 
     310def force_escape(value): 
     311    """ 
     312    Escapes a string's HTML. This returns a new string containing the escaped 
     313    characters (as opposed to "escape", which marks the content for later 
     314    possible escaping). 
     315    """ 
    266316    from django.utils.html import escape 
    267     return escape(value
     317    return mark_safe(escape(value)
    268318escape = stringfilter(escape) 
    269  
    270 def linebreaks(value): 
     319force_escape.is_safe = True 
     320 
     321def linebreaks(value, autoescape=None): 
    271322    """ 
    272323    Replaces line breaks in plain text with appropriate HTML; a single 
     
    275326    """ 
    276327    from django.utils.html import linebreaks 
    277     return linebreaks(value) 
     328    autoescape = autoescape and not isinstance(value, SafeData) 
     329    return mark_safe(linebreaks(value, autoescape)) 
     330linebreaks.is_safe = True 
     331linebreaks.needs_autoescape = True 
    278332linebreaks = stringfilter(linebreaks) 
    279333 
    280 def linebreaksbr(value): 
     334def linebreaksbr(value, autoescape=None): 
    281335    """ 
    282336    Converts all newlines in a piece of plain text to HTML line breaks 
    283337    (``<br />``). 
    284338    """ 
    285     return value.replace('\n', '<br />') 
     339    if autoescape and not isinstance(value, SafeData): 
     340        from django.utils.html import escape 
     341        value = escape(value) 
     342    return mark_safe(value.replace('\n', '<br />')) 
     343linebreaksbr.is_safe = True 
     344linebreaksbr.needs_autoescape = True 
    286345linebreaksbr = stringfilter(linebreaksbr) 
     346 
     347def safe(value): 
     348    """ 
     349    Marks the value as a string that should not be auto-escaped. 
     350    """ 
     351    from django.utils.safestring import mark_safe 
     352    return mark_safe(value) 
     353safe.is_safe = True 
     354safe = stringfilter(safe) 
    287355 
    288356def removetags(value, tags): 
     
    295363    value = endtag_re.sub(u'', value) 
    296364    return value 
     365removetags.is_safe = True 
    297366removetags = stringfilter(removetags) 
    298367 
     
    301370    from django.utils.html import strip_tags 
    302371    return strip_tags(value) 
     372striptags.is_safe = True 
    303373striptags = stringfilter(striptags) 
    304374 
     
    316386    decorated.sort() 
    317387    return [item[1] for item in decorated] 
     388dictsort.is_safe = False 
    318389 
    319390def dictsortreversed(value, arg): 
     
    327398    decorated.reverse() 
    328399    return [item[1] for item in decorated] 
     400dictsortreversed.is_safe = False 
    329401 
    330402def first(value): 
     
    334406    except IndexError: 
    335407        return u'' 
     408first.is_safe = True 
    336409 
    337410def join(value, arg): 
    338411    """Joins a list with a string, like Python's ``str.join(list)``.""" 
    339412    try: 
    340         return arg.join(map(force_unicode, value)) 
     413        data = arg.join(map(force_unicode, value)) 
    341414    except AttributeError: # fail silently but nicely 
    342415        return value 
     416    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), 
     417            value, True) 
     418    if safe_args: 
     419        return mark_safe(data) 
     420    else: 
     421        return data 
     422join.is_safe = True 
    343423 
    344424def length(value): 
    345425    """Returns the length of the value - useful for lists.""" 
    346426    return len(value) 
     427length.is_safe = True 
    347428 
    348429def length_is(value, arg): 
    349430    """Returns a boolean of whether the value's length is the argument.""" 
    350431    return len(value) == int(arg) 
     432length_is.is_safe = True 
    351433 
    352434def random(value): 
    353435    """Returns a random item from the list.""" 
    354436    return random_module.choice(value) 
     437random.is_safe = True 
    355438 
    356439def slice_(value, arg): 
     
    373456    except (ValueError, TypeError): 
    374457        return value # Fail silently. 
    375  
    376 def unordered_list(value): 
     458slice_.is_safe = True 
     459 
     460def unordered_list(value, autoescape=None): 
    377461    """ 
    378462    Recursively takes a self-nested list and returns an HTML unordered list -- 
     
    395479        </li> 
    396480    """ 
     481    if autoescape: 
     482        from django.utils.html import conditional_escape 
     483        escaper = conditional_escape 
     484    else: 
     485        escaper = lambda x: x 
    397486    def convert_old_style_list(list_): 
    398487        """ 
     
    444533                sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist, 
    445534                                                         indent, indent) 
    446             output.append('%s<li>%s%s</li>' % (indent, force_unicode(title), 
    447                                               sublist)) 
     535            output.append('%s<li>%s%s</li>' % (indent, 
     536                    escaper(force_unicode(title)), sublist)) 
    448537            i += 1 
    449538        return '\n'.join(output) 
    450539    value, converted = convert_old_style_list(value) 
    451     return _helper(value) 
     540    return mark_safe(_helper(value)) 
     541unordered_list.is_safe = True 
     542unordered_list.needs_autoescape = True 
    452543 
    453544################### 
     
    458549    """Adds the arg to the value.""" 
    459550    return int(value) + int(arg) 
     551add.is_safe = False 
    460552 
    461553def get_digit(value, arg): 
     
    477569    except IndexError: 
    478570        return 0 
     571get_digit.is_safe = False 
    479572 
    480573################### 
     
    490583        arg = settings.DATE_FORMAT 
    491584    return format(value, arg) 
     585date.is_safe = False 
    492586 
    493587def time(value, arg=None): 
     
    499593        arg = settings.TIME_FORMAT 
    500594    return time_format(value, arg) 
     595time.is_safe = False 
    501596 
    502597def timesince(value, arg=None): 
     
    508603        return timesince(arg, value) 
    509604    return timesince(value) 
     605timesince.is_safe = False 
    510606 
    511607def timeuntil(value, arg=None): 
     
    518614        return timesince(arg, value) 
    519615    return timesince(datetime.now(), value) 
     616timeuntil.is_safe = False 
    520617 
    521618################### 
     
    526623    """If value is unavailable, use given default.""" 
    527624    return value or arg 
     625default.is_safe = False 
    528626 
    529627def default_if_none(value, arg): 
     
    532630        return arg 
    533631    return value 
     632default_if_none.is_safe = False 
    534633 
    535634def divisibleby(value, arg): 
    536635    """Returns True if the value is devisible by the argument.""" 
    537636    return int(value) % int(arg) == 0 
     637divisibleby.is_safe = False 
    538638 
    539639def yesno(value, arg=None): 
     
    567667        return yes 
    568668    return no 
     669yesno.is_safe = False 
    569670 
    570671################### 
     
    589690        return ugettext("%.1f MB") % (bytes / (1024 * 1024)) 
    590691    return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) 
     692filesizeformat.is_safe = True 
    591693 
    592694def pluralize(value, arg=u's'): 
     
    595697    the suffix: 
    596698 
    597     * If value is 0, vote{{ value|plurlize }} displays "0 votes". 
    598     * If value is 1, vote{{ value|plurlize }} displays "1 vote". 
    599     * If value is 2, vote{{ value|plurlize }} displays "2 votes". 
     699    * If value is 0, vote{{ value|pluralize }} displays "0 votes". 
     700    * If value is 1, vote{{ value|pluralize }} displays "1 vote". 
     701    * If value is 2, vote{{ value|pluralize }} displays "2 votes". 
    600702 
    601703    If an argument is provided, that string is used instead: 
    602704 
    603     * If value is 0, class{{ value|plurlize:"es" }} displays "0 classes". 
    604     * If value is 1, class{{ value|plurlize:"es" }} displays "1 class". 
    605     * If value is 2, class{{ value|plurlize:"es" }} displays "2 classes". 
     705    * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes". 
     706    * If value is 1, class{{ value|pluralize:"es" }} displays "1 class". 
     707    * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes". 
    606708 
    607709    If the provided argument contains a comma, the text before the comma is 
     
    609711    plural case: 
    610712 
    611     * If value is 0, cand{{ value|plurlize:"y,ies" }} displays "0 candies". 
    612     * If value is 1, cand{{ value|plurlize:"y,ies" }} displays "1 candy". 
    613     * If value is 2, cand{{ value|plurlize:"y,ies" }} displays "2 candies". 
     713    * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies". 
     714    * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy". 
     715    * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies". 
    614716    """ 
    615717    if not u',' in arg: 
     
    632734            pass 
    633735    return singular_suffix 
     736pluralize.is_safe = False 
    634737 
    635738def phone2numeric(value): 
     
    637740    from django.utils.text import phone2numeric 
    638741    return phone2numeric(value) 
     742phone2numeric.is_safe = True 
    639743 
    640744def pprint(value): 
     
    645749    except Exception, e: 
    646750        return u"Error in formatting: %s" % force_unicode(e, errors="replace") 
     751pprint.is_safe = True 
    647752 
    648753# Syntax: register.filter(name of filter, callback) 
     
    663768register.filter(fix_ampersands) 
    664769register.filter(floatformat) 
     770register.filter(force_escape) 
    665771register.filter(get_digit) 
    666772register.filter(iriencode) 
     
    680786register.filter(random) 
    681787register.filter(rjust) 
     788register.filter(safe) 
    682789register.filter('slice', slice_) 
    683790register.filter(slugify) 
  • django/trunk/django/template/defaulttags.py

    r6641 r6671  
    1515from django.utils.encoding import smart_str, smart_unicode 
    1616from django.utils.itercompat import groupby 
     17from django.utils.safestring import mark_safe 
    1718 
    1819register = Library() 
     20 
     21class AutoEscapeControlNode(Node): 
     22    """Implements the actions of the autoescape tag.""" 
     23    def __init__(self, setting, nodelist): 
     24        self.setting, self.nodelist = setting, nodelist 
     25 
     26    def render(self, context): 
     27        old_setting = context.autoescape 
     28        context.autoescape = self.setting 
     29        output = self.nodelist.render(context) 
     30        context.autoescape = old_setting 
     31        if self.setting: 
     32            return mark_safe(output) 
     33        else: 
     34            return output 
    1935 
    2036class CommentNode(Node): 
     
    394410 
    395411#@register.tag 
     412def autoescape(parser, token): 
     413    """ 
     414    Force autoescape behaviour for this block. 
     415    """ 
     416    args = token.contents.split() 
     417    if len(args) != 2: 
     418        raise TemplateSyntaxError("'Autoescape' tag requires exactly one argument.") 
     419    arg = args[1] 
     420    if arg not in (u'on', u'off'): 
     421        raise TemplateSyntaxError("'Autoescape' argument should be 'on' or 'off'") 
     422    nodelist = parser.parse(('endautoescape',)) 
     423    parser.delete_first_token() 
     424    return AutoEscapeControlNode((arg == 'on'), nodelist) 
     425autoescape = register.tag(autoescape) 
     426 
     427#@register.tag 
    396428def comment(parser, token): 
    397429    """ 
     
    493525    Sample usage:: 
    494526 
    495         {% filter escape|lower %} 
     527        {% filter force_escape|lower %} 
    496528            This text will be HTML-escaped, and will appear in lowercase. 
    497529        {% endfilter %} 
     
    499531    _, rest = token.contents.split(None, 1) 
    500532    filter_expr = parser.compile_filter("var|%s" % (rest)) 
     533    for func, unused in filter_expr.filters: 
     534        if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'): 
     535            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__) 
    501536    nodelist = parser.parse(('endfilter',)) 
    502537    parser.delete_first_token() 
  • django/trunk/django/template/__init__.py

    r6399 r6671  
    5858from django.utils.encoding import smart_unicode, force_unicode 
    5959from django.utils.translation import ugettext as _ 
     60from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 
     61from django.utils.html import escape 
    6062 
    6163__all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 
     
    596598                else: 
    597599                    arg_vals.append(arg.resolve(context)) 
    598             obj = func(obj, *arg_vals) 
     600            if getattr(func, 'needs_autoescape', False): 
     601                new_obj = func(obj, autoescape=context.autoescape, *arg_vals) 
     602            else: 
     603                new_obj = func(obj, *arg_vals) 
     604            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 
     605                obj = mark_safe(new_obj) 
     606            elif isinstance(obj, EscapeData): 
     607                obj = mark_for_escaping(new_obj) 
     608            else: 
     609                obj = new_obj 
    599610        return obj 
    600611 
     
    638649    Returns the resolved variable, which may contain attribute syntax, within 
    639650    the given context. 
    640      
     651 
    641652    Deprecated; use the Variable class instead. 
    642653    """ 
     
    648659    a hard-coded string (if it begins and ends with single or double quote 
    649660    marks):: 
    650      
     661 
    651662        >>> c = {'article': {'section':'News'}} 
    652663        >>> Variable('article.section').resolve(c) 
     
    663674    (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') 
    664675    """ 
    665      
     676 
    666677    def __init__(self, var): 
    667678        self.var = var 
    668679        self.literal = None 
    669680        self.lookups = None 
    670          
     681 
    671682        try: 
    672683            # First try to treat this variable as a number. 
    673684            # 
    674             # Note that this could cause an OverflowError here that we're not  
     685            # Note that this could cause an OverflowError here that we're not 
    675686            # catching. Since this should only happen at compile time, that's 
    676687            # probably OK. 
    677688            self.literal = float(var) 
    678          
     689 
    679690            # So it's a float... is it an int? If the original value contained a 
    680691            # dot or an "e" then it was a float, not an int. 
    681692            if '.' not in var and 'e' not in var.lower(): 
    682693                self.literal = int(self.literal) 
    683                  
     694 
    684695            # "2." is invalid 
    685696            if var.endswith('.'): 
     
    692703            if var[0] in "\"'" and var[0] == var[-1]: 
    693704                self.literal = var[1:-1] 
    694              
     705 
    695706            else: 
    696707                # Otherwise we'll set self.lookups so that resolve() knows we're 
    697708                # dealing with a bonafide variable 
    698709                self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) 
    699      
     710 
    700711    def resolve(self, context): 
    701712        """Resolve this variable against a given context.""" 
     
    706717            # We're dealing with a literal, so it's already been "resolved" 
    707718            return self.literal 
    708              
     719 
    709720    def __repr__(self): 
    710721        return "<%s: %r>" % (self.__class__.__name__, self.var) 
    711      
     722 
    712723    def __str__(self): 
    713724        return self.var 
     
    716727        """ 
    717728        Performs resolution of a real variable (i.e. not a literal) against the 
    718         given context.  
    719          
     729        given context. 
     730 
    720731        As indicated by the method's name, this method is an implementation 
    721732        detail and shouldn't be called by external code. Use Variable.resolve() 
     
    758769                    else: 
    759770                        raise 
    760      
    761         if isinstance(current, (basestring, Promise)): 
    762             try: 
    763                 current = force_unicode(current) 
    764             except UnicodeDecodeError: 
    765                 # Failing to convert to unicode can happen sometimes (e.g. debug 
    766                 # tracebacks). So we allow it in this particular instance. 
    767                 pass 
     771 
    768772        return current 
    769773 
     
    839843 
    840844    def render(self, context): 
    841         return self.filter_expression.resolve(context) 
     845        try: 
     846            output = force_unicode(self.filter_expression.resolve(context)) 
     847        except UnicodeDecodeError: 
     848            # Unicode conversion can fail sometimes for reasons out of our 
     849            # control (e.g. exception rendering). In that case, we fail quietly. 
     850            return '' 
     851        if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 
     852            return force_unicode(escape(output)) 
     853        else: 
     854            return force_unicode(output) 
    842855 
    843856class DebugVariableNode(VariableNode): 
    844857    def render(self, context): 
    845858        try: 
    846             return self.filter_expression.resolve(context
     859            output = force_unicode(self.filter_expression.resolve(context)
    847860        except TemplateSyntaxError, e: 
    848861            if not hasattr(e, 'source'): 
    849862                e.source = self.source 
    850863            raise 
     864        except UnicodeDecodeError: 
     865            return '' 
     866        if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 
     867            return escape(output) 
     868        else: 
     869            return output 
    851870 
    852871def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
     
    962981                            t = get_template(file_name) 
    963982                        self.nodelist = t.nodelist 
    964                     return self.nodelist.render(context_class(dict)) 
     983                    return self.nodelist.render(context_class(dict, 
     984                            autoescape=context.autoescape)) 
    965985 
    966986            compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) 
  • django/trunk/django/utils/encoding.py

    r6649 r6671  
    44 
    55from django.utils.functional import Promise 
     6from django.utils.safestring import SafeData, mark_safe 
    67 
    78class DjangoUnicodeDecodeError(UnicodeDecodeError): 
     
    5253                s = unicode(str(s), encoding, errors) 
    5354        elif not isinstance(s, unicode): 
    54             s = unicode(s, encoding, errors) 
     55            # Note: We use .decode() here, instead of unicode(s, encoding, 
     56            # errors), so that if s is a SafeString, it ends up being a 
     57            # SafeUnicode at the end. 
     58            s = s.decode(encoding, errors) 
    5559    except UnicodeDecodeError, e: 
    5660        raise DjangoUnicodeDecodeError(s, *e.args) 
  • django/trunk/django/utils/html.py

    r5717 r6671  
    44import string 
    55 
     6from django.utils.safestring import SafeData, mark_safe 
    67from django.utils.encoding import force_unicode 
    78from django.utils.functional import allow_lazy 
     
    2829def escape(html): 
    2930    "Return the given HTML with ampersands, quotes and carets encoded." 
    30     return force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'
     31    return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;')
    3132escape = allow_lazy(escape, unicode) 
    3233 
    33 def linebreaks(value): 
    34     "Convert newlines into <p> and <br />s." 
     34def conditional_escape(html): 
     35    """ 
     36    Similar to escape(), except that it doesn't operate on pre-escaped strings. 
     37    """ 
     38    if isinstance(html, SafeData): 
     39        return html 
     40    else: 
     41        return escape(html) 
     42 
     43def linebreaks(value, autoescape=False): 
     44    "Converts newlines into <p> and <br />s" 
    3545    value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines 
    3646    paras = re.split('\n{2,}', value) 
    37     paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 
     47    if autoescape: 
     48        paras = [u'<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras] 
     49    else: 
     50        paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 
    3851    return u'\n\n'.join(paras) 
    39 linebreaks = allow_lazy(linebreaks, unicode) 
     52linebreaks = allow_lazy(linebreaks, unicode)  
    4053 
    4154def strip_tags(value): 
     
    5972fix_ampersands = allow_lazy(fix_ampersands, unicode) 
    6073 
    61 def urlize(text, trim_url_limit=None, nofollow=False): 
     74def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): 
    6275    """ 
    6376    Convert any URLs in text into clickable links. 
     
    7386    attribute. 
    7487    """ 
    75     trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x 
     88    if autoescape: 
     89        trim_url = lambda x, limit=trim_url_limit: conditional_escape(limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x) 
     90    else: 
     91        trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x 
     92    safe_input = isinstance(text, SafeData) 
    7693    words = word_split_re.split(force_unicode(text)) 
    7794    nofollow_attr = nofollow and ' rel="nofollow"' or '' 
     
    8097        if match: 
    8198            lead, middle, trail = match.groups() 
     99            if safe_input: 
     100                middle = mark_safe(middle) 
    82101            if middle.startswith('www.') or ('@' not in middle and not middle.startswith('http://') and \ 
    83102                    len(middle) > 0 and middle[0] in string.letters + string.digits and \ 
  • django/trunk/django/views/debug.py

    r6585 r6671  
    334334</head> 
    335335<body> 
    336  
    337336<div id="summary"> 
    338337  <h1>{{ exception_type }} at {{ request.path|escape }}</h1> 
     
    396395   <h2>Template error</h2> 
    397396   <p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p> 
    398    <h3>{{ template_info.message|escape }}</h3> 
     397   <h3>{{ template_info.message }}</h3> 
    399398   <table class="source{% if template_info.top %} cut-top{% endif %}{% ifnotequal template_info.bottom template_info.total %} cut-bottom{% endifnotequal %}"> 
    400399   {% for source_line in template_info.source_lines %} 
     
    414413  <div class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></div> 
    415414  <br/> 
     415  {% autoescape off %} 
    416416  <div id="browserTraceback"> 
    417417    <ul class="traceback"> 
     
    423423            <div class="context" id="c{{ frame.id }}"> 
    424424              {% if frame.pre_context %} 
    425                 <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol> 
     425                <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line }}</li>{% endfor %}</ol> 
    426426              {% endif %} 
    427               <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol> 
     427              <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line }} <span>...</span></li></ol> 
    428428              {% if frame.post_context %} 
    429                 <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol> 
     429                <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line }}</li>{% endfor %}</ol> 
    430430              {% endif %} 
    431431            </div> 
     
    447447                  <tr> 
    448448                    <td>{{ var.0 }}</td> 
    449                     <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     449                    <td class="code"><div>{{ var.1|pprint }}</div></td> 
    450450                  </tr> 
    451451                {% endfor %} 
     
    467467  File "{{ frame.filename }}" in {{ frame.function }}<br/> 
    468468  {% if frame.context_line %} 
    469     &nbsp;&nbsp;{{ frame.lineno }}. {{ frame.context_line|escape }}<br/> 
     469    &nbsp;&nbsp;{{ frame.lineno }}. {{ frame.context_line }}<br/> 
    470470  {% endif %} 
    471471{% endfor %}<br/> 
     
    477477    </table> 
    478478  </div> 
     479  {% endautoescape %} 
    479480</div> 
    480481 
     
    495496          <tr> 
    496497            <td>{{ var.0 }}</td> 
    497             <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     498            <td class="code"><div>{{ var.1|pprint }}</div></td> 
    498499          </tr> 
    499500        {% endfor %} 
     
    517518          <tr> 
    518519            <td>{{ var.0 }}</td> 
    519             <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     520            <td class="code"><div>{{ var.1|pprint }}</div></td> 
    520521          </tr> 
    521522        {% endfor %} 
     
    539540          <tr> 
    540541            <td>{{ var.0 }}</td> 
    541             <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     542            <td class="code"><div>{{ var.1|pprint }}</div></td> 
    542543          </tr> 
    543544        {% endfor %} 
     
    560561        <tr> 
    561562          <td>{{ var.0 }}</td> 
    562           <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     563          <td class="code"><div>{{ var.1|pprint }}</div></td> 
    563564        </tr> 
    564565      {% endfor %} 
     
    579580        <tr> 
    580581          <td>{{ var.0 }}</td> 
    581           <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     582          <td class="code"><div>{{ var.1|pprint }}</div></td> 
    582583        </tr> 
    583584      {% endfor %} 
     
    594595  </p> 
    595596</div> 
    596  
    597597</body> 
    598598</html> 
     
    646646      <ol> 
    647647        {% for pattern in urlpatterns %} 
    648           <li>{{ pattern|escape }}</li> 
     648          <li>{{ pattern }}</li> 
    649649        {% endfor %} 
    650650      </ol> 
    651651      <p>The current URL, <code>{{ request_path|escape }}</code>, didn't match any of these.</p> 
    652652    {% else %} 
    653       <p>{{ reason|escape }}</p> 
     653      <p>{{ reason }}</p> 
    654654    {% endif %} 
    655655  </div> 
  • django/trunk/docs/templates_python.txt

    r6462 r6671  
    220220    While ``TEMPLATE_STRING_IF_INVALID`` can be a useful debugging tool, 
    221221    it is a bad idea to turn it on as a 'development default'. 
    222      
     222 
    223223    Many templates, including those in the Admin site, rely upon the 
    224224    silence of the template system when a non-existent variable is 
     
    226226    ``TEMPLATE_STRING_IF_INVALID``, you will experience rendering 
    227227    problems with these templates and sites. 
    228      
     228 
    229229    Generally, ``TEMPLATE_STRING_IF_INVALID`` should only be enabled 
    230230    in order to debug a specific template problem, then cleared 
     
    723723will use the function's name as the filter name. 
    724724 
     725Filters and auto-escaping 
     726~~~~~~~~~~~~~~~~~~~~~~~~~ 
     727 
     728**New in Django development version** 
     729 
     730When you are writing a custom filter, you need to give some thought to how 
     731this filter will interact with Django's auto-escaping behaviour.  Firstly, you 
     732should realise that there are three types of strings that can be passed around 
     733inside the template code: 
     734 
     735 * raw strings are the native Python ``str`` or ``unicode`` types. On 
     736   output, they are escaped if auto-escaping is in effect and presented 
     737   unchanged, otherwise. 
     738 
     739 * "safe" strings are strings that are safe from further escaping at output 
     740   time. Any necessary escaping has already been done. They are commonly used 
     741   for output that contains raw HTML that is intended to be intrepreted on the 
     742   client side. 
     743 
     744   Internally, these strings are of type ``SafeString`` or ``SafeUnicode``, 
     745   although they share a common base class in ``SafeData``, so you can test 
     746   for them using code like:: 
     747 
     748    if isinstance(value, SafeData): 
     749        # Do something with the "safe" string. 
     750 
     751 * strings which are marked as "needing escaping" are *always* escaped on 
     752   output, regardless of whether they are in an ``autoescape`` block or not. 
     753   These strings are only escaped once, however, even if auto-escaping 
     754   applies. This type of string is internally represented by the types 
     755   ``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry 
     756   about these; they exist for the implementation of the ``escape`` filter. 
     757 
     758Inside your filter, you will need to think about three areas in order to be 
     759auto-escaping compliant: 
     760 
     761 1. If your filter returns a string that is ready for direct output (it should 
     762    be considered a "safe" string), you should call 
     763    ``django.utils.safestring.mark_safe()`` on the result prior to returning. 
     764    This will turn the result into the appropriate ``SafeData`` type. This is 
     765    often the case when you are returning raw HTML, for example. 
     766 
     767 2. If your filter is given a "safe" string, is it guaranteed to return a 
     768    "safe" string? If so, set the ``is_safe`` attribute on the function to be 
     769    ``True``. For example, a filter that replaced a word consisting only of 
     770    digits with the number spelt out in words is going to be 
     771    safe-string-preserving, since it cannot introduce any of the five dangerous 
     772    characters: <, >, ", ' or &. We can write:: 
     773 
     774        @register.filter 
     775        def convert_to_words(value): 
     776            # ... implementation here ... 
     777            return result 
     778 
     779        convert_to_words.is_safe = True 
     780 
     781    Note that this filter does not return a universally safe result (it does 
     782    not return ``mark_safe(result)``) because if it is handed a raw string such 
     783    as '<a>', this will need further escaping in an auto-escape environment. 
     784    The ``is_safe`` attribute only talks about the the result when a safe 
     785    string is passed into the filter. 
     786 
     787 3. Will your filter behave differently depending upon whether auto-escaping 
     788    is currently in effect or not? This is normally a concern when you are 
     789    returning mixed content (HTML elements mixed with user-supplied content). 
     790    For example, the ``ordered_list`` filter that ships with Django needs to 
     791    know whether to escape its content or not. It will always return a safe 
     792    string. Since it returns raw HTML, we cannot apply escaping to the 
     793    result -- it needs to be done in-situ. 
     794 
     795    For these cases, the filter function needs to be told what the current 
     796    auto-escaping setting is. Set the ``needs_autoescape`` attribute on the 
     797    filter to ``True`` and have your function take an extra argument called 
     798    ``autoescape`` with a default value of ``None``. When the filter is called, 
     799    the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in 
     800    effect. For example, the ``unordered_list`` filter is written as:: 
     801 
     802        def unordered_list(value, autoescape=None): 
     803            # ... lots of code here ... 
     804 
     805            return mark_safe(...) 
     806 
     807        unordered_list.is_safe = True 
     808        unordered_list.needs_autoescape = True 
     809 
     810By default, both the ``is_safe`` and ``needs_autoescape`` attributes are 
     811``False``. You do not need to specify them if ``False`` is an acceptable 
     812value. 
     813 
    725814Writing custom template tags 
    726815---------------------------- 
     
    841930without having to be parsed multiple times. 
    842931 
     932Auto-escaping considerations 
     933~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     934 
     935The output from template tags is not automatically run through the 
     936auto-escaping filters. However, there are still a couple of things you should 
     937keep in mind when writing a template tag: 
     938 
     939If the ``render()`` function of your template stores the result in a context 
     940variable (rather than returning the result in a string), it should take care 
     941to call ``mark_safe()`` if appropriate. When the variable is ultimately 
     942rendered, it will be affected by the auto-escape setting in effect at the 
     943time, so content that should be safe from further escaping needs to be marked 
     944as such. 
     945 
     946Also, if your template tag creates a new context for performing some 
     947sub-rendering, you should be careful to set the auto-escape attribute to the 
     948current context's value. The ``__init__`` method for the ``Context`` class 
     949takes a parameter called ``autoescape`` that you can use for this purpose. For 
     950example:: 
     951 
     952    def render(self, context): 
     953        # ... 
     954        new_context = Context({'var': obj}, autoescape=context.autoescape) 
     955        # ... Do something with new_context ... 
     956 
     957This is not a very common situation, but it is sometimes useful, particularly 
     958if you are rendering a template yourself. For example:: 
     959 
     960    def render(self, context): 
     961        t = template.load_template('small_fragment.html') 
     962        return t.render(Context({'var': obj}, autoescape=context.autoescape)) 
     963 
     964If we had neglected to pass in the current ``context.autoescape`` value to our 
     965new ``Context`` in this example, the results would have *always* been 
     966automatically escaped, which may not be the desired behaviour if the template 
     967tag is used inside a ``{% autoescape off %}`` block. 
     968 
    843969Registering the tag 
    844970~~~~~~~~~~~~~~~~~~~ 
     
    9181044            self.date_to_be_formatted = date_to_be_formatted 
    9191045            self.format_string = format_string 
    920          
     1046 
    9211047        def render(self, context): 
    9221048            try: 
     
    9351061    in favor of a new ``template.Variable`` class. Using this class will usually 
    9361062    be more efficient than calling ``template.resolve_variable`` 
    937      
     1063 
    9381064    To use the ``Variable`` class, simply instantiate it with the name of the 
    9391065    variable to be resolved, and then call ``variable.resolve(context)``. So, 
    9401066    in the development version, the above example would be more correctly 
    9411067    written as: 
    942      
     1068 
    9431069    .. parsed-literal:: 
    944      
     1070 
    9451071        class FormatTimeNode(template.Node): 
    9461072            def __init__(self, date_to_be_formatted, format_string): 
    9471073                self.date_to_be_formatted = **Variable(date_to_be_formatted)** 
    9481074                self.format_string = format_string 
    949          
     1075 
    9501076            def render(self, context): 
    9511077                try: 
     
    9541080                except template.VariableDoesNotExist: 
    9551081                    return '' 
    956      
     1082 
    9571083    Changes are highlighted in bold. 
    9581084 
  • django/trunk/docs/templates.txt

    r6648 r6671  
    300300wouldn't know which one of the blocks' content to use. 
    301301 
     302Automatic HTML escaping 
     303======================= 
     304 
     305**New in Django development version** 
     306 
     307A very real problem when creating HTML (and other) output using templates and 
     308variable substitution is the possibility of accidently inserting some variable 
     309value that affects the resulting HTML. For example, a template fragment such as 
     310:: 
     311 
     312    Hello, {{ name }}. 
     313 
     314seems like a harmless way to display the user's name. However, if you are 
     315displaying data that the user entered directly and they had entered their name as :: 
     316 
     317    <script>alert('hello')</script> 
     318 
     319this would always display a Javascript alert box when the page was loaded. 
     320Similarly, if you were displaying some data generated by another process and it 
     321contained a '<' symbol, you couldn't just dump this straight into your HTML, 
     322because it would be treated as the start of an element. The effects of these 
     323sorts of problems can vary from merely annoying to allowing exploits via `Cross 
     324Site Scripting`_ (XSS) attacks. 
     325 
     326.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting 
     327 
     328In order to provide some protection against these problems, Django 
     329provides automatic (but controllable) HTML escaping for data coming from 
     330tempate variables. Inside this tag, any data that comes from template 
     331variables is examined to see if it contains one of the five HTML characters 
     332(<, >, ', " and &) that often need escaping and those characters are converted 
     333to their respective HTML entities. It causes no harm if a character is 
     334converted to an entity when it doesn't need to be, so all five characters are 
     335always converted. 
     336 
     337Since some variables will contain data that is *intended* to be rendered 
     338as HTML, template tag and filter writers can mark their output strings as 
     339requiring no further escaping. For example, the ``unordered_list`` filter is 
     340designed to return raw HTML and we want the template processor to simply 
     341display the results as returned, without applying any escaping. That is taken 
     342care of by the filter. The template author need do nothing special in that 
     343case. 
     344 
     345By default, automatic HTML escaping is always applied. However, sometimes you 
     346will not want this to occur (for example, if you're using the templating 
     347system to create an email). To control automatic escaping inside your template, 
     348wrap the affected content in the ``autoescape`` tag, like so:: 
     349 
     350    {% autoescape off %} 
     351        Hello {{ name }} 
     352    {% endautoescape %} 
     353 
     354The auto-escaping tag passes its effect onto templates that extend the 
     355current one as well as templates included via the ``include`` tag, just like 
     356all block tags. 
     357 
     358The ``autoescape`` tag takes either ``on`` or ``off`` as its argument. At times, you might want to force auto-escaping when it would otherwise be disabled. For example:: 
     359 
     360    Auto-escaping is on by default. Hello {{ name }} 
     361 
     362    {% autoescape off %} 
     363        This will not be auto-escaped: {{ data }}. 
     364 
     365        Nor this: {{ other_data }} 
     366        {% autoescape on %} 
     367            Auto-escaping applies again, {{ name }} 
     368        {% endautoescape %} 
     369    {% endautoescape %} 
     370 
     371For individual variables, the ``safe`` filter can also be used to indicate 
     372that the contents should not be automatically escaped:: 
     373 
     374    This will be escaped: {{ data }} 
     375    This will not be escaped: {{ data|safe }} 
     376 
     377Think of *safe* as shorthand for *safe from further escaping* or *can be 
     378safely interpreted as HTML*. In this example, if ``data`` contains ``'<a>'``, 
     379the output will be:: 
     380 
     381    This will be escaped: &lt;a&gt; 
     382    This will not be escaped: <a> 
     383 
     384Generally, you won't need to worry about auto-escaping very much. View 
     385developers and custom filter authors need to think about when their data 
     386shouldn't be escaped and mark it appropriately. They are in a better position 
     387to know when that should happen than the template author, so it is their 
     388responsibility. By default, all output is escaped unless the template 
     389processor is explicitly told otherwise. 
     390 
     391You should also note that if you are trying to write a template that might be 
     392used in situations where automatic escaping is enabled or disabled and you 
     393don't know which (such as when your template is included in other templates), 
     394you can safely write as if you were in an ``{% autoescape off %}`` situation. 
     395Scatter ``escape`` filters around for any variables that need escaping. When 
     396auto-escaping is on, these extra filters won't change the output -- any 
     397variables that use the ``escape`` filter do not have further automatic 
     398escaping applied to them. 
     399 
    302400Using the built-in reference 
    303401============================ 
     
    374472Built-in tag reference 
    375473---------------------- 
     474 
     475autoescape 
     476~~~~~~~~~~ 
     477 
     478**New in Django development version** 
     479 
     480Control the current auto-escaping behaviour. This tag takes either ``on`` or 
     481``off`` as an argument and that determines whether auto-escaping is in effect 
     482inside the block. 
     483 
     484When auto-escaping is in effect, all variable content has HTML escaping applied 
     485to it before placing the result into the output (but after any filters have 
     486been applied). This is equivalent to manually applying the ``escape`` filter 
     487attached to each variable. 
     488 
     489The only exceptions are variables that are already marked as 'safe' from 
     490escaping, either by the code that populated the variable, or because it has 
     491the ``safe`` or ``escape`` filters applied. 
    376492 
    377493block 
     
    453569Sample usage:: 
    454570 
    455     {% filter escape|lower %} 
     571    {% filter force_escape|lower %} 
    456572        This text will be HTML-escaped, and will appear in all lowercase. 
    457573    {% endfilter %} 
     
    10771193~~~~~~ 
    10781194 
     1195**New in Django development version:** The behaviour of this filter has 
     1196changed slightly in the development version (the affects are only applied 
     1197once, after all other filters). 
     1198 
    10791199Escapes a string's HTML. Specifically, it makes these replacements: 
    10801200 
     
    10841204    * ``'"'`` (double quote) to ``'&quot;'`` 
    10851205    * ``"'"`` (single quote) to ``'&#39;'`` 
     1206 
     1207The escaping is only applied when the string is output, so it does not matter 
     1208where in a chained sequence of filters you put ``escape``: it will always be 
     1209applied as though it were the last filter. If you want escaping to be applied 
     1210immediately, use the ``force_escape`` filter. 
     1211 
     1212Applying ``escape`` to a variable that would normally have auto-escaping 
     1213applied to the result will only result in one round of escaping being done. So 
     1214it is safe to use this function even in auto-escaping environments. If you want 
     1215multiple escaping passes to be applied, use the ``force_escape`` filter. 
    10861216 
    10871217filesizeformat 
     
    11411271with an argument of ``-1``. 
    11421272 
     1273force_escape 
     1274~~~~~~~~~~~~ 
     1275 
     1276**New in Django development version** 
     1277 
     1278Applies HTML escaping to a string (see the ``escape`` filter for details). 
     1279This filter is applied *immediately* and returns a new, escaped string. This 
     1280is useful in the rare cases where you need multiple escaping or want to apply 
     1281other filters to the escaped results. Normally, you want to use the ``escape`` 
     1282filter. 
     1283 
    11431284get_digit 
    11441285~~~~~~~~~ 
     
    12641405 
    12651406**Argument:** field size 
     1407 
     1408safe 
     1409~~~~ 
     1410 
     1411Marks a string as not requiring further HTML escaping prior to output. When 
     1412autoescaping is off, this filter has no effect. 
    12661413 
    12671414slice 
  • django/trunk/tests/regressiontests/defaultfilters/tests.py

    r6647 r6671  
    195195u'a string to be mangled' 
    196196 
    197 >>> escape(u'<some html & special characters > here') 
     197>>> force_escape(u'<some html & special characters > here') 
    198198u'&lt;some html &amp; special characters &gt; here' 
    199199 
    200 >>> escape(u'<some html & special characters > here ĐÅ€£') 
     200>>> force_escape(u'<some html & special characters > here ĐÅ€£') 
    201201u'&lt;some html &amp; special characters &gt; here \xc4\x90\xc3\x85\xe2\x82\xac\xc2\xa3' 
    202202 
  • django/trunk/tests/regressiontests/forms/forms.py

    r6379 r6671  
    15551555>>> print t.render(Context({'form': UserRegistration(auto_id=False)})) 
    15561556<form action=""> 
    1557 <p>Username: <input type="text" name="username" maxlength="10" /><br />Good luck picking a username that doesn't already exist.</p> 
     1557<p>Username: <input type="text" name="username" maxlength="10" /><br />Good luck picking a username that doesn&#39;t already exist.</p> 
    15581558<p>Password1: <input type="password" name="password1" /></p> 
    15591559<p>Password2: <input type="password" name="password2" /></p> 
  • django/trunk/tests/regressiontests/forms/tests.py

    r6625 r6671  
    5151    'localflavor_uk_tests': localflavor_uk_tests, 
    5252    'localflavor_us_tests': localflavor_us_tests, 
    53     'regressions_tests': regression_tests, 
     53    'regression_tests': regression_tests, 
    5454    'util_tests': util_tests, 
    5555    'widgets_tests': widgets_tests, 
  • django/trunk/tests/regressiontests/humanize/tests.py

    r5985 r6671  
    44from django.utils.dateformat import DateFormat 
    55from django.utils.translation import ugettext as _ 
     6from django.utils.html import escape 
    67 
    78add_to_builtins('django.contrib.humanize.templatetags.humanize') 
     
    1617            t = Template('{{ test_content|%s }}' % method) 
    1718            rendered = t.render(Context(locals())).strip() 
    18             self.assertEqual(rendered, result_list[index]
     19            self.assertEqual(rendered, escape(result_list[index])
    1920                             msg="%s test failed, produced %s, should've produced %s" % (method, rendered, result_list[index])) 
    2021 
  • django/trunk/tests/regressiontests/templates/tests.py

    r6582 r6671  
    1515from django.template.loaders import app_directories, filesystem 
    1616from django.utils.translation import activate, deactivate, ugettext as _ 
     17from django.utils.safestring import mark_safe 
    1718from django.utils.tzinfo import LocalTimezone 
    1819 
    1920from unicode import unicode_tests 
     21import filters 
    2022 
    2123# Some other tests we would like to run 
     
    121123 
    122124    def test_templates(self): 
    123         # NOW and NOW_tz are used by timesince tag tests. 
    124         NOW = datetime.now() 
    125         NOW_tz = datetime.now(LocalTimezone(datetime.now())) 
    126  
     125        template_tests = self.get_template_tests() 
     126        filter_tests = filters.get_filter_tests() 
     127 
     128        # Quickly check that we aren't accidentally using a name in both 
     129        # template and filter tests. 
     130        overlapping_names = [name for name in filter_tests if name in 
     131                template_tests] 
     132        assert not overlapping_names, 'Duplicate test name(s): %s' % ', '.join(overlapping_names) 
     133 
     134        template_tests.update(filter_tests) 
     135 
     136        # Register our custom template loader. 
     137        def test_template_loader(template_name, template_dirs=None): 
     138            "A custom template loader that loads the unit-test templates." 
     139            try: 
     140                return (template_tests[template_name][0] , "test:%s" % template_name) 
     141            except KeyError: 
     142                raise template.TemplateDoesNotExist, template_name 
     143 
     144        old_template_loaders = loader.template_source_loaders 
     145        loader.template_source_loaders = [test_template_loader] 
     146 
     147        failures = [] 
     148        tests = template_tests.items() 
     149        tests.sort() 
     150 
     151        # Turn TEMPLATE_DEBUG off, because tests assume that. 
     152        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 
     153 
     154        # Set TEMPLATE_STRING_IF_INVALID to a known string 
     155        old_invalid = settings.TEMPLATE_STRING_IF_INVALID 
     156        expected_invalid_str = 'INVALID' 
     157 
     158        for name, vals in tests: 
     159            if isinstance(vals[2], tuple): 
     160                normal_string_result = vals[2][0] 
     161                invalid_string_result = vals[2][1] 
     162                if '%s' in invalid_string_result: 
     163                    expected_invalid_str = 'INVALID %s' 
     164                    invalid_string_result = invalid_string_result % vals[2][2] 
     165                    template.invalid_var_format_string = True 
     166            else: 
     167                normal_string_result = vals[2] 
     168                invalid_string_result = vals[2] 
     169 
     170            if 'LANGUAGE_CODE' in vals[1]: 
     171                activate(vals[1]['LANGUAGE_CODE']) 
     172            else: 
     173                activate('en-us') 
     174 
     175            for invalid_str, result in [('', normal_string_result), 
     176                                        (expected_invalid_str, invalid_string_result)]: 
     177                settings.TEMPLATE_STRING_IF_INVALID = invalid_str 
     178                try: 
     179                    test_template = loader.get_template(name) 
     180                    output = self.render(test_template, vals) 
     181                except Exception, e: 
     182                    if e.__class__ != result: 
     183                        failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e)) 
     184                    continue 
     185                if output != result: 
     186                    failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output)) 
     187 
     188            if 'LANGUAGE_CODE' in vals[1]: 
     189                deactivate() 
     190 
     191            if template.invalid_var_format_string: 
     192                expected_invalid_str = 'INVALID' 
     193                template.invalid_var_format_string = False 
     194 
     195        loader.template_source_loaders = old_template_loaders 
     196        deactivate() 
     197        settings.TEMPLATE_DEBUG = old_td 
     198        settings.TEMPLATE_STRING_IF_INVALID = old_invalid 
     199 
     200        self.assertEqual(failures, [], '\n'.join(failures)) 
     201 
     202    def render(self, test_template, vals): 
     203        return test_template.render(template.Context(vals[1])) 
     204 
     205    def get_template_tests(self): 
    127206        # SYNTAX -- 
    128207        # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class) 
    129         TEMPLATE_TESTS = { 
    130  
    131             ### BASIC SYNTAX ########################################################## 
     208        return { 
     209            ### BASIC SYNTAX ################################################ 
    132210 
    133211            # Plain text should go through the template parser untouched 
    134212            'basic-syntax01': ("something cool", {}, "something cool"), 
    135213 
    136             # Variables should be replaced with their value in the current context 
     214            # Variables should be replaced with their value in the current 
     215            # context 
    137216            'basic-syntax02': ("{{ headline }}", {'headline':'Success'}, "Success"), 
    138217 
     
    241320 
    242321            # Escaped string as argument 
    243             'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), 
     322            'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', 
     323                    {"var": None}, ' endquote&quot; hah'), 
    244324 
    245325            # Variable as argument 
     
    761841        #    'now04' : ('{% now "j \nn\n Y"%}', {}, str(datetime.now().day) + '\n' + str(datetime.now().month) + '\n' + str(datetime.now().year)) 
    762842 
    763             ### TIMESINCE TAG ################################################## 
    764             # Default compare with datetime.now() 
    765             'timesince01' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'), 
    766             'timesince02' : ('{{ a|timesince }}', {'a':(datetime.now() - timedelta(days=1, minutes = 1))}, '1 day'), 
    767             'timesince03' : ('{{ a|timesince }}', {'a':(datetime.now() - 
    768                 timedelta(hours=1, minutes=25, seconds = 10))}, '1 hour, 25 minutes'), 
    769  
    770             # Compare to a given parameter 
    771             'timesince04' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2), 'b':NOW + timedelta(days=1)}, '1 day'), 
    772             'timesince05' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2, minutes=1), 'b':NOW + timedelta(days=2)}, '1 minute'), 
    773  
    774             # Check that timezone is respected 
    775             'timesince06' : ('{{ a|timesince:b }}', {'a':NOW_tz + timedelta(hours=8), 'b':NOW_tz}, '8 hours'), 
    776  
    777             # Check times in the future. 
    778             'timesince07' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(minutes=1, seconds=10)}, '0 minutes'), 
    779             'timesince08' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(days=1, minutes=1)}, '0 minutes'), 
    780  
    781             ### TIMEUNTIL TAG ################################################## 
    782             # Default compare with datetime.now() 
    783             'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'), 
    784             'timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'), 
    785             'timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8 hours, 10 minutes'), 
    786  
    787             # Compare to a given parameter 
    788             'timeuntil04' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=1), 'b':NOW - timedelta(days=2)}, '1 day'), 
    789             'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'), 
    790  
    791             # Check times in the past. 
    792             'timeuntil07' : ('{{ a|timeuntil }}', {'a':datetime.now() - timedelta(minutes=1, seconds=10)}, '0 minutes'), 
    793             'timeuntil08' : ('{{ a|timeuntil }}', {'a':datetime.now() - timedelta(days=1, minutes=1)}, '0 minutes'), 
    794  
    795843            ### URL TAG ######################################################## 
    796844            # Successes 
     
    820868            'cache09' : ('{% load cache %}{% cache 1 %}{% endcache %}', {}, template.TemplateSyntaxError), 
    821869            'cache10' : ('{% load cache %}{% cache foo bar %}{% endcache %}', {}, template.TemplateSyntaxError), 
     870 
     871            ### AUTOESCAPE TAG ############################################## 
     872            'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"), 
     873            'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), 
     874            'autoescape-tag03': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "&lt;b&gt;hello&lt;/b&gt;"), 
     875 
     876            # Autoescape disabling and enabling nest in a predictable way. 
     877            'autoescape-tag04': ("{% autoescape off %}{{ first }} {% autoescape  on%}{{ first }}{% endautoescape %}{% endautoescape %}", {"first": "<a>"}, "<a> &lt;a&gt;"), 
     878 
     879            'autoescape-tag05': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "<b>first</b>"}, "&lt;b&gt;first&lt;/b&gt;"), 
     880 
     881            # Strings (ASCII or unicode) already marked as "safe" are not 
     882            # auto-escaped 
     883            'autoescape-tag06': ("{{ first }}", {"first": mark_safe("<b>first</b>")}, "<b>first</b>"), 
     884            'autoescape-tag07': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": mark_safe(u"<b>Apple</b>")}, u"<b>Apple</b>"), 
     885 
     886            # String arguments to filters, if used in the result, are escaped, 
     887            # too. 
     888            'basic-syntax08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote&quot; hah'), 
     889 
     890            # The "safe" and "escape" filters cannot work due to internal 
     891            # implementation details (fortunately, the (no)autoescape block 
     892            # tags can be used in those cases) 
     893            'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError), 
    822894        } 
    823  
    824         # Register our custom template loader. 
    825         def test_template_loader(template_name, template_dirs=None): 
    826             "A custom template loader that loads the unit-test templates." 
    827             try: 
    828                 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 
    829             except KeyError: 
    830                 raise template.TemplateDoesNotExist, template_name 
    831  
    832         old_template_loaders = loader.template_source_loaders 
    833         loader.template_source_loaders = [test_template_loader] 
    834  
    835         failures = [] 
    836         tests = TEMPLATE_TESTS.items() 
    837         tests.sort() 
    838  
    839         # Turn TEMPLATE_DEBUG off, because tests assume that. 
    840         old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 
    841  
    842         # Set TEMPLATE_STRING_IF_INVALID to a known string 
    843         old_invalid = settings.TEMPLATE_STRING_IF_INVALID 
    844         expected_invalid_str = 'INVALID' 
    845  
    846         for name, vals in tests: 
    847             if isinstance(vals[2], tuple): 
    848                 normal_string_result = vals[2][0] 
    849                 invalid_string_result = vals[2][1] 
    850                 if '%s' in invalid_string_result: 
    851                     expected_invalid_str = 'INVALID %s' 
    852                     invalid_string_result = invalid_string_result % vals[2][2] 
    853                     template.invalid_var_format_string = True 
    854             else: 
    855                 normal_string_result = vals[2] 
    856                 invalid_string_result = vals[2] 
    857  
    858             if 'LANGUAGE_CODE' in vals[1]: 
    859                 activate(vals[1]['LANGUAGE_CODE']) 
    860             else: 
    861                 activate('en-us') 
    862  
    863             for invalid_str, result in [('', normal_string_result), 
    864                                         (expected_invalid_str, invalid_string_result)]: 
    865                 settings.TEMPLATE_STRING_IF_INVALID = invalid_str 
    866                 try: 
    867                     output = loader.get_template(name).render(template.Context(vals[1])) 
    868                 except Exception, e: 
    869                     if e.__class__ != result: 
    870                         failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e)) 
    871                     continue 
    872                 if output != result: 
    873                     failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output)) 
    874  
    875             if 'LANGUAGE_CODE' in vals[1]: 
    876                 deactivate() 
    877  
    878             if template.invalid_var_format_string: 
    879                 expected_invalid_str = 'INVALID' 
    880                 template.invalid_var_format_string = False 
    881  
    882         loader.template_source_loaders = old_template_loaders 
    883         deactivate() 
    884         settings.TEMPLATE_DEBUG = old_td 
    885         settings.TEMPLATE_STRING_IF_INVALID = old_invalid 
    886  
    887         self.assertEqual(failures, [], '\n'.join(failures)) 
    888895 
    889896if __name__ == "__main__":