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) |
|---|
-
a/django/oldforms/__init__.py
old new 1 1 from django.core import validators 2 2 from django.core.exceptions import PermissionDenied 3 3 from django.utils.html import escape 4 from django.utils.safestring import mark_safe 4 5 from django.conf import settings 5 6 from django.utils.translation import gettext, ngettext 6 7 … … 181 182 182 183 def html_error_list(self): 183 184 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()])) 185 186 else: 186 return ''187 return mark_safe('') 187 188 188 189 def get_id(self): 189 190 return self.formfield.get_id() … … 215 216 return bool(len(self.errors())) 216 217 217 218 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')])) 219 220 220 221 class InlineObjectCollection(object): 221 222 "An object that acts like a sparse list of form field collections." … … 399 400 maxlength = 'maxlength="%s" ' % self.maxlength 400 401 if isinstance(data, unicode): 401 402 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/>' % \ 403 404 (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)) 405 406 406 407 def html2python(data): 407 408 return data … … 425 426 data = '' 426 427 if isinstance(data, unicode): 427 428 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>' % \ 429 430 (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))) 431 432 432 433 class HiddenField(FormField): 433 434 def __init__(self, field_name, is_required=False, validator_list=None): … … 436 437 self.validator_list = validator_list[:] 437 438 438 439 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))) 441 442 442 443 class CheckboxField(FormField): 443 444 def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False): … … 451 452 checked_html = '' 452 453 if data or (data is '' and self.checked_by_default): 453 454 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 />' % \ 455 456 (self.get_id(), self.__class__.__name__, 456 self.field_name, checked_html) 457 self.field_name, checked_html)) 457 458 458 459 def html2python(data): 459 460 "Convert value from browser ('on' or '') to a Python boolean" … … 484 485 selected_html = ' selected="selected"' 485 486 output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) 486 487 output.append(' </select>') 487 return '\n'.join(output)488 return mark_safe('\n'.join(output)) 488 489 489 490 def isValidChoice(self, data, form): 490 491 str_data = str(data) … … 537 538 output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')] 538 539 output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 539 540 output.append('</ul>') 540 return ''.join(output)541 return mark_safe(''.join(output)) 541 542 def __iter__(self): 542 543 for d in self.datalist: 543 544 yield d … … 552 553 datalist.append({ 553 554 'value': value, 554 555 '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>' % \ 558 559 (self.get_id() + '_' + str(i), display_name), 559 })560 )}) 560 561 return RadioFieldRenderer(datalist, self.ul_class) 561 562 562 563 def isValidChoice(self, data, form): … … 595 596 selected_html = ' selected="selected"' 596 597 output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) 597 598 output.append(' </select>') 598 return '\n'.join(output)599 return mark_safe('\n'.join(output)) 599 600 600 601 def isValidChoice(self, field_data, all_data): 601 602 # data is something like ['1', '2', '3'] … … 648 649 (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html, 649 650 self.get_id() + escape(value), choice)) 650 651 output.append('</ul>') 651 return '\n'.join(output)652 return mark_safe('\n'.join(output)) 652 653 653 654 #################### 654 655 # FILE UPLOADS # … … 669 670 raise validators.CriticalValidationError, gettext("The submitted file is empty.") 670 671 671 672 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)) 674 675 675 676 def html2python(data): 676 677 if data is None: -
a/django/template/__init__.py
old new 60 60 from django.template.context import Context, 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, EscapeData, mark_safe, mark_for_escaping 64 from django.utils.html import escape 63 65 64 66 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 65 67 … … 574 576 arg_vals.append(arg) 575 577 else: 576 578 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 578 590 return obj 579 591 580 592 def args_check(name, func, provided): … … 766 778 767 779 def render(self, context): 768 780 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 770 786 771 787 class DebugVariableNode(VariableNode): 772 788 def render(self, context): … … 776 792 if not hasattr(e, 'source'): 777 793 e.source = self.source 778 794 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 780 800 781 801 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 782 802 "Returns a template.Node subclass." -
a/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_] … … 98 101 processors = tuple(processors) 99 102 for processor in get_standard_processors() + processors: 100 103 self.update(processor(request)) 104 -
a/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 … … 49 50 "Adds slashes - useful for passing strings to JavaScript, for example." 50 51 return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 51 52 addslashes = stringfilter(addslashes) 53 addslashes.is_safe = True 52 54 53 55 def capfirst(value): 54 56 "Capitalizes the first character of the value" 55 57 return value and value[0].upper() + value[1:] 56 58 capfirst = stringfilter(capfirst) 57 59 capfirst.is_safe = True 60 58 61 def fix_ampersands(value): 59 62 "Replaces ampersands with ``&`` entities" 60 63 from django.utils.html import fix_ampersands 61 64 return fix_ampersands(value) 62 65 fix_ampersands = stringfilter(fix_ampersands) 66 fix_ampersands.is_safe = True 63 67 64 68 def floatformat(text, arg=-1): 65 69 """ … … 92 96 return '%d' % int(f) 93 97 else: 94 98 formatstr = '%%.%df' % abs(d) 95 return formatstr % f 99 return mark_safe(formatstr % f) 100 floatformat.is_safe = True 96 101 97 def linenumbers(value ):102 def linenumbers(value, autoescape = None): 98 103 "Displays text with line numbers" 99 104 from django.utils.html import escape 100 105 lines = value.split('\n') 101 106 # Find the maximum width of the line count, for use with zero padding string format command 102 107 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)) 106 115 linenumbers = stringfilter(linenumbers) 116 linenumbers.is_safe = True 117 linenumbers.needs_autoescape = True 107 118 108 119 def lower(value): 109 120 "Converts a string into all lowercase" 110 121 return value.lower() 111 122 lower = stringfilter(lower) 123 lower.is_safe = True 112 124 113 125 def make_list(value): 114 126 """ … … 117 129 """ 118 130 return list(value) 119 131 make_list = stringfilter(make_list) 132 make_list.is_safe = False 120 133 121 134 def slugify(value): 122 135 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 123 136 value = re.sub('[^\w\s-]', '', value).strip().lower() 124 return re.sub('[-\s]+', '-', value)137 return mark_safe(re.sub('[-\s]+', '-', value)) 125 138 slugify = stringfilter(slugify) 139 slugify.is_safe = True 126 140 127 141 def stringformat(value, arg): 128 142 """ … … 137 151 return ("%" + str(arg)) % value 138 152 except (ValueError, TypeError): 139 153 return "" 154 stringformat.is_safe = True 140 155 141 156 def title(value): 142 157 "Converts a string into titlecase" 143 158 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 144 159 title = stringfilter(title) 160 title.is_safe = False 145 161 146 162 def truncatewords(value, arg): 147 163 """ … … 158 174 value = str(value) 159 175 return truncate_words(value, length) 160 176 truncatewords = stringfilter(truncatewords) 177 truncatewords.is_safe = True 161 178 162 179 def truncatewords_html(value, arg): 163 180 """ … … 179 196 "Converts a string into all uppercase" 180 197 return value.upper() 181 198 upper = stringfilter(upper) 199 upper.is_safe = False 182 200 183 201 def urlencode(value): 184 202 "Escapes a value for use in a URL" … … 187 205 value = str(value) 188 206 return urllib.quote(value) 189 207 urlencode = stringfilter(urlencode) 208 urlencode.is_safe = False 190 209 191 210 def urlize(value): 192 211 "Converts URLs in plain text into clickable links" 193 212 from django.utils.html import urlize 194 return urlize(value, nofollow=True)213 return mark_safe(urlize(value, nofollow=True)) 195 214 urlize = stringfilter(urlize) 215 urlize.is_safe = True 196 216 197 217 def urlizetrunc(value, limit): 198 218 """ … … 202 222 Argument: Length to truncate URLs to. 203 223 """ 204 224 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)) 206 226 urlizetrunc = stringfilter(urlizetrunc) 227 urlize.is_safe = True 207 228 208 229 def wordcount(value): 209 230 "Returns the number of words" 210 231 return len(value.split()) 211 232 wordcount = stringfilter(wordcount) 233 wordcount.is_safe = False 212 234 213 235 def wordwrap(value, arg): 214 236 """ … … 219 241 from django.utils.text import wrap 220 242 return wrap(value, int(arg)) 221 243 wordwrap = stringfilter(wordwrap) 244 wordwrap.is_safe = True 222 245 223 246 def ljust(value, arg): 224 247 """ … … 228 251 """ 229 252 return value.ljust(int(arg)) 230 253 ljust = stringfilter(ljust) 254 ljust.is_safe = True 231 255 232 256 def rjust(value, arg): 233 257 """ … … 237 261 """ 238 262 return value.rjust(int(arg)) 239 263 rjust = stringfilter(rjust) 264 rjust.is_safe = True 240 265 241 266 def center(value, arg): 242 267 "Centers the value in a field of a given width" 243 268 return value.center(int(arg)) 244 269 center = stringfilter(center) 270 center.is_safe = True 245 271 246 272 def cut(value, arg): 247 273 "Removes all values of arg from the given string" 248 274 return value.replace(arg, '') 249 275 cut = stringfilter(cut) 276 cut.is_safe = False 250 277 251 278 ################### 252 279 # HTML STRINGS # 253 280 ################### 254 281 255 282 def 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) 286 escape = stringfilter(escape) 287 escape.is_safe = True 288 289 def 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).""" 257 293 from django.utils.html import escape 258 return escape(value)294 return mark_safe(escape(value)) 259 295 escape = stringfilter(escape) 296 force_escape.is_safe = True 260 297 261 def linebreaks(value ):298 def linebreaks(value, autoescape = None): 262 299 "Converts newlines into <p> and <br />s" 263 300 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)) 265 303 linebreaks = stringfilter(linebreaks) 304 linebreaks.is_safe = True 305 linebreaks.needs_autoescape = True 266 306 267 def linebreaksbr(value ):307 def linebreaksbr(value, autoescape = None): 268 308 "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 />')) 270 315 linebreaksbr = stringfilter(linebreaksbr) 316 linebreaksbr.is_safe = True 317 linebreaksbr.needs_autoescape = True 318 319 def 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) 323 safe = stringfilter(safe) 324 safe.is_safe = True 271 325 272 326 def removetags(value, tags): 273 327 "Removes a space separated list of [X]HTML tags from the output" … … 279 333 value = endtag_re.sub('', value) 280 334 return value 281 335 removetags = stringfilter(removetags) 336 removetags.is_safe = True 282 337 283 338 def striptags(value): 284 339 "Strips all [X]HTML tags" 285 340 from django.utils.html import strip_tags 286 341 return strip_tags(value) 287 342 striptags = stringfilter(striptags) 343 striptags.is_safe = True 288 344 289 345 ################### 290 346 # LISTS # … … 298 354 decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value] 299 355 decorated.sort() 300 356 return [item[1] for item in decorated] 357 dictsort.is_safe = False 301 358 302 359 def dictsortreversed(value, arg): 303 360 """ … … 308 365 decorated.sort() 309 366 decorated.reverse() 310 367 return [item[1] for item in decorated] 368 dictsortreversed.is_safe = False 311 369 312 370 def first(value): 313 371 "Returns the first item in a list" … … 315 373 return value[0] 316 374 except IndexError: 317 375 return '' 376 first.is_safe = True 318 377 319 378 def join(value, arg): 320 379 "Joins a list with a string, like Python's ``str.join(list)``" 321 380 try: 322 returnarg.join(map(smart_string, value))381 data = arg.join(map(smart_string, value)) 323 382 except AttributeError: # fail silently but nicely 324 383 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 389 join.is_safe = True 325 390 326 391 def length(value): 327 392 "Returns the length of the value - useful for lists" 328 393 return len(value) 394 length.is_safe = False 329 395 330 396 def length_is(value, arg): 331 397 "Returns a boolean of whether the value's length is the argument" 332 398 return len(value) == int(arg) 399 length.is_safe = False 333 400 334 401 def random(value): 335 402 "Returns a random item from the list" 336 403 return random_module.choice(value) 404 length.is_safe = True 337 405 338 406 def slice_(value, arg): 339 407 """ … … 354 422 355 423 except (ValueError, TypeError): 356 424 return value # Fail silently. 425 slice_.is_safe = True 357 426 358 def unordered_list(value ):427 def unordered_list(value, autoescape = None): 359 428 """ 360 429 Recursively takes a self-nested list and returns an HTML unordered list -- 361 430 WITHOUT opening and closing <ul> tags. … … 376 445 </ul> 377 446 </li> 378 447 """ 448 if autoescape: 449 from django.utils.html import conditional_escape 450 escaper = conditional_escape 451 else: 452 escaper = lambda x: x 453 379 454 def _helper(value, tabs): 380 455 indent = '\t' * tabs 381 456 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, 383 458 '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 384 459 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)) 462 unordered_list.is_safe = True 463 unordered_list.needs_autoescape = True 387 464 388 465 ################### 389 466 # INTEGERS # … … 392 469 def add(value, arg): 393 470 "Adds the arg to the value" 394 471 return int(value) + int(arg) 472 add.is_safe = False 395 473 396 474 def get_digit(value, arg): 397 475 """ … … 411 489 return int(str(value)[-arg]) 412 490 except IndexError: 413 491 return 0 492 get_digit.is_safe = False 414 493 415 494 ################### 416 495 # DATES # … … 424 503 if arg is None: 425 504 arg = settings.DATE_FORMAT 426 505 return format(value, arg) 506 date.is_safe = False 427 507 428 508 def time(value, arg=None): 429 509 "Formats a time according to the given format" … … 433 513 if arg is None: 434 514 arg = settings.TIME_FORMAT 435 515 return time_format(value, arg) 516 time.is_safe = False 436 517 437 518 def timesince(value, arg=None): 438 519 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' … … 442 523 if arg: 443 524 return timesince(arg, value) 444 525 return timesince(value) 526 timesince.is_safe = False 445 527 446 528 def timeuntil(value, arg=None): 447 529 'Formats a date as the time until that date (i.e. "4 days, 6 hours")' … … 452 534 if arg: 453 535 return timesince(arg, value) 454 536 return timesince(datetime.now(), value) 537 timeuntil.is_safe = False 455 538 456 539 ################### 457 540 # LOGIC # … … 460 543 def default(value, arg): 461 544 "If value is unavailable, use given default" 462 545 return value or arg 546 default.is_safe = False 463 547 464 548 def default_if_none(value, arg): 465 549 "If value is None, use given default" 466 550 if value is None: 467 551 return arg 468 552 return value 553 default_if_none.is_safe = False 469 554 470 555 def divisibleby(value, arg): 471 556 "Returns true if the value is devisible by the argument" 472 557 return int(value) % int(arg) == 0 558 divisibleby.is_safe = False 473 559 474 560 def yesno(value, arg=None): 475 561 """ … … 500 586 if value: 501 587 return yes 502 588 return no 589 yesno.is_safe = False 503 590 504 591 ################### 505 592 # MISC # … … 522 609 if bytes < 1024 * 1024 * 1024: 523 610 return "%.1f MB" % (bytes / (1024 * 1024)) 524 611 return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 612 filesizeformat.is_safe = True 525 613 526 614 def pluralize(value, arg='s'): 527 615 """ … … 549 637 except TypeError: # len() of unsized object 550 638 pass 551 639 return singular_suffix 640 pluralize.is_safe = False 552 641 553 642 def phone2numeric(value): 554 643 "Takes a phone number and converts it in to its numerical equivalent" 555 644 from django.utils.text import phone2numeric 556 645 return phone2numeric(value) 646 phone2numeric.is_safe = True 557 647 558 648 def pprint(value): 559 649 "A wrapper around pprint.pprint -- for debugging, really" … … 562 652 return pformat(value) 563 653 except Exception, e: 564 654 return "Error in formatting:%s" % e 655 pprint.is_safe = True 565 656 566 657 # Syntax: register.filter(name of filter, callback) 567 658 register.filter(add) … … 580 671 register.filter(first) 581 672 register.filter(fix_ampersands) 582 673 register.filter(floatformat) 674 register.filter(force_escape) 583 675 register.filter(get_digit) 584 676 register.filter(join) 585 677 register.filter(length) … … 596 688 register.filter(removetags) 597 689 register.filter(random) 598 690 register.filter(rjust) 691 register.filter(safe) 599 692 register.filter('slice', slice_) 600 693 register.filter(slugify) 601 694 register.filter(stringformat) -
a/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, COMMENT_TAG_START, COMMENT_TAG_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 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 11 27 class CommentNode(Node): 12 28 def render(self, context): 13 29 return '' … … 41 57 def render(self, context): 42 58 output = self.nodelist.render(context) 43 59 # 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) 45 63 46 64 class FirstOfNode(Node): 47 65 def __init__(self, vars): … … 234 252 return '' 235 253 output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 236 254 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) 238 258 # TODO: Is this a sensible way to determine equality? 239 259 if output and repr(output[-1]['grouper']) == repr(grouper): 240 260 output[-1]['list'].append(obj) … … 355 375 return str(int(round(ratio))) 356 376 357 377 #@register.tag 378 def 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) 385 autoescape = register.tag(autoescape) 386 387 #@register.tag 358 388 def comment(parser, token): 359 389 """ 360 390 Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` … … 448 478 449 479 Sample usage:: 450 480 451 {% filter escape|lower %}481 {% filter force_escape|lower %} 452 482 This text will be HTML-escaped, and will appear in lowercase. 453 483 {% endfilter %} 454 484 """ 455 485 _, rest = token.contents.split(None, 1) 456 486 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__) 457 490 nodelist = parser.parse(('endfilter',)) 458 491 parser.delete_first_token() 459 492 return FilterNode(filter_expr, nodelist) … … 694 727 ifchanged = register.tag(ifchanged) 695 728 696 729 #@register.tag 730 def 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) 737 autoescape = register.tag(noautoescape) 738 739 #@register.tag 697 740 def ssi(parser, token): 698 741 """ 699 742 Output the contents of a given file into the page. -
a/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): -
/dev/null
old new 1 """ 2 Functions for working with "safe strings": strings that can be displayed safely 3 without further escaping in HTML. Here, a "safe string" means that the producer 4 of the string has already turned characters that should not be interpreted by 5 the HTML engine (e.g. '<') into the appropriate entities. 6 """ 7 from django.utils.functional import curry 8 9 class EscapeData(object): 10 pass 11 12 class EscapeString(str, EscapeData): 13 """ 14 A string that should be HTML-escaped when output. 15 """ 16 pass 17 18 class EscapeUnicode(unicode, EscapeData): 19 """ 20 A unicode object that should be HTML-escaped when output. 21 """ 22 pass 23 24 class SafeData(object): 25 pass 26 27 class 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 47 class 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 79 def 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 94 def 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 270 270 two similarly-named ``{% block %}`` tags in a template, that template's parent 271 271 wouldn't know which one of the blocks' content to use. 272 272 273 Automatic HTML escaping 274 ======================= 275 276 A very real problem when creating HTML (and other) output using templates and 277 variable substitution is the possibility of accidently inserting some variable 278 value that affects the resulting HTML. For example, a template fragment like 279 280 :: 281 282 Hello, {{ name }}. 283 284 seems like a harmless way to display the user's name. However, if you are 285 displaying data that the user entered directly and they entered their name as 286 287 :: 288 289 <script>alert('hello')</script> 290 291 this would always display a Javascript alert box whenever the page was loaded. 292 Similarly, if you were displaying some data generated by another process and 293 it contained a '<' symbol, you couldn't just dump this straight into your 294 HTML, because it would be treated as the start of an element. The effects of 295 these 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 300 In order to provide some protection against these problems, Django provides an 301 auto-escaping template tag. Inside this tag, any data that comes from template 302 variables is examined to see if it contains one of the five HTML characters 303 (<, >, ', " and &) that often need escaping and those characters are converted 304 to their respective HTML entities. 305 306 Because some variables will contain data that is *intended* to be rendered 307 as HTML, template tag and filter writers can mark their output strings as 308 requiring no further escaping. For example, the ``unordered_list`` filter is 309 designed to return raw HTML and we want the template processor to simply 310 display the results as returned, without applying any escaping. That is taken 311 care of by the filter. The template author need do nothing special in that 312 case. 313 314 By default, auto-escaping is not in effect. To enable it inside your template, 315 wrap the affected content in the ``autoescape`` tag, like so:: 316 317 {% autoescape %} 318 Hello {{ name }} 319 {% endautoescape %} 320 321 Since the auto-escaping tag passes its effect onto templates that extend the 322 current one as well as templates included via the ``include`` tag (just like 323 all block tags), if you wrap your main HTML content in an ``autoescape`` tag, 324 you will have automatic escaping applied to all of your content. 325 326 At times, you might want to disable auto-escaping when it would otherwise be 327 in 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
