Ticket #2359: autoescape.diff
File autoescape.diff, 44.4 KB (added by , 18 years ago) |
---|
-
django/contrib/markup/templatetags/markup.py
diff -urN --exclude-from=django.exclue django_src_current/django/contrib/markup/templatetags/markup.py django_autoescape/django/contrib/markup/templatetags/markup.py
old new 16 16 17 17 from django import template 18 18 from django.conf import settings 19 from django.utils.safestring import mark_safe 19 20 20 21 register = template.Library() 21 22 … … 25 26 except ImportError: 26 27 if settings.DEBUG: 27 28 raise template.TemplateSyntaxError, "Error in {% textile %} filter: The Python textile library isn't installed." 28 return value29 return mark_safe(value) 29 30 else: 30 return textile.textile(value, encoding=settings.DEFAULT_CHARSET, output=settings.DEFAULT_CHARSET) 31 return mark_safe(textile.textile(value, encoding=settings.DEFAULT_CHARSET, output=settings.DEFAULT_CHARSET)) 32 textile.is_safe = True 31 33 32 34 def markdown(value): 33 35 try: … … 35 37 except ImportError: 36 38 if settings.DEBUG: 37 39 raise template.TemplateSyntaxError, "Error in {% markdown %} filter: The Python markdown library isn't installed." 38 return value40 return mark_safe(value) 39 41 else: 40 return markdown.markdown(value) 42 return mark_safe(markdown.markdown(value)) 43 markdown.is_safe = True 41 44 42 45 def restructuredtext(value): 43 46 try: … … 45 48 except ImportError: 46 49 if settings.DEBUG: 47 50 raise template.TemplateSyntaxError, "Error in {% restructuredtext %} filter: The Python docutils library isn't installed." 48 return value51 return mark_safe(value) 49 52 else: 50 53 docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {}) 51 54 parts = publish_parts(source=value, writer_name="html4css1", settings_overrides=docutils_settings) 52 return parts["fragment"] 55 return mark_safe(parts["fragment"]) 56 restructuredtext.is_safe = True 53 57 54 58 register.filter(textile) 55 59 register.filter(markdown) -
django/template/context.py
diff -urN --exclude-from=django.exclue django_src_current/django/template/context.py django_autoescape/django/template/context.py
old new 9 9 10 10 class Context(object): 11 11 "A stack container for variable context" 12 13 autoescape = False 14 12 15 def __init__(self, dict_=None): 13 16 dict_ = dict_ or {} 14 17 self.dicts = [dict_] … … 95 98 processors = tuple(processors) 96 99 for processor in get_standard_processors() + processors: 97 100 self.update(processor(request)) 101 102 class SafeContext(Context): 103 "A Context derivative that autoescapes by default." 104 autoescape = True 105 106 class SafeRequestContext(RequestContext): 107 autoescape = True -
django/template/defaultfilters.py
diff -urN --exclude-from=django.exclue django_src_current/django/template/defaultfilters.py django_autoescape/django/template/defaultfilters.py
old new 3 3 from django.template import resolve_variable, Library 4 4 from django.conf import settings 5 5 from django.utils.translation import gettext 6 from django.utils.safestring import mark_safe, SafeData 6 7 import re 7 8 import random as random_module 8 9 … … 16 17 def addslashes(value): 17 18 "Adds slashes - useful for passing strings to JavaScript, for example." 18 19 return value.replace('"', '\\"').replace("'", "\\'") 20 addslashes.is_safe = True 19 21 20 22 def capfirst(value): 21 23 "Capitalizes the first character of the value" 22 24 value = str(value) 23 25 return value and value[0].upper() + value[1:] 26 capfirst.is_safe = True 24 27 25 28 def fix_ampersands(value): 26 29 "Replaces ampersands with ``&`` entities" 27 30 from django.utils.html import fix_ampersands 28 31 return fix_ampersands(value) 32 fix_ampersands.is_safe = True 29 33 30 34 def floatformat(text): 31 35 """ … … 40 44 if m: 41 45 return '%.1f' % f 42 46 else: 43 return '%d' % int(f) 47 return mark_safe('%d' % int(f)) 48 floatformat.is_safe = True 44 49 45 def linenumbers(value ):50 def linenumbers(value, autoescape = None): 46 51 "Displays text with line numbers" 47 52 from django.utils.html import escape 48 53 lines = value.split('\n') 49 54 # Find the maximum width of the line count, for use with zero padding string format command 50 55 width = str(len(str(len(lines)))) 51 for i, line in enumerate(lines): 52 lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) 53 return '\n'.join(lines) 56 if not autoescape or isinstance(value, SafeData): 57 for i, line in enumerate(lines): 58 lines[i] = ("%0" + width + "d. %s") % (i + 1, line) 59 else: 60 for i, line in enumerate(lines): 61 lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) 62 return mark_safe('\n'.join(lines)) 63 linenumbers.is_safe = True 64 linenumbers.needs_autoescape = True 54 65 55 66 def lower(value): 56 67 "Converts a string into all lowercase" 57 68 return value.lower() 69 lower.is_safe = True 58 70 59 71 def make_list(value): 60 72 """ … … 62 74 digits. For a string, it's a list of characters. 63 75 """ 64 76 return list(str(value)) 77 make_list.is_safe = False 65 78 66 79 def slugify(value): 67 80 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 68 81 value = re.sub('[^\w\s-]', '', value).strip().lower() 69 return re.sub('[-\s]+', '-', value) 82 return mark_safe(re.sub('[-\s]+', '-', value)) 83 slugify.is_safe = True 70 84 71 85 def stringformat(value, arg): 72 86 """ … … 81 95 return ("%" + arg) % value 82 96 except (ValueError, TypeError): 83 97 return "" 98 stringformat.is_safe = True 84 99 85 100 def title(value): 86 101 "Converts a string into titlecase" 87 102 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 103 title.is_safe = False 88 104 89 105 def truncatewords(value, arg): 90 106 """ … … 100 116 if not isinstance(value, basestring): 101 117 value = str(value) 102 118 return truncate_words(value, length) 119 truncatewords.is_safe = True 103 120 104 121 def upper(value): 105 122 "Converts a string into all uppercase" 106 123 return value.upper() 124 upper.is_safe = False 107 125 108 126 def urlencode(value): 109 127 "Escapes a value for use in a URL" 110 128 import urllib 111 129 return urllib.quote(value) 130 urlencode.is_safe = False 112 131 113 132 def urlize(value): 114 133 "Converts URLs in plain text into clickable links" 115 134 from django.utils.html import urlize 116 return urlize(value, nofollow=True) 135 return mark_safe(urlize(value, nofollow=True)) 136 urlize.is_safe = True 117 137 118 138 def urlizetrunc(value, limit): 119 139 """ … … 123 143 Argument: Length to truncate URLs to. 124 144 """ 125 145 from django.utils.html import urlize 126 return urlize(value, trim_url_limit=int(limit), nofollow=True) 146 return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 147 urlize.is_safe = True 127 148 128 149 def wordcount(value): 129 150 "Returns the number of words" 130 151 return len(value.split()) 152 wordcount.is_safe = False 131 153 132 154 def wordwrap(value, arg): 133 155 """ … … 137 159 """ 138 160 from django.utils.text import wrap 139 161 return wrap(str(value), int(arg)) 162 wordwrap.is_safe = True 140 163 141 164 def ljust(value, arg): 142 165 """ … … 145 168 Argument: field size 146 169 """ 147 170 return str(value).ljust(int(arg)) 171 ljust.is_safe = True 148 172 149 173 def rjust(value, arg): 150 174 """ … … 153 177 Argument: field size 154 178 """ 155 179 return str(value).rjust(int(arg)) 180 rjust.is_safe = True 156 181 157 182 def center(value, arg): 158 183 "Centers the value in a field of a given width" 159 184 return str(value).center(int(arg)) 185 center.is_safe = True 160 186 161 187 def cut(value, arg): 162 188 "Removes all values of arg from the given string" 163 189 return value.replace(arg, '') 190 cut.is_safe = False 164 191 165 192 ################### 166 193 # HTML STRINGS # … … 168 195 169 196 def escape(value): 170 197 "Escapes a string's HTML" 171 from django.utils.html import escape 172 return escape(value) 198 if not isinstance(value, SafeData): 199 from django.utils.html import escape 200 return mark_safe(escape(value)) 201 else: 202 return value 203 escape.is_safe = True 173 204 174 def linebreaks(value ):205 def linebreaks(value, autoescape = None): 175 206 "Converts newlines into <p> and <br />s" 176 207 from django.utils.html import linebreaks 177 return linebreaks(value) 208 autoescape = autoescape and not isinstance(value, SafeData) 209 return mark_safe(linebreaks(value, autoescape)) 210 linebreaks.is_safe = True 211 linebreaks.needs_autoescape = True 178 212 179 def linebreaksbr(value ):213 def linebreaksbr(value, autoescape = None): 180 214 "Converts newlines into <br />s" 181 return value.replace('\n', '<br />') 215 if autoescape and not isinstance(value, SafeData): 216 from django.utils.html import escape 217 data = escape(value) 218 else: 219 data = value 220 return mark_safe(data.replace('\n', '<br />')) 221 linebreaksbr.is_safe = True 222 linebreaksbr.needs_autoescape = True 223 224 def noescape(value): 225 "Marks the value as a string that should not be auto-escaped." 226 from django.utils.safestring import mark_safe 227 return mark_safe(value) 228 noescape.is_safe = True 182 229 183 230 def removetags(value, tags): 184 231 "Removes a space separated list of [X]HTML tags from the output" … … 189 236 value = starttag_re.sub('', value) 190 237 value = endtag_re.sub('', value) 191 238 return value 239 removetags.is_safe = True 192 240 193 241 def striptags(value): 194 242 "Strips all [X]HTML tags" … … 196 244 if not isinstance(value, basestring): 197 245 value = str(value) 198 246 return strip_tags(value) 247 striptags.is_safe = True 199 248 200 249 ################### 201 250 # LISTS # … … 209 258 decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 210 259 decorated.sort() 211 260 return [item[1] for item in decorated] 261 dictsort.is_safe = False 212 262 213 263 def dictsortreversed(value, arg): 214 264 """ … … 219 269 decorated.sort() 220 270 decorated.reverse() 221 271 return [item[1] for item in decorated] 272 dictsortreversed.is_safe = False 222 273 223 274 def first(value): 224 275 "Returns the first item in a list" … … 226 277 return value[0] 227 278 except IndexError: 228 279 return '' 280 first.is_safe = True 229 281 230 282 def join(value, arg): 231 283 "Joins a list with a string, like Python's ``str.join(list)``" 232 284 try: 233 returnarg.join(map(str, value))285 data = arg.join(map(str, value)) 234 286 except AttributeError: # fail silently but nicely 235 287 return value 288 safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 289 if safe_args: 290 return mark_safe(data) 291 else: 292 return data 293 join.is_safe = True 236 294 237 295 def length(value): 238 296 "Returns the length of the value - useful for lists" 239 297 return len(value) 298 length.is_safe = False 240 299 241 300 def length_is(value, arg): 242 301 "Returns a boolean of whether the value's length is the argument" 243 302 return len(value) == int(arg) 303 length.is_safe = False 244 304 245 305 def random(value): 246 306 "Returns a random item from the list" 247 307 return random_module.choice(value) 308 length.is_safe = True 248 309 249 310 def slice_(value, arg): 250 311 """ … … 265 326 266 327 except (ValueError, TypeError): 267 328 return value # Fail silently. 329 slice_.is_safe = True 268 330 269 def unordered_list(value ):331 def unordered_list(value, autoescape = None): 270 332 """ 271 333 Recursively takes a self-nested list and returns an HTML unordered list -- 272 334 WITHOUT opening and closing <ul> tags. … … 287 349 </ul> 288 350 </li> 289 351 """ 352 if autoescape: 353 from django.utils.html import conditional_escape 354 escaper = conditional_escape 355 else: 356 escaper = lambda x: x 357 290 358 def _helper(value, tabs): 291 359 indent = '\t' * tabs 292 360 if value[1]: 293 return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent,361 return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent, 294 362 '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 295 363 else: 296 return '%s<li>%s</li>' % (indent, value[0]) 297 return _helper(value, 1) 364 return '%s<li>%s</li>' % (indent, escaper(value[0])) 365 return mark_safe(_helper(value, 1)) 366 unordered_list.is_safe = True 367 unordered_list.needs_autoescape = True 298 368 299 369 ################### 300 370 # INTEGERS # … … 303 373 def add(value, arg): 304 374 "Adds the arg to the value" 305 375 return int(value) + int(arg) 376 add.is_safe = False 306 377 307 378 def get_digit(value, arg): 308 379 """ … … 322 393 return int(str(value)[-arg]) 323 394 except IndexError: 324 395 return 0 396 get_digit.is_safe = False 325 397 326 398 ################### 327 399 # DATES # … … 335 407 if arg is None: 336 408 arg = settings.DATE_FORMAT 337 409 return format(value, arg) 410 date.is_safe = False 338 411 339 412 def time(value, arg=None): 340 413 "Formats a time according to the given format" … … 344 417 if arg is None: 345 418 arg = settings.TIME_FORMAT 346 419 return time_format(value, arg) 420 time.is_safe = False 347 421 348 422 def timesince(value, arg=None): 349 423 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' … … 353 427 if arg: 354 428 return timesince(arg, value) 355 429 return timesince(value) 430 timesince.is_safe = False 356 431 357 432 def timeuntil(value, arg=None): 358 433 'Formats a date as the time until that date (i.e. "4 days, 6 hours")' … … 363 438 if arg: 364 439 return timesince(arg, value) 365 440 return timesince(datetime.now(), value) 441 timeuntil.is_safe = False 366 442 367 443 ################### 368 444 # LOGIC # … … 371 447 def default(value, arg): 372 448 "If value is unavailable, use given default" 373 449 return value or arg 450 default.is_safe = False 374 451 375 452 def default_if_none(value, arg): 376 453 "If value is None, use given default" 377 454 if value is None: 378 455 return arg 379 456 return value 457 default_if_none.is_safe = False 380 458 381 459 def divisibleby(value, arg): 382 460 "Returns true if the value is devisible by the argument" 383 461 return int(value) % int(arg) == 0 462 divisibleby.is_safe = False 384 463 385 464 def yesno(value, arg=None): 386 465 """ … … 411 490 if value: 412 491 return yes 413 492 return no 493 yesno.is_safe = False 414 494 415 495 ################### 416 496 # MISC # … … 429 509 if bytes < 1024 * 1024 * 1024: 430 510 return "%.1f MB" % (bytes / (1024 * 1024)) 431 511 return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 512 filesizeformat.is_safe = True 432 513 433 514 def pluralize(value, arg='s'): 434 515 """ … … 456 537 except TypeError: # len() of unsized object 457 538 pass 458 539 return singular_suffix 540 pluralize.is_safe = False 459 541 460 542 def phone2numeric(value): 461 543 "Takes a phone number and converts it in to its numerical equivalent" 462 544 from django.utils.text import phone2numeric 463 545 return phone2numeric(value) 546 phone2numeric.is_safe = True 464 547 465 548 def pprint(value): 466 549 "A wrapper around pprint.pprint -- for debugging, really" … … 469 552 return pformat(value) 470 553 except Exception, e: 471 554 return "Error in formatting:%s" % e 555 pprint.is_safe = True 472 556 473 557 # Syntax: register.filter(name of filter, callback) 474 558 register.filter(add) … … 497 581 register.filter(ljust) 498 582 register.filter(lower) 499 583 register.filter(make_list) 584 register.filter(noescape) 500 585 register.filter(phone2numeric) 501 586 register.filter(pluralize) 502 587 register.filter(pprint) -
django/template/defaulttags.py
diff -urN --exclude-from=django.exclue django_src_current/django/template/defaulttags.py django_autoescape/django/template/defaulttags.py
old new 4 4 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END 5 5 from django.template import get_library, Library, InvalidTemplateLibrary 6 6 from django.conf import settings 7 from django.utils.safestring import mark_safe 7 8 import sys 8 9 9 10 register = Library() 10 11 12 class AutoEscapeNode(Node): 13 def __init__(self, setting, nodelist): 14 self.setting, self.nodelist = setting, nodelist 15 16 def render(self, context): 17 old_setting = context.autoescape 18 context.autoescape = self.setting 19 output = self.nodelist.render(context) 20 context.autoescape = old_setting 21 if self.setting: 22 return mark_safe(output) 23 else: 24 return output 25 11 26 class CommentNode(Node): 12 27 def render(self, context): 13 28 return '' … … 318 333 return str(int(round(ratio))) 319 334 320 335 #@register.tag 336 def autoescape(parser, token): 337 """ 338 Set autoescape behaviour for this block. Possible values are "on" and "off". 339 """ 340 unused, rest = token.contents.split(None, 1) 341 if rest not in ('on', 'off'): 342 raise TemplateSyntaxError("autoescape argument must be 'on' or 'off'") 343 setting = (rest == 'on') 344 nodelist = parser.parse(('endautoescape',)) 345 parser.delete_first_token() 346 return AutoEscapeNode(setting, nodelist) 347 autoescape = register.tag(autoescape) 348 349 #@register.tag 321 350 def comment(parser, token): 322 351 """ 323 352 Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` … … 417 446 """ 418 447 _, rest = token.contents.split(None, 1) 419 448 filter_expr = parser.compile_filter("var|%s" % (rest)) 449 for func, unused in filter_expr.filters: 450 if func.__name__ == 'noescape': 451 raise TemplateSyntaxError('"filter noescape" is not permitted. Use the "autoescape" tag instead.') 420 452 nodelist = parser.parse(('endfilter',)) 421 453 parser.delete_first_token() 422 454 return FilterNode(filter_expr, nodelist) -
django/template/__init__.py
diff -urN --exclude-from=django.exclue django_src_current/django/template/__init__.py django_autoescape/django/template/__init__.py
old new 57 57 import re 58 58 from inspect import getargspec 59 59 from django.conf import settings 60 from django.template.context import Context, RequestContext, ContextPopException60 from django.template.context import Context, SafeContext, RequestContext, ContextPopException 61 61 from django.utils.functional import curry 62 62 from django.utils.text import smart_split 63 from django.utils.safestring import SafeData, mark_safe 64 from django.utils.html import escape 63 65 64 __all__ = ('Template', 'Context', ' RequestContext', 'compile_string')66 __all__ = ('Template', 'Context', 'SafeContext', 'RequestContext', 'compile_string') 65 67 66 68 TOKEN_TEXT = 0 67 69 TOKEN_VAR = 1 … … 558 560 arg_vals.append(arg) 559 561 else: 560 562 arg_vals.append(resolve_variable(arg, context)) 561 obj = func(obj, *arg_vals) 563 if getattr(func, 'needs_autoescape', False): 564 new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 565 else: 566 new_obj = func(obj, *arg_vals) 567 if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 568 obj = mark_safe(new_obj) 569 else: 570 obj = new_obj 571 562 572 return obj 563 573 564 574 def args_check(name, func, provided): … … 744 754 745 755 def render(self, context): 746 756 output = self.filter_expression.resolve(context) 747 return self.encode_output(output) 757 encoded_output = self.encode_output(output) 758 if context.autoescape and not isinstance(encoded_output, SafeData): 759 return escape(encoded_output) 760 else: 761 return encoded_output 748 762 749 763 class DebugVariableNode(VariableNode): 750 764 def render(self, context): … … 754 768 if not hasattr(e, 'source'): 755 769 e.source = self.source 756 770 raise 757 return self.encode_output(output) 771 encoded_output = self.encode_output(output) 772 if context.autoescape and not isinstance(encoded_output, SafeData): 773 return escape(encoded_output) 774 else: 775 return encoded_output 758 776 759 777 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 760 778 "Returns a template.Node subclass." -
django/utils/html.py
diff -urN --exclude-from=django.exclue django_src_current/django/utils/html.py django_autoescape/django/utils/html.py
old new 1 1 "HTML utilities suitable for global use." 2 2 3 3 import re, string 4 from django.utils.safestring import SafeData 4 5 5 6 # Configuration for urlize() function 6 7 LEADING_PUNCTUATION = ['(', '<', '<'] … … 27 28 html = str(html) 28 29 return html.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''') 29 30 30 def linebreaks(value): 31 def 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 38 def linebreaks(value, autoescape = False): 31 39 "Converts newlines into <p> and <br />s" 32 40 value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines 33 41 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] 35 46 return '\n\n'.join(paras) 36 47 37 48 def strip_tags(value): -
django/utils/safestring.py
diff -urN --exclude-from=django.exclue django_src_current/django/utils/safestring.py django_autoescape/django/utils/safestring.py
old new 1 """ 2 Functions for working with "safe strings": strings that can be display safely 3 without further modification in HTML. 4 """ 5 from django.utils.functional import curry 6 7 class SafeData(object): 8 pass 9 10 class SafeString(str, SafeData): 11 """ 12 A string subclass that has been specifically marked as "safe" for output 13 purposes. 14 """ 15 def __add__(self, rhs): 16 """ 17 Concatenating a safe string with another safe string or safe unicode 18 object is safe. Otherwise, the result is no longer safe. 19 """ 20 if isinstance(rhs, SafeUnicode): 21 return SafeUnicode(self + rhs) 22 elif isinstance(rhs, SafeString): 23 return SafeString(self, rhs) 24 else: 25 return super(SafeString, self).__add__(rhs) 26 27 class SafeUnicode(unicode, SafeData): 28 """ 29 A unicode subclass that has been specifically marked as "safe" for output 30 purposes. 31 """ 32 def __add__(self, rhs): 33 """ 34 Concatenating a safe unicode object with another safe string or safe 35 unicode object is safe. Otherwise, the result is no longer safe. 36 """ 37 if isinstance(rhs, SafeData): 38 return SafeUnicode(self + rhs) 39 else: 40 return super(SafeUnicode, self).__add__(rhs) 41 42 def _proxy_method(self, *args, **kwargs): 43 """ 44 Wrap a call to a normal unicode method up so that we return safe 45 results. The method that is being wrapped is passed in the 'method' 46 argument. 47 """ 48 method = kwargs.pop('method') 49 data = method(self, *args, **kwargs) 50 if isinstance(data, str): 51 return SafeString(data) 52 else: 53 return SafeUnicode(data) 54 55 encode = curry(_proxy_method, method = unicode.encode) 56 decode = curry(_proxy_method, method = unicode.decode) 57 58 59 def mark_safe(s): 60 """ 61 Explicitly mark a string as safe for output purposes. The returned object 62 can be used everywhere a string or unicode object is appropriate. 63 64 Can safely be called multiple times on a single string. 65 """ 66 if isinstance(s, SafeData): 67 return s 68 if isinstance(s, str): 69 return SafeString(s) 70 if isinstance(s, unicode): 71 return SafeUnicode(s) 72 raise TypeError, "Input must be str or unicode (got %s)" % type(s) 73 -
django/views/debug.py
diff -urN --exclude-from=django.exclue django_src_current/django/views/debug.py django_autoescape/django/views/debug.py
old new 1 1 from django.conf import settings 2 from django.template import Template, Context, TemplateDoesNotExist2 from django.template import Template, Context, SafeContext, TemplateDoesNotExist 3 3 from django.utils.html import escape 4 4 from django.http import HttpResponseServerError, HttpResponseNotFound 5 5 import os, re … … 118 118 'lineno': '?', 119 119 }] 120 120 t = Template(TECHNICAL_500_TEMPLATE) 121 c = Context({121 c = SafeContext({ 122 122 'exception_type': exc_type.__name__, 123 123 'exception_value': exc_value, 124 124 'frames': frames, … … 157 157 def empty_urlconf(request): 158 158 "Create an empty URLconf 404 error response." 159 159 t = Template(EMPTY_URLCONF_TEMPLATE) 160 c = Context({160 c = SafeContext({ 161 161 'project_name': settings.SETTINGS_MODULE.split('.')[0] 162 162 }) 163 163 return HttpResponseNotFound(t.render(c), mimetype='text/html') … … 295 295 296 296 <div id="summary"> 297 297 <h1>{{ exception_type }} at {{ request.path }}</h1> 298 <h2>{{ exception_value |escape}}</h2>298 <h2>{{ exception_value }}</h2> 299 299 <table class="meta"> 300 300 <tr> 301 301 <th>Request Method:</th> … … 340 340 <div id="template"> 341 341 <h2>Template error</h2> 342 342 <p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p> 343 <h3>{{ template_info.message |escape}}</h3>343 <h3>{{ template_info.message }}</h3> 344 344 <table class="source{% if template_info.top %} cut-top{% endif %}{% ifnotequal template_info.bottom template_info.total %} cut-bottom{% endifnotequal %}"> 345 345 {% for source_line in template_info.source_lines %} 346 346 {% ifequal source_line.0 template_info.line %} … … 367 367 {% if frame.context_line %} 368 368 <div class="context" id="c{{ frame.id }}"> 369 369 {% if frame.pre_context %} 370 <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line |escape}}</li>{% endfor %}</ol>370 <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line }}</li>{% endfor %}</ol> 371 371 {% endif %} 372 <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line |escape}} <span>...</span></li></ol>372 <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line }} <span>...</span></li></ol> 373 373 {% if frame.post_context %} 374 <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line |escape}}</li>{% endfor %}</ol>374 <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line }}</li>{% endfor %}</ol> 375 375 {% endif %} 376 376 </div> 377 377 {% endif %} … … 391 391 {% for var in frame.vars|dictsort:"0" %} 392 392 <tr> 393 393 <td>{{ var.0 }}</td> 394 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>394 <td class="code"><div>{{ var.1|pprint }}</div></td> 395 395 </tr> 396 396 {% endfor %} 397 397 </tbody> … … 411 411 {% for frame in frames %} 412 412 File "{{ frame.filename }}" in {{ frame.function }}<br/> 413 413 {% if frame.context_line %} 414 {{ frame.lineno }}. {{ frame.context_line |escape}}<br/>414 {{ frame.lineno }}. {{ frame.context_line }}<br/> 415 415 {% endif %} 416 416 {% endfor %}<br/> 417 417 {{ exception_type }} at {{ request.path }}<br/> 418 {{ exception_value |escape}}</code>418 {{ exception_value }}</code> 419 419 </td> 420 420 </tr> 421 421 </tbody> … … 439 439 {% for var in request.GET.items %} 440 440 <tr> 441 441 <td>{{ var.0 }}</td> 442 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>442 <td class="code"><div>{{ var.1|pprint }}</div></td> 443 443 </tr> 444 444 {% endfor %} 445 445 </tbody> … … 461 461 {% for var in request.POST.items %} 462 462 <tr> 463 463 <td>{{ var.0 }}</td> 464 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>464 <td class="code"><div>{{ var.1|pprint }}</div></td> 465 465 </tr> 466 466 {% endfor %} 467 467 </tbody> … … 483 483 {% for var in request.COOKIES.items %} 484 484 <tr> 485 485 <td>{{ var.0 }}</td> 486 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>486 <td class="code"><div>{{ var.1|pprint }}</div></td> 487 487 </tr> 488 488 {% endfor %} 489 489 </tbody> … … 504 504 {% for var in request.META.items|dictsort:"0" %} 505 505 <tr> 506 506 <td>{{ var.0 }}</td> 507 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>507 <td class="code"><div>{{ var.1|pprint }}</div></td> 508 508 </tr> 509 509 {% endfor %} 510 510 </tbody> … … 523 523 {% for var in settings.items|dictsort:"0" %} 524 524 <tr> 525 525 <td>{{ var.0 }}</td> 526 <td class="code"><div>{{ var.1|pprint |escape}}</div></td>526 <td class="code"><div>{{ var.1|pprint }}</div></td> 527 527 </tr> 528 528 {% endfor %} 529 529 </tbody> … … 590 590 </p> 591 591 <ol> 592 592 {% for pattern in urlpatterns %} 593 <li>{{ pattern |escape}}</li>593 <li>{{ pattern }}</li> 594 594 {% endfor %} 595 595 </ol> 596 596 <p>The current URL, <code>{{ request.path }}</code>, didn't match any of these.</p> 597 597 {% else %} 598 <p>{{ reason |escape}}</p>598 <p>{{ reason }}</p> 599 599 {% endif %} 600 600 </div> 601 601 -
tests/othertests/autoescape.py
diff -urN --exclude-from=django.exclue django_src_current/tests/othertests/autoescape.py django_autoescape/tests/othertests/autoescape.py
old new 1 from django.conf import settings 2 3 if __name__ == '__main__': 4 # When running this file in isolation, we need to set up the configuration 5 # before importing 'template'. 6 settings.configure() 7 8 import templates 9 from django import template 10 from django.template import loader 11 from django.utils.translation import activate, deactivate, install 12 from django.utils.tzinfo import LocalTimezone 13 from django.utils.safestring import mark_safe 14 from datetime import datetime, timedelta 15 import traceback 16 17 # We want to check all the normal template tests against SafeContext by 18 # default. We just update results that would change with autoescaping and add 19 # in new tests. 20 TEMPLATE_TESTS = templates.TEMPLATE_TESTS.copy() 21 22 # SYNTAX -- 23 # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class) 24 TEMPLATE_TESTS.update({ 25 26 ### BASIC SYNTAX ########################################################## 27 28 # Escaped string as argument (this replaces the non-escaped version) 29 'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), 30 31 # Autoescaping is applied by default 32 'autoescape-basic01': ("{{ first }}", {"first": "<b>first</b>"}, "<b>first</b>"), 33 34 # Strings (ASCII or unicode) already marked as "safe" are not auto-escaped 35 'autoescape-basic02': ("{{ first }}", {"first": mark_safe("<b>first</b>")}, "<b>first</b>"), 36 'autoescape-basic03': ("{{ first }}", {"first": mark_safe(u"<b>Apple</b>")}, u"<b>Apple</b>"), 37 38 39 ### AUTOESCAPE TAG ######################################################## 40 'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"), 41 'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), 42 'autoescape-tag03': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), 43 44 ### FILTER TAG ############################################################ 45 46 # The "noescape" filter cannot work due to internal implementation details 47 # (fortunately, it is equivalent to using the autoescape tag in these 48 # cases). 49 'autoescape-filtertag01': ("{{ first }}{% filter noescape %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError), 50 51 ### FILTER TESTS ########################################################## 52 53 'ae-filter-addslash01': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"<a>\' <a>\'"), 54 55 'ae-filter-capfirst01': ("{{ a|capfirst }} {{ b|capfirst }}", {"a": "fred>", "b": mark_safe("fred>")}, "Fred> Fred>"), 56 57 # Note that applying fix_ampsersands in autoescape mode leads to double 58 # escaping. 59 'ae-filter-fix_ampersands01': ("{{ a|fix_ampersands }} {{ b|fix_ampersands }}", {"a": "a&b", "b": mark_safe("a&b")}, "a&amp;b a&b"), 60 61 'ae-filter-floatformat01': ("{{ a|floatformat }} {{ b|floatformat }}", {"a": "1.42", "b": mark_safe("1.42")}, "1.4 1.4"), 62 63 # The contents of "linenumbers" is escaped according to the current 64 # autoescape setting. 65 'ae-filter-linenumbers01': ("{{ a|linenumbers }} {{ b|linenumbers }}", {"a": "one\n<two>\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. <two>\n3. three"), 66 'ae-filter-linenumbers02': ("{% autoescape off %}{{ a|linenumbers }} {{ b|linenumbers }}{% endautoescape %}", {"a": "one\n<two>\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. <two>\n3. three"), 67 68 'ae-filter-lower01': ("{{ a|lower }} {{ b|lower }}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, "apple & banana apple & banana"), 69 70 # The make_list filter can destroy # existing encoding, so the results are 71 # escaped. 72 'ae-filter-make_list01': ("{{ a|make_list }}", {"a": mark_safe("&")}, "['&']"), 73 'ae-filter-make_list02': ('{{ a|make_list|stringformat:"s"|noescape }}', {"a": mark_safe("&")}, "['&']"), 74 75 # Running slugify on a pre-escaped string leads to odd behaviour, but the 76 # result is still safe. 77 'ae-filter-slugify01': ("{{ a|slugify }} {{ b|slugify }}", {"a": "a & b", "b": mark_safe("a & b")}, "a-b a-amp-b"), 78 79 # Notice that escaping is applied *after* any filters, so the string 80 # formatting here only needs to deal with pre-escaped characters. 81 'ae-filter-stringformat01': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")}, ". a<b. . a<b."), 82 83 # XXX No test for "title" filter; needs an actual object. 84 85 'ae-filter-truncatewords01': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}', {"a": "alpha & bravo", "b": mark_safe("alpha & bravo")}, "alpha & ... alpha & ..."), 86 87 # The "upper" filter messes up entities (which are case-sensitive), so it's 88 # not safe for non-escaping purposes. 89 'ae-filter-upper01': ('{{ a|upper }} {{ b|upper }}', {"a": "a & b", "b": mark_safe("a & b")}, "A & B A &AMP; B"), 90 91 'ae-filter-urlize01': ('{{ a|urlize }} {{ b|urlize }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, '<a href="http://example.com/x=&y=" rel="nofollow">http://example.com/x=&y=</a> <a href="http://example.com?x=&y=" rel="nofollow">http://example.com?x=&y=</a>'), 92 'ae-filter-urlize02': ('{{ a|urlize }}', {"a": mark_safe("a & b")}, 'a & b'), 93 94 'ae-filter-urlizetrunc01': ('{{ a|urlizetrunc:"5" }} {{ b|urlizetrunc:"5" }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, '<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'), 95 96 'ae-filter-wordcount01': ('{{ a|wordcount }} {{ b|wordcount }}', {"a": "a & b", "b": mark_safe("a & b")}, "3 3"), 97 98 'ae-filter-wordwrap01': ('{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}', {"a": "a & b", "b": mark_safe("a & b")}, "a &\nb a &\nb"), 99 100 'ae-filter-ljust01': ('.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ".a&b . .a&b ."), 101 102 'ae-filter-rjust01': ('.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b. . a&b."), 103 104 'ae-filter-center01': ('.{{ a|center:"5" }}. .{{ b|center:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b . . a&b ."), 105 106 # Because "cut" might remove a leading ampersand, so the results are not 107 # safe. 108 'ae-filter-cut01': ('{{ a|cut:"x" }} {{ b|cut:"x" }}', {"a": "x&y", "b": mark_safe("x&y")}, "&y &amp;y"), 109 'ae-filter-cut02': ('{{ a|cut:"&" }} {{ b|cut:"&" }}', {"a": "x&y", "b": mark_safe("x&y")}, "xy xamp;y"), 110 111 # The "escape" filter works the same whether autoescape is on or off, but 112 # it has no effect on strings already marked as safe. 113 'ae-filter-escape01': ('{{ a|escape }} {{ b|escape }}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"), 114 'ae-filter-escape02': ('{% autoescape off %}{{ a|escape }} {{ b|escape }}{% endautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"), 115 'ae-filter-escape03': ('{{ a|escape|escape }}', {"a": "x&y"}, "x&y"), 116 117 # The contents in "linebreaks" and "linebreaksbr" are escaped according to 118 # the current autoescape setting. 119 'ae-filter-linebreaks01': ('{{ a|linebreaks }} {{ b|linebreaks }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "<p>x&<br />y</p> <p>x&<br />y</p>"), 120 'ae-filter-linebreaks02': ('{% autoescape off %}{{ a|linebreaks }} {{ b|linebreaks }}{% endautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "<p>x&<br />y</p> <p>x&<br />y</p>"), 121 122 'ae-filter-linebreaksbr01': ('{{ a|linebreaksbr }} {{ b|linebreaksbr }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&<br />y x&<br />y"), 123 'ae-filter-linebreaksbr02': ('{% autoescape off %}{{ a|linebreaksbr }} {{ b|linebreaksbr }}{% endautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&<br />y x&<br />y"), 124 125 'ae-filter-noescape01': ("{{ a }} -- {{ a|noescape }}", {"a": "<b>hello</b>"}, "<b>hello</b> -- <b>hello</b>"), 126 'ae-filter-noescape02': ("{% autoescape off %}{{ a }} -- {{ a|noescape }}{% endautoescape %}", {"a": "<b>hello</b>"}, "<b>hello</b> -- <b>hello</b>"), 127 128 'ae-filter-removetags01': ('{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}', {"a": "<a>x</a> <p><b>y</b></p>", "b": mark_safe("<a>x</a> <p><b>y</b></p>")}, "x <p>y</p> x <p>y</p>"), 129 130 'ae-filter-striptags01': ('{{ a|striptags }} {{ b|striptags }}', {"a": "<a>x</a> <p><b>y</b></p>", "b": mark_safe("<a>x</a> <p><b>y</b></p>")}, "x y x y"), 131 132 'ae-filter-first01': ('{{ a|first }} {{ b|first }}', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}, "a&b a&b"), 133 134 'ae-filter-random01': ('{{ a|random }} {{ b|random }}', {"a": ["a&b", "a&b"], "b": [mark_safe("a&b"), mark_safe("a&b")]}, "a&b a&b"), 135 136 'ae-filter-slice01': ('{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}', {"a": "a&b", "b": mark_safe("a&b")}, "&b &b"), 137 138 'ae-filter-unordered_list01': ('{{ a|unordered_list }}', {"a": ["x>", [["<y", []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 139 'ae-filter-unordered_list02': ('{{ a|unordered_list }}', {"a": ["x>", [[mark_safe("<y"), []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 140 'ae-filter-unordered_list03': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["<y", []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 141 142 # If the input to "default" filter is marked as safe, then so is the 143 # output. However, if the default arg is used, auto-escaping kicks in (if 144 # enabled), because we cannot mark the default as safe. 145 # Note: we have to use {"a": ""} here, otherwise the invalid template 146 # variable string interferes with the test result. 147 'ae-filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), 148 'ae-filter-default02': ('{{ a|default:"x<" }}', {"a": mark_safe("x>")}, "x>"), 149 150 'ae-filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), 151 152 'ae-filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"), 153 154 # Chaining a bunch of safeness-preserving filters should not alter the safe 155 # status either way. 156 'ae-chaining01': ('{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}', {"a": "a < b", "b": mark_safe("a < b")}, " A < b . A < b "), 157 158 # Using a filter that forces a string back to unsafe: 159 'ae-chaining02': ('{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}', {"a": "a < b", "b": mark_safe("a < b")}, "A < .A < "), 160 161 # Using a filter that forces safeness does not lead to double-escaping 162 'ae-chaining03': ('{{ a|escape|capfirst }}', {"a": "a < b"}, "A < b"), 163 164 # Force to safe, then back (also showing why using escape too early in a 165 # chain can lead to unexpected results). 166 'ae-chaining04': ('{{ a|escape|cut:"b" }}', {"a": "a < b"}, "a &lt; "), 167 'ae-chaining05': ('{{ a|cut:"b"|escape }}', {"a": "a < b"}, "a < "), 168 'ae-chaining06': ('{{ a|cut:"b"|noescape }}', {"a": "a < b"}, "a < "), 169 'ae-chaining07': ('{{ a|noescape|escape }}', {"a": "a < b"}, "a < b"), 170 }) 171 172 def test_template_loader(template_name, template_dirs=None): 173 "A custom template loader that loads the unit-test templates." 174 try: 175 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 176 except KeyError: 177 raise template.TemplateDoesNotExist, template_name 178 179 def run_tests(verbosity=0, standalone=False): 180 # Register our custom template loader. 181 old_template_loaders = loader.template_source_loaders 182 loader.template_source_loaders = [test_template_loader] 183 184 failed_tests = [] 185 tests = TEMPLATE_TESTS.items() 186 tests.sort() 187 188 # Turn TEMPLATE_DEBUG off, because tests assume that. 189 old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 190 # Set TEMPLATE_STRING_IF_INVALID to a known string 191 old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID' 192 193 for name, vals in tests: 194 install() 195 if 'LANGUAGE_CODE' in vals[1]: 196 activate(vals[1]['LANGUAGE_CODE']) 197 else: 198 activate('en-us') 199 try: 200 output = loader.get_template(name).render(template.SafeContext(vals[1])) 201 except Exception, e: 202 if e.__class__ == vals[2]: 203 if verbosity: 204 print "Template test: %s -- Passed" % name 205 else: 206 if verbosity: 207 traceback.print_exc() 208 print "Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e) 209 failed_tests.append(name) 210 continue 211 if 'LANGUAGE_CODE' in vals[1]: 212 deactivate() 213 if output == vals[2]: 214 if verbosity: 215 print "Template test: %s -- Passed" % name 216 else: 217 if verbosity: 218 print "Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output) 219 failed_tests.append(name) 220 loader.template_source_loaders = old_template_loaders 221 deactivate() 222 settings.TEMPLATE_DEBUG = old_td 223 settings.TEMPLATE_STRING_IF_INVALID = old_invalid 224 225 if failed_tests and not standalone: 226 msg = "Template tests %s failed." % failed_tests 227 if not verbosity: 228 msg += " Re-run tests with -v1 to see actual failures" 229 raise Exception, msg 230 231 if __name__ == "__main__": 232 run_tests(1, True)