Django

Code

Ticket #2359: 01-core-changes.diff

File 01-core-changes.diff, 60.8 kB (added by mtredinnick, 2 years ago)

Update core changes (supercedes previous patch)

  • a/django/forms/__init__.py

    old new  
    11from django.core import validators 
    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 gettext, gettext_lazy, ngettext 
    67 
     
    175176 
    176177    def html_error_list(self): 
    177178        if self.errors(): 
    178             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]
     179            return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
    179180        else: 
    180             return '' 
     181            return mark_safe('') 
    181182 
    182183    def get_id(self): 
    183184        return self.formfield.get_id() 
     
    209210        return bool(len(self.errors())) 
    210211 
    211212    def html_combined_error_list(self): 
    212         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]
     213        return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
    213214 
    214215class InlineObjectCollection(object): 
    215216    "An object that acts like a sparse list of form field collections." 
     
    393394            maxlength = 'maxlength="%s" ' % self.maxlength 
    394395        if isinstance(data, unicode): 
    395396            data = data.encode(settings.DEFAULT_CHARSET) 
    396         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
     397        return mark_safe('<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    397398            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    398             self.field_name, self.length, escape(data), maxlength) 
     399            self.field_name, self.length, escape(data), maxlength)) 
    399400 
    400401    def html2python(data): 
    401402        return data 
     
    419420            data = '' 
    420421        if isinstance(data, unicode): 
    421422            data = data.encode(settings.DEFAULT_CHARSET) 
    422         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
     423        return mark_safe('<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
    423424            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    424             self.field_name, self.rows, self.cols, escape(data)) 
     425            self.field_name, self.rows, self.cols, escape(data))) 
    425426 
    426427class HiddenField(FormField): 
    427428    def __init__(self, field_name, is_required=False, validator_list=None): 
     
    430431        self.validator_list = validator_list[:] 
    431432 
    432433    def render(self, data): 
    433         return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
    434             (self.get_id(), self.field_name, escape(data)) 
     434        return mark_safe('<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
     435            (self.get_id(), self.field_name, escape(data))) 
    435436 
    436437class CheckboxField(FormField): 
    437438    def __init__(self, field_name, checked_by_default=False, validator_list=None): 
     
    445446        checked_html = '' 
    446447        if data or (data is '' and self.checked_by_default): 
    447448            checked_html = ' checked="checked"' 
    448         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
     449        return mark_safe('<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
    449450            (self.get_id(), self.__class__.__name__, 
    450             self.field_name, checked_html) 
     451            self.field_name, checked_html)) 
    451452 
    452453    def html2python(data): 
    453454        "Convert value from browser ('on' or '') to a Python boolean" 
     
    478479                selected_html = ' selected="selected"' 
    479480            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) 
    480481        output.append('  </select>') 
    481         return '\n'.join(output
     482        return mark_safe('\n'.join(output)
    482483 
    483484    def isValidChoice(self, data, form): 
    484485        str_data = str(data) 
     
    531532                output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')] 
    532533                output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 
    533534                output.append('</ul>') 
    534                 return ''.join(output
     535                return mark_safe(''.join(output)
    535536            def __iter__(self): 
    536537                for d in self.datalist: 
    537538                    yield d 
     
    546547            datalist.append({ 
    547548                'value': value, 
    548549                'name': display_name, 
    549                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
    550                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html)
    551                 'label': '<label for="%s">%s</label>' % \ 
     550                'field': mark_safe('<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
     551                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html))
     552                'label': mark_safe('<label for="%s">%s</label>' % \ 
    552553                    (self.get_id() + '_' + str(i), display_name), 
    553             }) 
     554            )}) 
    554555        return RadioFieldRenderer(datalist, self.ul_class) 
    555556 
    556557    def isValidChoice(self, data, form): 
     
    589590                selected_html = ' selected="selected"' 
    590591            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) 
    591592        output.append('  </select>') 
    592         return '\n'.join(output
     593        return mark_safe('\n'.join(output)
    593594 
    594595    def isValidChoice(self, field_data, all_data): 
    595596        # data is something like ['1', '2', '3'] 
     
    640641            field_name = '%s%s' % (self.field_name, value) 
    641642            output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \ 
    642643                (self.get_id() + value , self.__class__.__name__, field_name, checked_html, 
    643                 self.get_id() + value, choice)) 
     644                self.get_id() + value, escape(choice))) 
    644645        output.append('</ul>') 
    645         return '\n'.join(output
     646        return mark_safe('\n'.join(output)
    646647 
    647648#################### 
    648649# FILE UPLOADS     # 
     
    663664            raise validators.CriticalValidationError, gettext("The submitted file is empty.") 
    664665 
    665666    def render(self, data): 
    666         return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    667             (self.get_id(), self.__class__.__name__, self.field_name) 
     667        return mark_safe('<input type="file" id="%s" class="v%s" name="%s" />' % \ 
     668            (self.get_id(), self.__class__.__name__, self.field_name)) 
    668669 
    669670    def html2python(data): 
    670671        if data is None: 
  • a/django/template/__init__.py

    old new  
    6060from django.template.context import Context, RequestContext, ContextPopException 
    6161from django.utils.functional import curry 
    6262from django.utils.text import smart_split 
     63from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 
     64from django.utils.html import escape 
    6365 
    6466__all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 
    6567 
     
    558560                    arg_vals.append(arg) 
    559561                else: 
    560562                    arg_vals.append(resolve_variable(arg, context)) 
    561             obj = func(obj, *arg_vals) 
     563            if getattr(func, 'needs_autoescape', False): 
     564                new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 
     565            else: 
     566                new_obj = func(obj, *arg_vals) 
     567            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 
     568                obj = mark_safe(new_obj) 
     569            elif isinstance(obj, EscapeData): 
     570                obj = mark_for_escaping(new_obj) 
     571            else: 
     572                obj = new_obj 
     573                 
    562574        return obj 
    563575 
    564576    def args_check(name, func, provided): 
     
    744756 
    745757    def render(self, context): 
    746758        output = self.filter_expression.resolve(context) 
    747         return self.encode_output(output) 
     759        encoded_output = self.encode_output(output) 
     760        if (context.autoescape and not isinstance(encoded_output, SafeData)) or isinstance(encoded_output, EscapeData): 
     761            return escape(encoded_output) 
     762        else: 
     763            return encoded_output 
    748764 
    749765class DebugVariableNode(VariableNode): 
    750766    def render(self, context): 
     
    754770            if not hasattr(e, 'source'): 
    755771                e.source = self.source 
    756772            raise 
    757         return self.encode_output(output) 
     773        encoded_output = self.encode_output(output) 
     774        if context.autoescape and not isinstance(encoded_output, SafeData): 
     775            return escape(encoded_output) 
     776        else: 
     777            return encoded_output 
    758778 
    759779def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
    760780    "Returns a template.Node subclass." 
  • a/django/template/context.py

    old new  
    99 
    1010class Context(object): 
    1111    "A stack container for variable context" 
     12 
     13    autoescape = False 
     14 
    1215    def __init__(self, dict_=None): 
    1316        dict_ = dict_ or {} 
    1417        self.dicts = [dict_] 
     
    9598            processors = tuple(processors) 
    9699        for processor in get_standard_processors() + processors: 
    97100            self.update(processor(request)) 
     101 
  • a/django/template/defaultfilters.py

    old new  
    33from django.template import resolve_variable, Library 
    44from django.conf import settings 
    55from django.utils.translation import gettext 
     6from django.utils.safestring import mark_safe, SafeData 
    67import re 
    78import random as random_module 
    89 
     
    1617def addslashes(value): 
    1718    "Adds slashes - useful for passing strings to JavaScript, for example." 
    1819    return value.replace('"', '\\"').replace("'", "\\'") 
     20addslashes.is_safe = True 
    1921 
    2022def capfirst(value): 
    2123    "Capitalizes the first character of the value" 
    2224    value = str(value) 
    2325    return value and value[0].upper() + value[1:] 
     26capfirst.is_safe = True 
    2427 
    2528def fix_ampersands(value): 
    2629    "Replaces ampersands with ``&amp;`` entities" 
    2730    from django.utils.html import fix_ampersands 
    2831    return fix_ampersands(value) 
     32fix_ampersands.is_safe = True 
    2933 
    3034def floatformat(text): 
    3135    """ 
     
    4044    if m: 
    4145        return '%.1f' % f 
    4246    else: 
    43         return '%d' % int(f) 
     47        return mark_safe('%d' % int(f)) 
     48floatformat.is_safe = True 
    4449 
    45 def linenumbers(value): 
     50def linenumbers(value, autoescape = None): 
    4651    "Displays text with line numbers" 
    4752    from django.utils.html import escape 
    4853    lines = value.split('\n') 
    4954    # Find the maximum width of the line count, for use with zero padding string format command 
    5055    width = str(len(str(len(lines)))) 
    51     for i, line in enumerate(lines): 
    52         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    53     return '\n'.join(lines) 
     56    if not autoescape or isinstance(value, SafeData): 
     57        for i, line in enumerate(lines): 
     58            lines[i] = ("%0" + width  + "d. %s") % (i + 1, line) 
     59    else: 
     60        for i, line in enumerate(lines): 
     61            lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
     62    return mark_safe('\n'.join(lines)) 
     63linenumbers.is_safe = True 
     64linenumbers.needs_autoescape = True 
    5465 
    5566def lower(value): 
    5667    "Converts a string into all lowercase" 
    5768    return value.lower() 
     69lower.is_safe = True 
    5870 
    5971def make_list(value): 
    6072    """ 
     
    6274    digits. For a string, it's a list of characters. 
    6375    """ 
    6476    return list(str(value)) 
     77make_list.is_safe = False 
    6578 
    6679def slugify(value): 
    6780    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 
    6881    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    69     return re.sub('[-\s]+', '-', value) 
     82    return mark_safe(re.sub('[-\s]+', '-', value)) 
     83slugify.is_safe = True 
    7084 
    7185def stringformat(value, arg): 
    7286    """ 
     
    8195        return ("%" + arg) % value 
    8296    except (ValueError, TypeError): 
    8397        return "" 
     98stringformat.is_safe = True 
    8499 
    85100def title(value): 
    86101    "Converts a string into titlecase" 
    87102    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     103title.is_safe = False 
    88104 
    89105def truncatewords(value, arg): 
    90106    """ 
     
    100116    if not isinstance(value, basestring): 
    101117        value = str(value) 
    102118    return truncate_words(value, length) 
     119truncatewords.is_safe = True 
    103120 
    104121def upper(value): 
    105122    "Converts a string into all uppercase" 
    106123    return value.upper() 
     124upper.is_safe = False 
    107125 
    108126def urlencode(value): 
    109127    "Escapes a value for use in a URL" 
    110128    import urllib 
    111129    return urllib.quote(value) 
     130urlencode.is_safe = False 
    112131 
    113132def urlize(value): 
    114133    "Converts URLs in plain text into clickable links" 
    115134    from django.utils.html import urlize 
    116     return urlize(value, nofollow=True) 
     135    return mark_safe(urlize(value, nofollow=True)) 
     136urlize.is_safe = True 
    117137 
    118138def urlizetrunc(value, limit): 
    119139    """ 
     
    123143    Argument: Length to truncate URLs to. 
    124144    """ 
    125145    from django.utils.html import urlize 
    126     return urlize(value, trim_url_limit=int(limit), nofollow=True) 
     146    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 
     147urlize.is_safe = True 
    127148 
    128149def wordcount(value): 
    129150    "Returns the number of words" 
    130151    return len(value.split()) 
     152wordcount.is_safe = False 
    131153 
    132154def wordwrap(value, arg): 
    133155    """ 
     
    137159    """ 
    138160    from django.utils.text import wrap 
    139161    return wrap(str(value), int(arg)) 
     162wordwrap.is_safe = True 
    140163 
    141164def ljust(value, arg): 
    142165    """ 
     
    145168    Argument: field size 
    146169    """ 
    147170    return str(value).ljust(int(arg)) 
     171ljust.is_safe = True 
    148172 
    149173def rjust(value, arg): 
    150174    """ 
     
    153177    Argument: field size 
    154178    """ 
    155179    return str(value).rjust(int(arg)) 
     180rjust.is_safe = True 
    156181 
    157182def center(value, arg): 
    158183    "Centers the value in a field of a given width" 
    159184    return str(value).center(int(arg)) 
     185center.is_safe = True 
    160186 
    161187def cut(value, arg): 
    162188    "Removes all values of arg from the given string" 
    163189    return value.replace(arg, '') 
     190cut.is_safe = False 
    164191 
    165192################### 
    166193# HTML STRINGS    # 
    167194################### 
    168195 
    169196def escape(value): 
    170     "Escapes a string's HTML" 
     197    "Marks the value as a string that should not be auto-escaped." 
     198    from django.utils.safestring import mark_for_escaping 
     199    return mark_for_escaping(value) 
     200escape.is_safe = True 
     201 
     202def force_escape(value): 
     203    """Escapes a string's HTML. This returns a new string containing the escaped 
     204    characters (as opposed to "escape", which marks the content for later 
     205    possible escaping).""" 
    171206    from django.utils.html import escape 
    172     return escape(value) 
     207    return mark_safe(escape(value)) 
     208force_escape.is_safe = True 
    173209 
    174 def linebreaks(value): 
     210def linebreaks(value, autoescape = None): 
    175211    "Converts newlines into <p> and <br />s" 
    176212    from django.utils.html import linebreaks 
    177     return linebreaks(value) 
     213    autoescape = autoescape and not isinstance(value, SafeData) 
     214    return mark_safe(linebreaks(value, autoescape)) 
     215linebreaks.is_safe = True 
     216linebreaks.needs_autoescape = True 
    178217 
    179 def linebreaksbr(value): 
     218def linebreaksbr(value, autoescape = None): 
    180219    "Converts newlines into <br />s" 
    181     return value.replace('\n', '<br />') 
     220    if autoescape and not isinstance(value, SafeData): 
     221        from django.utils.html import escape 
     222        data = escape(value) 
     223    else: 
     224        data = value 
     225    return mark_safe(data.replace('\n', '<br />')) 
     226linebreaksbr.is_safe = True 
     227linebreaksbr.needs_autoescape = True 
     228 
     229def safe(value): 
     230    "Marks the value as a string that should not be auto-escaped." 
     231    from django.utils.safestring import mark_safe 
     232    return mark_safe(value) 
     233safe.is_safe = True 
    182234 
    183235def removetags(value, tags): 
    184236    "Removes a space separated list of [X]HTML tags from the output" 
     
    189241    value = starttag_re.sub('', value) 
    190242    value = endtag_re.sub('', value) 
    191243    return value 
     244removetags.is_safe = True 
    192245 
    193246def striptags(value): 
    194247    "Strips all [X]HTML tags" 
     
    196249    if not isinstance(value, basestring): 
    197250        value = str(value) 
    198251    return strip_tags(value) 
     252striptags.is_safe = True 
    199253 
    200254################### 
    201255# LISTS           # 
     
    209263    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 
    210264    decorated.sort() 
    211265    return [item[1] for item in decorated] 
     266dictsort.is_safe = False 
    212267 
    213268def dictsortreversed(value, arg): 
    214269    """ 
     
    219274    decorated.sort() 
    220275    decorated.reverse() 
    221276    return [item[1] for item in decorated] 
     277dictsortreversed.is_safe = False 
    222278 
    223279def first(value): 
    224280    "Returns the first item in a list" 
     
    226282        return value[0] 
    227283    except IndexError: 
    228284        return '' 
     285first.is_safe = True 
    229286 
    230287def join(value, arg): 
    231288    "Joins a list with a string, like Python's ``str.join(list)``" 
    232289    try: 
    233         return arg.join(map(str, value)) 
     290        data = arg.join(map(str, value)) 
    234291    except AttributeError: # fail silently but nicely 
    235292        return value 
     293    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 
     294    if safe_args: 
     295        return mark_safe(data) 
     296    else: 
     297        return data 
     298join.is_safe = True 
    236299 
    237300def length(value): 
    238301    "Returns the length of the value - useful for lists" 
    239302    return len(value) 
     303length.is_safe = False 
    240304 
    241305def length_is(value, arg): 
    242306    "Returns a boolean of whether the value's length is the argument" 
    243307    return len(value) == int(arg) 
     308length.is_safe = False 
    244309 
    245310def random(value): 
    246311    "Returns a random item from the list" 
    247312    return random_module.choice(value) 
     313length.is_safe = True 
    248314 
    249315def slice_(value, arg): 
    250316    """ 
     
    265331 
    266332    except (ValueError, TypeError): 
    267333        return value # Fail silently. 
     334slice_.is_safe = True 
    268335 
    269 def unordered_list(value): 
     336def unordered_list(value, autoescape = None): 
    270337    """ 
    271338    Recursively takes a self-nested list and returns an HTML unordered list -- 
    272339    WITHOUT opening and closing <ul> tags. 
     
    287354        </ul> 
    288355        </li> 
    289356    """ 
     357    if autoescape: 
     358        from django.utils.html import conditional_escape 
     359        escaper = conditional_escape 
     360    else: 
     361        escaper = lambda x: x 
     362 
    290363    def _helper(value, tabs): 
    291364        indent = '\t' * tabs 
    292365        if value[1]: 
    293             return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent, 
     366            return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent, 
    294367                '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 
    295368        else: 
    296             return '%s<li>%s</li>' % (indent, value[0]) 
    297     return _helper(value, 1) 
     369            return '%s<li>%s</li>' % (indent, escaper(value[0])) 
     370    return mark_safe(_helper(value, 1)) 
     371unordered_list.is_safe = True 
     372unordered_list.needs_autoescape = True 
    298373 
    299374################### 
    300375# INTEGERS        # 
     
    303378def add(value, arg): 
    304379    "Adds the arg to the value" 
    305380    return int(value) + int(arg) 
     381add.is_safe = False 
    306382 
    307383def get_digit(value, arg): 
    308384    """ 
     
    322398        return int(str(value)[-arg]) 
    323399    except IndexError: 
    324400        return 0 
     401get_digit.is_safe = False 
    325402 
    326403################### 
    327404# DATES           # 
     
    335412    if arg is None: 
    336413        arg = settings.DATE_FORMAT 
    337414    return format(value, arg) 
     415date.is_safe = False 
    338416 
    339417def time(value, arg=None): 
    340418    "Formats a time according to the given format" 
     
    344422    if arg is None: 
    345423        arg = settings.TIME_FORMAT 
    346424    return time_format(value, arg) 
     425time.is_safe = False 
    347426 
    348427def timesince(value, arg=None): 
    349428    'Formats a date as the time since that date (i.e. "4 days, 6 hours")' 
     
    353432    if arg: 
    354433        return timesince(arg, value) 
    355434    return timesince(value) 
     435timesince.is_safe = False 
    356436 
    357437def timeuntil(value, arg=None): 
    358438    'Formats a date as the time until that date (i.e. "4 days, 6 hours")' 
     
    363443    if arg: 
    364444        return timesince(arg, value) 
    365445    return timesince(datetime.now(), value) 
     446timeuntil.is_safe = False 
    366447 
    367448################### 
    368449# LOGIC           # 
     
    371452def default(value, arg): 
    372453    "If value is unavailable, use given default" 
    373454    return value or arg 
     455default.is_safe = False 
    374456 
    375457def default_if_none(value, arg): 
    376458    "If value is None, use given default" 
    377459    if value is None: 
    378460        return arg 
    379461    return value 
     462default_if_none.is_safe = False 
    380463 
    381464def divisibleby(value, arg): 
    382465    "Returns true if the value is devisible by the argument" 
    383466    return int(value) % int(arg) == 0 
     467divisibleby.is_safe = False 
    384468 
    385469def yesno(value, arg=None): 
    386470    """ 
     
    411495    if value: 
    412496        return yes 
    413497    return no 
     498yesno.is_safe = False 
    414499 
    415500################### 
    416501# MISC            # 
     
    429514    if bytes < 1024 * 1024 * 1024: 
    430515        return "%.1f MB" % (bytes / (1024 * 1024)) 
    431516    return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 
     517filesizeformat.is_safe = True 
    432518 
    433519def pluralize(value, arg='s'): 
    434520    """ 
     
    456542        except TypeError: # len() of unsized object 
    457543            pass 
    458544    return singular_suffix 
     545pluralize.is_safe = False 
    459546 
    460547def phone2numeric(value): 
    461548    "Takes a phone number and converts it in to its numerical equivalent" 
    462549    from django.utils.text import phone2numeric 
    463550    return phone2numeric(value) 
     551phone2numeric.is_safe = True 
    464552 
    465553def pprint(value): 
    466554    "A wrapper around pprint.pprint -- for debugging, really" 
     
    469557        return pformat(value) 
    470558    except Exception, e: 
    471559        return "Error in formatting:%s" % e 
     560pprint.is_safe = True 
    472561 
    473562# Syntax: register.filter(name of filter, callback) 
    474563register.filter(add) 
     
    487576register.filter(first) 
    488577register.filter(fix_ampersands) 
    489578register.filter(floatformat) 
     579register.filter(force_escape) 
    490580register.filter(get_digit) 
    491581register.filter(join) 
    492582register.filter(length) 
     
    503593register.filter(removetags) 
    504594register.filter(random) 
    505595register.filter(rjust) 
     596register.filter(safe) 
    506597register.filter('slice', slice_) 
    507598register.filter(slugify) 
    508599register.filter(stringformat) 
  • a/django/template/defaulttags.py

    old new  
    44from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END 
    55from django.template import get_library, Library, InvalidTemplateLibrary 
    66from django.conf import settings 
     7from django.utils.safestring import mark_safe 
    78import sys 
    89 
    910register = Library() 
    1011 
     12class AutoEscapeControlNode(Node): 
     13    """Implements the actions of both the autoescape and noautescape tags.""" 
     14    def __init__(self, setting, nodelist): 
     15        self.setting, self.nodelist = setting, nodelist 
     16 
     17    def render(self, context): 
     18        old_setting = context.autoescape 
     19        context.autoescape = self.setting 
     20        output = self.nodelist.render(context) 
     21        context.autoescape = old_setting 
     22        if self.setting: 
     23            return mark_safe(output) 
     24        else: 
     25            return output 
     26 
    1127class CommentNode(Node): 
    1228    def render(self, context): 
    1329        return '' 
     
    3753    def render(self, context): 
    3854        output = self.nodelist.render(context) 
    3955        # apply filters 
    40         return self.filter_expr.resolve(Context({'var': output})) 
     56        ctxt = Context({'var': output}) 
     57        ctxt.autoescape = context.autoescape 
     58        return self.filter_expr.resolve(ctxt) 
    4159 
    4260class FirstOfNode(Node): 
    4361    def __init__(self, vars): 
     
    218236            return '' 
    219237        output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 
    220238        for obj in obj_list: 
    221             grouper = self.expression.resolve(Context({'var': obj})) 
     239            ctxt = Context({'var': obj}) 
     240            ctxt.autoescape = context.autoescape 
     241            grouper = self.expression.resolve(ctxt) 
    222242            # TODO: Is this a sensible way to determine equality? 
    223243            if output and repr(output[-1]['grouper']) == repr(grouper): 
    224244                output[-1]['list'].append(obj) 
     
    318338        return str(int(round(ratio))) 
    319339 
    320340#@register.tag 
     341def autoescape(parser, token): 
     342    """ 
     343    Force autoescape behaviour for this block. 
     344    """ 
     345    nodelist = parser.parse(('endautoescape',)) 
     346    parser.delete_first_token() 
     347    return AutoEscapeControlNode(True, nodelist) 
     348autoescape = register.tag(autoescape) 
     349 
     350#@register.tag 
    321351def comment(parser, token): 
    322352    """ 
    323353    Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` 
     
    411441 
    412442    Sample usage:: 
    413443 
    414         {% filter escape|lower %} 
     444        {% filter force_escape|lower %} 
    415445            This text will be HTML-escaped, and will appear in lowercase. 
    416446        {% endfilter %} 
    417447    """ 
    418448    _, rest = token.contents.split(None, 1) 
    419449    filter_expr = parser.compile_filter("var|%s" % (rest)) 
     450    for func, unused in filter_expr.filters: 
     451        if func.__name__ in ('escape', 'safe'): 
     452            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__) 
    420453    nodelist = parser.parse(('endfilter',)) 
    421454    parser.delete_first_token() 
    422455    return FilterNode(filter_expr, nodelist) 
     
    646679ifchanged = register.tag(ifchanged) 
    647680 
    648681#@register.tag 
     682def noautoescape(parser, token): 
     683    """ 
     684    Force autoescape behaviour to be disabled for this block. 
     685    """ 
     686    nodelist = parser.parse(('endnoautoescape',)) 
     687    parser.delete_first_token() 
     688    return AutoEscapeControlNode(False, nodelist) 
     689autoescape = register.tag(noautoescape) 
     690 
     691#@register.tag 
    649692def ssi(parser, token): 
    650693    """ 
    651694    Output the contents of a given file into the page. 
  • a/django/utils/html.py

    old new  
    11"HTML utilities suitable for global use." 
    22 
    33import re, string 
     4from django.utils.safestring import SafeData 
    45 
    56# Configuration for urlize() function 
    67LEADING_PUNCTUATION  = ['(', '<', '&lt;'] 
     
    2728        html = str(html) 
    2829    return html.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;') 
    2930 
    30 def linebreaks(value): 
     31def conditional_escape(html): 
     32    "Similar to escape(), except that it does not operate on pre-escaped strings" 
     33    if isinstance(html, SafeData): 
     34        return html 
     35    else: 
     36        return escape(html) 
     37 
     38def linebreaks(value, autoescape = False): 
    3139    "Converts newlines into <p> and <br />s" 
    3240    value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines 
    3341    paras = re.split('\n{2,}', value) 
    34     paras = ['<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 
     42    if autoescape: 
     43        paras = ['<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras] 
     44    else: 
     45        paras = ['<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 
    3546    return '\n\n'.join(paras) 
    3647 
    3748def strip_tags(value): 
  • /dev/null

    old new  
     1""" 
     2Functions for working with "safe strings": strings that can be displayed safely 
     3without further escaping in HTML. Here, a "safe string" means that the producer 
     4of the string has already turned characters that should not be interpreted by 
     5the HTML engine (e.g. '<') into the appropriate entities. 
     6""" 
     7from django.utils.functional import curry 
     8 
     9class EscapeData(object): 
     10    pass 
     11 
     12class EscapeString(str, EscapeData): 
     13    """ 
     14    A string that should be HTML-escaped when output. 
     15    """ 
     16    pass 
     17 
     18class EscapeUnicode(unicode, EscapeData): 
     19    """ 
     20    A unicode object that should be HTML-escaped when output. 
     21    """ 
     22    pass 
     23 
     24class SafeData(object): 
     25    pass 
     26 
     27class SafeString(str, SafeData): 
     28    """ 
     29    A string subclass that has been specifically marked as "safe" for HTML 
     30    output purposes. 
     31    """ 
     32    def __add__(self, rhs): 
     33        """ 
     34        Concatenating a safe string with another safe string or safe unicode 
     35        object is safe. Otherwise, the result is no longer safe. 
     36        """ 
     37        if isinstance(rhs, SafeUnicode): 
     38            return SafeUnicode(self + rhs) 
     39        elif isinstance(rhs, SafeString): 
     40            return SafeString(self, rhs) 
     41        else: 
     42            return super(SafeString, self).__add__(rhs) 
     43 
     44class SafeUnicode(unicode, SafeData): 
     45    """ 
     46    A unicode subclass that has been specifically marked as "safe" for HTML 
     47    output purposes. 
     48    """ 
     49    def __add__(self, rhs): 
     50        """ 
     51        Concatenating a safe unicode object with another safe string or safe 
     52        unicode object is safe. Otherwise, the result is no longer safe. 
     53        """ 
     54        if isinstance(rhs, SafeData): 
     55            return SafeUnicode(self + rhs) 
     56        else: 
     57            return super(SafeUnicode, self).__add__(rhs) 
     58 
     59    def _proxy_method(self, *args, **kwargs): 
     60        """ 
     61        Wrap a call to a normal unicode method up so that we return safe 
     62        results. The method that is being wrapped is passed in the 'method' 
     63        argument. 
     64        """ 
     65        method = kwargs.pop('method') 
     66        data = method(self, *args, **kwargs) 
     67        if isinstance(data, str): 
     68            return SafeString(data) 
     69        else: 
     70            return SafeUnicode(data) 
     71 
     72    encode = curry(_proxy_method, method = unicode.encode) 
     73    decode = curry(_proxy_method, method = unicode.decode) 
     74 
     75 
     76def mark_safe(s): 
     77    """ 
     78    Explicitly mark a string as safe for (HTML) output purposes. The returned 
     79    object can be used everywhere a string or unicode object is appropriate. 
     80 
     81    Can safely be called multiple times on a single string. 
     82    """ 
     83    if isinstance(s, SafeData): 
     84        return s 
     85    if isinstance(s, str): 
     86        return SafeString(s) 
     87    if isinstance(s, unicode): 
     88        return SafeUnicode(s) 
     89    return SafeString(str(s)) 
     90 
     91def mark_for_escaping(s): 
     92    """ 
     93    Explicitly mark a string as requiring HTML escaping upon output. Has no 
     94    effect on SafeData subclasses. 
     95 
     96    Can be safely called multiple times on a single string (the effect is only 
     97    applied once). 
     98    """ 
     99    if isinstance(s, SafeData) or isinstance(s, EscapeData): 
     100        return s 
     101    if isinstance(s, str): 
     102        return EscapeString(s) 
     103    if isinstance(s, unicode): 
     104        return EscapeUnicode(s) 
     105    return EscapeString(str(s)) 
     106 
  • a/docs/templates.txt

    old new  
    243243two similarly-named ``{% block %}`` tags in a template, that template's parent 
    244244wouldn't know which one of the blocks' content to use. 
    245245 
     246Automatic HTML escaping 
     247======================= 
     248 
     249A very real problem when creating HTML (and other) output using templates and 
     250variable substitution is the possibility of accidently inserting some variable 
     251value that affects the resulting HTML. For example, a template fragment like 
     252 
     253:: 
     254 
     255    Hello, {{ name }}. 
     256 
     257seems like a harmless way to display the user's name. However, if you are 
     258displaying data that the user entered directly and they entered their name as 
     259 
     260:: 
     261 
     262    <script>alert('hello')</script> 
     263 
     264this would always display a Javascript alert box whenever the page was loaded. 
     265Similarly, if you were displaying some data generated by another process and 
     266it contained a '<' symbol, you couldn't just dump this straight into your 
     267HTML, because it would be treated as the start of an element.  The effects of 
     268these sorts of problems can vary from merely annoying to allowing exploits via 
     269`Cross Site Scripting`_ (XSS) attacks. 
     270 
     271.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting 
     272 
     273In order to provide some protection against these problems, Django provides an 
     274auto-escaping template tag. Inside this tag, any data that comes from template 
     275variables is examined to see if it contains one of the five HTML characters 
     276(<, >, ', " and &) that often need escaping and those characters are converted 
     277to their respective HTML entities. 
     278 
     279Because some variables will contain data that is *intended* to be rendered 
     280as HTML, template tag and filter writers can mark their output strings as 
     281requiring no further escaping. For example, the ``unordered_list`` filter is 
     282designed to return raw HTML and we want the template processor to simply 
     283display the results as returned, without applying any escaping. That is taken 
     284care of by the filter. The template author need do nothing special in that 
     285case. 
     286 
     287By default, auto-escaping is not in effect. To enable it inside your template, 
     288wrap the affected content in the ``autoescape`` tag, like so:: 
     289 
     290    {% autoescape %} 
     291        Hello {{ name }} 
     292    {% endautoescape %} 
     293 
     294Since the auto-escaping tag passes its effect onto templates that extend the 
     295current one as well as templates included via the ``include`` tag (just like 
     296all block tags), if you wrap your main HTML content in an ``autoescape`` tag, 
     297you will have automatic escaping applied to all of your content. 
     298 
     299At times, you might want to disable auto-escaping when it would otherwise be 
     300in effect. You can do this with the ``noautoescape`` tag. For example:: 
     301 
     302    {% autoescape %} 
     303        Hello {{ name }} 
     304 
     305        {% noautoescape %} 
     306            This will not be auto-escaped: {{ data }}. 
     307 
     308            Nor this: {{ other_data }} 
     309        {% endnoautoescape %} 
     310    {% endautoescape %} 
     311 
     312For individual variables, the ``safe`` filter can also be used. 
     313 
     314Generally, you will not need to worry about auto-escaping very much. Enable it 
     315in your base template once you are entering the main HTML region and then 
     316write your templates normally. The view developers and custom filter authors 
     317need to think about when their data should not be escaped and mark it 
     318appropriately.  They are in a better position to know when that should happen 
     319than the template author, so it is their responsibility. By default, when 
     320auto-escaping is enabled, all output is escaped unless the template processor 
     321is explicitly told otherwise. 
     322 
    246323Using the built-in reference 
    247324============================ 
    248325 
     
    318395Built-in tag reference 
    319396---------------------- 
    320397 
     398autoescape 
     399~~~~~~~~~~ 
     400 </