1 """
2 This file is part of web2py Web Framework (Copyrighted, 2007)
3 Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu>
4 License: GPL v2
5 """
6
7 import os, re, random, copy, sys, types, datetime, time, cgi, hmac
8 import gluon.sql
9 try:
10 import hashlib
11 have_hashlib=True
12 except:
13 import sha, md5
14 have_hashlib=False
15 from storage import Storage
16
17 __all__=['IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_EMAIL', 'IS_EXPR','IS_FLOAT_IN_RANGE', 'IS_INT_IN_RANGE', 'IS_IN_SET', 'IS_LENGTH', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_TIME', 'IS_URL', 'CLEANUP', 'CRYPT', 'IS_IN_DB', 'IS_NOT_IN_DB', 'IS_UPPER', 'IS_NULL_OR']
18
20 """
21 example:
22
23 INPUT(_type='text',_name='name',requires=IS_MATCH('.+'))
24
25 the argument of IS_MATCH is a regular expression.
26
27 IS_MATCH('.+')('hello') returns ('hello',None)
28 IS_MATCH('.+')('') returns ('','invalid!')
29 """
30 - def __init__(self,expression,error_message='invalid expression!'):
34 match=self.regex.match(value)
35 if match: return (match.group(),None)
36 return (value,self.error_message)
37
39 """
40 example:
41
42 INPUT(_type='text',_name='name',requires=IS_EXPR('5<int(value)<10'))
43
44 the argument of IS_EXPR must be python condition
45
46 IS_EXPR('int(value)<2')('1') returns (1,None)
47 IS_EXPR('int(value)<2')('2') returns ('2','invalid expression!')
48 """
49 - def __init__(self,expression,error_message='invalid expression!'):
53 environment={'value':value}
54 exec('__ret__='+self.expression) in environment
55 if environment['__ret__']: return (value,None)
56 return (value,self.error_message)
57
59 """
60 example:
61
62 INPUT(_type='text',_name='name',requires=IS_LENGTH(32))
63
64 the argument of IS_LENGTH is the man number of characters
65 """
66 - def __init__(self,size,error_message='too long!'):
70 if isinstance(value,cgi.FieldStorage):
71 if value.file:
72 value.file.seek(0,os.SEEK_END)
73 length=value.file.tell()
74 value.file.seek(0,os.SEEK_SET)
75 else:
76 val=value.value
77 if val: length=len(val)
78 else: length=0
79 if length<=self.size: return (value,None)
80 elif isinstance(value,(str,unicode)):
81 if len(value)<=self.size: return (value,None)
82 elif len(str(value))<=self.size: return (value,None)
83 return (value,self.error_message)
84
86 """
87 example:
88
89 INPUT(_type='text',_name='name',requires=IS_IN_SET(['max','john']))
90
91 the argument of IS_IN_SET must be a list or set
92 """
93 - def __init__(self,theset,labels=None,error_message='value not allowed!'):
98 if value in self.theset: return (value,None)
99 return (value,self.error_message)
100
101 -def IS_IN_DB(dbset,field,label=None,error_message='value not in database!'):
102 """
103 example:
104
105 INPUT(_type='text',_name='name',requires=IS_IN_DB(db,db.table))
106
107 used for reference fields, rendered as a dropbox
108 """
109 try: dbset=dbset()
110 except TypeError: pass
111 ktable=str(field).split('.')[0]
112 kfield=str(field).split('.')[-1]
113 if not label: label='%%(%s)s' % kfield
114 elif str(label).find('%')<0: label='%%(%s)s' % str(label).split('.')[-1]
115 ks=re.compile('%\((?P<name>[^\)]+)\)s').findall(label)
116 if not kfield in ks: ks+=[kfield]
117 fields=['%s.%s'%(ktable,k) for k in ks]
118 records=dbset.select(*fields,**dict(orderby=', '.join(fields)))
119 theset=[r[kfield] for r in records]
120 labels=[label % dict(r) for r in records]
121 return IS_IN_SET(theset,labels,error_message)
122
124 """
125 example:
126
127 INPUT(_type='text',_name='name',requires=IS_NOT_IN_DB(db,db.table))
128
129 makes the field unique
130 """
131 - def __init__(self,dbset,field,error_message='value already in database!'):
137 fieldname=str(self.field)
138 id_field='%s.id' % fieldname[:fieldname.find('.')]
139 value_field=gluon.sql.sql_represent(value,'string',None)
140 if not self.record_id: value_id='0'
141 else: value_id=gluon.sql.sql_represent(self.record_id,'integer',None)
142 fetched=self.dbset("%s=%s AND %s<>%s" % (fieldname,value_field,id_field,value_id)).select('count(*)')
143 if fetched[0]['count(*)']==0: return (value,None)
144 return (value,self.error_message)
145
147 """
148 example:
149
150 INPUT(_type='text',_name='name',requires=IS_INT_IN_RANGE(0,10))
151 """
152 - def __init__(self,minimum,maximum,error_message='too small or too large!'):
157 try:
158 fvalue=float(value)
159 value=int(value)
160 if value==fvalue and self.minimum<=value<self.maximum:
161 return (value,None)
162 except ValueError: pass
163 return (value,self.error_message)
164
166 """
167 example:
168
169 INPUT(_type='text',_name='name',requires=IS_FLOAT_IN_RANGE(0,10))
170 """
171 - def __init__(self,minimum,maximum,error_message='too small or too large!'):
176 try:
177 value=float(value)
178 if self.minimum<=value<=self.maximum: return (value,None)
179 except ValueError: pass
180 return (value,self.error_message)
181
183 """
184 example:
185
186 INPUT(_type='text',_name='name',requires=IS_NOT_EMPTY())
187 """
188 - def __init__(self,error_message='cannot be empty!'):
191 if value==None or value=='': return (value,self.error_message)
192 return (value,None)
193
195 """
196 example:
197
198 INPUT(_type='text',_name='name',requires=IS_ALPHANUMERIC())
199 """
200 - def __init__(self,error_message='must be alphanumeric!'):
202
204 """
205 example:
206
207 INPUT(_type='text',_name='name',requires=IS_EMAIL())
208 """
209 - def __init__(self,error_message='invalid email!'):
211
213 """
214 example:
215
216 INPUT(_type='text',_name='name',requires=IS_URL())
217 """
218 - def __init__(self,error_message='invalid url!'):
220
222 """
223 example:
224
225 INPUT(_type='text',_name='name',requires=IS_TIME())
226
227 understands the follwing formats
228 hh:mm:ss [am/pm]
229 hh:mm [am/pm]
230 hh [am/pm]
231
232 [am/pm] is options, ':' can be replaced by any other non-digit
233 """
234 - def __init__(self,error_message='must be HH:MM:SS!'):
237 try:
238 ivalue=value
239 value=re.compile('((?P<h>[0-9]+))([^0-9 ]+(?P<m>[0-9 ]+))?([^0-9ap ]+(?P<s>[0-9]*))?((?P<d>[ap]m))?').match(value.lower())
240 h,m,s=int(value.group('h')),0,0
241 if value.group('m')!=None: m=int(value.group('m'))
242 if value.group('s')!=None: s=int(value.group('s'))
243 if value.group('d')=='pm' and 0<h<12: h=h+12
244 if not (h in range(24) and m in range(60) and s in range(60)):
245 raise ValueError
246 value='%.2i:%.2i:%.2i' % (h,m,s)
247 return (value,None)
248 except AttributeError: pass
249 except ValueError: pass
250 return (ivalue,self.error_message)
251
253 """
254 example:
255
256 INPUT(_type='text',_name='name',requires=IS_DATE())
257
258 date has to be in the ISO8960 format YYYY-MM-DD
259 """
260 - def __init__(self,format='%Y-%m-%d',error_message='must be YYYY-MM-DD!'):
264 try:
265 y, m, d, hh, mm, ss, t0, t1, t2=time.strptime(value,str(self.format))
266 value=datetime.date(y,m,d).isoformat()
267 return (value,None)
268 except:
269 return (value,self.error_message)
272
274 """
275 example:
276
277 INPUT(_type='text',_name='name',requires=IS_DATETIME())
278
279 datetime has to be in the ISO8960 format YYYY-MM-DD hh:mm:ss
280 """
281 isodatetime='%Y-%m-%d %H:%M:%S'
282 - def __init__(self,format='%Y-%m-%d %H:%M:%S',error_message='must be YYYY-MM-DD HH:MM:SS!'):
286 try:
287 y, m, d, hh, mm, ss, t0, t1, t2=time.strptime(value,str(self.format))
288 value=datetime.datetime(y,m,d,hh,mm,ss).strftime(self.isodatetime)
289 return (value,None)
290 except:
291 return (value,self.error_message)
294
297
300
303 self.other,self.null=other,null
305 if not value: return (self.null,None)
306 return self.other(value)
311
313 """
314 example:
315
316 INPUT(_type='text',_name='name',requires=CLEANUP())
317
318 removes special characters on validation
319 """
322 v=''
323 for c in str(value).strip():
324 if ord(c) in [10,13]+range(32,127): v+=c
325 return (v,None)
326
328 """
329 example:
330
331 INPUT(_type='text',_name='name',requires=CRYPT())
332
333 encodes the value on validation with md5 checkshum
334 """
338 if self.key:
339 if have_hashlib: return (hmac.new(self.key,value,hashlib.sha512).hexdigest(),None)
340 else: return (hmac.new(self.key,value,sha).hexdigest(),None)
341 if have_hashlib: return (hashlib.md5(value).hexdigest(),None)
342 else: return (md5.new(value).hexdigest(),None)
343