Django

Code

Ticket #2359: 01-core-changes.2.diff

File 01-core-changes.2.diff, 69.7 kB (added by Michael Radziej <mir@noris.de>, 1 year ago)

updated patch

  • a/django/oldforms/__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, ngettext 
    67 
     
    181182 
    182183    def html_error_list(self): 
    183184        if self.errors(): 
    184             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]
     185            return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
    185186        else: 
    186             return '' 
     187            return mark_safe('') 
    187188 
    188189    def get_id(self): 
    189190        return self.formfield.get_id() 
     
    215216        return bool(len(self.errors())) 
    216217 
    217218    def html_combined_error_list(self): 
    218         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]
     219        return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
    219220 
    220221class InlineObjectCollection(object): 
    221222    "An object that acts like a sparse list of form field collections." 
     
    399400            maxlength = 'maxlength="%s" ' % self.maxlength 
    400401        if isinstance(data, unicode): 
    401402            data = data.encode(settings.DEFAULT_CHARSET) 
    402         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
     403        return mark_safe('<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    403404            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    404             self.field_name, self.length, escape(data), maxlength) 
     405            self.field_name, self.length, escape(data), maxlength)) 
    405406 
    406407    def html2python(data): 
    407408        return data 
     
    425426            data = '' 
    426427        if isinstance(data, unicode): 
    427428            data = data.encode(settings.DEFAULT_CHARSET) 
    428         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
     429        return mark_safe('<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
    429430            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    430             self.field_name, self.rows, self.cols, escape(data)) 
     431            self.field_name, self.rows, self.cols, escape(data))) 
    431432 
    432433class HiddenField(FormField): 
    433434    def __init__(self, field_name, is_required=False, validator_list=None): 
     
    436437        self.validator_list = validator_list[:] 
    437438 
    438439    def render(self, data): 
    439         return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
    440             (self.get_id(), self.field_name, escape(data)) 
     440        return mark_safe('<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
     441            (self.get_id(), self.field_name, escape(data))) 
    441442 
    442443class CheckboxField(FormField): 
    443444    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False): 
     
    451452        checked_html = '' 
    452453        if data or (data is '' and self.checked_by_default): 
    453454            checked_html = ' checked="checked"' 
    454         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
     455        return mark_safe('<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
    455456            (self.get_id(), self.__class__.__name__, 
    456             self.field_name, checked_html) 
     457            self.field_name, checked_html)) 
    457458 
    458459    def html2python(data): 
    459460        "Convert value from browser ('on' or '') to a Python boolean" 
     
    484485                selected_html = ' selected="selected"' 
    485486            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) 
    486487        output.append('  </select>') 
    487         return '\n'.join(output
     488        return mark_safe('\n'.join(output)
    488489 
    489490    def isValidChoice(self, data, form): 
    490491        str_data = str(data) 
     
    537538                output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')] 
    538539                output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 
    539540                output.append('</ul>') 
    540                 return ''.join(output
     541                return mark_safe(''.join(output)
    541542            def __iter__(self): 
    542543                for d in self.datalist: 
    543544                    yield d 
     
    552553            datalist.append({ 
    553554                'value': value, 
    554555                'name': display_name, 
    555                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
    556                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html)
    557                 'label': '<label for="%s">%s</label>' % \ 
     556                'field': mark_safe('<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
     557                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html))
     558                'label': mark_safe('<label for="%s">%s</label>' % \ 
    558559                    (self.get_id() + '_' + str(i), display_name), 
    559             }) 
     560            )}) 
    560561        return RadioFieldRenderer(datalist, self.ul_class) 
    561562 
    562563    def isValidChoice(self, data, form): 
     
    595596                selected_html = ' selected="selected"' 
    596597            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) 
    597598        output.append('  </select>') 
    598         return '\n'.join(output
     599        return mark_safe('\n'.join(output)
    599600 
    600601    def isValidChoice(self, field_data, all_data): 
    601602        # data is something like ['1', '2', '3'] 
     
    648649                (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html, 
    649650                self.get_id() + escape(value), choice)) 
    650651        output.append('</ul>') 
    651         return '\n'.join(output
     652        return mark_safe('\n'.join(output)
    652653 
    653654#################### 
    654655# FILE UPLOADS     # 
     
    669670            raise validators.CriticalValidationError, gettext("The submitted file is empty.") 
    670671 
    671672    def render(self, data): 
    672         return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    673             (self.get_id(), self.__class__.__name__, self.field_name) 
     673        return mark_safe('<input type="file" id="%s" class="v%s" name="%s" />' % \ 
     674            (self.get_id(), self.__class__.__name__, self.field_name)) 
    674675 
    675676    def html2python(data): 
    676677        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 
     
    568570                    arg_vals.append(arg) 
    569571                else: 
    570572                    arg_vals.append(resolve_variable(arg, context)) 
    571             obj = func(obj, *arg_vals) 
     573            if getattr(func, 'needs_autoescape', False): 
     574                new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 
     575            else: 
     576                new_obj = func(obj, *arg_vals) 
     577            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 
     578                obj = mark_safe(new_obj) 
     579            elif isinstance(obj, EscapeData): 
     580                obj = mark_for_escaping(new_obj) 
     581            else: 
     582                obj = new_obj 
     583                 
    572584        return obj 
    573585 
    574586    def args_check(name, func, provided): 
     
    754766 
    755767    def render(self, context): 
    756768        output = self.filter_expression.resolve(context) 
    757         return self.encode_output(output) 
     769        encoded_output = self.encode_output(output) 
     770        if (context.autoescape and not isinstance(encoded_output, SafeData)) or isinstance(encoded_output, EscapeData): 
     771            return escape(encoded_output) 
     772        else: 
     773            return encoded_output 
    758774 
    759775class DebugVariableNode(VariableNode): 
    760776    def render(self, context): 
     
    764780            if not hasattr(e, 'source'): 
    765781                e.source = self.source 
    766782            raise 
    767         return self.encode_output(output) 
     783        encoded_output = self.encode_output(output) 
     784        if context.autoescape and not isinstance(encoded_output, SafeData): 
     785            return escape(encoded_output) 
     786        else: 
     787            return encoded_output 
    768788 
    769789def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
    770790    "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('"', '\\"').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, arg=-1): 
    3135    """ 
     
    5862        return '%d' % int(f) 
    5963    else: 
    6064        formatstr = '%%.%df' % abs(d) 
    61         return formatstr % f 
     65        return mark_safe(formatstr % f) 
     66floatformat.is_safe = True 
    6267 
    63 def linenumbers(value): 
     68def linenumbers(value, autoescape = None): 
    6469    "Displays text with line numbers" 
    6570    from django.utils.html import escape 
    6671    lines = value.split('\n') 
    6772    # Find the maximum width of the line count, for use with zero padding string format command 
    6873    width = str(len(str(len(lines)))) 
    69     for i, line in enumerate(lines): 
    70         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    71     return '\n'.join(lines) 
     74    if not autoescape or isinstance(value, SafeData): 
     75        for i, line in enumerate(lines): 
     76            lines[i] = ("%0" + width  + "d. %s") % (i + 1, line) 
     77    else: 
     78        for i, line in enumerate(lines): 
     79            lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
     80    return mark_safe('\n'.join(lines)) 
     81linenumbers.is_safe = True 
     82linenumbers.needs_autoescape = True 
    7283 
    7384def lower(value): 
    7485    "Converts a string into all lowercase" 
    7586    return value.lower() 
     87lower.is_safe = True 
    7688 
    7789def make_list(value): 
    7890    """ 
     
    8092    digits. For a string, it's a list of characters. 
    8193    """ 
    8294    return list(str(value)) 
     95make_list.is_safe = False 
    8396 
    8497def slugify(value): 
    8598    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 
    8699    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    87     return re.sub('[-\s]+', '-', value) 
     100    return mark_safe(re.sub('[-\s]+', '-', value)) 
     101slugify.is_safe = True 
    88102 
    89103def stringformat(value, arg): 
    90104    """ 
     
    99113        return ("%" + arg) % value 
    100114    except (ValueError, TypeError): 
    101115        return "" 
     116stringformat.is_safe = True 
    102117 
    103118def title(value): 
    104119    "Converts a string into titlecase" 
    105120    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     121title.is_safe = False 
    106122 
    107123def truncatewords(value, arg): 
    108124    """ 
     
    118134    if not isinstance(value, basestring): 
    119135        value = str(value) 
    120136    return truncate_words(value, length) 
     137truncatewords.is_safe = True 
    121138 
    122139def upper(value): 
    123140    "Converts a string into all uppercase" 
    124141    return value.upper() 
     142upper.is_safe = False 
    125143 
    126144def urlencode(value): 
    127145    "Escapes a value for use in a URL" 
    128146    import urllib 
    129147    return urllib.quote(value) 
     148urlencode.is_safe = False 
    130149 
    131150def urlize(value): 
    132151    "Converts URLs in plain text into clickable links" 
    133152    from django.utils.html import urlize 
    134     return urlize(value, nofollow=True) 
     153    return mark_safe(urlize(value, nofollow=True)) 
     154urlize.is_safe = True 
    135155 
    136156def urlizetrunc(value, limit): 
    137157    """ 
     
    141161    Argument: Length to truncate URLs to. 
    142162    """ 
    143163    from django.utils.html import urlize 
    144     return urlize(value, trim_url_limit=int(limit), nofollow=True) 
     164    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 
     165urlize.is_safe = True 
    145166 
    146167def wordcount(value): 
    147168    "Returns the number of words" 
    148169    return len(value.split()) 
     170wordcount.is_safe = False 
    149171 
    150172def wordwrap(value, arg): 
    151173    """ 
     
    155177    """ 
    156178    from django.utils.text import wrap 
    157179    return wrap(str(value), int(arg)) 
     180wordwrap.is_safe = True 
    158181 
    159182def ljust(value, arg): 
    160183    """ 
     
    163186    Argument: field size 
    164187    """ 
    165188    return str(value).ljust(int(arg)) 
     189ljust.is_safe = True 
    166190 
    167191def rjust(value, arg): 
    168192    """ 
     
    171195    Argument: field size 
    172196    """ 
    173197    return str(value).rjust(int(arg)) 
     198rjust.is_safe = True 
    174199 
    175200def center(value, arg): 
    176201    "Centers the value in a field of a given width" 
    177202    return str(value).center(int(arg)) 
     203center.is_safe = True 
    178204 
    179205def cut(value, arg): 
    180206    "Removes all values of arg from the given string" 
    181207    return value.replace(arg, '') 
     208cut.is_safe = False 
    182209 
    183210################### 
    184211# HTML STRINGS    # 
    185212################### 
    186213 
    187214def escape(value): 
    188     "Escapes a string's HTML" 
     215    "Marks the value as a string that should not be auto-escaped." 
     216    from django.utils.safestring import mark_for_escaping 
     217    return mark_for_escaping(value) 
     218escape.is_safe = True 
     219 
     220def force_escape(value): 
     221    """Escapes a string's HTML. This returns a new string containing the escaped 
     222    characters (as opposed to "escape", which marks the content for later 
     223    possible escaping).""" 
    189224    from django.utils.html import escape 
    190     return escape(value) 
     225    return mark_safe(escape(value)) 
     226force_escape.is_safe = True 
    191227 
    192 def linebreaks(value): 
     228def linebreaks(value, autoescape = None): 
    193229    "Converts newlines into <p> and <br />s" 
    194230    from django.utils.html import linebreaks 
    195     return linebreaks(value) 
     231    autoescape = autoescape and not isinstance(value, SafeData) 
     232    return mark_safe(linebreaks(value, autoescape)) 
     233linebreaks.is_safe = True 
     234linebreaks.needs_autoescape = True 
    196235 
    197 def linebreaksbr(value): 
     236def linebreaksbr(value, autoescape = None): 
    198237    "Converts newlines into <br />s" 
    199     return value.replace('\n', '<br />') 
     238    if autoescape and not isinstance(value, SafeData): 
     239        from django.utils.html import escape 
     240        data = escape(value) 
     241    else: 
     242        data = value 
     243    return mark_safe(data.replace('\n', '<br />')) 
     244linebreaksbr.is_safe = True 
     245linebreaksbr.needs_autoescape = True 
     246 
     247def safe(value): 
     248    "Marks the value as a string that should not be auto-escaped." 
     249    from django.utils.safestring import mark_safe 
     250    return mark_safe(value) 
     251safe.is_safe = True 
    200252 
    201253def removetags(value, tags): 
    202254    "Removes a space separated list of [X]HTML tags from the output" 
     
    207259    value = starttag_re.sub('', value) 
    208260    value = endtag_re.sub('', value) 
    209261    return value 
     262removetags.is_safe = True 
    210263 
    211264def striptags(value): 
    212265    "Strips all [X]HTML tags" 
     
    214267    if not isinstance(value, basestring): 
    215268        value = str(value) 
    216269    return strip_tags(value) 
     270striptags.is_safe = True 
    217271 
    218272################### 
    219273# LISTS           # 
     
    227281    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 
    228282    decorated.sort() 
    229283    return [item[1] for item in decorated] 
     284dictsort.is_safe = False 
    230285 
    231286def dictsortreversed(value, arg): 
    232287    """ 
     
    237292    decorated.sort() 
    238293    decorated.reverse() 
    239294    return [item[1] for item in decorated] 
     295dictsortreversed.is_safe = False 
    240296 
    241297def first(value): 
    242298    "Returns the first item in a list" 
     
    244300        return value[0] 
    245301    except IndexError: 
    246302        return '' 
     303first.is_safe = True 
    247304 
    248305def join(value, arg): 
    249306    "Joins a list with a string, like Python's ``str.join(list)``" 
    250307    try: 
    251         return arg.join(map(str, value)) 
     308        data = arg.join(map(str, value)) 
    252309    except AttributeError: # fail silently but nicely 
    253310        return value 
     311    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 
     312    if safe_args: 
     313        return mark_safe(data) 
     314    else: 
     315        return data 
     316join.is_safe = True 
    254317 
    255318def length(value): 
    256319    "Returns the length of the value - useful for lists" 
    257320    return len(value) 
     321length.is_safe = False 
    258322 
    259323def length_is(value, arg): 
    260324    "Returns a boolean of whether the value's length is the argument" 
    261325    return len(value) == int(arg) 
     326length.is_safe = False 
    262327 
    263328def random(value): 
    264329    "Returns a random item from the list" 
    265330    return random_module.choice(value) 
     331length.is_safe = True 
    266332 
    267333def slice_(value, arg): 
    268334    """ 
     
    283349 
    284350    except (ValueError, TypeError): 
    285351        return value # Fail silently. 
     352slice_.is_safe = True 
    286353 
    287 def unordered_list(value): 
     354def unordered_list(value, autoescape = None): 
    288355    """ 
    289356    Recursively takes a self-nested list and returns an HTML unordered list -- 
    290357    WITHOUT opening and closing <ul> tags. 
     
    305372        </ul> 
    306373        </li> 
    307374    """ 
     375    if autoescape: 
     376        from django.utils.html import conditional_escape 
     377        escaper = conditional_escape 
     378    else: 
     379        escaper = lambda x: x 
     380 
    308381    def _helper(value, tabs): 
    309382        indent = '\t' * tabs 
    310383        if value[1]: 
    311             return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent, 
     384            return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent, 
    312385                '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 
    313386        else: 
    314             return '%s<li>%s</li>' % (indent, value[0]) 
    315     return _helper(value, 1) 
     387            return '%s<li>%s</li>' % (indent, escaper(value[0])) 
     388    return mark_safe(_helper(value, 1)) 
     389unordered_list.is_safe = True 
     390unordered_list.needs_autoescape = True 
    316391 
    317392################### 
    318393# INTEGERS        # 
     
    321396def add(value, arg): 
    322397    "Adds the arg to the value" 
    323398    return int(value) + int(arg) 
     399add.is_safe = False 
    324400 
    325401def get_digit(value, arg): 
    326402    """ 
     
    340416        return int(str(value)[-arg]) 
    341417    except IndexError: 
    342418        return 0 
     419get_digit.is_safe = False 
    343420 
    344421################### 
    345422# DATES           # 
     
    353430    if arg is None: 
    354431        arg = settings.DATE_FORMAT 
    355432    return format(value, arg) 
     433date.is_safe = False 
    356434 
    357435def time(value, arg=None): 
    358436    "Formats a time according to the given format" 
     
    362440    if arg is None: 
    363441        arg = settings.TIME_FORMAT 
    364442    return time_format(value, arg) 
     443time.is_safe = False 
    365444 
    366445def timesince(value, arg=None): 
    367446    'Formats a date as the time since that date (i.e. "4 days, 6 hours")' 
     
    371450    if arg: 
    372451        return timesince(arg, value) 
    373452    return timesince(value) 
     453timesince.is_safe = False 
    374454 
    375455def timeuntil(value, arg=None): 
    376456    'Formats a date as the time until that date (i.e. "4 days, 6 hours")' 
     
    381461    if arg: 
    382462        return timesince(arg, value) 
    383463    return timesince(datetime.now(), value) 
     464timeuntil.is_safe = False 
    384465 
    385466################### 
    386467# LOGIC           # 
     
    389470def default(value, arg): 
    390471    "If value is unavailable, use given default" 
    391472    return value or arg 
     473default.is_safe = False 
    392474 
    393475def default_if_none(value, arg): 
    394476    "If value is None, use given default" 
    395477    if value is None: 
    396478        return arg 
    397479    return value 
     480default_if_none.is_safe = False 
    398481 
    399482def divisibleby(value, arg): 
    400483    "Returns true if the value is devisible by the argument" 
    401484    return int(value) % int(arg) == 0 
     485divisibleby.is_safe = False 
    402486 
    403487def yesno(value, arg=None): 
    404488    """ 
     
    429513    if value: 
    430514        return yes 
    431515    return no 
     516yesno.is_safe = False 
    432517 
    433518################### 
    434519# MISC            # 
     
    451536    if bytes < 1024 * 1024 * 1024: 
    452537        return "%.1f MB" % (bytes / (1024 * 1024)) 
    453538    return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 
     539filesizeformat.is_safe = True 
    454540 
    455541def pluralize(value, arg='s'): 
    456542    """ 
     
    478564        except TypeError: # len() of unsized object 
    479565            pass 
    480566    return singular_suffix 
     567pluralize.is_safe = False 
    481568 
    482569def phone2numeric(value): 
    483570    "Takes a phone number and converts it in to its numerical equivalent" 
    484571    from django.utils.text import phone2numeric 
    485572    return phone2numeric(value) 
     573phone2numeric.is_safe = True 
    486574 
    487575def pprint(value): 
    488576    "A wrapper around pprint.pprint -- for debugging, really" 
     
    491579        return pformat(value) 
    492580    except Exception, e: 
    493581        return "Error in formatting:%s" % e 
     582pprint.is_safe = True 
    494583 
    495584# Syntax: register.filter(name of filter, callback) 
    496585register.filter(add) 
     
    509598register.filter(first) 
    510599register.filter(fix_ampersands) 
    511600register.filter(floatformat) 
     601register.filter(force_escape) 
    512602register.filter(get_digit) 
    513603register.filter(join) 
    514604register.filter(length) 
     
    525615register.filter(removetags) 
    526616register.filter(random) 
    527617register.filter(rjust) 
     618register.filter(safe) 
    528619register.filter('slice', slice_) 
    529620register.filter(slugify) 
    530621register.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, COMMENT_TAG_START, COMMENT_TAG_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 '' 
     
    4157    def render(self, context): 
    4258        output = self.nodelist.render(context) 
    4359        # apply filters 
    44         return self.filter_expr.resolve(Context({'var': output})) 
     60        ctxt = Context({'var': output}) 
     61        ctxt.autoescape = context.autoescape 
     62        return self.filter_expr.resolve(ctxt) 
    4563 
    4664class FirstOfNode(Node): 
    4765    def __init__(self, vars): 
     
    234252            return '' 
    235253        output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 
    236254        for obj in obj_list: 
    237             grouper = self.expression.resolve(Context({'var': obj}), True) 
     255            ctxt = Context({'var': obj}) 
     256            ctxt.autoescape = context.autoescape 
     257            grouper = self.expression.resolve(ctxt, True) 
    238258            # TODO: Is this a sensible way to determine equality? 
    239259            if output and repr(output[-1]['grouper']) == repr(grouper): 
    240260                output[-1]['list'].append(obj) 
     
    336356        return str(int(round(ratio))) 
    337357 
    338358#@register.tag 
     359def autoescape(parser, token): 
     360    """ 
     361    Force autoescape behaviour for this block. 
     362    """ 
     363    nodelist = parser.parse(('endautoescape',)) 
     364    parser.delete_first_token() 
     365    return AutoEscapeControlNode(True, nodelist) 
     366autoescape = register.tag(autoescape) 
     367 
     368#@register.tag 
    339369def comment(parser, token): 
    340370    """ 
    341371    Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` 
     
    429459 
    430460    Sample usage:: 
    431461 
    432         {% filter escape|lower %} 
     462        {% filter force_escape|lower %} 
    433463            This text will be HTML-escaped, and will appear in lowercase. 
    434464        {% endfilter %} 
    435465    """ 
    436466    _, rest = token.contents.split(None, 1) 
    437467    filter_expr = parser.compile_filter("var|%s" % (rest)) 
     468    for func, unused in filter_expr.filters: 
     469        if func.__name__ in ('escape', 'safe'): 
     470            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__) 
    438471    nodelist = parser.parse(('endfilter',)) 
    439472    parser.delete_first_token() 
    440473    return FilterNode(filter_expr, nodelist) 
     
    675708ifchanged = register.tag(ifchanged) 
    676709 
    677710#@register.tag 
     711def noautoescape(parser, token): 
     712    """ 
     713    Force autoescape behaviour to be disabled for this block. 
     714    """ 
     715    nodelist = parser.parse(('endnoautoescape',)) 
     716    parser.delete_first_token() 
     717    return AutoEscapeControlNode(False, nodelist) 
     718autoescape = register.tag(noautoescape) 
     719 
     720#@register.tag 
    678721def ssi(parser, token): 
    679722    """ 
    680723    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 
     44    def __str__(self): 
     45        return self 
     46 
     47class SafeUnicode(unicode, SafeData): 
     48    """ 
     49    A unicode subclass that has been specifically marked as "safe" for HTML 
     50    output purposes. 
     51    """ 
     52    def __add__(self, rhs): 
     53        """ 
     54        Concatenating a safe unicode object with another safe string or safe 
     55        unicode object is safe. Otherwise, the result is no longer safe. 
     56        """ 
     57        if isinstance(rhs, SafeData): 
     58            return SafeUnicode(self + rhs) 
     59        else: 
     60            return super(SafeUnicode, self).__add__(rhs) 
     61 
     62    def _proxy_method(self, *args, **kwargs): 
     63        """ 
     64        Wrap a call to a normal unicode method up so that we return safe 
     65        results. The method that is being wrapped is passed in the 'method' 
     66        argument. 
     67        """ 
     68        method = kwargs.pop('method') 
     69        data = method(self, *args, **kwargs) 
     70        if isinstance(data, str): 
     71            return SafeString(data) 
     72        else: 
     73            return SafeUnicode(data) 
     74 
     75    encode = curry(_proxy_method, method = unicode.encode) 
     76    decode = curry(_proxy_method, method = unicode.decode) 
     77 
     78 
     79def mark_safe(s): 
     80    """ 
     81    Explicitly mark a string as safe for (HTML) output purposes. The returned 
     82    object can be used everywhere a string or unicode object is appropriate. 
     83 
     84    Can safely be called multiple times on a single string. 
     85    """ 
     86    if isinstance(s, SafeData): 
     87        return s 
     88    if isinstance(s, str): 
     89        return SafeString(s) 
     90    if isinstance(s, unicode): 
     91        return SafeUnicode(s) 
     92    return SafeString(str(s)) 
     93 
     94def mark_for_escaping(s): 
     95    """ 
     96    Explicitly mark a string as requiring HTML escaping upon output. Has no 
     97    effect on SafeData subclasses. 
     98 
     99    Can be safely called multiple times on a single string (the effect is only 
     100    applied once). 
     101    """ 
     102    if isinstance(s, SafeData) or isinstance(s, EscapeData): 
     103        return s 
     104    if isinstance(s, str): 
     105        return EscapeString(s) 
     106    if isinstance(s, unicode): 
     107        return EscapeUnicode(s) 
     108    return EscapeString(str(s)) 
     109 
  • a/docs/templates.txt

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