1
2
3
4 """
5 This file is part of web2py Web Framework (Copyrighted, 2007-2010).
6 Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu>.
7 License: GPL v2
8 """
9
10 import uuid
11 import cgi
12 import os
13 import re
14 import copy
15 import types
16 import urllib
17 import base64
18 import sanitizer
19 import rewrite
20
21 from storage import Storage
22 from validators import *
23 from highlight import highlight
24
25 regex_crlf = re.compile('\r|\n')
26
27 __all__ = [
28 'A',
29 'B',
30 'BEAUTIFY',
31 'BODY',
32 'BR',
33 'CENTER',
34 'CODE',
35 'DIV',
36 'EM',
37 'EMBED',
38 'FIELDSET',
39 'FORM',
40 'H1',
41 'H2',
42 'H3',
43 'H4',
44 'H5',
45 'H6',
46 'HEAD',
47 'HR',
48 'HTML',
49 'I',
50 'IFRAME',
51 'IMG',
52 'INPUT',
53 'LABEL',
54 'LEGEND',
55 'LI',
56 'LINK',
57 'OL',
58 'UL',
59 'MENU',
60 'META',
61 'OBJECT',
62 'ON',
63 'OPTION',
64 'P',
65 'PRE',
66 'SCRIPT',
67 'SELECT',
68 'SPAN',
69 'STYLE',
70 'TABLE',
71 'TAG',
72 'TD',
73 'TEXTAREA',
74 'TH',
75 'THEAD',
76 'TBODY',
77 'TFOOT',
78 'TITLE',
79 'TR',
80 'TT',
81 'URL',
82 'XHTML',
83 'XML',
84 'xmlescape',
85 'embed64',
86 ]
87
88
90 """
91 returns an escaped string of the provided data
92
93 :param data: the data to be escaped
94 :param quote: optional (default False)
95 """
96
97
98 try:
99 return data.xml()
100 except AttributeError:
101 pass
102 except TypeError:
103 pass
104
105
106 if not isinstance(data, (str, unicode)):
107 data = str(data)
108 elif isinstance(data, unicode):
109 data = data.encode('utf8', 'xmlcharrefreplace')
110
111
112 data = cgi.escape(data, quote)
113 return data
114
115
116 -def URL(
117 a=None,
118 c=None,
119 f=None,
120 r=None,
121 args=[],
122 vars={},
123 anchor='',
124 extension=None,
125 ):
126 """
127 generate a relative URL
128
129 example::
130
131 >>> URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
132 ... vars={'p':1, 'q':2}, anchor='1')
133 '/a/c/f/x/y/z#1?q=2&p=1'
134
135 generates a url \"/a/c/f\" corresponding to application a, controller c
136 and function f. If r=request is passed, a, c, f are set, respectively,
137 to r.application, r.controller, r.function.
138
139 The more typical usage is:
140
141 URL(r=request, f='index') that generates a url for the index function
142 within the present application and controller.
143
144 :param a: application (default to current if r is given)
145 :param c: controller (default to current if r is given)
146 :param f: function (default to current if r is given)
147 :param r: request (optional)
148 :param args: any arguments (optional)
149 :param vars: any variables (optional)
150 :param anchor: anchorname, without # (optional)
151
152 :raises SyntaxError: when no application, controller or function is
153 available
154 :raises SyntaxError: when a CRLF is found in the generated url
155 """
156
157 application = controller = function = None
158 if r:
159 application = r.application
160 controller = r.controller
161 function = r.function
162 if a:
163 application = a
164 if c:
165 controller = c
166 if f:
167 if isinstance(f, str):
168 function = f
169 else:
170 function = f.__name__
171
172 if not (application and controller and function):
173 raise SyntaxError, 'not enough information to build the url'
174
175 other = ''
176 if args != [] and not isinstance(args, (list, tuple)):
177 args = [args]
178 if args:
179 other = urllib.quote('/' + '/'.join([str(x) for x in args]))
180 if extension:
181 function += '.'+extension
182 if anchor:
183 other += '#' + urllib.quote(str(anchor))
184 if vars:
185 other += '?%s' % urllib.urlencode(vars)
186
187 url = '/%s/%s/%s%s' % (application, controller, function, other)
188
189 if regex_crlf.search(url):
190 raise SyntaxError, 'CRLF Injection Detected'
191 return rewrite.filter_out(url)
192
193
194 ON = True
195
196
198 """
199 Abstract root for all Html components
200 """
201
202
203
205 raise NotImplementedError
206
207
208 -class XML(XmlComponent):
209 """
210 use it to wrap a string that contains XML/HTML so that it will not be
211 escaped by the template
212
213 example:
214
215 >>> XML('<h1>Hello</h1>').xml()
216 '<h1>Hello</h1>'
217 """
218
219 - def __init__(
220 self,
221 text,
222 sanitize = False,
223 permitted_tags = [
224 'a',
225 'b',
226 'blockquote',
227 'br/',
228 'i',
229 'li',
230 'ol',
231 'ul',
232 'p',
233 'cite',
234 'code',
235 'pre',
236 'img/',
237 ],
238 allowed_attributes = {
239 'a': ['href', 'title'],
240 'img': ['src', 'alt'],
241 'blockquote': ['type']
242 },
243 ):
244 """
245 :param text: the XML text
246 :param sanitize: sanitize text using the permitted tags and allowed
247 attributes (default False)
248 :param permitted_tags: list of permitted tags (default: simple list of
249 tags)
250 :param allowed_attributes: dictionary of allowed attributed (default
251 for A, IMG and BlockQuote).
252 The key is the tag; the value is a list of allowed attributes.
253 """
254
255 if sanitize:
256 text = sanitizer.sanitize(text, permitted_tags,
257 allowed_attributes)
258 if isinstance(text, unicode):
259 text = text.encode('utf8', 'xmlcharrefreplace')
260 elif not isinstance(text, str):
261 text = str(text)
262 self.text = text
263
266
269
270
271 -class DIV(XmlComponent):
272 """
273 HTML helper, for easy generating and manipulating a DOM structure.
274 Little or no validation is done.
275
276 Behaves like a dictionary regarding updating of attributes.
277 Behaves like a list regarding inserting/appending components.
278
279 example::
280
281 >>> DIV('hello', 'world', _style='color:red;').xml()
282 '<div style=\"color:red;\">helloworld</div>'
283
284 all other HTML helpers are derived from DIV.
285
286 _something=\"value\" attributes are transparently translated into
287 something=\"value\" HTML attributes
288 """
289
290
291
292
293 tag = 'div'
294
295 - def __init__(self, *components, **attributes):
296 """
297 :param *components: any components that should be nested in this element
298 :param **attributes: any attributes you want to give to this element
299
300 :raises SyntaxError: when a stand alone tag receives components
301 """
302
303 if self.tag[-1:] == '/' and components:
304 raise SyntaxError, '<%s> tags cannot have components'\
305 % self.tag
306 if len(components) == 1 and isinstance(components[0], (list,
307 tuple)):
308 self.components = list(components[0])
309 else:
310 self.components = list(components)
311 self.attributes = attributes
312 self._fixup()
313
314 self._postprocessing()
315
317 """
318 dictionary like updating of the tag attributes
319 """
320
321 for (key, value) in kargs.items():
322 self[key] = value
323 return self
324
326 """
327 list style appending of components
328 """
329
330 return self.components.append(value)
331
333 """
334 list style inserting of components
335 """
336
337 return self.components.insert(i, value)
338
340 """
341 gets attribute with name 'i' or component #i.
342 If attribute 'i' is not found returns None
343
344 :param i: index
345 if i is a string: the name of the attribute
346 otherwise references to number of the component
347 """
348
349 if isinstance(i, str):
350 try:
351 return self.attributes[i]
352 except KeyError:
353 return None
354 else:
355 return self.components[i]
356
358 """
359 sets attribute with name 'i' or component #i.
360
361 :param i: index
362 if i is a string: the name of the attribute
363 otherwise references to number of the component
364 :param value: the new value
365 """
366
367 if isinstance(i, str):
368 self.attributes[i] = value
369 else:
370 self.components[i] = value
371
373 """
374 deletes attribute with name 'i' or component #i.
375
376 :param i: index
377 if i is a string: the name of the attribute
378 otherwise references to number of the component
379 """
380
381 if isinstance(i, str):
382 del self.attributes[i]
383 else:
384 del self.components[i]
385
387 """
388 returns the number of included components
389 """
390 return len(self.components)
391
393 """
394 always return True
395 """
396 return True
397
399 """
400 Handling of provided components.
401
402 Nothing to fixup yet. May be overridden by subclasses,
403 eg for wrapping some components in another component or blocking them.
404 """
405 return
406
407 - def _wrap_components(self, allowed_parents, wrap_parent = None,
408 wrap_lambda = None):
409 """
410 helper for _fixup. Checks if a component is in allowed_parents,
411 otherwise wraps it in wrap_parent
412
413 :param allowed_parents: (tuple) classes that the component should be an
414 instance of
415 :param wrap_parent: the class to wrap the component in, if needed
416 :param wrap_lambda: lambda to use for wrapping, if needed
417
418 """
419 components = []
420 for c in self.components:
421 if isinstance(c, allowed_parents):
422 components.append(c)
423 else:
424 if wrap_lambda:
425 components.append(wrap_lambda(c))
426 else:
427 components.append(wrap_parent(c))
428 self.components = components
429
430 - def _postprocessing(self):
431 """
432 Handling of attributes (normally the ones not prefixed with '_').
433
434 Nothing to postprocess yet. May be overridden by subclasses
435 """
436 return
437
439
440 newstatus = status
441 for c in self.components:
442 if hasattr(c, '_traverse'):
443 c.vars = self.vars
444 c.request_vars = self.request_vars
445 c.errors = self.errors
446 c.latest = self.latest
447 c.session = self.session
448 c.formname = self.formname
449 newstatus = c._traverse(status) and newstatus
450
451
452
453
454 name = self['_name']
455 if newstatus:
456 newstatus = self._validate()
457 self._postprocessing()
458 elif 'old_value' in self.attributes:
459 self['value'] = self['old_value']
460 self._postprocessing()
461 elif name and name in self.vars:
462 self['value'] = self.vars[name]
463 self._postprocessing()
464 if name:
465 self.latest[name] = self['value']
466 return newstatus
467
469 """
470 nothing to validate yet. May be overridden by subclasses
471 """
472 return True
473
475 """
476 helper for xml generation. Returns separately:
477 - the component attributes
478 - the generated xml of the inner components
479
480 Component attributes start with an underscore ('_') and
481 do not have a False or None value. The underscore is removed
482 and the name will be in lower case.
483 A value of True is replaced with the attribute name.
484
485 :returns: tuple: (attributes, components)
486 """
487
488
489
490 fa = ''
491 for key in sorted(self.attributes):
492 value = self[key]
493 if key[:1] != '_':
494 continue
495 name = key[1:].lower()
496 if value is True:
497 value = name
498 elif value is False or value is None:
499 continue
500 fa += ' %s="%s"' % (name, xmlescape(value, True))
501
502
503 co = ''.join([xmlescape(component) for component in
504 self.components])
505
506 return (fa, co)
507
509 """
510 generates the xml for this component.
511 """
512
513 (fa, co) = self._xml()
514
515 if not self.tag:
516 return co
517
518 if self.tag[-1:] == '/':
519
520 return '<%s%s />' % (self.tag[:-1], fa)
521
522
523 return '<%s%s>%s</%s>' % (self.tag, fa, co, self.tag)
524
526 """
527 str(COMPONENT) returns equals COMPONENT.xml()
528 """
529
530 return self.xml()
531
533 """
534 find all component that match the supplied attribute dictionary,
535 or None if nothing could be found
536
537 All components of the components are searched.
538 """
539
540 components = [self]
541 matches = []
542 first_only = False
543 if kargs.has_key("first_only"):
544 first_only = kargs["first_only"]
545 del kargs["first_only"]
546
547 for c in components:
548 try:
549
550
551 components += copy.copy(c.components)
552
553
554 check = True
555 tag = getattr(c,'tag').replace("/","")
556 if args and tag not in args:
557 check = False
558 for (key, value) in kargs.items():
559 if c[key] != value:
560 check = False
561
562 if check:
563 matches.append(c)
564 if first_only: break
565 except:
566 pass
567 return matches
568
569 - def element(self, *args, **kargs):
570 """
571 find the first component that matches the supplied attribute dictionary,
572 or None if nothing could be found
573
574 Also the components of the components are searched.
575 """
576 kargs['first_only'] = True
577 elements = self.elements(*args, **kargs)
578 if not elements:
579
580 return None
581 return elements[0]
582
583
585
586 """
587 TAG factory example::
588
589 >>> print TAG.first(TAG.second('test'), _key = 3)
590 <first key=\"3\"><second>test</second></first>
591
592 """
593
596
598 if name[-1:] == '_':
599 name = name[:-1] + '/'
600
601 class __tag__(DIV):
602
603 tag = name
604
605
606 return lambda *a, **b: __tag__(*a, **b)
607
608
609 TAG = __TAG__()
610
611
613 """
614 There are four predefined document type definitions.
615 They can be specified in the 'doctype' parameter:
616
617 -'strict' enables strict doctype
618 -'transitional' enables transitional doctype (default)
619 -'frameset' enables frameset doctype
620 -'html5' enables HTML 5 doctype
621 -any other string will be treated as user's own doctype
622
623 'lang' parameter specifies the language of the document.
624 Defaults to 'en'.
625
626 See also :class:`DIV`
627 """
628
629 tag = 'html'
630
631 strict = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n'
632 transitional = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'
633 frameset = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">\n'
634 html5 = '<!DOCTYPE HTML>\n'
635
637 lang = self['lang']
638 if not lang:
639 lang = 'en'
640 self.attributes['_lang'] = lang
641 doctype = self['doctype']
642 if doctype:
643 if doctype == 'strict':
644 doctype = self.strict
645 elif doctype == 'transitional':
646 doctype = self.transitional
647 elif doctype == 'frameset':
648 doctype = self.frameset
649 elif doctype == 'html5':
650 doctype = self.html5
651 else:
652 doctype = '%s\n' % doctype
653 else:
654 doctype = self.transitional
655 (fa, co) = self._xml()
656 return '%s<%s%s>%s</%s>' % (doctype, self.tag, fa, co, self.tag)
657
658
660 """
661 This is XHTML version of the HTML helper.
662
663 There are three predefined document type definitions.
664 They can be specified in the 'doctype' parameter:
665
666 -'strict' enables strict doctype
667 -'transitional' enables transitional doctype (default)
668 -'frameset' enables frameset doctype
669 -any other string will be treated as user's own doctype
670
671 'lang' parameter specifies the language of the document and the xml document.
672 Defaults to 'en'.
673
674 'xmlns' parameter specifies the xml namespace.
675 Defaults to 'http://www.w3.org/1999/xhtml'.
676
677 See also :class:`DIV`
678 """
679
680 tag = 'html'
681
682 strict = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
683 transitional = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
684 frameset = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">\n'
685 xmlns = 'http://www.w3.org/1999/xhtml'
686
688 xmlns = self['xmlns']
689 if xmlns:
690 self.attributes['_xmlns'] = xmlns
691 else:
692 self.attributes['_xmlns'] = self.xmlns
693 lang = self['lang']
694 if not lang:
695 lang = 'en'
696 self.attributes['_lang'] = lang
697 self.attributes['_xml:lang'] = lang
698 doctype = self['doctype']
699 if doctype:
700 if doctype == 'strict':
701 doctype = self.strict
702 elif doctype == 'transitional':
703 doctype = self.transitional
704 elif doctype == 'frameset':
705 doctype = self.frameset
706 else:
707 doctype = '%s\n' % doctype
708 else:
709 doctype = self.transitional
710 (fa, co) = self._xml()
711 return '%s<%s%s>%s</%s>' % (doctype, self.tag, fa, co, self.tag)
712
713
717
718
722
723
727
728
732
733
735
736 tag = 'script'
737
739 (fa, co) = self._xml()
740
741 co = '\n'.join([str(component) for component in
742 self.components])
743 if co:
744
745
746
747
748 return '<%s%s><!--\n%s\n//--></%s>' % (self.tag, fa, co, self.tag)
749 else:
750 return DIV.xml(self)
751
752
754
755 tag = 'style'
756
758 (fa, co) = self._xml()
759
760 co = '\n'.join([str(component) for component in
761 self.components])
762 if co:
763
764
765
766 return '<%s%s><!--/*--><![CDATA[/*><!--*/\n%s\n/*]]>*/--></%s>' % (self.tag, fa, co, self.tag)
767 else:
768 return DIV.xml(self)
769
770
774
775
779
780
784
785
789
790
794
795
799
800
804
805
809
810
814
815
817 """
818 Will replace ``\\n`` by ``<br />`` if the `cr2br` attribute is provided.
819
820 see also :class:`DIV`
821 """
822
823 tag = 'p'
824
826 text = DIV.xml(self)
827 if self['cr2br']:
828 text = text.replace('\n', '<br />')
829 return text
830
831
835
836
840
841
845
846
850
851
855
856
860
861
865
866
870
871
875
876
878
879 """
880 displays code in HTML with syntax highlighting.
881
882 :param attributes: optional attributes:
883
884 - language: indicates the language, otherwise PYTHON is assumed
885 - link: can provide a link
886 - styles: for styles
887
888 Example::
889
890 {{=CODE(\"print 'hello world'\", language='python', link=None,
891 counter=1, styles={})}}
892
893
894 supported languages are \"python\", \"html_plain\", \"c\", \"cpp\",
895 \"web2py\", \"html\".
896 The \"html\" language interprets {{ and }} tags as \"web2py\" code,
897 \"html_plain\" doesn't.
898
899 if a link='/examples/global/vars/' is provided web2py keywords are linked to
900 the online docs.
901
902 the counter is used for line numbering, counter can be None or a prompt
903 string.
904 """
905
907 language = self['language'] or 'PYTHON'
908 link = self['link']
909 counter = self.attributes.get('counter', 1)
910 styles = self['styles'] or {}
911 return highlight(
912 ''.join(self.components),
913 language=language,
914 link=link,
915 counter=counter,
916 styles=styles,
917 attributes=self.attributes,
918 )
919
920
924
925
929
930
932 """
933 UL Component.
934
935 If subcomponents are not LI-components they will be wrapped in a LI
936
937 see also :class:`DIV`
938 """
939
940 tag = 'ul'
941
944
945
949
950
954
955
959
960
962 """
963 TR Component.
964
965 If subcomponents are not TD/TH-components they will be wrapped in a TD
966
967 see also :class:`DIV`
968 """
969
970 tag = 'tr'
971
974
975
979
980
984
985
989
990
992 """
993 TABLE Component.
994
995 If subcomponents are not TR/TBODY/THEAD/TFOOT-components
996 they will be wrapped in a TR
997
998 see also :class:`DIV`
999 """
1000
1001 tag = 'table'
1002
1005
1009
1013
1014
1116
1117
1118 -class TEXTAREA(INPUT):
1119
1120 """
1121 example::
1122
1123 TEXTAREA(_name='sometext', value='blah '*100, requires=IS_NOT_EMPTY())
1124
1125 'blah blah blah ...' will be the content of the textarea field.
1126 """
1127
1128 tag = 'textarea'
1129
1130 - def _postprocessing(self):
1131 if not '_rows' in self.attributes:
1132 self['_rows'] = 10
1133 if not '_cols' in self.attributes:
1134 self['_cols'] = 40
1135 if self['value'] != None:
1136 self.components = [self['value']]
1137 elif self.components:
1138 self['value'] = self.components[0]
1139
1140
1142
1143 tag = 'option'
1144
1146 if not '_value' in self.attributes:
1147 self.attributes['_value'] = str(self.components[0])
1148
1149
1153
1154
1156
1157 """
1158 example::
1159
1160 >>> SELECT('yes', 'no', _name='selector', value='yes',
1161 ... requires=IS_IN_SET(['yes', 'no'])).xml()
1162 '<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>'
1163
1164 """
1165
1166 tag = 'select'
1167
1169 components = []
1170 for c in self.components:
1171 if isinstance(c, OPTION):
1172 components.append(c)
1173 else:
1174 components.append(OPTION(c, _value=str(c)))
1175 self.components = components
1176
1177 - def _postprocessing(self):
1178 if self['value'] != None:
1179 if not self['_multiple']:
1180 for c in self.components:
1181 if self['value'] and str(c['_value'])\
1182 == str(self['value']):
1183 c['_selected'] = 'selected'
1184 else:
1185 c['_selected'] = None
1186 else:
1187 values = re.compile('[\w\-:]+').findall(str(self['value']))
1188 for c in self.components:
1189 if self['value'] and str(c['_value']) in values:
1190 c['_selected'] = 'selected'
1191 else:
1192 c['_selected'] = None
1193
1194
1196
1197 tag = 'fieldset'
1198
1199
1203
1204
1307
1308
1310
1311 """
1312 example::
1313
1314 >>> BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml()
1315 '<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;"><div>hello</div></td><td valign="top">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
1316
1317 turns any list, dictionary, etc into decent looking html.
1318 """
1319
1320 tag = 'div'
1321
1322 - def __init__(self, component, **attributes):
1323 self.components = [component]
1324 self.attributes = attributes
1325 components = []
1326 attributes = copy.copy(self.attributes)
1327 if '_class' in attributes:
1328 attributes['_class'] += 'i'
1329 for c in self.components:
1330 s = dir(c)
1331 if 'xml' in s:
1332 components.append(c)
1333 continue
1334 elif 'keys' in s:
1335 rows = []
1336 try:
1337 for key in sorted(c):
1338 if str(key)[:1] == '_':
1339 continue
1340 value = c[key]
1341 if type(value) == types.LambdaType:
1342 continue
1343 rows.append(TR(TD(BEAUTIFY(key,
1344 **attributes), _style='font-weight:bold;'), TD(':',
1345 _valign='top'), TD(BEAUTIFY(value,
1346 **attributes))))
1347 components.append(TABLE(*rows, **attributes))
1348 continue
1349 except:
1350 pass
1351 if isinstance(c, str):
1352 components.append(str(c))
1353 elif isinstance(c, unicode):
1354 components.append(c.encode('utf8'))
1355 elif isinstance(c, (list, tuple)):
1356 items = [TR(TD(BEAUTIFY(item, **attributes)))
1357 for item in c]
1358 components.append(TABLE(*items, **attributes))
1359 elif isinstance(c, cgi.FieldStorage):
1360 components.append('FieldStorage object')
1361 else:
1362 components.append(repr(c))
1363 self.components = components
1364
1365
1367 """
1368 Used to build menus
1369
1370 Optional arguments
1371 _class: defaults to 'web2py-menu web2py-menu-vertical'
1372 ul_class: defaults to 'web2py-menu-vertical'
1373 li_class: defaults to 'web2py-menu-expand'
1374
1375 Example:
1376 menu = MENU([['name', False, URL(...), [submenu]], ...])
1377 {{=menu}}
1378 """
1379
1380 tag = 'ul'
1381
1383 self.data = data
1384 self.attributes = args
1385 if not '_class' in self.attributes:
1386 self['_class'] = 'web2py-menu web2py-menu-vertical'
1387 if not 'ul_class' in self.attributes:
1388 self['ul_class'] = 'web2py-menu-vertical'
1389 if not 'li_class' in self.attributes:
1390 self['li_class'] = 'web2py-menu-expand'
1391 if not 'li_active' in self.attributes:
1392 self['li_active'] = 'web2py-menu-active'
1393
1395 if level == 0:
1396 ul = UL(**self.attributes)
1397 else:
1398 ul = UL(_class=self['ul_class'])
1399 for item in data:
1400 (name, active, link) = item[:3]
1401 if link:
1402 li = LI(A(name, _href=link))
1403 else:
1404 li = LI(A(name, _href='#null'))
1405 if len(item) > 3 and item[3]:
1406 li['_class'] = self['li_class']
1407 li.append(self.serialize(item[3], level+1))
1408 if active:
1409 if li['_class']:
1410 li['_class'] = li['_class']+' '+self['li_active']
1411 else:
1412 li['_class'] = self['li_active']
1413 ul.append(li)
1414 return ul
1415
1418
1419
1420 -def embed64(
1421 filename = None,
1422 file = None,
1423 data = None,
1424 extension = 'image/gif',
1425 ):
1426 """
1427 helper to encode the provided (binary) data into base64.
1428
1429 :param filename: if provided, opens and reads this file in 'rb' mode
1430 :param file: if provided, reads this file
1431 :param data: if provided, uses the provided data
1432 """
1433
1434 if filename and os.path.exists(file):
1435 fp = open(filename, 'rb')
1436 data = fp.read()
1437 fp.close()
1438 data = base64.b64encode(data)
1439 return 'data:%s;base64,%s' % (extension, data)
1440
1441
1443 """
1444 Example:
1445
1446 >>> from validators import *
1447 >>> print DIV(A('click me', _href=URL(a='a', c='b', f='c')), BR(), HR(), DIV(SPAN(\"World\"), _class='unknown')).xml()
1448 <div><a href=\"/a/b/c\">click me</a><br /><hr /><div class=\"unknown\"><span>World</span></div></div>
1449 >>> print DIV(UL(\"doc\",\"cat\",\"mouse\")).xml()
1450 <div><ul><li>doc</li><li>cat</li><li>mouse</li></ul></div>
1451 >>> print DIV(UL(\"doc\", LI(\"cat\", _class='feline'), 18)).xml()
1452 <div><ul><li>doc</li><li class=\"feline\">cat</li><li>18</li></ul></div>
1453 >>> print TABLE(['a', 'b', 'c'], TR('d', 'e', 'f'), TR(TD(1), TD(2), TD(3))).xml()
1454 <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>
1455 >>> form=FORM(INPUT(_type='text', _name='myvar', requires=IS_EXPR('int(value)<10')))
1456 >>> print form.xml()
1457 <form action=\"\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"myvar\" type=\"text\" /></form>
1458 >>> print form.accepts({'myvar':'34'}, formname=None)
1459 False
1460 >>> print form.xml()
1461 <form action="" enctype="multipart/form-data" method="post"><input name="myvar" type="text" value="34" /><div class="error" id="myvar__error">invalid expression</div></form>
1462 >>> print form.accepts({'myvar':'4'}, formname=None, keepvalues=True)
1463 True
1464 >>> print form.xml()
1465 <form action=\"\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"myvar\" type=\"text\" value=\"4\" /></form>
1466 >>> form=FORM(SELECT('cat', 'dog', _name='myvar'))
1467 >>> print form.accepts({'myvar':'dog'}, formname=None, keepvalues=True)
1468 True
1469 >>> print form.xml()
1470 <form action=\"\" enctype=\"multipart/form-data\" method=\"post\"><select name=\"myvar\"><option value=\"cat\">cat</option><option selected=\"selected\" value=\"dog\">dog</option></select></form>
1471 >>> form=FORM(INPUT(_type='text', _name='myvar', requires=IS_MATCH('^\w+$', 'only alphanumeric!')))
1472 >>> print form.accepts({'myvar':'as df'}, formname=None)
1473 False
1474 >>> print form.xml()
1475 <form action=\"\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"myvar\" type=\"text\" value=\"as df\" /><div class=\"error\" id=\"myvar__error\">only alphanumeric!</div></form>
1476 >>> session={}
1477 >>> form=FORM(INPUT(value=\"Hello World\", _name=\"var\", requires=IS_MATCH('^\w+$')))
1478 >>> if form.accepts({}, session,formname=None): print 'passed'
1479 >>> if form.accepts({'var':'test ', '_formkey': session['_formkey[None]']}, session, formname=None): print 'passed'
1480 """
1481
1482 pass
1483
1484
1485 if __name__ == '__main__':
1486 import doctest
1487 doctest.testmod()
1488