Django

Code

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

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

updated patch, see comment

  • 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 
     
    183184 
    184185    def html_error_list(self): 
    185186        if self.errors(): 
    186             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]
     187            return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
    187188        else: 
    188             return '' 
     189            return mark_safe('') 
    189190 
    190191    def get_id(self): 
    191192        return self.formfield.get_id() 
     
    217218        return bool(len(self.errors())) 
    218219 
    219220    def html_combined_error_list(self): 
    220         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]
     221        return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
    221222 
    222223class InlineObjectCollection(object): 
    223224    "An object that acts like a sparse list of form field collections." 
     
    404405            maxlength = 'maxlength="%s" ' % self.maxlength 
    405406        if isinstance(data, unicode): 
    406407            data = data.encode(settings.DEFAULT_CHARSET) 
    407         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
     408        return mark_safe('<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 
    408409            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    409             self.field_name, self.length, escape(data), maxlength) 
     410            self.field_name, self.length, escape(data), maxlength)) 
    410411 
    411412    def html2python(data): 
    412413        return data 
     
    430431            data = '' 
    431432        if isinstance(data, unicode): 
    432433            data = data.encode(settings.DEFAULT_CHARSET) 
    433         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
     434        return mark_safe('<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 
    434435            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 
    435             self.field_name, self.rows, self.cols, escape(data)) 
     436            self.field_name, self.rows, self.cols, escape(data))) 
    436437 
    437438class HiddenField(FormField): 
    438439    def __init__(self, field_name, is_required=False, validator_list=None): 
     
    441442        self.validator_list = validator_list[:] 
    442443 
    443444    def render(self, data): 
    444         return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
    445             (self.get_id(), self.field_name, escape(data)) 
     445        return mark_safe('<input type="hidden" id="%s" name="%s" value="%s" />' % \ 
     446            (self.get_id(), self.field_name, escape(data))) 
    446447 
    447448class CheckboxField(FormField): 
    448449    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False): 
     
    456457        checked_html = '' 
    457458        if data or (data is '' and self.checked_by_default): 
    458459            checked_html = ' checked="checked"' 
    459         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
     460        return mark_safe('<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 
    460461            (self.get_id(), self.__class__.__name__, 
    461             self.field_name, checked_html) 
     462            self.field_name, checked_html)) 
    462463 
    463464    def html2python(data): 
    464465        "Convert value from browser ('on' or '') to a Python boolean" 
     
    489490                selected_html = ' selected="selected"' 
    490491            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) 
    491492        output.append('  </select>') 
    492         return '\n'.join(output
     493        return mark_safe('\n'.join(output)
    493494 
    494495    def isValidChoice(self, data, form): 
    495496        str_data = str(data) 
     
    542543                output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')] 
    543544                output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 
    544545                output.append('</ul>') 
    545                 return ''.join(output
     546                return mark_safe(''.join(output)
    546547            def __iter__(self): 
    547548                for d in self.datalist: 
    548549                    yield d 
     
    557558            datalist.append({ 
    558559                'value': value, 
    559560                'name': display_name, 
    560                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
    561                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html)
    562                 'label': '<label for="%s">%s</label>' % \ 
     561                'field': mark_safe('<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 
     562                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html))
     563                'label': mark_safe('<label for="%s">%s</label>' % \ 
    563564                    (self.get_id() + '_' + str(i), display_name), 
    564             }) 
     565            )}) 
    565566        return RadioFieldRenderer(datalist, self.ul_class) 
    566567 
    567568    def isValidChoice(self, data, form): 
     
    600601                selected_html = ' selected="selected"' 
    601602            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) 
    602603        output.append('  </select>') 
    603         return '\n'.join(output
     604        return mark_safe('\n'.join(output)
    604605 
    605606    def isValidChoice(self, field_data, all_data): 
    606607        # data is something like ['1', '2', '3'] 
     
    653654                (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html, 
    654655                self.get_id() + escape(value), choice)) 
    655656        output.append('</ul>') 
    656         return '\n'.join(output
     657        return mark_safe('\n'.join(output)
    657658 
    658659#################### 
    659660# FILE UPLOADS     # 
     
    674675            raise validators.CriticalValidationError, gettext("The submitted file is empty.") 
    675676 
    676677    def render(self, data): 
    677         return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    678             (self.get_id(), self.__class__.__name__, self.field_name) 
     678        return mark_safe('<input type="file" id="%s" class="v%s" name="%s" />' % \ 
     679            (self.get_id(), self.__class__.__name__, self.field_name)) 
    679680 
    680681    def html2python(data): 
    681682        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 
     
    576578                    arg_vals.append(arg) 
    577579                else: 
    578580                    arg_vals.append(resolve_variable(arg, context)) 
    579             obj = func(obj, *arg_vals) 
     581            if getattr(func, 'needs_autoescape', False): 
     582                new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 
     583            else: 
     584                new_obj = func(obj, *arg_vals) 
     585            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 
     586                obj = mark_safe(new_obj) 
     587            elif isinstance(obj, EscapeData): 
     588                obj = mark_for_escaping(new_obj) 
     589            else: 
     590                obj = new_obj 
     591                 
    580592        return obj 
    581593 
    582594    def args_check(name, func, provided): 
     
    765777 
    766778    def render(self, context): 
    767779        output = self.filter_expression.resolve(context) 
    768         return self.encode_output(output) 
     780        encoded_output = self.encode_output(output) 
     781        if (context.autoescape and not isinstance(encoded_output, SafeData)) or isinstance(encoded_output, EscapeData): 
     782            return escape(encoded_output) 
     783        else: 
     784            return encoded_output 
    769785 
    770786class DebugVariableNode(VariableNode): 
    771787    def render(self, context): 
     
    775791            if not hasattr(e, 'source'): 
    776792                e.source = self.source 
    777793            raise 
    778         return self.encode_output(output) 
     794        encoded_output = self.encode_output(output) 
     795        if context.autoescape and not isinstance(encoded_output, SafeData): 
     796            return escape(encoded_output) 
     797        else: 
     798            return encoded_output 
    779799 
    780800def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
    781801    "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    """ 
     
    9397        return '%d' % int(f) 
    9498    else: 
    9599        formatstr = '%%.%df' % abs(d) 
    96         return formatstr % f 
     100        return mark_safe(formatstr % f) 
     101floatformat.is_safe = True 
    97102 
    98 def linenumbers(value): 
     103def linenumbers(value, autoescape = None): 
    99104    "Displays text with line numbers" 
    100105    from django.utils.html import escape 
    101106    lines = value.split('\n') 
    102107    # Find the maximum width of the line count, for use with zero padding string format command 
    103108    width = str(len(str(len(lines)))) 
    104     for i, line in enumerate(lines): 
    105         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    106     return '\n'.join(lines) 
     109    if not autoescape or isinstance(value, SafeData): 
     110        for i, line in enumerate(lines): 
     111            lines[i] = ("%0" + width  + "d. %s") % (i + 1, line) 
     112    else: 
     113        for i, line in enumerate(lines): 
     114            lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
     115    return mark_safe('\n'.join(lines)) 
    107116linenumbers = stringfilter(linenumbers) 
     117linenumbers.is_safe = True 
     118linenumbers.needs_autoescape = True 
    108119 
    109120def lower(value): 
    110121    "Converts a string into all lowercase" 
    111122    return value.lower() 
    112123lower = stringfilter(lower) 
     124lower.is_safe = True 
    113125 
    114126def make_list(value): 
    115127    """ 
     
    118130    """ 
    119131    return list(value) 
    120132make_list = stringfilter(make_list) 
     133make_list.is_safe = False 
    121134 
    122135def slugify(value): 
    123136    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 
    124137    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    125     return re.sub('[-\s]+', '-', value
     138    return mark_safe(re.sub('[-\s]+', '-', value)
    126139slugify = stringfilter(slugify) 
     140slugify.is_safe = True 
    127141 
    128142def stringformat(value, arg): 
    129143    """ 
     
    138152        return ("%" + str(arg)) % value 
    139153    except (ValueError, TypeError): 
    140154        return "" 
     155stringformat.is_safe = True 
    141156 
    142157def title(value): 
    143158    "Converts a string into titlecase" 
    144159    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
    145160title = stringfilter(title) 
     161title.is_safe = False 
    146162 
    147163def truncatewords(value, arg): 
    148164    """ 
     
    159175        value = str(value) 
    160176    return truncate_words(value, length) 
    161177truncatewords = stringfilter(truncatewords) 
     178truncatewords.is_safe = True 
    162179 
    163180def truncatewords_html(value, arg): 
    164181    """ 
     
    180197    "Converts a string into all uppercase" 
    181198    return value.upper() 
    182199upper = stringfilter(upper) 
     200upper.is_safe = False 
    183201 
    184202def urlencode(value): 
    185203    "Escapes a value for use in a URL" 
     
    188206        value = str(value) 
    189207    return urllib.quote(value) 
    190208urlencode = stringfilter(urlencode) 
     209urlencode.is_safe = False 
    191210 
    192211def urlize(value): 
    193212    "Converts URLs in plain text into clickable links" 
    194213    from django.utils.html import urlize 
    195     return urlize(value, nofollow=True
     214    return mark_safe(urlize(value, nofollow=True)
    196215urlize = stringfilter(urlize) 
     216urlize.is_safe = True 
    197217 
    198218def urlizetrunc(value, limit): 
    199219    """ 
     
    203223    Argument: Length to truncate URLs to. 
    204224    """ 
    205225    from django.utils.html import urlize 
    206     return urlize(value, trim_url_limit=int(limit), nofollow=True
     226    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)
    207227urlizetrunc = stringfilter(urlizetrunc) 
     228urlize.is_safe = True 
    208229 
    209230def wordcount(value): 
    210231    "Returns the number of words" 
    211232    return len(value.split()) 
    212233wordcount = stringfilter(wordcount) 
     234wordcount.is_safe = False 
    213235 
    214236def wordwrap(value, arg): 
    215237    """ 
     
    220242    from django.utils.text import wrap 
    221243    return wrap(value, int(arg)) 
    222244wordwrap = stringfilter(wordwrap) 
     245wordwrap.is_safe = True 
    223246 
    224247def ljust(value, arg): 
    225248    """ 
     
    229252    """ 
    230253    return value.ljust(int(arg)) 
    231254ljust = stringfilter(ljust) 
     255ljust.is_safe = True 
    232256 
    233257def rjust(value, arg): 
    234258    """ 
     
    238262    """ 
    239263    return value.rjust(int(arg)) 
    240264rjust = stringfilter(rjust) 
     265rjust.is_safe = True 
    241266 
    242267def center(value, arg): 
    243268    "Centers the value in a field of a given width" 
    244269    return value.center(int(arg)) 
    245270center = stringfilter(center) 
     271center.is_safe = True 
    246272 
    247273def cut(value, arg): 
    248274    "Removes all values of arg from the given string" 
    249275    return value.replace(arg, '') 
    250276cut = stringfilter(cut) 
     277cut.is_safe = False 
    251278 
    252279################### 
    253280# HTML STRINGS    # 
    254281################### 
    255282 
    256283def escape(value): 
    257     "Escapes a string's HTML" 
     284    "Marks the value as a string that should not be auto-escaped." 
     285    from django.utils.safestring import mark_for_escaping 
     286    return mark_for_escaping(value) 
     287escape = stringfilter(escape) 
     288escape.is_safe = True 
     289 
     290def force_escape(value): 
     291    """Escapes a string's HTML. This returns a new string containing the escaped 
     292    characters (as opposed to "escape", which marks the content for later 
     293    possible escaping).""" 
    258294    from django.utils.html import escape 
    259     return escape(value
     295    return mark_safe(escape(value)
    260296escape = stringfilter(escape) 
     297force_escape.is_safe = True 
    261298 
    262 def linebreaks(value): 
     299def linebreaks(value, autoescape = None): 
    263300    "Converts newlines into <p> and <br />s" 
    264301    from django.utils.html import linebreaks 
    265     return linebreaks(value) 
     302    autoescape = autoescape and not isinstance(value, SafeData) 
     303    return mark_safe(linebreaks(value, autoescape)) 
    266304linebreaks = stringfilter(linebreaks) 
     305linebreaks.is_safe = True 
     306linebreaks.needs_autoescape = True 
    267307 
    268 def linebreaksbr(value): 
     308def linebreaksbr(value, autoescape = None): 
    269309    "Converts newlines into <br />s" 
    270     return value.replace('\n', '<br />') 
     310    if autoescape and not isinstance(value, SafeData): 
     311        from django.utils.html import escape 
     312        data = escape(value) 
     313    else: 
     314        data = value 
     315    return mark_safe(data.replace('\n', '<br />')) 
    271316linebreaksbr = stringfilter(linebreaksbr) 
     317linebreaksbr.is_safe = True 
     318linebreaksbr.needs_autoescape = True 
     319 
     320def safe(value): 
     321    "Marks the value as a string that should not be auto-escaped." 
     322    from django.utils.safestring import mark_safe 
     323    return mark_safe(value) 
     324safe = stringfilter(safe) 
     325safe.is_safe = True 
    272326 
    273327def removetags(value, tags): 
    274328    "Removes a space separated list of [X]HTML tags from the output" 
     
    280334    value = endtag_re.sub('', value) 
    281335    return value 
    282336removetags = stringfilter(removetags) 
     337removetags.is_safe = True 
    283338 
    284339def striptags(value): 
    285340    "Strips all [X]HTML tags" 
    286341    from django.utils.html import strip_tags 
    287342    return strip_tags(value) 
    288343striptags = stringfilter(striptags) 
     344striptags.is_safe = True 
    289345 
    290346################### 
    291347# LISTS           # 
     
    299355    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 
    300356    decorated.sort() 
    301357    return [item[1] for item in decorated] 
     358dictsort.is_safe = False 
    302359 
    303360def dictsortreversed(value, arg): 
    304361    """ 
     
    309366    decorated.sort() 
    310367    decorated.reverse() 
    311368    return [item[1] for item in decorated] 
     369dictsortreversed.is_safe = False 
    312370 
    313371def first(value): 
    314372    "Returns the first item in a list" 
     
    316374        return value[0] 
    317375    except IndexError: 
    318376        return '' 
     377first.is_safe = True 
    319378 
    320379def join(value, arg): 
    321380    "Joins a list with a string, like Python's ``str.join(list)``" 
    322381    try: 
    323         return arg.join(map(smart_string, value)) 
     382        data = arg.join(map(smart_string, value)) 
    324383    except AttributeError: # fail silently but nicely 
    325384        return value 
     385    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 
     386    if safe_args: 
     387        return mark_safe(data) 
     388    else: 
     389        return data 
     390join.is_safe = True 
    326391 
    327392def length(value): 
    328393    "Returns the length of the value - useful for lists" 
    329394    return len(value) 
     395length.is_safe = False 
    330396 
    331397def length_is(value, arg): 
    332398    "Returns a boolean of whether the value's length is the argument" 
    333399    return len(value) == int(arg) 
     400length.is_safe = False 
    334401 
    335402def random(value): 
    336403    "Returns a random item from the list" 
    337404    return random_module.choice(value) 
     405length.is_safe = True 
    338406 
    339407def slice_(value, arg): 
    340408    """ 
     
    355423 
    356424    except (ValueError, TypeError): 
    357425        return value # Fail silently. 
     426slice_.is_safe = True 
    358427 
    359 def unordered_list(value): 
     428def unordered_list(value, autoescape = None): 
    360429    """ 
    361430    Recursively takes a self-nested list and returns an HTML unordered list -- 
    362431    WITHOUT opening and closing <ul> tags. 
     
    377446        </ul> 
    378447        </li> 
    379448    """ 
     449    if autoescape: 
     450        from django.utils.html import conditional_escape 
     451        escaper = conditional_escape 
     452    else: 
     453        escaper = lambda x: x 
     454 
    380455    def _helper(value, tabs): 
    381456        indent = '\t' * tabs 
    382457        if value[1]: 
    383             return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent, 
     458            return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent, 
    384459                '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 
    385460        else: 
    386             return '%s<li>%s</li>' % (indent, value[0]) 
    387     return _helper(value, 1) 
     461            return '%s<li>%s</li>' % (indent, escaper(value[0])) 
     462    return mark_safe(_helper(value, 1)) 
     463unordered_list.is_safe = True 
     464unordered_list.needs_autoescape = True 
    388465 
    389466################### 
    390467# INTEGERS        # 
     
    393470def add(value, arg): 
    394471    "Adds the arg to the value" 
    395472    return int(value) + int(arg) 
     473add.is_safe = False 
    396474 
    397475def get_digit(value, arg): 
    398476    """ 
     
    412490        return int(str(value)[-arg]) 
    413491    except IndexError: 
    414492        return 0 
     493get_digit.is_safe = False 
    415494 
    416495################### 
    417496# DATES           # 
     
    425504    if arg is None: 
    426505        arg = settings.DATE_FORMAT 
    427506    return format(value, arg) 
     507date.is_safe = False 
    428508 
    429509def time(value, arg=None): 
    430510    "Formats a time according to the given format" 
     
    434514    if arg is None: 
    435515        arg = settings.TIME_FORMAT 
    436516    return time_format(value, arg) 
     517time.is_safe = False 
    437518 
    438519def timesince(value, arg=None): 
    439520    'Formats a date as the time since that date (i.e. "4 days, 6 hours")' 
     
    443524    if arg: 
    444525        return timesince(arg, value) 
    445526    return timesince(value) 
     527timesince.is_safe = False 
    446528 
    447529def timeuntil(value, arg=None): 
    448530    'Formats a date as the time until that date (i.e. "4 days, 6 hours")' 
     
    453535    if arg: 
    454536        return timesince(arg, value) 
    455537    return timesince(datetime.now(), value) 
     538timeuntil.is_safe = False 
    456539 
    457540################### 
    458541# LOGIC           # 
     
    461544def default(value, arg): 
    462545    "If value is unavailable, use given default" 
    463546    return value or arg 
     547default.is_safe = False 
    464548 
    465549def default_if_none(value, arg): 
    466550    "If value is None, use given default" 
    467551    if value is None: 
    468552        return arg 
    469553    return value 
     554default_if_none.is_safe = False 
    470555 
    471556def divisibleby(value, arg): 
    472557    "Returns true if the value is devisible by the argument" 
    473558    return int(value) % int(arg) == 0 
     559divisibleby.is_safe = False 
    474560 
    475561def yesno(value, arg=None): 
    476562    """ 
     
    501587    if value: 
    502588        return yes 
    503589    return no 
     590yesno.is_safe = False 
    504591 
    505592################### 
    506593# MISC            # 
     
    523610    if bytes < 1024 * 1024 * 1024: 
    524611        return "%.1f MB" % (bytes / (1024 * 1024)) 
    525612    return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 
     613filesizeformat.is_safe = True 
    526614 
    527615def pluralize(value, arg='s'): 
    528616    """ 
     
    550638        except TypeError: # len() of unsized object 
    551639            pass 
    552640    return singular_suffix 
     641pluralize.is_safe = False 
    553642 
    554643def phone2numeric(value): 
    555644    "Takes a phone number and converts it in to its numerical equivalent" 
    556645    from django.utils.text import phone2numeric 
    557646    return phone2numeric(value) 
     647phone2numeric.is_safe = True 
    558648 
    559649def pprint(value): 
    560650    "A wrapper around pprint.pprint -- for debugging, really" 
     
    563653        return pformat(value) 
    564654    except Exception, e: 
    565655        return "Error in formatting:%s" % e 
     656pprint.is_safe = True 
    566657 
    567658# Syntax: register.filter(name of filter, callback) 
    568659register.filter(add) 
     
    581672register.filter(first) 
    582673register.filter(fix_ampersands) 
    583674register.filter(floatformat) 
     675register.filter(force_escape) 
    584676register.filter(get_digit) 
    585677register.filter(join) 
    586678register.filter(length) 
     
    597689register.filter(removetags) 
    598690register.filter(random) 
    599691register.filter(rjust) 
     692register.filter(safe) 
    600693register.filter('slice', slice_) 
    601694register.filter(slugify) 
    602695register.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 %}`` 
     
    457487 
    458488    Sample usage:: 
    459489 
    460         {% filter escape|lower %} 
     490        {% filter force_escape|lower %} 
    461491            This text will be HTML-escaped, and will appear in lowercase. 
    462492        {% endfilter %} 
    463493    """ 
    464494    _, rest = token.contents.split(None, 1) 
    465495    filter_expr = parser.compile_filter("var|%s" % (rest)) 
     496    for func, unused in filter_expr.filters: 
     497        if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'): 
     498            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__) 
    466499    nodelist = parser.parse(('endfilter',)) 
    467500    parser.delete_first_token() 
    468501    return FilterNode(filter_expr, nodelist) 
     
    704737ifchanged = register.tag(ifchanged) 
    705738 
    706739#@register.tag 
     740def noautoescape(parser, token): 
     741    """ 
     742    Force autoescape behaviour to be disabled for this block. 
     743    """ 
     744    nodelist = parser.parse(('endnoautoescape',)) 
     745    parser.delete_first_token() 
     746    return AutoEscapeControlNode(False, nodelist) 
     747autoescape = register.tag(noautoescape) 
     748 
     749#@register.tag 
    707750def ssi(parser, token): 
    708751    """ 
    709752    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    &