source: ipk/source.sh4/swapnetwork_webif/var/swap/extensions/WebInterface/webif.py@ 14969

Last change on this file since 14969 was 14969, checked in by obi, 14 years ago

fix

File size: 17.5 KB
Line 
1# -*- coding: UTF-8 -*-
2Version = '$Header$';
3# things to improve:
4# - better error handling
5# - use namespace parser
6
7from Tools.Import import my_import
8
9from Components.Sources.Source import ObsoleteSource
10from Components.Converter.Converter import Converter
11from Components.Element import Element
12
13from xml.sax import make_parser
14from xml.sax.handler import ContentHandler, feature_namespaces
15from xml.sax.saxutils import escape as escape_xml
16from twisted.python import util
17from urllib2 import quote
18
19#DO NOT REMOVE THIS IMPORT
20#It IS used (dynamically)
21from WebScreens import *
22#DO NOT REMOVE THIS IMPORT
23
24
25global screen_cache
26screen_cache = {}
27
28# The classes and Function in File handle all ScreenPage-based requests
29# ScreenPages use enigma2 standard functionality to bring contents to a webfrontend
30#
31# Like Skins a ScreenPage can consist of several Elements and Converters
32
33#===============================================================================
34# OneTimeElement
35#
36# This is the Standard Element for Rendering a "standard" WebElement
37#===============================================================================
38class OneTimeElement(Element):
39 def __init__(self, id):
40 Element.__init__(self)
41 self.source_id = id
42
43 def handleCommand(self, args):
44 if ',' in self.source_id:
45 paramlist = self.source_id.split(",")
46 list = {}
47 for key in paramlist:
48 arg = args.get(key, ())
49 Len = len(arg)
50 if Len == 0:
51 list[key] = None
52 elif Len == 1:
53 list[key] = "".join(arg)
54 elif Len == 2:
55 list[key] = arg[0]
56 self.source.handleCommand(list)
57 else:
58 for c in args.get(self.source_id, ()):
59 self.source.handleCommand(c)
60
61 def render(self, request):
62 t = self.source.getHTML(self.source_id)
63 request.write(t)
64
65 def execBegin(self):
66 self.suspended = False
67
68 def execEnd(self):
69 self.suspended = True
70
71 def onShow(self):
72 pass
73
74 def onHide(self):
75 pass
76
77 def destroy(self):
78 pass
79
80#===============================================================================
81# MacroElement
82#
83# A MacroElement helps using OneTimeElements inside a (Simple)ListFiller Loop
84#===============================================================================
85class MacroElement(OneTimeElement):
86 def __init__(self, id, macro_dict, macro_name):
87 OneTimeElement.__init__(self, id)
88 self.macro_dict = macro_dict
89 self.macro_name = macro_name
90
91 def render(self, request):
92 self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
93
94#===============================================================================
95# StreamingElement
96#
97# In difference to an OneTimeElement a StreamingElement sends an ongoing Stream
98# of Data. The end of the Streaming is usually when the client disconnects
99#===============================================================================
100class StreamingElement(OneTimeElement):
101 def __init__(self, id):
102 OneTimeElement.__init__(self, id)
103 self.request = None
104
105 def changed(self, what):
106 if self.request:
107 self.render(self.request)
108
109 def setRequest(self, request):
110 self.request = request
111
112#===============================================================================
113# ListItem
114#
115# a to-be-filled list item
116#===============================================================================
117class ListItem:
118 def __init__(self, name, filternum):
119 self.name = name
120 self.filternum = filternum
121
122#===============================================================================
123# ListMacroItem
124#
125# MacroItem inside a (Simple)ListFiller
126#===============================================================================
127class ListMacroItem:
128 def __init__(self, macrodict, macroname):
129 self.macrodict = macrodict
130 self.macroname = macroname
131
132
133#===============================================================================
134# TextToHTML
135#
136# Returns the String as is
137#===============================================================================
138class TextToHTML(Converter):
139 def __init__(self, arg):
140 Converter.__init__(self, arg)
141
142 def getHTML(self, id):
143 return self.source.text.replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") # encode & etc. here!
144
145#===============================================================================
146# TextToXML
147#
148# Escapes the given Text to be XML conform
149#===============================================================================
150class TextToXML(Converter):
151 def __init__(self, arg):
152 Converter.__init__(self, arg)
153
154 def getHTML(self, id):
155 return escape_xml(self.source.text).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
156
157#===============================================================================
158# TextToURL
159#
160# Escapes the given Text so it can be used inside a URL
161#===============================================================================
162class TextToURL(Converter):
163 def __init__(self, arg):
164 Converter.__init__(self, arg)
165
166 def getHTML(self, id):
167 return self.source.text.replace(" ", "%20").replace("+", "%2b").replace("&", "%26").replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8")
168
169#===============================================================================
170# ReturnEmptyXML
171#
172# Returns a XML only consisting of <rootElement />
173#===============================================================================
174class ReturnEmptyXML(Converter):
175 def __init__(self, arg):
176 Converter.__init__(self, arg)
177
178 def getHTML(self, id):
179 return "<rootElement />"
180
181#===============================================================================
182# Null
183# Return simply NOTHING
184# Useful if you only want to issue a command.
185#===============================================================================
186class Null(Converter):
187 def __init__(self, arg):
188 Converter.__init__(self, arg)
189
190 def getHTML(self, id):
191 return ""
192
193
194#===============================================================================
195# JavascriptUpdate
196#
197# Transforms a string into a javascript update pattern
198#===============================================================================
199class JavascriptUpdate(Converter):
200 def __init__(self, arg):
201 Converter.__init__(self, arg)
202
203 def getHTML(self, id):
204 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
205 # all other will replace this in JS
206 return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '&deg;'))
207
208#===============================================================================
209# SimpleListFiller
210#
211# The performant 'one-dimensonial listfiller' engine (podlfe)
212#===============================================================================
213class SimpleListFiller(Converter):
214 def __init__(self, arg):
215 Converter.__init__(self, arg)
216
217 def getText(self):
218 l = self.source.simplelist
219 conv_args = self.converter_arguments
220
221 list = [ ]
222 for element in conv_args:
223 if isinstance(element, basestring):
224 list.append((element, None))
225 elif isinstance(element, ListItem):
226 list.append((element, element.filternum))
227 elif isinstance(element, ListMacroItem):
228 list.append(element.macrodict[element.macroname], None)
229 else:
230 raise Exception("neither string, ListItem nor ListMacroItem")
231
232 strlist = [ ]
233 append = strlist.append
234 for item in l:
235 if item is None:
236 item = ""
237
238 for (element, filternum) in list:
239 #filter out "non-displayable" Characters - at the very end, do it the hard way...
240 item = str(item).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
241
242 if not filternum:
243 append(element)
244 elif filternum == 2:
245 append(item.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
246 elif filternum == 3:
247 append(escape_xml(item))
248 elif filternum == 4:
249 append(item.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
250 elif filternum == 5:
251 append(quote(item))
252 elif filternum == 6:
253 time = parseint(item) or 0
254 t = localtime(time)
255 append("%02d:%02d" % (t.tm_hour, t.tm_min))
256 elif filternum == 7:
257 time = parseint(item) or 0
258 t = localtime(time)
259 append("%d min" % (time / 60))
260 else:
261 append(item)
262 # (this will be done in c++ later!)
263
264 return ''.join(strlist)
265
266 text = property(getText)
267
268#===============================================================================
269# the performant 'listfiller'-engine (plfe)
270#===============================================================================
271class ListFiller(Converter):
272 def __init__(self, arg):
273 Converter.__init__(self, arg)
274# print "ListFiller-arg: ",arg
275
276 def getText(self):
277 l = self.source.list
278 lut = self.source.lut
279 conv_args = self.converter_arguments
280
281 # now build a ["string", 1, "string", 2]-styled list, with indices into the
282 # list to avoid lookup of item name for each entry
283 lutlist = [ ]
284 for element in conv_args:
285 if isinstance(element, basestring):
286 lutlist.append((element, None))
287 elif isinstance(element, ListItem):
288 lutlist.append((lut[element.name], element.filternum))
289 elif isinstance(element, ListMacroItem):
290 lutlist.append((element.macrodict[element.macroname], None))
291 else:
292 raise Exception("neither string, ListItem nor ListMacroItem")
293
294 # now, for the huge list, do:
295 strlist = [ ]
296 append = strlist.append
297 for item in l:
298 for (element, filternum) in lutlist:
299 #None becomes ""
300 curitem = ""
301 if filternum:
302 #filter out "non-displayable" Characters - at the very end, do it the hard way...
303 curitem = str(item[element]).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
304 if curitem is None:
305 curitem = ""
306 else:
307 if element is None:
308 element = ""
309
310 if not filternum:
311 append(element)
312 elif filternum == 2:
313 append(curitem.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
314 elif filternum == 3:
315 append( escape_xml( curitem ))
316 elif filternum == 4:
317 append(curitem.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
318 elif filternum == 5:
319 append(quote(curitem))
320 elif filternum == 6:
321 from time import localtime
322 time = int(float(curitem)) or 0
323 t = localtime(time)
324 append("%02d:%02d" % (t.tm_hour, t.tm_min))
325 elif filternum == 7:
326 from time import localtime
327 time = int(float(curitem)) or 0
328 t = localtime(time)
329 append("%d min" % (time / 60))
330 else:
331 append(curitem)
332 # (this will be done in c++ later!)
333
334 return ''.join(strlist)
335
336 text = property(getText)
337
338#===============================================================================
339# webifHandler
340#
341# Handles the Content of a Web-Request
342# It looks up the source, instantiates the Element and Calls the Converter
343#===============================================================================
344class webifHandler(ContentHandler):
345 def __init__(self, session, request):
346 self.res = [ ]
347 self.mode = 0
348 self.screen = None
349 self.session = session
350 self.screens = [ ]
351 self.request = request
352 self.macros = { }
353
354 def start_element(self, attrs):
355 scr = self.screen
356
357 wsource = attrs["source"]
358
359 path = wsource.split('.')
360 while len(path) > 1:
361 scr = self.screen.getRelatedScreen(path[0])
362 if scr is None:
363 print "[webif.py] Parent Screen not found!"
364 print wsource
365 path = path[1:]
366
367 source = scr.get(path[0])
368
369 if isinstance(source, ObsoleteSource):
370 # however, if we found an "obsolete source", issue warning, and resolve the real source.
371 print "WARNING: WEBIF '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
372 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
373 if source.description:
374 print source.description
375
376 wsource = source.new_source
377 else:
378 pass
379 # otherwise, use that source.
380
381 self.source = source
382 self.source_id = str(attrs.get("id", wsource))
383 self.is_streaming = "streaming" in attrs
384 self.macro_name = attrs.get("macro") or None
385
386 def end_element(self):
387 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
388 if not self.is_streaming:
389 if self.macro_name is None:
390 c = OneTimeElement(self.source_id)
391 else:
392 c = MacroElement(self.source_id, self.macros, self.macro_name)
393 else:
394 assert self.macro_name is None
395 c = StreamingElement(self.source_id)
396
397 c.connect(self.source)
398 self.res.append(c)
399 self.screen.renderer.append(c)
400 del self.source
401
402 def start_convert(self, attrs):
403 ctype = attrs["type"]
404
405 # TODO: we need something better here
406 if ctype[:4] == "web:": # for now
407 self.converter = eval(ctype[4:])
408 else:
409 try:
410 self.converter = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
411 except ImportError:
412 self.converter = my_import('.'.join(("Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype))).__dict__.get(ctype)
413 self.sub = [ ]
414
415 def end_convert(self):
416 if len(self.sub) == 1:
417 self.sub = self.sub[0]
418 c = self.converter(self.sub)
419 c.connect(self.source)
420 self.source = c
421 del self.sub
422
423 def parse_item(self, attrs):
424 if "name" in attrs:
425 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4, "urlencode": 5, "time": 6, "minutes": 7}[attrs.get("filter", "")]
426 self.sub.append(ListItem(attrs["name"], filter))
427 else:
428 assert "macro" in attrs, "e2:item must have a name= or macro= attribute!"
429 self.sub.append(ListMacroItem(self.macros, attrs["macro"]))
430
431 def startElement(self, name, attrs):
432 if name == "e2:screen":
433 if "external_module" in attrs:
434 exec "from " + attrs["external_module"] + " import *"
435 self.screen = eval(attrs["name"])(self.session, self.request) # fixme
436 self.screens.append(self.screen)
437 return
438
439 if name[:3] == "e2:":
440 self.mode += 1
441
442 tag = '<' + name + ''.join([' %s="%s"' % x for x in attrs.items()]) + '>'
443 #tag = tag.encode('utf-8')
444
445 if self.mode == 0:
446 self.res.append(tag)
447 elif self.mode == 1: # expect "<e2:element>"
448 assert name == "e2:element", "found %s instead of e2:element" % name
449 self.start_element(attrs)
450 elif self.mode == 2: # expect "<e2:convert>"
451 if name[:3] == "e2:":
452 assert name == "e2:convert"
453 self.start_convert(attrs)
454 else:
455 self.sub.append(tag)
456 elif self.mode == 3:
457 assert name == "e2:item", "found %s instead of e2:item!" % name
458
459 self.parse_item(attrs)
460
461 def endElement(self, name):
462 if name == "e2:screen":
463 self.screen = None
464 return
465
466 tag = "</" + name + ">"
467 if self.mode == 0:
468 self.res.append(tag)
469 elif self.mode == 2 and name[:3] != "e2:":
470 self.sub.append(tag)
471 elif self.mode == 2: # closed 'convert' -> sub
472 self.end_convert()
473 elif self.mode == 1: # closed 'element'
474 self.end_element()
475 if name[:3] == "e2:":
476 self.mode -= 1
477
478 def processingInstruction(self, target, data):
479 self.res.append('<?' + target + ' ' + data + '>')
480
481 def characters(self, ch):
482 ch = ch.encode('utf-8')
483 if self.mode == 0:
484 self.res.append(ch)
485 elif self.mode == 2:
486 self.sub.append(ch)
487
488 def startEntity(self, name):
489 self.res.append('&' + name + ';');
490
491 def execBegin(self):
492 for screen in self.screens:
493 screen.execBegin()
494
495 def cleanup(self):
496 print "screen cleanup!"
497 for screen in self.screens:
498 screen.execEnd()
499 screen.doClose()
500 self.screens = [ ]
501
502#===============================================================================
503# renderPage
504#
505# Creates the Handler for a Request and calls it
506# Also ensures that the Handler is finished after the Request is done
507#===============================================================================
508def renderPage(request, path, session):
509 # read in the template, create required screens
510 # we don't have persistense yet.
511 # if we had, this first part would only be done once.
512 handler = webifHandler(session, request)
513 parser = make_parser()
514 parser.setFeature(feature_namespaces, 0)
515 parser.setContentHandler(handler)
516 parser.parse(open(util.sibpath(__file__, path)))
517
518 # by default, we have non-streaming pages
519 finish = True
520
521 # first, apply "commands" (aka. URL argument)
522 for x in handler.res:
523 if isinstance(x, Element):
524 x.handleCommand(request.args)
525
526 handler.execBegin()
527
528 # now, we have a list with static texts mixed
529 # with non-static Elements.
530 # flatten this list, write into the request.
531 for x in handler.res:
532 if isinstance(x, Element):
533 if isinstance(x, StreamingElement):
534 finish = False
535 x.setRequest(request)
536 x.render(request)
537 else:
538 request.write(str(x))
539
540 # if we met a "StreamingElement", there is at least one
541 # element which wants to output data more than once,
542 # i.e. on host-originated changes.
543 # in this case, don't finish yet, don't cleanup yet,
544 # but instead do that when the client disconnects.
545 if finish:
546 requestFinish(handler, request)
547
548 else:
549 def requestFinishDeferred(nothing, handler, request):
550 from twisted.internet import reactor
551 reactor.callLater(0, requestFinish, handler, request)
552
553 d = request.notifyFinish()
554
555 d.addBoth( requestFinishDeferred, handler, request )
556
557#===============================================================================
558# requestFinish
559#
560# This has to be/is called at the end of every ScreenPage-based Request
561#===============================================================================
562def requestFinish(handler, request):
563 handler.cleanup()
564 request.finish()
565
566 del handler
Note: See TracBrowser for help on using the repository browser.