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 cgi, re, random, copy, sys, types, urllib, tokenize, keyword, base64
8 from storage import Storage
9 from validators import *
10 from highlight import highlight
11 import sanitizer
12
13 __all__=['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CENTER', 'CODE', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'IFRAME', 'IMG', 'INPUT', 'LABEL', 'LI', 'LINK', 'OL', 'UL', 'META', 'OBJECT', 'ON', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'STYLE', 'TABLE', 'TD', 'TEXTAREA', 'TH', 'TITLE', 'TR', 'TT', 'URL', 'XML', 'xmlescape', 'embed64']
14
16 try:
17 data=data.xml()
18 except AttributeError:
19 if not isinstance(data,(str,unicode)): data=str(data)
20 if isinstance(data,unicode): data=data.encode("utf8","xmlcharrefreplace")
21 data=cgi.escape(data,quote)
22 return data
23
24 -def URL(a=None,c=None,f=None,r=None,args=[],vars={}):
25 """
26 example:
27
28 >>> URL(a='a',c='c',f='f',args=['x','y','z'],vars={'p':1, 'q':2})
29 '/a/c/f/x/y/z?q=2&p=1'
30
31 generates a url "/a/c/f" corresponding to application a, controller c
32 and function f. If r=request is passed, a,c,f are set, respectively,
33 to r.applicaiton, r.controller, r.function.
34
35 The more typical usage is:
36
37 URL(r=request,f='index') that generates a url for the index function
38 within the present application and controller.
39 """
40 application=controller=function=None
41 if r:
42 application=r.application
43 controller=r.controller
44 function=r.function
45 if a: application=a
46 if c: controller=c
47 if f:
48 if isinstance(f,str): function=f
49 else: function=f.__name__
50 if not (application and controller and function):
51 raise SyntaxError, 'not enough information to build the url'
52 other=''
53 if args: other='/'+'/'.join([str(x) for x in args])
54 if vars: other=other+'?'+urllib.urlencode(vars)
55 url='/%s/%s/%s%s' % (application, controller, function, other)
56 return url
57
58 ON=None
59
61 """
62 example:
63
64 >>> XML('<h1>Hello</h1>').xml()
65 '<h1>Hello</h1>'
66
67 use it to wrap a string that contains XML/HTML so that it will not be
68 escaped by the template
69 """
70 - def __init__(self,text,sanitize=False,permitted_tags=['a','b','blockquote','br/','i', 'li', 'ol','ul', 'p', 'cite','code','pre','img/'],allowed_attributes={'a':['href','title'],'img':['src','alt'],'blockquote':['type']}):
71 if sanitize: text=sanitizer.sanitize(text,permitted_tags,allowed_attributes)
72 self.text=text
77
79 """
80 example:
81
82 >>> DIV('hello','world',_style='color:red;').xml()
83 '<div style="color:red;">helloworld</div>'
84
85 all other HTML helpers are derived from DIV.
86 _something="value" attributes are transparently translated into
87 something="value" HTML attributes
88 """
89 tag='div'
90 - def __init__(self,*components,**attributes):
91 if self.tag[-1]=='/' and components:
92 raise SyntaxError, '<%s> tags cannot have components' % self.tag
93 self.components=list(components)
94 self.attributes=attributes
95 self.postprocessing()
96 self.errors=Storage()
97 self.vars=Storage()
98 self.session=None
99 self.formname=None
100 - def postprocessing(self):
102 - def rec_clear(self,clear_attributes_value=False):
103 if hasattr(self,'attributes'):
104 if clear_attributes_value:
105 if self.attributes.has_key('default'):
106 self.attributes['value']=self.attributes['default']
107 else:
108 self.attributes['value']=''
109 self.postprocessing()
110 if self.attributes.has_key('value'):
111 self.attributes['default']=self.attributes['value']
112 for c in self.components:
113 if hasattr(c,'rec_clear'):
114 c.errors=self.errors
115 c.vars=self.vars
116 c.session=self.session
117 c.formname=self.formname
118 c.rec_clear(clear_attributes_value)
119 - def accepts(self,vars,session=None,formname='default',keepvalues=False):
120 self.errors=Storage()
121 self.session=session
122 self.formname=formname
123 self.rec_clear()
124 form_key='_form_key[%s]' % formname
125 if session!=None and session.has_key(form_key):
126 form_key_value=session[form_key]
127 del session[form_key]
128 if not vars.has_key('_form_key') or \
129 vars['_form_key']!=form_key_value:
130 return False
131 if formname and formname!=vars._formname: return False
132 self.rec_accepts(vars)
133 if not len(self.errors) and not keepvalues: self.rec_clear(True)
134 return len(self.errors)==0
136 for c in self.components:
137 if hasattr(c,'rec_accepts'): c.rec_accepts(vars)
139 items=self.attributes.items()
140 fa=' '.join([key[1:].lower() for key,value in items if key[:1]=='_' and value==None]+['%s="%s"' % (key[1:].lower(),xmlescape(value,True)) for key,value in self.attributes.items() if key[:1]=='_' and value])
141 if fa: fa=' '+fa
142 co=''.join([xmlescape(component) for component in self.components])
143 return fa,co
145 fa,co=self._xml()
146 if self.tag[-1]=='/': return '<%s%s/>' % (self.tag[:-1],fa)
147 return '<%s%s>%s</%s>' % (self.tag,fa,co,self.tag)
150
152 tag='html'
154 fa,co=self._xml()
155 return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<%s%s>%s</%s>' % (self.tag,fa,co,self.tag)
156
158
160
162
164
166 """
167 """
168 tag='script'
170 fa,co=self._xml()
171 if co: return '<%s%s><!--\n%s\n//--></%s>' % (self.tag,fa,co,self.tag)
172 else: return DIV.xml(self)
173
175 """
176 """
177 tag='style'
178
180
182
183 -class BODY(DIV): tag='body'
184
186
188
190
192
194
196
198 tag='p'
200 text=DIV.xml(self)
201 if self.attributes.has_key('cr2br') and self.attributes['cr2br']:
202 text=text.replace('\n','<br/>')
203 return text
204
205 -class B(DIV): tag='B'
206
208
210
211 -class A(DIV): tag='a'
212
214
216
218
220
222
224 """
225 displays code in HTML with syntax highlighting. Exmaple:
226
227 {{=CODE("print 'hello world'",language='python',link=None,counter=1,styles={})}}
228
229 supported languages are "python", "html_plain", "c", "cpp", "web2py", "html".
230 The "html" language interprets {{ and }} tags as "web2py" code, "html_plain" doesn't.
231
232 if a link='/exmaples/global/vars/' is provided web2py keywords are linked to the online docs.
233 the counter is used for line numbering, counter can be None or a prompt string.
234 """
236 if not self.attributes.has_key('language'): language='PYTHON'
237 else: language=self.attributes['language']
238 if not self.attributes.has_key('link'): link=None
239 else: link=self.attributes['link']
240
241 if not self.attributes.has_key('counter'): counter=1
242 else: counter=self.attributes['counter']
243 if not self.attributes.has_key('styles'): styles={}
244 else: styles=self.attributes['styles']
245 return highlight(''.join(self.components),language=language,link=link,counter=counter,styles=styles,attributes=self.attributes)
246
248
250
252 tag='ul'
253 - def postprocessing(self):
254 components=[]
255 for c in self.components:
256 if isinstance(c,LI):
257 components.append(c)
258 else:
259 components.append(LI(c))
260 self.components=components
261
263
265
267
269 tag='tr'
270 - def postprocessing(self):
271 components=[]
272 for c in self.components:
273 if isinstance(c, (TD, TH)):
274 components.append(c)
275 else:
276 components.append(TD(c))
277 self.components=components
278
280 tag='table'
281 - def postprocessing(self):
282 components=[]
283 for c in self.components:
284 if isinstance(c,TR):
285 components.append(c)
286 else:
287 components.append(TR(*c))
288 self.components=components
289
291
365
366 -class TEXTAREA(INPUT):
367 """
368 TEXTAREA(_name='sometext',value='bla '*100,requires=IS_NOT_EMPTY())
369 'bla bla bla ...' will be the content of the textarea field.
370 """
371 tag='textarea'
372 - def postprocessing(self):
373 if not self.attributes.has_key('_rows'):
374 self.attributes['_rows']=10
375 if not self.attributes.has_key('_cols'):
376 self.attributes['_cols']=40
377 if self.attributes.has_key('value'):
378 if self.attributes['value']!=None:
379 self.components=[self.attributes['value']]
380 else:
381 self.components=[]
382
384
386
388 """
389 example:
390
391 >>> SELECT('yes','no',_name='selector',value='yes',requires=IS_IN_SET(['yes','no'])).xml()
392 '<select name="selector"><option selected value="yes">yes</option><option value="no">no</option></select>'
393 """
394 tag='select'
395 - def postprocessing(self):
396 components=[]
397 for c in self.components:
398 if isinstance(c,OPTION):
399 components.append(c)
400 else:
401 components.append(OPTION(c,_value=str(c)))
402 if self.attributes.has_key('value') and \
403 self.attributes['value']!=None and \
404 self.attributes['value']==components[-1].attributes['_value']:
405 components[-1].attributes['_selected']=ON
406 self.components=components
407
409
411 """
412 example:
413
414 >>> form=FORM(INPUT(_name="test",requires=IS_NOT_EMPTY()))
415 >>> form.xml()
416 '<form enctype="multipart/form-data" method="post"><input name="test"/></form>'
417
418 a FORM is container for INPUT, TEXTAREA, SELECT and other helpers
419
420 form has one important method:
421
422 form.accepts(request.vars, session)
423
424 if form is accepted (and all validators pass) form.vars containes the
425 accepted vars, otherwise form.errors contains the errors.
426 in case of errors the form is modified to present the errors to the user.
427 """
428 tag='form'
430 if not self.attributes.has_key('_action'): self.attributes['_action']=""
431 if not self.attributes.has_key('_method'): self.attributes['_method']="post"
432 if not self.attributes.has_key('_enctype'): self.attributes['_enctype']="multipart/form-data"
434 if self.session!=None:
435 try:
436 if self.components[-1].attributes['_name']=='_form_key':
437 self.components=self.components[:-1]
438 except: pass
439 form_key='_form_key[%s]' % self.formname
440 key=self.session[form_key]=str(random.random())[2:]
441 self.components.append(INPUT(_type='hidden',
442 _name='_form_key',_value=key))
443 if self.formname!=None:
444 self.components.append(INPUT(_type='hidden',
445 _name='_formname',_value=self.formname))
446 if self.attributes.has_key('hidden'):
447 hidden=self.attributes['hidden']
448 for key,value in hidden.items():
449 self.components.append(INPUT(_type='hidden',
450 _name=key,_value=value))
451 return DIV.xml(self)
452
454 """
455 example:
456
457 >>> BEAUTIFY(['a','b',{'hello':'world'}]).xml()
458 '<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td><B><div>hello</div></B></td><td align="top">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
459
460 turns any list, dictionarie, etc into decent looking html.
461 """
462 tag='div'
463 - def postprocessing(self):
464 components=[]
465 attributes=copy.copy(self.attributes)
466 if attributes.has_key('_class'): attributes['_class']+='i'
467 for c in self.components:
468 t=type(c)
469 s=dir(c)
470 if 'xml' in s:
471 components.append(c)
472 continue
473 elif 'keys' in s:
474 rows=[]
475 try:
476 keys=c.keys()
477 keys.sort()
478 for key in keys:
479 if str(key)[:1]=='_': continue
480 value=c[key]
481 if type(value)==types.LambdaType: continue
482 rows.append(TR(TD(B(BEAUTIFY(key,**attributes))),
483 TD(':',_align="top"),
484 TD(BEAUTIFY(value,**attributes))))
485 components.append(TABLE(*rows,**attributes))
486 continue
487 except: pass
488 if isinstance(c,(list,tuple)):
489 items=[TR(TD(BEAUTIFY(item,**attributes))) for item in c]
490 components.append(TABLE(*items,**attributes))
491 continue
492 elif isinstance(c,str): components.append(str(c))
493 elif isinstance(c,unicode): components.append(c.encode('utf8'))
494 else: components.append(repr(c))
495 self.components=components
496
497 -def embed64(filename=None,file=None,data=None,extension='image/gif'):
498 if filename: file=open(filename,'rb')
499 if file: data=file.read()
500 data=base64.b64encode(data)
501 return 'data:%s;base64,%s'%(extension,data)
502
504 """
505 Example:
506
507 >>> from validators import *
508 >>> print DIV(A('click me',_href=URL(a='a',c='b',f='c')),BR(),HR(),DIV(SPAN("World"),_class='unkown')).xml()
509 <div><a href="/a/b/c">click me</a><br/><hr/><div class="unkown"><span>World</span></div></div>
510 >>> print DIV(UL("doc","cat","mouse")).xml()
511 <div><lu><li>doc</li><li>cat</li><li>mouse</li></lu></div>
512 >>> print DIV(UL("doc",LI("cat", _class='felin'),18)).xml()
513 <div><lu><li>doc</li><li class="felin">cat</li><li>18</li></lu></div>
514 >>> print TABLE(['a','b','c'],TR('d','e','f'),TR(TD(1),TD(2),TD(3))).xml()
515 <table><tr><td>a</td><td>b</td><td>c</td></tr><tr><td>d</td><td>e</td><td>f</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>
516 >>> form=FORM(INPUT(_type='text',_name='myvar',requires=IS_EXPR('int(value)<10')))
517 >>> print form.xml()
518 <form enctype="multipart/form-data" method="post"><input type="text" name="myvar"/></form>
519 >>> print form.accepts({'myvar':'34'},formname=None)
520 False
521 >>> print form.xml()
522 <form enctype="multipart/form-data" method="post"><input value="34" type="text" name="myvar"/><div class="error">invalid expression!</div></form>
523 >>> print form.accepts({'myvar':'4'},formname=None,keepvalues=True)
524 True
525 >>> print form.xml()
526 <form enctype="multipart/form-data" method="post"><input value="4" type="text" name="myvar"/></form>
527 >>> form=FORM(SELECT('cat','dog',_name='myvar'))
528 >>> print form.accepts({'myvar':'dog'},formname=None)
529 True
530 >>> print form.xml()
531 <form enctype="multipart/form-data" method="post"><select name="myvar"><option value="cat">cat</option><option selected value="dog">dog</option></select></form>
532 >>> form=FORM(INPUT(_type='text',_name='myvar',requires=IS_MATCH('^\w+$','only alphanumeric!')))
533 >>> print form.accepts({'myvar':'as df'},formname=None)
534 False
535 >>> print form.xml()
536 <form enctype="multipart/form-data" method="post"><input value="as df" type="text" name="myvar"/><div class="error">only alphanumeric!</div></form>
537
538 >>> session={}
539 >>> form=FORM(INPUT(value="Hello World",_name="var",requires=IS_MATCH('^\w+$')))
540 >>> if form.accepts({},session,formname=None): print 'passed'
541 >>> tmp=form.xml() # form has to be generated or _form_key is not stored
542 >>> if form.accepts({'var':'test ','_form_key':session['_form_key[None]']},session,formname=None): print 'passed'
543 """
544 pass
545
546
547 if __name__=='__main__':
548 import doctest
549 doctest.testmod()
550
551 '''
552 form=FORM(TABLE(TR('Name:',INPUT(_type='text',_name='name'),BR()),
553 TR('Password:',INPUT(_type='password',_name='password'))))
554 vars={}
555 session={}
556 if form.accepts(vars,session):
557 print 'accepted'
558 print 'not accepted'
559 print form.xml()
560 print form.session
561 vars['_form_key']=form.session['_form_key']
562 vars['name']='Massimo'
563 vars['password']='Dip'
564 if form.accepts(vars,session):
565 print 'accepted'
566 '''
567