comparison roundup/cgi/templating.py @ 5988:39b8bc511ed7

Fix html generation errors for boolean options. Code was generating xhtml form for html4 output. In xhtml, Boolean attributes like 'disabled', 'required' are represented with a value that is the same as the attribute name. E.G. <input required="required" ...>. Html 5 (and 4) represents the same as <input required ...> . Recognize booleans in one of two ways: value is None value is the same as the attribute name Make html4 and xhtml variants of cgi_escape_attrs. Have input_htmlp4 and input_xhtml call the proper variant. Also set HTMLInputMixin property cgi_escape_atrs to select proper one similar to self.input definition. Then replace calls to cgi_escape_attr with self.cgi_escape_attrs to get correct output.
author John Rouillard <rouilj@ieee.org>
date Sat, 14 Dec 2019 16:18:15 -0500
parents fe334430ca07
children b0940ad50f43
comparison
equal deleted inserted replaced
5987:ea3485c67f94 5988:39b8bc511ed7
435 else: 435 else:
436 dic['id'] = dic['name'] 436 dic['id'] = dic['name']
437 except KeyError: 437 except KeyError:
438 pass 438 pass
439 439
440 def cgi_escape_attrs(**attrs): 440 def html4_cgi_escape_attrs(**attrs):
441 ''' Boolean attributes like 'disabled', 'required'
442 are represented without a value. E.G.
443 <input required ..> not <input required="required" ...>
444 The latter is xhtml. Recognize booleans by:
445 value is None
446 value is the same as the atribute
447 Code can use either method to indicate a pure boolean.
448 '''
449 return ' '.join(['%s="%s"'%(k,html_escape(str(v), True))
450 if v != None and k != v else '%s'%(k)
451 for k,v in sorted(attrs.items())])
452
453 def xhtml_cgi_escape_attrs(**attrs):
454 ''' Boolean attributes like 'disabled', 'required'
455 are represented with a value that is the same as
456 the attribute name.. E.G.
457 <input required="required" ...> not <input required ..>
458 The latter is html4 or 5. Recognize booleans by:
459 value is None
460 value is the same as the atribute
461 Code can use either method to indicate a pure boolean.
462 '''
441 return ' '.join(['%s="%s"'%(k,html_escape(str(v), True)) 463 return ' '.join(['%s="%s"'%(k,html_escape(str(v), True))
442 for k,v in sorted(attrs.items())]) 464 if v != None else '%s="%s"'%(k,k)
465 for k,v in sorted(attrs.items())])
443 466
444 def input_html4(**attrs): 467 def input_html4(**attrs):
445 """Generate an 'input' (html4) element with given attributes""" 468 """Generate an 'input' (html4) element with given attributes"""
446 _set_input_default_args(attrs) 469 _set_input_default_args(attrs)
447 return '<input %s>'%cgi_escape_attrs(**attrs) 470 return '<input %s>'%html4_cgi_escape_attrs(**attrs)
448 471
449 def input_xhtml(**attrs): 472 def input_xhtml(**attrs):
450 """Generate an 'input' (xhtml) element with given attributes""" 473 """Generate an 'input' (xhtml) element with given attributes"""
451 _set_input_default_args(attrs) 474 _set_input_default_args(attrs)
452 return '<input %s/>'%cgi_escape_attrs(**attrs) 475 return '<input %s/>'%xhtml_cgi_escape_attrs(**attrs)
453 476
454 class HTMLInputMixin(object): 477 class HTMLInputMixin(object):
455 """ requires a _client property """ 478 """ requires a _client property """
456 def __init__(self): 479 def __init__(self):
457 html_version = 'html4' 480 html_version = 'html4'
458 if hasattr(self._client.instance.config, 'HTML_VERSION'): 481 if hasattr(self._client.instance.config, 'HTML_VERSION'):
459 html_version = self._client.instance.config.HTML_VERSION 482 html_version = self._client.instance.config.HTML_VERSION
460 if html_version == 'xhtml': 483 if html_version == 'xhtml':
461 self.input = input_xhtml 484 self.input = input_xhtml
485 self.cgi_escape_attrs=xhtml_cgi_escape_attrs
462 else: 486 else:
463 self.input = input_html4 487 self.input = input_html4
488 self.cgi_escape_attrs=html4_cgi_escape_attrs
464 # self._context is used for translations. 489 # self._context is used for translations.
465 # will be initialized by the first call to .gettext() 490 # will be initialized by the first call to .gettext()
466 self._context = None 491 self._context = None
467 492
468 def gettext(self, msgid): 493 def gettext(self, msgid):
765 (self.classname, properties, property, form, type, 790 (self.classname, properties, property, form, type,
766 group, sort, pagesize, filter) 791 group, sort, pagesize, filter)
767 onclick = "javascript:help_window('%s', '%s', '%s');return false;" % \ 792 onclick = "javascript:help_window('%s', '%s', '%s');return false;" % \
768 (help_url, width, height) 793 (help_url, width, height)
769 return '<a class="classhelp" href="%s" onclick="%s" %s>%s</a>' % \ 794 return '<a class="classhelp" href="%s" onclick="%s" %s>%s</a>' % \
770 (help_url, onclick, cgi_escape_attrs(**html_kwargs), 795 (help_url, onclick, self.cgi_escape_attrs(**html_kwargs),
771 self._(label)) 796 self._(label))
772 797
773 def submit(self, label=''"Submit New Entry", action="new"): 798 def submit(self, label=''"Submit New Entry", action="new"):
774 """ Generate a submit button (and action hidden element) 799 """ Generate a submit button (and action hidden element)
775 800
1602 else: 1627 else:
1603 value = html_escape(str(self._value)) 1628 value = html_escape(str(self._value))
1604 1629
1605 value = '&quot;'.join(value.split('"')) 1630 value = '&quot;'.join(value.split('"'))
1606 name = self._formname 1631 name = self._formname
1607 passthrough_args = cgi_escape_attrs(**kwargs) 1632 passthrough_args = self.cgi_escape_attrs(**kwargs)
1608 return ('<textarea %(passthrough_args)s name="%(name)s" id="%(name)s"' 1633 return ('<textarea %(passthrough_args)s name="%(name)s" id="%(name)s"'
1609 ' rows="%(rows)s" cols="%(cols)s">' 1634 ' rows="%(rows)s" cols="%(cols)s">'
1610 '%(value)s</textarea>') % locals() 1635 '%(value)s</textarea>') % locals()
1611 1636
1612 def email(self, escape=1): 1637 def email(self, escape=1):
2179 value = self._value 2204 value = self._value
2180 elif value == '-1': 2205 elif value == '-1':
2181 value = None 2206 value = None
2182 2207
2183 linkcl = self._db.getclass(self._prop.classname) 2208 linkcl = self._db.getclass(self._prop.classname)
2184 l = ['<select %s>'%cgi_escape_attrs(name = self._formname, 2209 l = ['<select %s>'%self.cgi_escape_attrs(name = self._formname,
2185 **html_kwargs)] 2210 **html_kwargs)]
2186 k = linkcl.labelprop(1) 2211 k = linkcl.labelprop(1)
2187 s = '' 2212 s = ''
2188 if value is None: 2213 if value is None:
2189 s = 'selected="selected" ' 2214 s = 'selected="selected" '
2441 height = len(options) 2466 height = len(options)
2442 if value: 2467 if value:
2443 # The "no selection" option. 2468 # The "no selection" option.
2444 height += 1 2469 height += 1
2445 height = min(height, 7) 2470 height = min(height, 7)
2446 l = ['<select multiple %s>'%cgi_escape_attrs(name = self._formname, 2471 l = ['<select multiple %s>'%self.cgi_escape_attrs(name = self._formname,
2447 size = height, 2472 size = height,
2448 **html_kwargs)] 2473 **html_kwargs)]
2449 k = linkcl.labelprop(1) 2474 k = linkcl.labelprop(1)
2450 2475
2451 if value: 2476 if value:

Roundup Issue Tracker: http://roundup-tracker.org/