Home | Trees | Indices | Help |
|
---|
|
1 """TurboGears widgets for HTML forms""" 2 3 __all__ = [ 4 'InputWidget', 'CompoundInputWidget', 'RepeatingInputWidget', 5 'FormField', 'FormFieldsContainer', 'CompoundFormField', 6 'RepeatingFormField', 'Label', 'TextField', 'PasswordField', 7 'HiddenField', 'FileField', 'Button', 'SubmitButton', 8 'ResetButton', 'ImageButton', 'CheckBox', 'TextArea', 9 'SelectionField', 'SingleSelectField', 'MultipleSelectField', 10 'RadioButtonList', 'CheckBoxList', 'FieldSet', 11 'RepeatingFieldSet', 'Form', 'TableForm', 'ListForm', 'WidgetsList'] 12 13 from cherrypy import request 14 from turbogears import validators, expose 15 from turbogears.util import Bunch, request_available 16 from turbogears.widgets.base import (Widget, CompoundWidget, WidgetsList, 17 CoreWD, RenderOnlyWD)18 19 20 ############################################################################# 21 # Functions and classes to manage input widgets # 22 ############################################################################# 23 24 -def append_to_path(widget, repetition):25 path = [] 26 if request_available(): 27 if hasattr(request, 'tg_widgets_path'): 28 path = request.tg_widgets_path 29 else: 30 request.tg_widgets_path = path 31 if not path or path[-1].widget is not widget: 32 path.append(Bunch(widget=widget, repetition=repetition)) 33 return True 34 else: 35 return False36 4144 try: 45 return request.tg_widgets_path 46 except AttributeError: 47 return [Bunch(widget=default_widget, repetition=default_repetition)]4851 52 def _update_path(self, *args, **kw): 53 update = append_to_path(self, None) 54 returnval = func(self, *args, **kw) 55 if update: 56 pop_from_path() 57 return returnval58 59 return _update_path 60 6467 path = [] 68 if isinstance(item, basestring): 69 path = item.split('.') 70 index = 0 71 for key in path: 72 if '-' in key: 73 key, num = key.split('-', 1) 74 path[index] = key, int(num) 75 else: 76 path[index] = key, None 77 index += 1 78 elif hasattr(item, 'name'): 79 path = [(item.name, None)] 80 if base_path: 81 if not isinstance(base_path[0], tuple): 82 base_path = adapt_path(base_path) 83 path = base_path + path 84 return path8588 if not path: 89 return None 90 else: 91 if not isinstance(path[0], tuple): 92 path = adapt_path(path) 93 returnval = value 94 for name, index in path: 95 if isinstance(returnval, dict): 96 returnval = returnval.get(name) 97 if index is not None: 98 if isinstance(returnval, list): 99 try: 100 returnval = returnval[index] 101 except IndexError: 102 returnval = None 103 else: 104 returnval = None 105 return returnval106109 if not path: 110 return None 111 else: 112 if not isinstance(path[0], tuple): 113 path = adapt_path(path) 114 for name, index in path: 115 params_to_parse = params.copy() 116 params = {} 117 for k, v in params_to_parse.iteritems(): 118 if isinstance(v, dict): 119 if name in v: 120 params[k] = v[name] 121 if index is not None: 122 if isinstance(params[k], list): 123 try: 124 params[k] = params[k][index] 125 except IndexError: 126 params[k] = None 127 else: 128 params[k] = None 129 return params130133 name = [] 134 for p in path: 135 if p.repetition is not None: 136 name.append(p.widget.name + repeating_marker + str(p.repetition)) 137 else: 138 name.append(p.widget.name) 139 return nesting_marker.join(name)140141 142 ############################################################################### 143 # Base class for a widget that can generate input for the application. 144 ############################################################################### 145 146 -class InputWidget(Widget):147 148 validator = None 149 params = ['convert'] 150 params_doc = { 151 'convert': 'Should the value be coerced by the validator at display?' 152 } 153 convert = True 154269156 """Initialize an input widget. 157 158 It accepts the following parameters (besides those listed at params): 159 160 name: 161 Name of the input element. Will be the name of the variable 162 the widget's input will be available at when submitted. 163 164 validator: 165 Formencode validator that will validate and coerce the input 166 this widget generates. 167 168 """ 169 if name is not None and ('-' in name or '.' in name): 170 raise ValueError("The name of an InputWidget must not contain" 171 " the '-' or '.' characters") 172 173 super(InputWidget, self).__init__(name, **params) 174 175 if validator: 176 self.validator = validator177 178 @property 179 @update_path 182 183 @property185 if self.path and getattr(self.path[0].widget, 'form', False): 186 return self.path[1:] 187 else: 188 return self.path189 190 @property192 if self.path: 193 validated_form = getattr(request, 'validated_form', None) 194 return self.path[0].widget is validated_form 195 else: 196 return False197199 root_widget = self.path[0].widget 200 if root_widget is self: 201 return self.validator 202 else: 203 if getattr(root_widget, 'form', False): 204 name_path = self.name_path 205 else: 206 name_path = self.name_path[1:] 207 validator = root_widget.validator 208 for name in [i.widget.name for i in name_path]: 209 if hasattr(validator, 'fields'): 210 validator = validator.fields.get(name) 211 elif hasattr(validator, 'validators'): 212 for v in validator.validators: 213 if hasattr(v, 'fields') and name in v.fields: 214 validator = v.fields[name] 215 break 216 else: 217 break 218 return validator219221 if hasattr(request, 'input_values') and self.is_validated: 222 input_submitted = True 223 input_value = retrieve_value_by_path( 224 request.input_values, self.name_path) 225 else: 226 input_submitted = False 227 input_value = None 228 # there are some input fields that when nothing is checked/selected 229 # instead of sending a nice name='' are totally missing from 230 # input_values, this little workaround let's us manage them nicely 231 # without interfering with other types of fields, we need this to 232 # keep track of their empty status otherwise if the form is going to be 233 # redisplayed for some errors they end up to use their defaults values 234 # instead of being empty since FE doesn't validate a failing Schema. 235 # posterity note: this is also why we need if_missing=None in 236 # validators.Schema, see ticket #696. 237 no_input_if_empty = getattr(self, 'no_input_if_empty', False) 238 if input_value is not None or (input_submitted and no_input_if_empty): 239 value = input_value 240 else: 241 if self.validator and params['convert'] and not input_submitted: 242 value = self.validator.from_python(value) 243 return value244 245 @update_path 248 249 @update_path 252 253 @property 254 @update_path256 return build_name_from_path(self.name_path)257 258 @property 259 @update_path261 errors = getattr(request, 'validation_errors', {}) 262 return retrieve_value_by_path(errors, self.name_path)263265 super(InputWidget, self).update_params(d) 266 d['name'] = build_name_from_path(self.name_path) 267 errors = getattr(request, 'validation_errors', {}) 268 d['error'] = retrieve_value_by_path(errors, self.name_path)272340 361274 super(CompoundInputWidget, self).update_params(params) 275 params['error_for'] = lambda f: self.error_for(f, True)276278 """Return the value for a child widget. 279 280 ``item`` is the child widget instance or its name, ``value`` is a 281 dict containing the value for this compound widget. 282 283 """ 284 path = path_from_item(item) 285 return retrieve_value_by_path(value, path)286288 """Return the parameters for a child widget. 289 290 ``item`` is the child widget instance or its name, ``params`` is a 291 dict containing the parameters passed to this compound widget. 292 293 """ 294 path = path_from_item(item) 295 return retrieve_params_by_path(params, path)296298 """Return the Invalid exception associated with a child widget. 299 300 The exception is stored in the request local storage. ``item`` is the 301 child widget instance or its name. 302 303 """ 304 if self.is_validated: 305 path = path_from_item(item, self.name_path) 306 errors = getattr(request, 'validation_errors', {}) 307 returnval = retrieve_value_by_path(errors, path) 308 if suppress_errors and isinstance(returnval, (dict, list)): 309 return None 310 else: 311 return returnval 312 else: 313 return None314316 """Convert value into a dict suitable for propagating values to child 317 widgets. 318 319 If value is a dict it will pass through, if it's another kind of 320 object, attributes which match child widgets' names will tried to be 321 fetched. 322 323 """ 324 if isinstance(value, dict): 325 return value 326 else: 327 value_as_dict = {} 328 for w in self.iter_member_widgets(): 329 try: 330 value_as_dict[w.name] = getattr(value, w.name) 331 except AttributeError: 332 pass 333 return value_as_dict334336 """Adjust a value for displaying in a widget.""" 337 if value is not None: 338 value = self.dictify_value(value) 339 return super(CompoundInputWidget, self).adjust_value(value, **params)364 """Base class for a compound widget which can be repeated.""" 365 366 repeating = True 367 params = ['repetitions'] 368 params_doc = { 369 'repetitions': 'Number of repetitions that should be rendered' 370 } 371 repetitions = 1 372432374 path_reference = self.path[-1] 375 repetition = path_reference.repetition 376 if repetition is not None: 377 path_reference.repetition = None 378 super(RepeatingInputWidget, self).update_params(d) 379 if repetition is None: 380 repetitions = d.pop('repetitions') 381 if isinstance(repetitions, int): 382 repetitions = range(repetitions) 383 else: 384 repetitions = [repetition] 385 d['repetitions'] = RepeatingRange(repetitions, path_reference)386388 """Return the value for a child widget. 389 390 ``item`` is the child widget instance or its name, ``value`` is a dict 391 containing the value for this compound widget. 392 393 """ 394 if isinstance(value, list): 395 try: 396 value = value[self.path[-1].repetition] 397 except IndexError: 398 value = None 399 else: 400 value = None 401 path = path_from_item(item) 402 return retrieve_value_by_path(value, path)403405 """Return the parameters for a child widget. 406 407 ``item`` is the child widget instance or its name, ``params`` is a 408 dict containing the parameters passed to this compound widget. 409 410 """ 411 item_params = {} 412 for k, v in params.iteritems(): 413 if isinstance(v, list) and k != 'repetitions': 414 try: 415 item_params[k] = v[self.path[-1].repetition] 416 except IndexError: 417 pass 418 path = path_from_item(item) 419 return retrieve_params_by_path(item_params, path)420422 """Convert list of values into a list of dicts suitable for propagating 423 values to child widgets. 424 425 If value is a list of dicts it will pass through, if it's another kind 426 of object, attributes which match child widgets' names will tried to be 427 fetched. 428 429 """ 430 return [super(RepeatingInputWidget, self).dictify_value(v) 431 for v in value]433 434 ############################################################################# 435 # Base classes # 436 ############################################################################# 437 438 -class FormField(InputWidget):439 """An input widget that can be included inside a Form or Fieldset. 440 441 It accepts the following parameters (besides those listed at params): 442 443 label 444 The label text that the container form/fieldset will generate for the 445 widget. If empty, the capitalized name will be used. 446 help_text 447 The help text that the container form/fieldset will generate for the 448 widget. 449 450 """ 451 452 _name = 'widget' 453 label = None 454 help_text = None 455 params = ['field_class', 'css_classes'] 456 params_doc = { 457 'field_class': 'CSS class for the field', 458 'css_classes': 'List of extra CSS classes for the field' 459 } 460 field_class = None 461 css_classes = [] 462511464 return self._name465 470 name = property(_get_name, _set_name) 471 472 @property474 validator = self._retrieve_validator_from_validation_schema() 475 if validator is None: 476 return False 477 else: 478 try: 479 validator.to_python('') 480 except validators.Invalid: 481 return True 482 else: 483 return False484 485 @property487 return build_name_from_path(self.path, '_', '_')488490 super(FormField, self).__init__(name, **kw) 491 if label is not None: 492 self.label = label 493 494 if help_text is not None: 495 self.help_text = help_text 496 497 if self.field_class is None: 498 self.field_class = self.__class__.__name__.lower()499501 super(FormField, self).update_params(d) 502 if self.is_required: 503 d['field_class'] = ' '.join([d['field_class'], 'requiredfield']) 504 if d.get('error', None): 505 d['field_class'] = ' '.join([d['field_class'], 'erroneousfield']) 506 if d['css_classes']: 507 d['field_class'] = ' '.join([d['field_class']] + d['css_classes']) 508 d['label'] = self.label 509 d['help_text'] = self.help_text 510 d['field_id'] = self.field_id512 513 # A decorator that provides field_for functionality to the 514 # decorated FormFieldsContainer's method 515 -def retrieve_field_for(func):516 517 @update_path 518 def retrieve_field_for(self=None, item=None, *args, **kw): 519 path = path_from_item(item) 520 field = self 521 for name, index in path: 522 if field is not None: 523 field = field.get_field_by_name(name) 524 append_to_path(field, index) 525 if field is not None: 526 returnval = func(self, field, *args, **kw) 527 else: 528 returnval = "Field for '%s' not found." % item 529 for i in range(len(path)): 530 pop_from_path() 531 return returnval532 533 return retrieve_field_for 534537 """A container for FormFields. 538 539 Has two member_widgets lists: 540 - fields 541 - hidden_fields 542 543 It provides the template with 3 useful functions: 544 - field_for(name) 545 Returns the child named ``name``. 546 - value_for(name) 547 Returns the value for the child named ``name`` (name can also be 548 a widget instance so fields can be iterated). 549 - params_for(name) 550 Returns the display-time parameters for the child named ``name`` 551 (can also be a widget instance so fields can be iterated). 552 - display_field_for(name) 553 Displays the child named ``name`` automatically propagating value 554 and params. ``name`` can also be a widget instance. 555 - error_for(name) 556 Returns the validation error the child named ``name`` generated. 557 Again, ``name`` can also be a widget instance. 558 559 """ 560 member_widgets = ['fields', 'hidden_fields'] 561 fields = [] 562 hidden_fields = [] 563 params = ['disabled_fields'] 564 disabled_fields = set() 565 566 @property620 626 630568 for widget in self.iter_member_widgets(): 569 if getattr(widget, 'file_upload', False): 570 return True 571 return False572574 for widget in self.iter_member_widgets(): 575 if widget.name == name: 576 return widget 577 return default578 579 @retrieve_field_for 582 583 @retrieve_field_for 586588 """Return the member widget named item. 589 590 This function should *only* be used inside a FormFieldsContainer 591 template, really, else the path acrobatics will lead to unexpected 592 results. 593 594 """ 595 field = self.get_field_by_name(item, None) 596 if field is None: 597 raise ValueError("Field for '%s' not found." % item) 598 return field599601 super(FormFieldsContainer, self).update_params(d) 602 d['display_field_for'] = lambda f: self.display_field_for(f, 603 d['value_for'](f), **d['params_for'](f)) 604 d['render_field_for'] = lambda f: self.display_field_for(f, 605 d['value_for'](f), **d['params_for'](f)) 606 d['field_for'] = self._field_for 607 visible_fields = [] 608 hidden_fields = [] 609 #XXX: Ugly hack, this badly needs a better fix. Note to myself: 610 # CompoundFormField has no fields or hidden_fields member_widgets, 611 # related to [1736]'s refactoring. 612 for field in d.get('fields', []) + d.get('hidden_fields', []): 613 if field.name not in d['disabled_fields']: 614 if getattr(field, 'hidden', False): 615 hidden_fields.append(field) 616 else: 617 visible_fields.append(field) 618 d['fields'] = visible_fields 619 d['hidden_fields'] = hidden_fields631 632 ############################################################################# 633 # Fields # 634 ############################################################################# 635 636 -class Label(FormField):637 """A simple label for a form field.""" 638 639 template = """ 640 <label xmlns:py="http://genshi.edgewall.org/" 641 id="${field_id}" 642 class="${field_class}" 643 py:content="value" 644 py:attrs="attrs" 645 /> 646 """ 647 params = ['attrs'] 648 params_doc = { 649 'attrs': 'Dictionary containing extra (X)HTML attributes' 650 ' for the label tag' 651 } 652 attrs = {}653 657660 """A standard, single-line text field.""" 661 662 template = """ 663 <input xmlns:py="http://genshi.edgewall.org/" 664 type="text" 665 name="${name}" 666 class="${field_class}" 667 id="${field_id}" 668 value="${value}" 669 py:attrs="attrs" 670 /> 671 """ 672 params = ['attrs'] 673 params_doc = { 674 'attrs': 'Dictionary containing extra (X)HTML attributes' 675 ' for the input tag' 676 } 677 attrs = {}678681 682 name = 'Text Field' 683 for_widget = TextField('your_name', default='Your Name Here', 684 attrs=dict(size='30'))685688 """A password field which masks letters with * characters.""" 689 690 template = """ 691 <input xmlns:py="http://genshi.edgewall.org/" 692 type="password" 693 name="${name}" 694 class="${field_class}" 695 id="${field_id}" 696 value="${value}" 697 py:attrs="attrs" 698 /> 699 """ 700 params = ['attrs'] 701 params_doc = { 702 'attrs': 'Dictionary containing extra (X)HTML attributes' 703 ' for the password input tag' 704 } 705 attrs = {}706709 710 name = 'Password Field' 711 for_widget = PasswordField('your_secret', default='Top Secret Password')712715 template = """ 716 <input xmlns:py="http://genshi.edgewall.org/" 717 type="hidden" 718 name="${name}" 719 class="${field_class}" 720 id="${field_id}" 721 value="${value}" 722 py:attrs="attrs" 723 /> 724 """ 725 params = ['attrs'] 726 params_doc = { 727 'attrs': 'Dictionary containing extra (X)HTML attributes' 728 ' for the hidden input tag' 729 } 730 attrs = {} 731 hidden = True732 737740 template = """ 741 <input xmlns:py="http://genshi.edgewall.org/" 742 type="file" 743 name="${name}" 744 class="${field_class}" 745 id="${field_id}" 746 py:attrs="attrs" 747 /> 748 """ 749 params = ['attrs'] 750 params_doc = { 751 'attrs': 'Dictionary containing extra (X)HTML attributes' 752 ' for the file input tag' 753 } 754 attrs = {} 755 file_upload = True 756 759762 768771 772 template = """ 773 <input xmlns:py="http://genshi.edgewall.org/" 774 type="button" 775 class="${field_class}" 776 value="${value}" 777 py:attrs="attrs" 778 /> 779 """ 780 params = ['attrs'] 781 params_doc = { 782 'attrs': 'Dictionary containing extra (X)HTML attributes' 783 ' for the button input tag' 784 } 785 attrs = {} 786792 798788 super(Button, self).update_params(d) 789 if self.is_named: 790 d['attrs']['name'] = d['name'] 791 d['attrs']['id'] = d['field_id']801 802 template = """ 803 <input xmlns:py="http://genshi.edgewall.org/" 804 type="submit" 805 class="${field_class}" 806 value="${value}" 807 py:attrs="attrs" 808 /> 809 """810 816819 820 template = """ 821 <input xmlns:py="http://genshi.edgewall.org/" 822 type="reset" 823 class="${field_class}" 824 value="${value}" 825 py:attrs="attrs" 826 /> 827 """828 834837 838 template = """ 839 <input xmlns:py="http://genshi.edgewall.org/" 840 type="image" 841 src="${src}" 842 width="${width}" 843 height="${height}" 844 alt="${alt}" 845 class="${field_class}" 846 value="${value}" 847 py:attrs="attrs" 848 /> 849 """ 850 params = ['src', 'width', 'height', 'alt'] 851 params_doc = { 852 'src': 'Source of the image', 853 'width': 'Width of the image', 854 'height': 'Height of the image', 855 'alt': 'Alternate text for the image' 856 } 857 src = None 858 width = None 859 height = None 860 alt = None861864 865 name = 'Image Button' 866 for_widget = ImageButton('your_image_button', 867 src='/tg_static/images/toolbox_logo.png')868871 872 template = """ 873 <input xmlns:py="http://genshi.edgewall.org/" 874 type="checkbox" 875 name="${name}" 876 class="${field_class}" 877 id="${field_id}" 878 py:attrs="attrs" 879 /> 880 """ 881 # an unchecked checkbox doesn't submit anything 882 no_input_if_empty = True 883 params = ['attrs'] 884 params_doc = { 885 'attrs': 'Dictionary containing extra (X)HTML attributes' 886 ' for the checkbox input tag' 887 } 888 attrs = {} 889903891 super(CheckBox, self).__init__(*args, **kw) 892 if not self.validator: 893 self.validator = validators.Bool()894896 super(CheckBox, self).update_params(d) 897 try: 898 value = self.validator.to_python(d['value']) 899 except validators.Invalid: 900 value = False 901 if value: 902 d['attrs']['checked'] = 'checked'906 907 for_widget = CheckBox(name='your_checkbox', 908 default=True, help_text="Just click me...") 909 template = """ 910 <div xmlns:py="http://genshi.edgewall.org/"> 911 ${for_widget.display()} 912 <label for="${for_widget.field_id}" 913 py:content="for_widget.help_text" 914 /> 915 </div> 916 """917920 921 template = """ 922 <textarea xmlns:py="http://genshi.edgewall.org/" 923 name="${name}" 924 class="${field_class}" 925 id="${field_id}" 926 rows="${rows}" 927 cols="${cols}" 928 py:attrs="attrs" 929 py:content="value" 930 /> 931 """ 932 params = ['rows', 'cols', 'attrs'] 933 params_doc = { 934 'attrs': 'Dictionary containing extra (X)HTML attributes' 935 ' for the textarea tag', 936 'rows': 'Number of rows to render', 937 'cols': 'Number of columns to render' 938 } 939 rows = 7 940 cols = 50 941 attrs = {}942945 946 name = 'Text Area' 947 for_widget = TextArea(name='your_textarea', 948 default="Your Comment Here", rows=5, cols=40)949952 953 # an empty selection doesn't submit anything 954 no_input_if_empty = True 955 _multiple_selection = False 956 _selected_verb = None 957 params = ['options'] 958 params_doc = { 959 'options': 'A list of tuples with the options for the select field' 960 } 961 options = [] 962 convert = False 9631089965 super(SelectionField, self).__init__(*args, **kw) 966 if not self.validator: 967 try: 968 self.validator = self._guess_validator() 969 except Exception, err: 970 raise ValueError( 971 "No validator specified and couldn't guess one:\n%s\n" 972 "Please explicitly specify a validator for the %s." 973 % (err, self.__class__.__name__)) 974 # Only override the user-provided validator if it's not a ForEach one, 975 # which usually means the user needs to perform validation on the list 976 # as a whole. 977 if (self._multiple_selection and 978 not isinstance(self.validator, validators.ForEach)): 979 self.validator = validators.MultipleSelection(self.validator)980982 """Inspect sample option value to guess validator (crude).""" 983 sample_option = self._get_sample_option() 984 if isinstance(sample_option, int): 985 return validators.Int() 986 elif isinstance(sample_option, basestring): 987 return validators.String() 988 else: 989 raise TypeError("Unknown option type in SelectionField: %r" 990 % (sample_option,))991993 options = self._extend_options(self.options) 994 if options: 995 if isinstance(options[0][1], list): 996 sample = options[0][1][0] 997 else: 998 sample = options[0] 999 return sample[0] 1000 else: 1001 return None10021004 if (len(opts) > 0) and not isinstance(opts[0], (tuple, list)): 1005 new_opts = [] 1006 for opt in opts: 1007 new_opts.append((opt, opt)) 1008 return new_opts 1009 return opts10101012 super(SelectionField, self).update_params(d) 1013 grouped_options = [] 1014 options = [] 1015 d['options'] = self._extend_options(d['options']) 1016 for optgroup in d['options']: 1017 if isinstance(optgroup[1], list): 1018 group = True 1019 optlist = optgroup[1][:] 1020 else: 1021 group = False 1022 optlist = [optgroup] 1023 for i, option in enumerate(optlist): 1024 option_value = option[0] 1025 option_attrs = len(option) > 2 and dict(option[2]) or {} 1026 if self._is_option_selected(option_value, d['value']): 1027 option_attrs[self._selected_verb] = self._selected_verb 1028 option_value = self.validator.from_python(option_value) 1029 if self._multiple_selection: 1030 if option_value: 1031 option_value = option_value[0] 1032 else: 1033 option_value = None 1034 if option_value is None: 1035 option_value = '' 1036 optlist[i] = (option_value, option[1], option_attrs) 1037 options.extend(optlist) 1038 if group: 1039 grouped_options.append((optgroup[0], optlist)) 1040 # options provides a list of *flat* options leaving out any eventual 1041 # group, useful for backward compatibility and simpler widgets 1042 d['options'] = options 1043 if grouped_options: 1044 d['grouped_options'] = grouped_options 1045 else: 1046 d['grouped_options'] = [(None, options)]10471049 """Check whether an option value has been selected.""" 1050 if self._multiple_selection: 1051 if isinstance(value, basestring): 1052 # single unconverted value 1053 try: 1054 value = self.validator.to_python(value) 1055 except validators.Invalid: 1056 return False 1057 if not value: 1058 return False 1059 else: 1060 # multiple values - check whether list or set may 1061 # need conversion by looking at its first item 1062 if not value: 1063 return False 1064 for v in value: 1065 if isinstance(v, basestring): 1066 try: 1067 value = self.validator.to_python(value) 1068 except validators.Invalid: 1069 return False 1070 if not value: 1071 return False 1072 break 1073 if option_value is None: 1074 for v in value: 1075 if v is None: 1076 return True 1077 return False 1078 return option_value in value 1079 else: 1080 if isinstance(value, basestring): 1081 # value may need conversion 1082 try: 1083 value = self.validator.to_python(value) 1084 except validators.Invalid: 1085 return False 1086 if option_value is None: 1087 return value is None 1088 return option_value == value1092 1093 template = """ 1094 <select xmlns:py="http://genshi.edgewall.org/" 1095 name="${name}" 1096 class="${field_class}" 1097 id="${field_id}" 1098 py:attrs="attrs" 1099 > 1100 <optgroup py:for="group, options in grouped_options" 1101 label="${group}" 1102 py:strip="not group" 1103 > 1104 <option py:for="value, desc, attrs in options" 1105 value="${value}" 1106 py:attrs="attrs" 1107 py:content="desc" 1108 /> 1109 </optgroup> 1110 </select> 1111 """ 1112 _selected_verb = 'selected' 1113 params = ['attrs'] 1114 params_doc = { 1115 'attrs': 'Dictionary containing extra (X)HTML attributes' 1116 ' for the select tag' 1117 } 1118 attrs = {}11191122 1123 name = 'Single Select Field' 1124 for_widget = SingleSelectField('your_single_select_field', 1125 options=[(1, "Python"), (2, "Java"), (3, "Pascal"), (4, "Ruby")], 1126 default=2)11271130 1131 template = """ 1132 <select xmlns:py="http://genshi.edgewall.org/" 1133 multiple="multiple" 1134 size="${size}" 1135 name="${name}" 1136 class="${field_class}" 1137 id="${field_id}" 1138 py:attrs="attrs" 1139 > 1140 <optgroup py:for="group, options in grouped_options" 1141 label="${group}" 1142 py:strip="not group" 1143 > 1144 <option py:for="value, desc, attrs in options" 1145 value="${value}" 1146 py:attrs="attrs" 1147 py:content="desc" 1148 /> 1149 </optgroup> 1150 </select> 1151 """ 1152 _multiple_selection = True 1153 _selected_verb = 'selected' 1154 params = ['size', 'attrs'] 1155 params_doc = { 1156 'size': 'Number of options to show without scrolling' 1157 } 1158 attrs = {} 1159 size = 511601163 1164 name = 'Multiple Select Field' 1165 for_widget = MultipleSelectField('your_multiple_select_field', 1166 options=[('a', "Python"), ('b', "Java"), 1167 ('c', "Pascal"), ('d', "Ruby")], default=['a', 'c', 'd'])11681171 template = """ 1172 <ul xmlns:py="http://genshi.edgewall.org/" 1173 class="${field_class}" 1174 id="${field_id}" 1175 py:attrs="list_attrs" 1176 > 1177 <li py:for="value, desc, attrs in options"> 1178 <input type="radio" 1179 name="${name}" 1180 id="${field_id}_${value}" 1181 value="${value}" 1182 py:attrs="attrs" 1183 /> 1184 <label for="${field_id}_${value}" py:content="desc" /> 1185 </li> 1186 </ul> 1187 """ 1188 _selected_verb = 'checked' 1189 params = ['list_attrs'] 1190 params_doc = { 1191 'list_attrs': 'Extra (X)HTML attributes for the ul tag' 1192 } 1193 list_attrs = {}11941197 1198 name = 'RadioButton List' 1199 for_widget = RadioButtonList('your_radiobutton_list', 1200 options=list(enumerate("Python Java Pascal Ruby".split())), default=3)12011204 1205 template = """ 1206 <ul xmlns:py="http://genshi.edgewall.org/" 1207 class="${field_class}" 1208 id="${field_id}" 1209 py:attrs="list_attrs" 1210 > 1211 <li py:for="value, desc, attrs in options"> 1212 <input type="checkbox" 1213 name="${name}" 1214 id="${field_id}_${value}" 1215 value="${value}" 1216 py:attrs="attrs" 1217 /> 1218 <label for="${field_id}_${value}" py:content="desc" /> 1219 </li> 1220 </ul> 1221 """ 1222 _multiple_selection = True 1223 _selected_verb = 'checked' 1224 params = ['list_attrs'] 1225 params_doc = { 1226 'list_attrs': 'Extra (X)HTML attributes for the ul tag' 1227 } 1228 list_attrs = {}12291232 1233 name = 'CheckBox List' 1234 for_widget = CheckBoxList('your_checkbox_list', 1235 options=list(enumerate("Python Java Pascal Ruby".split())), 1236 default=[0,3])12371240 1241 template = """ 1242 <fieldset xmlns:py="http://genshi.edgewall.org/" 1243 class="${field_class}" 1244 id="${field_id}" 1245 > 1246 <legend py:if="legend" py:content="legend" /> 1247 <div py:for="field in hidden_fields" 1248 py:replace="field.display(value_for(field), **params_for(field))" 1249 /> 1250 <div py:for="field in fields"> 1251 <label class="fieldlabel" for="${field.field_id}" py:content="field.label" /> 1252 <span py:replace="field.display(value_for(field), **params_for(field))" /> 1253 <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" /> 1254 <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" /> 1255 </div> 1256 </fieldset> 1257 """ 1258 params = ['legend'] 1259 params_doc = { 1260 'legend': 'Text to display as the legend for the fieldset' 1261 } 1262 legend = None12631266 1267 name = 'FieldSet' 1268 for_widget = FieldSet('your_fieldset', 1269 legend="Range", fields=[ 1270 TextField(name='lower_limit', label="Lower Limit"), 1271 TextField(name='upper_limit', label="Upper Limit")])12721275 """Make an infinitely repeating sequence from a string or a list.""" 1276 127912961281 return str(self.value)12821284 return unicode(self.value)12851287 if isinstance(self.value, basestring): 1288 return self.value 1289 elif self.value: 1290 return self.value[key % len(self.value)] 1291 else: 1292 return None12931295 return bool(self.value)1299 1300 template = """ 1301 <div xmlns:py="http://genshi.edgewall.org/"> 1302 <fieldset py:for="repetition in repetitions" 1303 class="${field_class}" 1304 id="${field_id}_${repetition}" 1305 > 1306 <legend py:if="legend[repetition]" py:content="legend[repetition]" /> 1307 <div py:for="field in hidden_fields" 1308 py:replace="field.display(value_for(field), **params_for(field))" 1309 /> 1310 <div py:for="field in fields"> 1311 <label class="fieldlabel" for="${field.field_id}" py:content="field.label" /> 1312 <span py:replace="field.display(value_for(field), **params_for(field))" /> 1313 <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" /> 1314 <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" /> 1315 </div> 1316 </fieldset> 1317 </div> 1318 """ 1319 params = ['legend'] 1320 params_doc = { 1321 'legend': 'Text or list of texts to display as the legend for the fieldset' 1322 } 132313271325 super(RepeatingFieldSet, self).update_params(d) 1326 d['legend'] = ValueRepeater(d.get('legend'))1330 1331 name = 'Repeating FieldSet' 1332 for_widget = RepeatingFieldSet('your_repeating_fieldset', 1333 legend="Range", repetitions=3, fields=[ 1334 TextField(name='lower_limit', label="Lower Limit"), 1335 TextField(name='upper_limit', label="Upper Limit")])13361337 1338 ############################################################################# 1339 # Forms # 1340 ############################################################################# 1341 1342 -class Form(FormFieldsContainer):1343 1344 form = True 1345 name = 'form' 1346 member_widgets = ['submit'] 1347 params = ['action', 'method', 'form_attrs', 'use_name', 'submit_text'] 1348 params_doc = { 1349 'action': 'Url to POST/GET the form data', 1350 'method': 'HTTP request method', 1351 'form_attrs': 'Extra (X)HTML attributes for the form tag', 1352 'use_name': 'Whether to use the name instead of the id attribute', 1353 'submit_text': 'Text for the submit button', 1354 'disabled_fields': 'List of names of the fields we want to disable' 1355 } 1356 submit = SubmitButton() 1357 action = '' 1358 method = 'post' 1359 form_attrs = {} 1360 use_name = False # because this is deprecated in HTML and invalid in XHTML 1361 submit_text = None 136213771364 name = d.get('name') 1365 super(Form, self).update_params(d) 1366 d['name'] = name or self.name 1367 if self.file_upload: 1368 d['form_attrs']['enctype'] = 'multipart/form-data'1369 1373 1374 @property1380 """Form with simple table layout.""" 1381 1382 # note that even hidden fields must live inside a block element 1383 template = """ 1384 <form xmlns:py="http://genshi.edgewall.org/" 1385 name="${use_name and name or None}" 1386 id="${not use_name and name or None}" 1387 action="${action}" 1388 method="${method}" 1389 class="tableform" 1390 py:attrs="form_attrs" 1391 > 1392 <div py:if="hidden_fields" style="display:none"> 1393 <div py:for="field in hidden_fields" 1394 py:replace="field.display(value_for(field), **params_for(field))" 1395 /> 1396 </div> 1397 <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs"> 1398 <tr py:for="i, field in enumerate(fields)" class="${i % 2 and 'odd' or 'even'}"> 1399 <th> 1400 <label class="fieldlabel" for="${field.field_id}" py:content="field.label" /> 1401 </th> 1402 <td> 1403 <span py:replace="field.display(value_for(field), **params_for(field))" /> 1404 <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" /> 1405 <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" /> 1406 </td> 1407 </tr> 1408 <tr> 1409 <td> </td> 1410 <td py:content="submit.display(submit_text)" /> 1411 </tr> 1412 </table> 1413 </form> 1414 """ 1415 params = ['table_attrs'] 1416 params_doc = { 1417 'table_attrs': 'Extra (X)HTML attributes for the Table tag' 1418 } 1419 table_attrs = {}14201423 1424 name = 'Table Form' 1425 full_class_name = 'turbogears.widgets.TableForm' 1426 field1 = TextField('name') 1427 field2 = TextField('address') 1428 field3 = TextField('occupation') 1429 field4 = PasswordField('password') 1430 field5 = PasswordField('reserved') # will never show 1431 field6 = HiddenField('hidden_info') 1432 for_widget = TableForm('TableForm', 1433 fields=[field1, field2, field3, field4, field5, field6], 1434 action='%s/save' % full_class_name, submit_text='Submit Me') 1435 template = """ 1436 <div> 1437 ${for_widget.display(disabled_fields=["reserved"])} 1438 </div> 1439 """ 1440 1441 @expose()14441447 """Form with simple list layout.""" 1448 1449 # note that even hidden fields must live inside a block element 1450 template = """ 1451 <form xmlns:py="http://genshi.edgewall.org/" 1452 name="${use_name and name or None}" 1453 id="${not use_name and name or None}" 1454 action="${action}" 1455 method="${method}" 1456 class="listform" 1457 py:attrs="form_attrs" 1458 > 1459 <div py:if="hidden_fields" style="display:none"> 1460 <div py:for="field in hidden_fields" 1461 py:replace="field.display(value_for(field), **params_for(field))" 1462 /> 1463 </div> 1464 <ul py:attrs="list_attrs"> 1465 <li py:for="i, field in enumerate(fields)" class="${i % 2 and 'odd' or 'even'}"> 1466 <label class="fieldlabel" for="${field.field_id}" py:content="field.label" /> 1467 <span py:replace="field.display(value_for(field), **params_for(field))" /> 1468 <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" /> 1469 <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" /> 1470 </li> 1471 <li py:content="submit.display(submit_text)" /> 1472 </ul> 1473 </form> 1474 """ 1475 params = ['list_attrs'] 1476 params_doc = { 1477 'list_attrs': 'Extra (X)HTML attributes for the ul tag' 1478 } 1479 list_attrs = {}14801483 name = 'List Form' 1484 full_class_name = 'turbogears.widgets.ListForm' 1485 field1 = TextField('name') 1486 field2 = TextField('address') 1487 field3 = TextField('occupation') 1488 field4 = PasswordField('password') 1489 field5 = PasswordField('reserved') # will never show 1490 field6 = HiddenField('hidden_info') 1491 for_widget = ListForm('ListForm', 1492 fields=[field1, field2, field3, field4, field5, field6], 1493 action='%s/save' % full_class_name, submit_text='Submit Me') 1494 template = """ 1495 <div> 1496 ${for_widget.display(disabled_fields=["reserved"])} 1497 </div> 1498 """ 1499 1500 @expose()1503
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Jul 19 17:20:05 2019 | http://epydoc.sourceforge.net |