Django

Code

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

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

updated patch, includes part of smurf's patch, for rev. 4659

  • 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 
     
    574576                    arg_vals.append(arg) 
    575577                else: 
    576578                    arg_vals.append(resolve_variable(arg, context)) 
    577             obj = func(obj, *arg_vals) 
     579            if getattr(func, 'needs_autoescape', False): 
     580                new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 
     581            else: 
     582                new_obj = func(obj, *arg_vals) 
     583            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 
     584                obj = mark_safe(new_obj) 
     585            elif isinstance(obj, EscapeData): 
     586                obj = mark_for_escaping(new_obj) 
     587            else: 
     588                obj = new_obj 
     589                 
    578590        return obj 
    579591 
    580592    def args_check(name, func, provided): 
     
    766778 
    767779    def render(self, context): 
    768780        output = self.filter_expression.resolve(context) 
    769         return self.encode_output(output) 
     781        encoded_output = self.encode_output(output) 
     782        if (context.autoescape and not isinstance(encoded_output, SafeData)) or isinstance(encoded_output, EscapeData): 
     783            return escape(encoded_output) 
     784        else: 
     785            return encoded_output 
    770786 
    771787class DebugVariableNode(VariableNode): 
    772788    def render(self, context): 
     
    776792            if not hasattr(e, 'source'): 
    777793                e.source = self.source 
    778794            raise 
    779         return self.encode_output(output) 
     795        encoded_output = self.encode_output(output) 
     796        if context.autoescape and not isinstance(encoded_output, SafeData): 
     797            return escape(encoded_output) 
     798        else: 
     799            return encoded_output 
    780800 
    781801def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
    782802    "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_] 
     
    98101            processors = tuple(processors) 
    99102        for processor in get_standard_processors() + processors: 
    100103            self.update(processor(request)) 
     104 
  • 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 
     
    4950    "Adds slashes - useful for passing strings to JavaScript, for example." 
    5051    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 
    5152addslashes = stringfilter(addslashes) 
     53addslashes.is_safe = True 
    5254 
    5355def capfirst(value): 
    5456    "Capitalizes the first character of the value" 
    5557    return value and value[0].upper() + value[1:] 
    5658capfirst = stringfilter(capfirst) 
    57   
     59capfirst.is_safe = True 
     60 
    5861def fix_ampersands(value): 
    5962    "Replaces ampersands with ``&amp;`` entities" 
    6063    from django.utils.html import fix_ampersands 
    6164    return fix_ampersands(value) 
    6265fix_ampersands = stringfilter(fix_ampersands) 
     66fix_ampersands.is_safe = True 
    6367 
    6468def floatformat(text, arg=-1): 
    6569    """ 
     
    9296        return '%d' % int(f) 
    9397    else: 
    9498        formatstr = '%%.%df' % abs(d) 
    95         return formatstr % f 
     99        return mark_safe(formatstr % f) 
     100floatformat.is_safe = True 
    96101 
    97 def linenumbers(value): 
     102def linenumbers(value, autoescape = None): 
    98103    "Displays text with line numbers" 
    99104    from django.utils.html import escape 
    100105    lines = value.split('\n') 
    101106    # Find the maximum width of the line count, for use with zero padding string format command 
    102107    width = str(len(str(len(lines)))) 
    103     for i, line in enumerate(lines): 
    104         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    105     return '\n'.join(lines) 
     108    if not autoescape or isinstance(value, SafeData): 
     109        for i, line in enumerate(lines): 
     110            lines[i] = ("%0" + width  + "d. %s") % (i + 1, line) 
     111    else: 
     112        for i, line in enumerate(lines): 
     113            lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
     114    return mark_safe('\n'.join(lines)) 
    106115linenumbers = stringfilter(linenumbers) 
     116linenumbers.is_safe = True 
     117linenumbers.needs_autoescape = True 
    107118 
    108119def lower(value): 
    109120    "Converts a string into all lowercase" 
    110121    return value.lower() 
    111122lower = stringfilter(lower) 
     123lower.is_safe = True 
    112124 
    113125def make_list(value): 
    114126    """ 
     
    117129    """ 
    118130    return list(value) 
    119131make_list = stringfilter(make_list) 
     132make_list.is_safe = False 
    120133 
    121134def slugify(value): 
    122135    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 
    123136    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    124     return re.sub('[-\s]+', '-', value
     137    return mark_safe(re.sub('[-\s]+', '-', value)
    125138slugify = stringfilter(slugify) 
     139slugify.is_safe = True 
    126140 
    127141def stringformat(value, arg): 
    128142    """ 
     
    137151        return ("%" + str(arg)) % value 
    138152    except (ValueError, TypeError): 
    139153        return "" 
     154stringformat.is_safe = True 
    140155 
    141156def title(value): 
    142157    "Converts a string into titlecase" 
    143158    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
    144159title = stringfilter(title) 
     160title.is_safe = False 
    145161 
    146162def truncatewords(value, arg): 
    147163    """ 
     
    158174        value = str(value) 
    159175    return truncate_words(value, length) 
    160176truncatewords = stringfilter(truncatewords) 
     177truncatewords.is_safe = True 
    161178 
    162179def truncatewords_html(value, arg): 
    163180    """ 
     
    179196    "Converts a string into all uppercase" 
    180197    return value.upper() 
    181198upper = stringfilter(upper) 
     199upper.is_safe = False 
    182200 
    183201def urlencode(value): 
    184202    "Escapes a value for use in a URL" 
     
    187205        value = str(value) 
    188206    return urllib.quote(value) 
    189207urlencode = stringfilter(urlencode) 
     208urlencode.is_safe = False 
    190209 
    191210def urlize(value): 
    192211    "Converts URLs in plain text into clickable links" 
    193212    from django.utils.html import urlize 
    194     return urlize(value, nofollow=True
     213    return mark_safe(urlize(value, nofollow=True)
    195214urlize = stringfilter(urlize) 
     215urlize.is_safe = True 
    196216 
    197217def urlizetrunc(value, limit): 
    198218    """ 
     
    202222    Argument: Length to truncate URLs to. 
    203223    """ 
    204224    from django.utils.html import urlize 
    205     return urlize(value, trim_url_limit=int(limit), nofollow=True
     225    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)
    206226urlizetrunc = stringfilter(urlizetrunc) 
     227urlize.is_safe = True 
    207228 
    208229def wordcount(value): 
    209230    "Returns the number of words" 
    210231    return len(value.split()) 
    211232wordcount = stringfilter(wordcount) 
     233wordcount.is_safe = False 
    212234 
    213235def wordwrap(value, arg): 
    214236    """ 
     
    219241    from django.utils.text import wrap 
    220242    return wrap(value, int(arg)) 
    221243wordwrap = stringfilter(wordwrap) 
     244wordwrap.is_safe = True 
    222245 
    223246def ljust(value, arg): 
    224247    """ 
     
    228251    """ 
    229252    return value.ljust(int(arg)) 
    230253ljust = stringfilter(ljust) 
     254ljust.is_safe = True 
    231255 
    232256def rjust(value, arg): 
    233257    """ 
     
    237261    """ 
    238262    return value.rjust(int(arg)) 
    239263rjust = stringfilter(rjust) 
     264rjust.is_safe = True 
    240265 
    241266def center(value, arg): 
    242267    "Centers the value in a field of a given width" 
    243268    return value.center(int(arg)) 
    244269center = stringfilter(center) 
     270center.is_safe = True 
    245271 
    246272def cut(value, arg): 
    247273    "Removes all values of arg from the given string" 
    248274    return value.replace(arg, '') 
    249275cut = stringfilter(cut) 
     276cut.is_safe = False 
    250277 
    251278################### 
    252279# HTML STRINGS    # 
    253280################### 
    254281 
    255282def escape(value): 
    256     "Escapes a string's HTML" 
     283    "Marks the value as a string that should not be auto-escaped." 
     284    from django.utils.safestring import mark_for_escaping 
     285    return mark_for_escaping(value) 
     286escape = stringfilter(escape) 
     287escape.is_safe = True 
     288 
     289def force_escape(value): 
     290    """Escapes a string's HTML. This returns a new string containing the escaped 
     291    characters (as opposed to "escape", which marks the content for later 
     292    possible escaping).""" 
    257293    from django.utils.html import escape 
    258     return escape(value
     294    return mark_safe(escape(value)
    259295escape = stringfilter(escape) 
     296force_escape.is_safe = True 
    260297 
    261 def linebreaks(value): 
     298def linebreaks(value, autoescape = None): 
    262299    "Converts newlines into <p> and <br />s" 
    263300    from django.utils.html import linebreaks 
    264     return linebreaks(value) 
     301    autoescape = autoescape and not isinstance(value, SafeData) 
     302    return mark_safe(linebreaks(value, autoescape)) 
    265303linebreaks = stringfilter(linebreaks) 
     304linebreaks.is_safe = True 
     305linebreaks.needs_autoescape = True 
    266306 
    267 def linebreaksbr(value): 
     307def linebreaksbr(value, autoescape = None): 
    268308    "Converts newlines into <br />s" 
    269     return value.replace('\n', '<br />') 
     309    if autoescape and not isinstance(value, SafeData): 
     310        from django.utils.html import escape 
     311        data = escape(value) 
     312    else: 
     313        data = value 
     314    return mark_safe(data.replace('\n', '<br />')) 
    270315linebreaksbr = stringfilter(linebreaksbr) 
     316linebreaksbr.is_safe = True 
     317linebreaksbr.needs_autoescape = True 
     318 
     319def safe(value): 
     320    "Marks the value as a string that should not be auto-escaped." 
     321    from django.utils.safestring import mark_safe 
     322    return mark_safe(value) 
     323safe = stringfilter(safe) 
     324safe.is_safe = True 
    271325 
    272326def removetags(value, tags): 
    273327    "Removes a space separated list of [X]HTML tags from the output" 
     
    279333    value = endtag_re.sub('', value) 
    280334    return value 
    281335removetags = stringfilter(removetags) 
     336removetags.is_safe = True 
    282337 
    283338def striptags(value): 
    284339    "Strips all [X]HTML tags" 
    285340    from django.utils.html import strip_tags 
    286341    return strip_tags(value) 
    287342striptags = stringfilter(striptags) 
     343striptags.is_safe = True 
    288344 
    289345################### 
    290346# LISTS           # 
     
    298354    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 
    299355    decorated.sort() 
    300356    return [item[1] for item in decorated] 
     357dictsort.is_safe = False 
    301358 
    302359def dictsortreversed(value, arg): 
    303360    """ 
     
    308365    decorated.sort() 
    309366    decorated.reverse() 
    310367    return [item[1] for item in decorated] 
     368dictsortreversed.is_safe = False 
    311369 
    312370def first(value): 
    313371    "Returns the first item in a list" 
     
    315373        return value[0] 
    316374    except IndexError: 
    317375        return '' 
     376first.is_safe = True 
    318377 
    319378def join(value, arg): 
    320379    "Joins a list with a string, like Python's ``str.join(list)``" 
    321380    try: 
    322         return arg.join(map(smart_string, value)) 
     381        data = arg.join(map(smart_string, value)) 
    323382    except AttributeError: # fail silently but nicely 
    324383        return value 
     384    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 
     385    if safe_args: 
     386        return mark_safe(data) 
     387    else: 
     388        return data 
     389join.is_safe = True 
    325390 
    326391def length(value): 
    327392    "Returns the length of the value - useful for lists" 
    328393    return len(value) 
     394length.is_safe = False 
    329395 
    330396def length_is(value, arg): 
    331397    "Returns a boolean of whether the value's length is the argument" 
    332398    return len(value) == int(arg) 
     399length.is_safe = False 
    333400 
    334401def random(value): 
    335402    "Returns a random item from the list" 
    336403    return random_module.choice(value) 
     404length.is_safe = True 
    337405 
    338406def slice_(value, arg): 
    339407    """ 
     
    354422 
    355423    except (ValueError, TypeError): 
    356424        return value # Fail silently. 
     425slice_.is_safe = True 
    357426 
    358 def unordered_list(value): 
     427def unordered_list(value, autoescape = None): 
    359428    """ 
    360429    Recursively takes a self-nested list and returns an HTML unordered list -- 
    361430    WITHOUT opening and closing <ul> tags. 
     
    376445        </ul> 
    377446        </li> 
    378447    """ 
     448    if autoescape: 
     449        from django.utils.html import conditional_escape 
     450        escaper = conditional_escape 
     451    else: 
     452        escaper = lambda x: x 
     453 
    379454    def _helper(value, tabs): 
    380455        indent = '\t' * tabs 
    381456        if value[1]: 
    382             return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent, 
     457            return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent, 
    383458                '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 
    384459        else: 
    385             return '%s<li>%s</li>' % (indent, value[0]) 
    386     return _helper(value, 1) 
     460            return '%s<li>%s</li>' % (indent, escaper(value[0])) 
     461    return mark_safe(_helper(value, 1)) 
     462unordered_list.is_safe = True 
     463unordered_list.needs_autoescape = True 
    387464 
    388465################### 
    389466# INTEGERS        # 
     
    392469def add(value, arg): 
    393470    "Adds the arg to the value" 
    394471    return int(value) + int(arg) 
     472add.is_safe = False 
    395473 
    396474def get_digit(value, arg): 
    397475    """ 
     
    411489        return int(str(value)[-arg]) 
    412490    except IndexError: 
    413491        return 0 
     492get_digit.is_safe = False 
    414493 
    415494################### 
    416495# DATES           # 
     
    424503    if arg is None: 
    425504        arg = settings.DATE_FORMAT 
    426505    return format(value, arg) 
     506date.is_safe = False 
    427507 
    428508def time(value, arg=None): 
    429509    "Formats a time according to the given format" 
     
    433513    if arg is None: 
    434514        arg = settings.TIME_FORMAT 
    435515    return time_format(value, arg) 
     516time.is_safe = False 
    436517 
    437518def timesince(value, arg=None): 
    438519    'Formats a date as the time since that date (i.e. "4 days, 6 hours")' 
     
    442523    if arg: 
    443524        return timesince(arg, value) 
    444525    return timesince(value) 
     526timesince.is_safe = False 
    445527 
    446528def timeuntil(value, arg=None): 
    447529    'Formats a date as the time until that date (i.e. "4 days, 6 hours")' 
     
    452534    if arg: 
    453535        return timesince(arg, value) 
    454536    return timesince(datetime.now(), value) 
     537timeuntil.is_safe = False 
    455538 
    456539################### 
    457540# LOGIC           # 
     
    460543def default(value, arg): 
    461544    "If value is unavailable, use given default" 
    462545    return value or arg 
     546default.is_safe = False 
    463547 
    464548def default_if_none(value, arg): 
    465549    "If value is None, use given default" 
    466550    if value is None: 
    467551        return arg 
    468552    return value 
     553default_if_none.is_safe = False 
    469554 
    470555def divisibleby(value, arg): 
    471556    "Returns true if the value is devisible by the argument" 
    472557    return int(value) % int(arg) == 0 
     558divisibleby.is_safe = False 
    473559 
    474560def yesno(value, arg=None): 
    475561    """ 
     
    500586    if value: 
    501587        return yes 
    502588    return no 
     589yesno.is_safe = False 
    503590 
    504591################### 
    505592# MISC            # 
     
    522609    if bytes < 1024 * 1024 * 1024: 
    523610        return "%.1f MB" % (bytes / (1024 * 1024)) 
    524611    return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 
     612filesizeformat.is_safe = True 
    525613 
    526614def pluralize(value, arg='s'): 
    527615    """ 
     
    549637        except TypeError: # len() of unsized object 
    550638            pass 
    551639    return singular_suffix 
     640pluralize.is_safe = False 
    552641 
    553642def phone2numeric(value): 
    554643    "Takes a phone number and converts it in to its numerical equivalent" 
    555644    from django.utils.text import phone2numeric 
    556645    return phone2numeric(value) 
     646phone2numeric.is_safe = True 
    557647 
    558648def pprint(value): 
    559649    "A wrapper around pprint.pprint -- for debugging, really" 
     
    562652        return pformat(value) 
    563653    except Exception, e: 
    564654        return "Error in formatting:%s" % e 
     655pprint.is_safe = True 
    565656 
    566657# Syntax: register.filter(name of filter, callback) 
    567658register.filter(add) 
     
    580671register.filter(first) 
    581672register.filter(fix_ampersands) 
    582673register.filter(floatformat) 
     674register.filter(force_escape) 
    583675register.filter(get_digit) 
    584676register.filter(join) 
    585677register.filter(length) 
     
    596688register.filter(removetags) 
    597689register.filter(random) 
    598690register.filter(rjust) 
     691register.filter(safe) 
    599692register.filter('slice', slice_) 
    600693register.filter(slugify) 
    601694register.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) 
     
    355375        return str(int(round(ratio))) 
    356376 
    357377#@register.tag 
     378def autoescape(parser, token): 
     379    """ 
     380    Force autoescape behaviour for this block. 
     381    """ 
     382    nodelist = parser.parse(('endautoescape',)) 
     383    parser.delete_first_token() 
     384    return AutoEscapeControlNode(True, nodelist) 
     385autoescape = register.tag(autoescape) 
     386 
     387#@register.tag 
    358388def comment(parser, token): 
    359389    """ 
    360390    Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` 
     
    448478 
    449479    Sample usage:: 
    450480 
    451         {% filter escape|lower %} 
     481        {% filter force_escape|lower %} 
    452482            This text will be HTML-escaped, and will appear in lowercase. 
    453483        {% endfilter %} 
    454484    """ 
    455485    _, rest = token.contents.split(None, 1) 
    456486    filter_expr = parser.compile_filter("var|%s" % (rest)) 
     487    for func, unused in filter_expr.filters: 
     488        if func.__name__ in ('escape', 'safe'): 
     489            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__) 
    457490    nodelist = parser.parse(('endfilter',)) 
    458491    parser.delete_first_token() 
    459492    return FilterNode(filter_expr, nodelist) 
     
    694727ifchanged = register.tag(ifchanged) 
    695728 
    696729#@register.tag 
     730def noautoescape(parser, token): 
     731    """ 
     732    Force autoescape behaviour to be disabled for this block. 
     733    """ 
     734    nodelist = parser.parse(('endnoautoescape',)) 
     735    parser.delete_first_token() 
     736    return AutoEscapeControlNode(False, nodelist) 
     737autoescape = register.tag(noautoescape) 
     738 
     739#@register.tag 
    697740def ssi(parser, token): 
    698741    """ 
    699742    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  
    270270two similarly-named ``{% block %}`` tags in a template, that template's parent 
    271271wouldn't know which one of the blocks' content to use. 
    272272 
     273Automatic HTML escaping 
     274======================= 
     275 
     276A very real problem when creating HTML (and other) output using templates and 
     277variable substitution is the possibility of accidently inserting some variable 
     278value that affects the resulting HTML. For example, a template fragment like 
     279 
     280:: 
     281 
     282    Hello, {{ name }}. 
     283 
     284seems like a harmless way to display the user's name. However, if you are 
     285displaying data that the user entered directly and they entered their name as 
     286 
     287:: 
     288 
     289    <script>alert('hello')</script> 
     290 
     291this would always display a Javascript alert box whenever the page was loaded. 
     292Similarly, if you were displaying some data generated by another process and 
     293it contained a '<' symbol, you couldn't just dump this straight into your 
     294HTML, because it would be treated as the start of an element.  The effects of 
     295these sorts of problems can vary from merely annoying to allowing exploits via 
     296`Cross Site Scripting`_ (XSS) attacks. 
     297 
     298.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting 
     299 
     300In order to provide some protection against these problems, Django provides an 
     301auto-escaping template tag. Inside this tag, any data that comes from template 
     302variables is examined to see if it contains one of the five HTML characters 
     303(<, >, ', " and &) that often need escaping and those characters are converted 
     304to their respective HTML entities. 
     305 
     306Because some variables will contain data that is *intended* to be rendered 
     307as HTML, template tag and filter writers can mark their output strings as 
     308requiring no further escaping. For example, the ``unordered_list`` filter is 
     309designed to return raw HTML and we want the template processor to simply 
     310display the results as returned, without applying any escaping. That is taken 
     311care of by the filter. The template author need do nothing special in that 
     312case. 
     313 
     314By default, auto-escaping is not in effect. To enable it inside your template, 
     315wrap the affected content in the ``autoescape`` tag, like so:: 
     316 
     317    {% autoescape %} 
     318        Hello {{ name }} 
     319    {% endautoescape %} 
     320 
     321Since the auto-escaping tag passes its effect onto templates that extend the 
     322current one as well as templates included via the ``include`` tag (just like 
     323all block tags), if you wrap your main HTML content in an ``autoescape`` tag, 
     324you will have automatic escaping applied to all of your content. 
     325 
     326At times, you might want to disable auto-escaping when it would otherwise be 
     327in effect. You can do this with the ``noautoescape`` tag. For example:: 
     328 
     329    {% autoescape %} 
     330        Hello {{ name }} 
     331 
     332        {% noautoescape %} 
     333            This will not be auto-escaped: {{ data }}. 
     334 
     335            Nor this: {{ other_data }} 
     336