OpenSecurity/install/web.py-0.37/build/lib/web/form.py
author om
Mon, 02 Dec 2013 14:02:05 +0100
changeset 3 65432e6c6042
permissions -rwxr-xr-x
initial deployment and project layout commit
om@3
     1
"""
om@3
     2
HTML forms
om@3
     3
(part of web.py)
om@3
     4
"""
om@3
     5
om@3
     6
import copy, re
om@3
     7
import webapi as web
om@3
     8
import utils, net
om@3
     9
om@3
    10
def attrget(obj, attr, value=None):
om@3
    11
    try:
om@3
    12
        if hasattr(obj, 'has_key') and obj.has_key(attr): 
om@3
    13
            return obj[attr]
om@3
    14
    except TypeError:
om@3
    15
        # Handle the case where has_key takes different number of arguments.
om@3
    16
        # This is the case with Model objects on appengine. See #134
om@3
    17
        pass
om@3
    18
    if hasattr(obj, attr):
om@3
    19
        return getattr(obj, attr)
om@3
    20
    return value
om@3
    21
om@3
    22
class Form(object):
om@3
    23
    r"""
om@3
    24
    HTML form.
om@3
    25
    
om@3
    26
        >>> f = Form(Textbox("x"))
om@3
    27
        >>> f.render()
om@3
    28
        u'<table>\n    <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
om@3
    29
    """
om@3
    30
    def __init__(self, *inputs, **kw):
om@3
    31
        self.inputs = inputs
om@3
    32
        self.valid = True
om@3
    33
        self.note = None
om@3
    34
        self.validators = kw.pop('validators', [])
om@3
    35
om@3
    36
    def __call__(self, x=None):
om@3
    37
        o = copy.deepcopy(self)
om@3
    38
        if x: o.validates(x)
om@3
    39
        return o
om@3
    40
    
om@3
    41
    def render(self):
om@3
    42
        out = ''
om@3
    43
        out += self.rendernote(self.note)
om@3
    44
        out += '<table>\n'
om@3
    45
        
om@3
    46
        for i in self.inputs:
om@3
    47
            html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post)
om@3
    48
            if i.is_hidden():
om@3
    49
                out += '    <tr style="display: none;"><th></th><td>%s</td></tr>\n' % (html)
om@3
    50
            else:
om@3
    51
                out += '    <tr><th><label for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, net.websafe(i.description), html)
om@3
    52
        out += "</table>"
om@3
    53
        return out
om@3
    54
        
om@3
    55
    def render_css(self): 
om@3
    56
        out = [] 
om@3
    57
        out.append(self.rendernote(self.note)) 
om@3
    58
        for i in self.inputs:
om@3
    59
            if not i.is_hidden():
om@3
    60
                out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description))) 
om@3
    61
            out.append(i.pre)
om@3
    62
            out.append(i.render()) 
om@3
    63
            out.append(self.rendernote(i.note))
om@3
    64
            out.append(i.post) 
om@3
    65
            out.append('\n')
om@3
    66
        return ''.join(out) 
om@3
    67
        
om@3
    68
    def rendernote(self, note):
om@3
    69
        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
om@3
    70
        else: return ""
om@3
    71
    
om@3
    72
    def validates(self, source=None, _validate=True, **kw):
om@3
    73
        source = source or kw or web.input()
om@3
    74
        out = True
om@3
    75
        for i in self.inputs:
om@3
    76
            v = attrget(source, i.name)
om@3
    77
            if _validate:
om@3
    78
                out = i.validate(v) and out
om@3
    79
            else:
om@3
    80
                i.set_value(v)
om@3
    81
        if _validate:
om@3
    82
            out = out and self._validate(source)
om@3
    83
            self.valid = out
om@3
    84
        return out
om@3
    85
om@3
    86
    def _validate(self, value):
om@3
    87
        self.value = value
om@3
    88
        for v in self.validators:
om@3
    89
            if not v.valid(value):
om@3
    90
                self.note = v.msg
om@3
    91
                return False
om@3
    92
        return True
om@3
    93
om@3
    94
    def fill(self, source=None, **kw):
om@3
    95
        return self.validates(source, _validate=False, **kw)
om@3
    96
    
om@3
    97
    def __getitem__(self, i):
om@3
    98
        for x in self.inputs:
om@3
    99
            if x.name == i: return x
om@3
   100
        raise KeyError, i
om@3
   101
om@3
   102
    def __getattr__(self, name):
om@3
   103
        # don't interfere with deepcopy
om@3
   104
        inputs = self.__dict__.get('inputs') or []
om@3
   105
        for x in inputs:
om@3
   106
            if x.name == name: return x
om@3
   107
        raise AttributeError, name
om@3
   108
    
om@3
   109
    def get(self, i, default=None):
om@3
   110
        try:
om@3
   111
            return self[i]
om@3
   112
        except KeyError:
om@3
   113
            return default
om@3
   114
            
om@3
   115
    def _get_d(self): #@@ should really be form.attr, no?
om@3
   116
        return utils.storage([(i.name, i.get_value()) for i in self.inputs])
om@3
   117
    d = property(_get_d)
om@3
   118
om@3
   119
class Input(object):
om@3
   120
    def __init__(self, name, *validators, **attrs):
om@3
   121
        self.name = name
om@3
   122
        self.validators = validators
om@3
   123
        self.attrs = attrs = AttributeList(attrs)
om@3
   124
        
om@3
   125
        self.description = attrs.pop('description', name)
om@3
   126
        self.value = attrs.pop('value', None)
om@3
   127
        self.pre = attrs.pop('pre', "")
om@3
   128
        self.post = attrs.pop('post', "")
om@3
   129
        self.note = None
om@3
   130
        
om@3
   131
        self.id = attrs.setdefault('id', self.get_default_id())
om@3
   132
        
om@3
   133
        if 'class_' in attrs:
om@3
   134
            attrs['class'] = attrs['class_']
om@3
   135
            del attrs['class_']
om@3
   136
        
om@3
   137
    def is_hidden(self):
om@3
   138
        return False
om@3
   139
        
om@3
   140
    def get_type(self):
om@3
   141
        raise NotImplementedError
om@3
   142
        
om@3
   143
    def get_default_id(self):
om@3
   144
        return self.name
om@3
   145
om@3
   146
    def validate(self, value):
om@3
   147
        self.set_value(value)
om@3
   148
om@3
   149
        for v in self.validators:
om@3
   150
            if not v.valid(value):
om@3
   151
                self.note = v.msg
om@3
   152
                return False
om@3
   153
        return True
om@3
   154
om@3
   155
    def set_value(self, value):
om@3
   156
        self.value = value
om@3
   157
om@3
   158
    def get_value(self):
om@3
   159
        return self.value
om@3
   160
om@3
   161
    def render(self):
om@3
   162
        attrs = self.attrs.copy()
om@3
   163
        attrs['type'] = self.get_type()
om@3
   164
        if self.value is not None:
om@3
   165
            attrs['value'] = self.value
om@3
   166
        attrs['name'] = self.name
om@3
   167
        return '<input %s/>' % attrs
om@3
   168
om@3
   169
    def rendernote(self, note):
om@3
   170
        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
om@3
   171
        else: return ""
om@3
   172
        
om@3
   173
    def addatts(self):
om@3
   174
        # add leading space for backward-compatibility
om@3
   175
        return " " + str(self.attrs)
om@3
   176
om@3
   177
class AttributeList(dict):
om@3
   178
    """List of atributes of input.
om@3
   179
    
om@3
   180
    >>> a = AttributeList(type='text', name='x', value=20)
om@3
   181
    >>> a
om@3
   182
    <attrs: 'type="text" name="x" value="20"'>
om@3
   183
    """
om@3
   184
    def copy(self):
om@3
   185
        return AttributeList(self)
om@3
   186
        
om@3
   187
    def __str__(self):
om@3
   188
        return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()])
om@3
   189
        
om@3
   190
    def __repr__(self):
om@3
   191
        return '<attrs: %s>' % repr(str(self))
om@3
   192
om@3
   193
class Textbox(Input):
om@3
   194
    """Textbox input.
om@3
   195
    
om@3
   196
        >>> Textbox(name='foo', value='bar').render()
om@3
   197
        u'<input type="text" id="foo" value="bar" name="foo"/>'
om@3
   198
        >>> Textbox(name='foo', value=0).render()
om@3
   199
        u'<input type="text" id="foo" value="0" name="foo"/>'
om@3
   200
    """        
om@3
   201
    def get_type(self):
om@3
   202
        return 'text'
om@3
   203
om@3
   204
class Password(Input):
om@3
   205
    """Password input.
om@3
   206
om@3
   207
        >>> Password(name='password', value='secret').render()
om@3
   208
        u'<input type="password" id="password" value="secret" name="password"/>'
om@3
   209
    """
om@3
   210
    
om@3
   211
    def get_type(self):
om@3
   212
        return 'password'
om@3
   213
om@3
   214
class Textarea(Input):
om@3
   215
    """Textarea input.
om@3
   216
    
om@3
   217
        >>> Textarea(name='foo', value='bar').render()
om@3
   218
        u'<textarea id="foo" name="foo">bar</textarea>'
om@3
   219
    """
om@3
   220
    def render(self):
om@3
   221
        attrs = self.attrs.copy()
om@3
   222
        attrs['name'] = self.name
om@3
   223
        value = net.websafe(self.value or '')
om@3
   224
        return '<textarea %s>%s</textarea>' % (attrs, value)
om@3
   225
om@3
   226
class Dropdown(Input):
om@3
   227
    r"""Dropdown/select input.
om@3
   228
    
om@3
   229
        >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
om@3
   230
        u'<select id="foo" name="foo">\n  <option value="a">a</option>\n  <option selected="selected" value="b">b</option>\n  <option value="c">c</option>\n</select>\n'
om@3
   231
        >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
om@3
   232
        u'<select id="foo" name="foo">\n  <option value="a">aa</option>\n  <option selected="selected" value="b">bb</option>\n  <option value="c">cc</option>\n</select>\n'
om@3
   233
    """
om@3
   234
    def __init__(self, name, args, *validators, **attrs):
om@3
   235
        self.args = args
om@3
   236
        super(Dropdown, self).__init__(name, *validators, **attrs)
om@3
   237
om@3
   238
    def render(self):
om@3
   239
        attrs = self.attrs.copy()
om@3
   240
        attrs['name'] = self.name
om@3
   241
        
om@3
   242
        x = '<select %s>\n' % attrs
om@3
   243
        
om@3
   244
        for arg in self.args:
om@3
   245
            x += self._render_option(arg)
om@3
   246
om@3
   247
        x += '</select>\n'
om@3
   248
        return x
om@3
   249
om@3
   250
    def _render_option(self, arg, indent='  '):
om@3
   251
        if isinstance(arg, (tuple, list)):
om@3
   252
            value, desc= arg
om@3
   253
        else:
om@3
   254
            value, desc = arg, arg 
om@3
   255
om@3
   256
        if self.value == value or (isinstance(self.value, list) and value in self.value):
om@3
   257
            select_p = ' selected="selected"'
om@3
   258
        else:
om@3
   259
            select_p = ''
om@3
   260
        return indent + '<option%s value="%s">%s</option>\n' % (select_p, net.websafe(value), net.websafe(desc))
om@3
   261
        
om@3
   262
om@3
   263
class GroupedDropdown(Dropdown):
om@3
   264
    r"""Grouped Dropdown/select input.
om@3
   265
    
om@3
   266
        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render()
om@3
   267
        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="Volvo">Volvo</option>\n    <option value="Saab">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="Mercedes">Mercedes</option>\n    <option selected="selected" value="Audi">Audi</option>\n  </optgroup>\n</select>\n'
om@3
   268
        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render()
om@3
   269
        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="v">Volvo</option>\n    <option value="s">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="m">Mercedes</option>\n    <option selected="selected" value="a">Audi</option>\n  </optgroup>\n</select>\n'
om@3
   270
om@3
   271
    """
om@3
   272
    def __init__(self, name, args, *validators, **attrs):
om@3
   273
        self.args = args
om@3
   274
        super(Dropdown, self).__init__(name, *validators, **attrs)
om@3
   275
om@3
   276
    def render(self):
om@3
   277
        attrs = self.attrs.copy()
om@3
   278
        attrs['name'] = self.name
om@3
   279
        
om@3
   280
        x = '<select %s>\n' % attrs
om@3
   281
        
om@3
   282
        for label, options in self.args:
om@3
   283
            x += '  <optgroup label="%s">\n' % net.websafe(label)
om@3
   284
            for arg in options:
om@3
   285
                x += self._render_option(arg, indent = '    ')
om@3
   286
            x +=  '  </optgroup>\n'
om@3
   287
            
om@3
   288
        x += '</select>\n'
om@3
   289
        return x
om@3
   290
om@3
   291
class Radio(Input):
om@3
   292
    def __init__(self, name, args, *validators, **attrs):
om@3
   293
        self.args = args
om@3
   294
        super(Radio, self).__init__(name, *validators, **attrs)
om@3
   295
om@3
   296
    def render(self):
om@3
   297
        x = '<span>'
om@3
   298
        for arg in self.args:
om@3
   299
            if isinstance(arg, (tuple, list)):
om@3
   300
                value, desc= arg
om@3
   301
            else:
om@3
   302
                value, desc = arg, arg 
om@3
   303
            attrs = self.attrs.copy()
om@3
   304
            attrs['name'] = self.name
om@3
   305
            attrs['type'] = 'radio'
om@3
   306
            attrs['value'] = value
om@3
   307
            if self.value == value:
om@3
   308
                attrs['checked'] = 'checked'
om@3
   309
            x += '<input %s/> %s' % (attrs, net.websafe(desc))
om@3
   310
        x += '</span>'
om@3
   311
        return x
om@3
   312
om@3
   313
class Checkbox(Input):
om@3
   314
    """Checkbox input.
om@3
   315
om@3
   316
    >>> Checkbox('foo', value='bar', checked=True).render()
om@3
   317
    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
om@3
   318
    >>> Checkbox('foo', value='bar').render()
om@3
   319
    u'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
om@3
   320
    >>> c = Checkbox('foo', value='bar')
om@3
   321
    >>> c.validate('on')
om@3
   322
    True
om@3
   323
    >>> c.render()
om@3
   324
    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
om@3
   325
    """
om@3
   326
    def __init__(self, name, *validators, **attrs):
om@3
   327
        self.checked = attrs.pop('checked', False)
om@3
   328
        Input.__init__(self, name, *validators, **attrs)
om@3
   329
        
om@3
   330
    def get_default_id(self):
om@3
   331
        value = utils.safestr(self.value or "")
om@3
   332
        return self.name + '_' + value.replace(' ', '_')
om@3
   333
om@3
   334
    def render(self):
om@3
   335
        attrs = self.attrs.copy()
om@3
   336
        attrs['type'] = 'checkbox'
om@3
   337
        attrs['name'] = self.name
om@3
   338
        attrs['value'] = self.value
om@3
   339
om@3
   340
        if self.checked:
om@3
   341
            attrs['checked'] = 'checked'            
om@3
   342
        return '<input %s/>' % attrs
om@3
   343
om@3
   344
    def set_value(self, value):
om@3
   345
        self.checked = bool(value)
om@3
   346
om@3
   347
    def get_value(self):
om@3
   348
        return self.checked
om@3
   349
om@3
   350
class Button(Input):
om@3
   351
    """HTML Button.
om@3
   352
    
om@3
   353
    >>> Button("save").render()
om@3
   354
    u'<button id="save" name="save">save</button>'
om@3
   355
    >>> Button("action", value="save", html="<b>Save Changes</b>").render()
om@3
   356
    u'<button id="action" value="save" name="action"><b>Save Changes</b></button>'
om@3
   357
    """
om@3
   358
    def __init__(self, name, *validators, **attrs):
om@3
   359
        super(Button, self).__init__(name, *validators, **attrs)
om@3
   360
        self.description = ""
om@3
   361
om@3
   362
    def render(self):
om@3
   363
        attrs = self.attrs.copy()
om@3
   364
        attrs['name'] = self.name
om@3
   365
        if self.value is not None:
om@3
   366
            attrs['value'] = self.value
om@3
   367
        html = attrs.pop('html', None) or net.websafe(self.name)
om@3
   368
        return '<button %s>%s</button>' % (attrs, html)
om@3
   369
om@3
   370
class Hidden(Input):
om@3
   371
    """Hidden Input.
om@3
   372
    
om@3
   373
        >>> Hidden(name='foo', value='bar').render()
om@3
   374
        u'<input type="hidden" id="foo" value="bar" name="foo"/>'
om@3
   375
    """
om@3
   376
    def is_hidden(self):
om@3
   377
        return True
om@3
   378
        
om@3
   379
    def get_type(self):
om@3
   380
        return 'hidden'
om@3
   381
om@3
   382
class File(Input):
om@3
   383
    """File input.
om@3
   384
    
om@3
   385
        >>> File(name='f').render()
om@3
   386
        u'<input type="file" id="f" name="f"/>'
om@3
   387
    """
om@3
   388
    def get_type(self):
om@3
   389
        return 'file'
om@3
   390
    
om@3
   391
class Validator:
om@3
   392
    def __deepcopy__(self, memo): return copy.copy(self)
om@3
   393
    def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
om@3
   394
    def valid(self, value): 
om@3
   395
        try: return self.test(value)
om@3
   396
        except: return False
om@3
   397
om@3
   398
notnull = Validator("Required", bool)
om@3
   399
om@3
   400
class regexp(Validator):
om@3
   401
    def __init__(self, rexp, msg):
om@3
   402
        self.rexp = re.compile(rexp)
om@3
   403
        self.msg = msg
om@3
   404
    
om@3
   405
    def valid(self, value):
om@3
   406
        return bool(self.rexp.match(value))
om@3
   407
om@3
   408
if __name__ == "__main__":
om@3
   409
    import doctest
om@3
   410
    doctest.testmod()