Changeset 231
- Timestamp:
- 07/04/06 09:53:31 (3 years ago)
- Files:
-
- trunk/ChangeLog (modified) (1 diff)
- trunk/formal/__init__.py (modified) (2 diffs)
- trunk/formal/examples/group.py (copied) (copied from branches/fieldgroups/formal/examples/group.py) (1 diff)
- trunk/formal/examples/hidden.py (copied) (copied from branches/fieldgroups/formal/examples/hidden.py)
- trunk/formal/examples/main.py (modified) (2 diffs)
- trunk/formal/form.py (modified) (14 diffs)
- trunk/formal/formal.css (modified) (1 diff)
- trunk/formal/util.py (modified) (2 diffs)
- trunk/formal/widget.py (modified) (14 diffs)
- trunk/formal/widgets/multiselect.py (modified) (2 diffs)
- trunk/formal/widgets/restwidget.py (modified) (3 diffs)
- trunk/setup.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/ChangeLog
r175 r231 1 2006-07-04 Matt Goodall <matt@pollenation.net> 2 3 Added groups of fields. A Group of Fields can now be added to a Form using 4 the Form.add() method. Fields are added to the Group using the Group.add() 5 method. A Group can also be added to another Group using Group.add() 6 7 A Group is reresented by a <fieldset> element and it completely contains 8 its Fields. A Group currently performs to validation of its own. 9 10 Each Group is named, just like a Field, and the name must be unique within 11 the context of the object it is added to (i.e. a Group of Form). The 12 effect of the Group's name is that an extra "segment" is added to the Form 13 data's dict keys to provide the namespace for Fields in a Group. 14 15 For instance, if a Field, 'bar', is added to a Group, 'foo', and that 16 Group is then added to a Form the field's data key is 'foo.bar'. 17 18 This change introduces a new Form.add() method. It is intended to replace 19 Form.addField() although that method still exists. All Form.addField(...) 20 now does is forward the call to Form.add(formal.Field(...)). 21 Form.addField() will probably be deprecated sometime. 22 23 I may have slighty broken the default (and your) stylesheet with this 24 change because I had been using a <fieldset> immediately inside the <form> 25 element. That turns out to be unnecessary (any block element will do) so I 26 decided to use a <fieldset> only for Groups to keep the <fieldset> nesting 27 as shallow as possible. 28 1 29 2005-12-21 Damian Staniforth <damian@pollenation.net> 2 30 trunk/formal/__init__.py
r225 r231 4 4 5 5 6 version_info = (0, 8, 2)6 version_info = (0, 9, 0) 7 7 version = '.'.join([str(i) for i in version_info]) 8 8 … … 14 14 from formal.widgets.restwidget import * 15 15 from formal.widgets.multiselect import * 16 from formal.form import Form, ResourceMixin, renderForm16 from formal.form import Form, Field, Group, ResourceMixin, renderForm 17 17 from formal import iformal 18 18 trunk/formal/examples/group.py
r213 r231 24 24 25 25 form = formal.Form() 26 form.add Field('before', formal.String())26 form.add(formal.Field('before', formal.String())) 27 27 form.add(makePersonGroup('me')) 28 28 form.add(makePersonGroup('you')) 29 form.add Field('after', formal.String())29 form.add(formal.Field('after', formal.String())) 30 30 form.addAction(self.submitted) 31 31 trunk/formal/examples/main.py
r197 r231 14 14 'formal.examples.missing.MissingFormPage', 15 15 'formal.examples.prepopulate.PrepopulateFormPage', 16 'formal.examples.group.GroupFormPage', 16 17 'formal.examples.fileupload.FileUploadFormPage', 17 18 'formal.examples.smartupload.SmartUploadFormPage', … … 22 23 'formal.examples.restwidget.ReSTWidgetFormPage', 23 24 'formal.examples.nofields.NoFieldsFormPage', 25 'formal.examples.hidden.HiddenFieldsFormPage', 24 26 ] 25 27 trunk/formal/form.py
r223 r231 6 6 from twisted.internet import defer 7 7 from twisted.python.components import registerAdapter 8 from nevow import appserver, context, loaders, inevow, tags as T, url8 from nevow import appserver, context, loaders, inevow, rend, tags as T, url 9 9 from nevow.util import getPOSTCharset 10 10 from formal import iformal, util, validation … … 71 71 72 72 73 74 class Field(object): 75 76 77 itemParent = None 78 79 80 def __init__(self, name, type, widgetFactory=None, label=None, 81 description=None, cssClass=None): 82 if not util.validIdentifier(name): 83 raise ValueError('%r is an invalid field name'%name) 84 if label is None: 85 label = util.titleFromName(name) 86 if widgetFactory is None: 87 widgetFactory = iformal.IWidget 88 self.name = name 89 self.type = type 90 self.widgetFactory = widgetFactory 91 self.label = label 92 self.description = description 93 self.cssClass = cssClass 94 95 96 def setItemParent(self, itemParent): 97 self.itemParent = itemParent 98 99 100 def _getKey(self): 101 parts = [self.name] 102 parent = self.itemParent 103 while parent is not None: 104 parts.append(parent.name) 105 parent = parent.itemParent 106 parts.reverse() 107 return '.'.join(parts) 108 109 110 key = property(_getKey) 111 112 113 def makeWidget(self): 114 return self.widgetFactory(self.type) 115 116 117 def process(self, ctx, form, args, errors): 118 119 # If the type is immutable then copy the original value to args in case 120 # another validation error causes this field to be re-rendered. 121 if self.type.immutable: 122 args[self.key] = form.data.get(self.key) 123 return 124 125 # Process the input using the widget, storing the data back on the form. 126 try: 127 form.data[self.key] = self.makeWidget().processInput(ctx, self.key, args) 128 except validation.FieldError, e: 129 if e.fieldName is None: 130 e.fieldName = self.key 131 errors.add(e) 132 133 134 135 class FieldFragment(rend.Fragment): 136 implements(inevow.IRenderer) 137 138 139 docFactory = loaders.stan( 140 T.div(id=T.slot('fieldId'), _class=T.slot('class'), 141 render=T.directive('field'))[ 142 T.label(_class='label', _for=T.slot('id'))[T.slot('label')], 143 T.div(_class='inputs')[T.slot('inputs')], 144 T.slot('description'), 145 T.slot('message'), 146 ]) 147 148 149 hiddenDocFactory = loaders.stan( 150 T.invisible(render=T.directive('field'))[T.slot('inputs')]) 151 152 153 def __init__(self, field): 154 self.field = field 155 # Nasty hack to work out if this is a hidden field. Keep the widget 156 # for later anyway. 157 self.widget = field.makeWidget() 158 if getattr(self.widget, 'inputType', None) == 'hidden': 159 self.docFactory = self.hiddenDocFactory 160 161 162 def render_field(self, ctx, data): 163 164 # The field we're rendering 165 field = self.field 166 167 # Get stuff from the context 168 formData = iformal.IFormData(ctx) 169 formErrors = iformal.IFormErrors(ctx, None) 170 171 # Find any error 172 if formErrors is None: 173 error = None 174 else: 175 error = formErrors.getFieldError(field.key) 176 177 # Build the error message 178 if error is None: 179 message = '' 180 else: 181 message = T.div(class_='message')[error.message] 182 183 # Create the widget (it's created in __init__ as a hack) 184 widget = self.widget 185 186 # Build the list of CSS classes 187 classes = [ 188 'field', 189 field.type.__class__.__name__.lower(), 190 widget.__class__.__name__.lower(), 191 ] 192 if field.type.required: 193 classes.append('required') 194 if field.cssClass: 195 classes.append(field.cssClass) 196 if error: 197 classes.append('error') 198 199 # Create the widget and decide the method that should be called 200 if field.type.immutable: 201 render = widget.renderImmutable 202 else: 203 render = widget.render 204 205 # Fill the slots 206 ctx.tag.fillSlots('id', util.render_cssid(field.key)) 207 ctx.tag.fillSlots('fieldId', [util.render_cssid(field.key), '-field']) 208 ctx.tag.fillSlots('class', ' '.join(classes)) 209 ctx.tag.fillSlots('label', field.label) 210 ctx.tag.fillSlots('inputs', render(ctx, field.key, formData, 211 formErrors)) 212 ctx.tag.fillSlots('message', message) 213 ctx.tag.fillSlots('description', 214 T.div(class_='description')[field.description or '']) 215 216 return ctx.tag 217 218 219 220 registerAdapter(FieldFragment, Field, inevow.IRenderer) 221 222 223 224 class Group(object): 225 226 227 itemParent = None 228 229 230 def __init__(self, name, label=None, description=None): 231 if label is None: 232 label = util.titleFromName(name) 233 self.name = name 234 self.label = label 235 self.description = description 236 self.items = FormItems(self) 237 # Forward to FormItems methods 238 self.add = self.items.add 239 self.getItemByName = self.items.getItemByName 240 241 242 def setItemParent(self, itemParent): 243 self.itemParent = itemParent 244 245 246 def process(self, ctx, form, args, errors): 247 for item in self.items: 248 item.process(ctx, form, args, errors) 249 250 251 252 class GroupFragment(rend.Fragment): 253 254 255 docFactory = loaders.stan( 256 T.fieldset(id=T.slot('id'), render=T.directive('group'))[ 257 T.legend[T.slot('label')], 258 T.div(class_='description')[T.slot('description')], 259 T.slot('items'), 260 ] 261 ) 262 263 264 def __init__(self, group): 265 super(GroupFragment, self).__init__() 266 self.group = group 267 268 269 def render_group(self, ctx, data): 270 group = self.group 271 ctx.tag.fillSlots('id', util.render_cssid(group.name)) 272 ctx.tag.fillSlots('label', group.label) 273 ctx.tag.fillSlots('description', group.description or '') 274 ctx.tag.fillSlots('items', [inevow.IRenderer(item) for item in 275 group.items]) 276 return ctx.tag 277 278 279 280 registerAdapter(GroupFragment, Group, inevow.IRenderer) 281 282 283 73 284 class Form(object): 74 285 … … 77 288 callback = None 78 289 actions = None 79 widgets = None80 290 81 291 def __init__(self, callback=None): … … 84 294 self.resourceManager = ResourceManager() 85 295 self.data = {} 86 self.items = [] 87 88 def addField(self, name, type, widgetFactory=None, label=None, description=None, cssClass=None): 89 if not util.validIdentifier(name): 90 raise ValueError('%r is an invalid field name'%name) 91 type.name = name 92 if label is None: 93 label = util.titleFromName(name) 94 self.items.append( (name,type,label,description,cssClass) ) 95 if widgetFactory is not None: 96 if self.widgets is None: 97 self.widgets = {} 98 self.widgets[name] = widgetFactory 296 self.items = FormItems(None) 297 # Forward to FormItems methods 298 self.add = self.items.add 299 self.getItemByName = self.items.getItemByName 300 301 def addField(self, name, type, widgetFactory=None, label=None, 302 description=None, cssClass=None): 303 self.add(Field(name, type, widgetFactory, label, description, cssClass)) 99 304 100 305 def addAction(self, callback, name="submit", validate=True, label=None): … … 105 310 self.actions.append( Action(callback, name, validate, label) ) 106 311 107 def getField(self,fieldName):108 for name, type, label, description, cssClass in self.items:109 if name == fieldName:110 return (name, type, label, description, cssClass)111 else:112 return None113 114 115 def widgetForItem(self, itemName):116 117 for name, type, label, description, cssClass in self.items:118 if name == itemName:119 break120 else:121 raise KeyError()122 123 if self.widgets is not None:124 try:125 widgetFactory = self.widgets[name]126 except KeyError:127 pass128 else:129 return widgetFactory(type)130 131 return iformal.IWidget(type)132 133 312 def process(self, ctx): 134 313 … … 138 317 # Decode the request arg names 139 318 charset = getPOSTCharset(ctx) 140 requestArgs = dict([(k.decode(charset),v) for k,v in requestArgs.iteritems()]) 141 142 # Unflatten the request into nested dicts. 143 args = {} 144 for name, value in requestArgs.iteritems(): 145 name = name.split('.') 146 group, name = name[:-1], name[-1] 147 d = args 148 for g in group: 149 d = args.setdefault(g,{}) 150 d[name] = value 319 args = dict([(k.decode(charset),v) for k,v in requestArgs.iteritems()]) 151 320 152 321 # Find the callback to use, defaulting to the form default … … 170 339 171 340 # Iterate the items and collect the form data and/or errors. 172 data = {} 173 for name, type, label, description, cssClass in self.items: 174 try: 175 if not type.immutable: 176 data[name] = self.widgetForItem(name).processInput(ctx, name, args) 177 else: 178 data[name] = self.data.get(name) 179 errors.data[name] = self.data.get(name) 180 except validation.FieldError, e: 181 if validate: 182 if e.fieldName is None: 183 e.fieldName = name 184 errors.add(e) 185 186 if errors: 341 for item in self.items: 342 item.process(ctx, self, args, errors) 343 344 if errors and validate: 187 345 return errors 188 346 … … 192 350 return r 193 351 194 d = defer.maybeDeferred(callback, ctx, self, data)352 d = defer.maybeDeferred(callback, ctx, self, self.data) 195 353 d.addCallback( _clearUpResources ) 196 354 d.addErrback(self._cbFormProcessingFailed, ctx) … … 203 361 errors.add(failure.value) 204 362 return errors 363 364 365 366 class FormItems(object): 367 """ 368 A managed collection of form items. 369 """ 370 371 372 def __init__(self, itemParent): 373 self.items = [] 374 self.itemParent = itemParent 375 376 377 def __iter__(self): 378 return iter(self.items) 379 380 381 def add(self, item): 382 # Check the item name is unique 383 if item.name in [i.name for i in self.items]: 384 raise ValueError('Item named %r already added to %r' % 385 (item.name, self)) 386 # Add to child items and set self the parent 387 self.items.append(item) 388 item.setItemParent(self.itemParent) 389 390 391 def getItemByName(self, name): 392 name = name.split('.', 1) 393 if len(name) == 1: 394 name, rest = name[0], None 395 else: 396 name, rest = name[0], name[1] 397 for item in self.items: 398 if item.name == name: 399 if rest is None: 400 return item 401 return item.getItemByName(rest) 402 raise KeyError("No item called %r" % name) 403 205 404 206 405 … … 246 445 def _fileFromWidget(self, form, ctx, segments): 247 446 ctx.remember(form, iformal.IForm) 248 widget = form. widgetForItem(segments[0])447 widget = form.getItemByName(segments[0]).makeWidget() 249 448 return widget.getResource(ctx, segments[0], segments[1:]) 250 449 … … 413 612 414 613 loader = loaders.stan( 415 T.form(id=T.slot('id'), action=T.slot('action'), class_='nevow-form', method='post', enctype='multipart/form-data', **{'accept-charset':'utf-8'})[ 416 T.fieldset[ 614 T.form(**{'id': T.slot('formId'), 'action': T.slot('formAction'), 615 'class': 'nevow-form', 'method': 'post', 'enctype': 616 'multipart/form-data', 'accept-charset': 'utf-8'})[ 617 T.div[ 417 618 T.input(type='hidden', name='_charset_'), 418 T.input(type='hidden', name=FORMS_KEY, value=T.slot('name')), 419 T.slot('errors'), 420 T.slot('items'), 421 T.div(id=T.slot('fieldId'), pattern='item', _class=T.slot('class'))[ 422 T.label(_class='label', _for=T.slot('id'))[T.slot('label')], 423 T.div(_class='inputs')[T.slot('inputs')], 424 T.slot('description'), 425 T.slot('message'), 426 ], 427 T.div(class_='hiddenitems')[ 428 T.slot('hiddenitems'), 429 T.invisible(pattern="hiddenitem")[T.slot('inputs')] 430 ], 619 T.input(type='hidden', name=FORMS_KEY, value=T.slot('formName')), 620 T.slot('formErrors'), 621 T.slot('formItems'), 431 622 T.div(class_='actions')[ 432 T.slot(' actions'),623 T.slot('formActions'), 433 624 ], 434 625 ], … … 442 633 def rend(self, ctx, data): 443 634 tag = T.invisible[self.loader.load()] 444 tag.fillSlots('name', self.original.name) 445 tag.fillSlots('id', util.keytocssid(ctx.key)) 446 tag.fillSlots('action', url.here) 447 tag.fillSlots('errors', self._renderErrors) 448 tag.fillSlots('items', self._renderItems) 449 tag.fillSlots('hiddenitems', self._renderHiddenItems) 450 tag.fillSlots('actions', self._renderActions) 635 tag.fillSlots('formName', self.original.name) 636 tag.fillSlots('formId', util.keytocssid(ctx.key)) 637 tag.fillSlots('formAction', url.here) 638 tag.fillSlots('formErrors', self._renderErrors) 639 tag.fillSlots('formItems', self._renderItems) 640 tag.fillSlots('formActions', self._renderActions) 451 641 return tag 452 642 … … 464 654 for error in errors: 465 655 if isinstance(error, validation.FieldError): 466 name, type, label, description, cssClass = self.original.getField(error.fieldName)467 errorList[ T.li[ T.strong[ label, ' : ' ], error.message ] ]656 item = self.original.getItemByName(error.fieldName) 657 errorList[ T.li[ T.strong[ item.label, ' : ' ], error.message ] ] 468 658 return T.div(class_='errors')[ T.p['Please correct the following errors:'], errorList ] 469 659 … … 472 662 yield '' 473 663 return 474 itemPattern = inevow.IQ(ctx).patternGenerator('item')475 664 for item in self.original.items: 476 widget = self.original.widgetForItem(item[0]) 477 if getattr(widget,'inputType','') != 'hidden': 478 yield itemPattern(key=item[0], data=item, render=self._renderItem) 479 480 def _renderHiddenItems(self, ctx, data): 481 if self.original.items is None: 482 yield '' 483 return 484 hiddenItemPattern = inevow.IQ(ctx).patternGenerator('hiddenitem') 485 for item in self.original.items: 486 widget = self.original.widgetForItem(item[0]) 487 if getattr(widget,'inputType','') == 'hidden': 488 yield hiddenItemPattern(key=item[0], data=item, render=self._renderHiddenItem) 489 490 def _renderItem(self, ctx, data): 491 492 def _(ctx, data): 493 494 name, type, label, description, cssClass = data 495 form = self.original 496 formErrors = iformal.IFormErrors(ctx, None) 497 formData = iformal.IFormData(ctx) 498 499 widget = form.widgetForItem(name) 500 if formErrors is None: 501 error = None 502 else: 503 error = formErrors.getFieldError(name) 504 505 # Basic classes are 'field', the type's class name and the widget's 506 # class name. 507 classes = [ 508 'field', 509 type.__class__.__name__.lower(), 510 widget.__class__.__name__.lower(), 511 ] 512 # Add a required class 513 if type.required: 514 classes.append('required') 515 # Add a user-specified class 516 if cssClass: 517 classes.append(cssClass) 518 519 if error is None: 520 message = '' 521 else: 522 classes.append('error') 523 message = T.div(class_='message')[error.message] 524 525 ctx.tag.fillSlots('class', ' '.join(classes)) 526 ctx.tag.fillSlots('fieldId', '%s-field'%util.keytocssid(ctx.key)) 527 ctx.tag.fillSlots('id', util.keytocssid(ctx.key)) 528 ctx.tag.fillSlots('label', label) 529 if type.immutable: 530 render = widget.renderImmutable 531 else: 532 render = widget.render 533 ctx.tag.fillSlots('inputs', render(ctx, name, formData, formErrors)) 534 ctx.tag.fillSlots('message', message) 535 ctx.tag.fillSlots('description', T.div(class_='description')[description or '']) 536 537 return ctx.tag 538 539 return _ 540 541 def _renderHiddenItem(self, ctx, data): 542 543 def _(ctx, data): 544 545 name, type, label, description, cssClass = data 546 form = self.original 547 formErrors = iformal.IFormErrors(ctx, None) 548 formData = iformal.IFormData(ctx) 549 550 widget = form.widgetForItem(name) 551 552 ctx.tag.fillSlots('fieldId', '%s-field'%util.keytocssid(ctx.key)) 553 ctx.tag.fillSlots('id', util.keytocssid(ctx.key)) 554 ctx.tag.fillSlots('inputs', widget.render(ctx, name, formData, formErrors)) 555 return ctx.tag 556 557 return _ 665 yield inevow.IRenderer(item) 558 666 559 667 def _renderActions(self, ctx, data): trunk/formal/formal.css
r220 r231 2 2 border: 1px solid #ccc; 3 3 padding: 5px; 4 }5 6 .nevow-form fieldset {7 margin: 0;8 border: 0;9 padding: 0;10 4 } 11 5 trunk/formal/util.py
r196 r231 1 1 import re 2 2 from zope.interface import implements 3 from nevow import inevow 3 from nevow import inevow, tags 4 4 from formal import iformal 5 5 … … 30 30 31 31 return ''.join(_()) 32 33 34 def render_cssid(fieldKey, *extras): 35 """ 36 Render the CSS id for the form field's key. 37 """ 38 l = [tags.slot('formName'), '-', '-'.join(fieldKey.split('.'))] 39 for extra in extras: 40 l.append('-') 41 l.append(extra) 42 return l 32 43 33 44 trunk/formal/widget.py
r225 r231 8 8 from nevow.i18n import _ 9 9 from formal import converters, iformal, validation 10 from formal.util import keytocssid10 from formal.util import render_cssid 11 11 from formal.form import widgetResourceURL, widgetResourceURLFromContext 12 12 from zope.interface import implements … … 33 33 34 34 def _renderTag(self, ctx, key, value, readonly): 35 tag=T.input(type=self.inputType, name=key, id= keytocssid(ctx.key), value=value)35 tag=T.input(type=self.inputType, name=key, id=render_cssid(key), value=value) 36 36 if readonly: 37 37 tag(class_='readonly', readonly='readonly') … … 69 69 70 70 def _renderTag(self, ctx, key, value, disabled): 71 tag = T.input(type='checkbox', name=key, id= keytocssid(ctx.key), value='True')71 tag = T.input(type='checkbox', name=key, id=render_cssid(key), value='True') 72 72 if value == 'True': 73 73 tag(checked='checked') … … 124 124 125 125 def _renderTag(self, ctx, key, value, readonly): 126 tag=T.textarea(name=key, id= keytocssid(ctx.key), cols=self.cols, rows=self.rows)[value or '']126 tag=T.textarea(name=key, id=render_cssid(key), cols=self.cols, rows=self.rows)[value or ''] 127 127 if readonly: 128 128 tag(class_='readonly', readonly='readonly') … … 161 161 values = ('', '') 162 162 return [ 163 T.input(type='password', name=key, id= keytocssid(ctx.key), value=values[0]),163 T.input(type='password', name=key, id=render_cssid(key), value=values[0]), 164 164 T.br, 165 T.label(for_= '%s__confirm'%keytocssid(ctx.key))[' Confirm '],166 T.input(type='password', name=key, id= '%s__confirm'%keytocssid(ctx.key), value=values[1]),165 T.label(for_=render_cssid(key, 'confirm'))[' Confirm '], 166 T.input(type='password', name=key, id=render_cssid(key, 'confirm'), value=values[1]), 167 167 ] 168 168 … … 170 170 values = ('', '') 171 171 return [ 172 T.input(type='password', name=key, id= keytocssid(ctx.key), value=values[0], class_='readonly', readonly='readonly'),172 T.input(type='password', name=key, id=render_cssid(key), value=values[0], class_='readonly', readonly='readonly'), 173 173 T.br, 174 T.label(for_='%s__confirm'%keytocssid(ctx.key))[' Confirm '], 175 T.input(type='password', name=key, id='%s__confirm'%keytocssid(ctx.key), value=values[1], class_='readonly', readonly='readonly') 174 T.label(for_=render_cssid(key, 'confirm'))[' Confirm '], 175 T.input(type='password', name=key, id=render_cssid(key, 'confirm'), 176 value=values[1], class_='readonly', readonly='readonly') 176 177 ] 177 178 … … 249 250 yield option 250 251 251 tag=T.select(name=key, id= keytocssid(ctx.key), data=self.options)[renderOptions]252 tag=T.select(name=key, id=render_cssid(key), data=self.options)[renderOptions] 252 253 if disabled: 253 254 tag(class_='disabled', disabled='disabled') … … 355 356 tag = T.invisible[self.template.load(ctx)] 356 357 tag.fillSlots('key', key) 357 tag.fillSlots('id', keytocssid(ctx.key))358 tag.fillSlots('id', render_cssid(key)) 358 359 tag.fillSlots('options', optionTags) 359 360 tag.fillSlots('otherValue', otherValue) … … 383 384 384 385 def renderOption(ctx, itemKey, itemLabel, num, selected): 385 cssid = (keytocssid(ctx.key),'-',num)386 cssid = render_cssid(key, num) 386 387 tag = T.input(name=key, type='radio', id=cssid, value=itemKey) 387 388 if selected: … … 632 633 optLabel = iformal.ILabel(item).label() 633 634 optValue = converter.fromType(optValue) 634 optid = (keytocssid(ctx.key),'-',n) 635 checkbox = T.input(type='checkbox', name=key, value=optValue, id=optid ) 635 optid = render_cssid(key, n) 636 checkbox = T.input(type='checkbox', name=key, value=optValue, 637 id=optid) 636 638 if optValue in values: 637 639 checkbox = checkbox(checked='checked') … … 683 685 684 686 def _renderTag(self, ctx, key, disabled): 685 tag=T.input(name=key, id= keytocssid(ctx.key),type='file')687 tag=T.input(name=key, id=render_cssid(key),type='file') 686 688 if disabled: 687 689 tag(class_='disabled', disabled='disabled') … … 733 735 734 736 yield T.input(name=namer('value'),value=value,type='hidden') 735 tag=T.input(name=key, id= keytocssid(ctx.key),type='file')737 tag=T.input(name=key, id=render_cssid(key),type='file') 736 738 if disabled: 737 739 tag(class_='disabled', disabled='disabled') … … 863 865 yield T.p[T.strong['Nothing uploaded']] 864 866 865 yield T.input(name=key, id= keytocssid(ctx.key),type='file')867 yield T.input(name=key, id=render_cssid(key),type='file') 866 868 867 869 # Id of uploaded file in the resource manager … … 983 985 else: 984 986 value = iformal.IStringConvertible(self.original).fromType(args.get(key)) 985 return T.input(type=self.inputType, name=key, id= keytocssid(ctx.key), value=value)987 return T.input(type=self.inputType, name=key, id=render_cssid(key), value=value) 986 988 987 989 def renderImmutable(self, ctx, key, args, errors): trunk/formal/widgets/multiselect.py
r196 r231 2 2 from nevow import tags as T 3 3 from formal import iformal 4 from formal.util import keytocssid4 from formal.util import render_cssid 5 5 6 6 _UNSET = object() … … 73 73 yield option 74 74 75 tag=T.select(name=key, id= keytocssid(ctx.key), data=self.options, multiple="multiple")[renderOptions]75 tag=T.select(name=key, id=render_cssid(key), data=self.options, multiple="multiple")[renderOptions] 76 76 77 77 if disabled: trunk/formal/widgets/restwidget.py
r196 r231 5 5 from nevow import inevow, loaders, rend, tags as T 6 6 from formal import iformal, widget 7 from formal.util import keytocssid7 from formal.util import render_cssid 8 8 from formal.form import widgetResourceURLFromContext 9 9 … … 25 25 def _renderTag(self, ctx, key, value, readonly): 26 26 tag=T.invisible() 27 ta=T.textarea(name=key, id= keytocssid(ctx.key), cols=self.cols, rows=self.rows)[value or '']27 ta=T.textarea(name=key, id=render_cssid(key), cols=self.cols, rows=self.rows)[value or ''] 28 28 if readonly: 29 29 ta(class_='readonly', readonly='readonly') … … 37 37 else: 38 38 form = iformal.IForm( ctx ) 39 srcId = keytocssid(ctx.key)40 previewDiv = srcId + '-preview-div'41 frameId = srcId + '-preview-frame'39 srcId = render_cssid(key) 40 previewDiv = render_cssid(key, 'preview-div') 41 frameId = render_cssid(key, 'preview-frame') 42 42 targetURL = widgetResourceURLFromContext(ctx, form.name).child(key).child( srcId ) 43 43 tag[T.br()] 44 tag[T.button(onClick="return Forms.Util.previewShow('%s', '%s', '%s');"%(previewDiv, frameId, targetURL))['Preview ...']] 44 onclick = ["return Forms.Util.previewShow('",previewDiv, "', '", 45 frameId, "', '", targetURL, "');"] 46 tag[T.button(onclick=onclick)['Preview ...']] 45 47 tag[T.div(id=previewDiv, class_="preview-hidden")[ 46 48 T.iframe(class_="preview-frame", name=frameId, id=frameId), 47 49 T.br(), 48 T.button(on Click="return Forms.Util.previewHide('%s');"%(previewDiv))['Close']50 T.button(onclick=["return Forms.Util.previewHide('", previewDiv, "');"])['Close'] 49 51 ] 50 52 ] trunk/setup.py
r230 r231 3 3 setup( 4 4 name='formal', 5 version='0. 8.2',5 version='0.9', 6 6 description='HTML forms framework for Nevow', 7 7 author='Matt Goodall',
