1 # -*- coding: UTF-8 -*-
6 # - screens need to be defined somehow else.
7 # I don't know how, yet. Probably each in an own file.
8 # - better error handling
9 # - use namespace parser
11 from enigma import eServiceReference
13 from Screens.Screen import Screen
14 from Tools.Import import my_import
16 from Components.Sources.Source import ObsoleteSource
17 from Components.Converter.Converter import Converter
18 from Components.Element import Element
19 from WebComponents.Sources.RequestData import RequestData
21 from xml.sax import make_parser
22 from xml.sax.handler import ContentHandler, feature_namespaces
23 from xml.sax.saxutils import escape as escape_xml
24 from twisted.python import util
25 from urllib2 import quote
27 # prototype of the new web frontend template system.
29 class WebScreen(Screen):
30 def __init__(self, session, request):
31 Screen.__init__(self, session)
32 self.stand_alone = True
33 self.request = request
36 class DummyWebScreen(WebScreen):
37 #use it, if you dont need any source, just to can do a static file with an xml-file
38 def __init__(self, session, request):
39 WebScreen.__init__(self, session, request)
41 class UpdateWebScreen(WebScreen):
42 def __init__(self, session, request):
43 WebScreen.__init__(self, session, request)
44 from Components.Sources.Clock import Clock
46 self["CurrentTime"] = Clock()
49 class MessageWebScreen(WebScreen):
50 def __init__(self, session, request):
51 WebScreen.__init__(self, session, request)
52 from WebComponents.Sources.Message import Message
54 self["Message"] = Message(session, func=Message.PRINT)
55 self["GetAnswer"] = Message(session, func=Message.ANSWER)
57 class ServiceListReloadWebScreen(WebScreen):
58 def __init__(self, session, request):
59 WebScreen.__init__(self, session, request)
60 from WebComponents.Sources.ServiceListReload import ServiceListReload
62 self["ServiceListReload"] = ServiceListReload(session)
64 class AudioWebScreen(WebScreen):
65 def __init__(self, session, request):
66 WebScreen.__init__(self, session, request)
67 from WebComponents.Sources.AudioTracks import AudioTracks
69 self["AudioTracks"] = AudioTracks(session, func=AudioTracks.GET)
70 self["SelectAudioTrack"] = AudioTracks(session, func=AudioTracks.SET)
72 class AboutWebScreen(WebScreen):
73 def __init__(self, session, request):
74 WebScreen.__init__(self, session, request)
75 from WebComponents.Sources.About import About
76 from WebComponents.Sources.Frontend import Frontend
77 from WebComponents.Sources.Hdd import Hdd
78 from WebComponents.Sources.Network import Network
79 from Components.config import config
80 from Components.About import about
81 from Components.Sources.StaticText import StaticText
82 from Tools.DreamboxHardware import getFPVersion
83 from Tools.HardwareInfo import HardwareInfo
87 self["About"] = About(session)
89 self["Network"] = Network()
91 self["Frontends"] = Frontend()
92 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
93 self["ImageVersion"] = StaticText(about.getVersionString())
94 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
95 self["FpVersion"] = StaticText(str(getFPVersion()))
96 self["DeviceName"] = StaticText(hw.get_device_name())
98 class VolumeWebScreen(WebScreen):
99 def __init__(self, session, request):
100 WebScreen.__init__(self, session, request)
102 from WebComponents.Sources.Volume import Volume
103 self["Volume"] = Volume(session)
105 class SettingsWebScreen(WebScreen):
106 def __init__(self, session, request):
107 WebScreen.__init__(self, session, request)
108 from WebComponents.Sources.Settings import Settings
110 self["Settings"] = Settings(session)
112 class SubServiceWebScreen(WebScreen):
113 def __init__(self, session, request):
114 WebScreen.__init__(self, session, request)
115 from WebComponents.Sources.SubServices import SubServices
117 self["SubServices"] = SubServices(session)
119 class StreamSubServiceWebScreen(WebScreen):
120 def __init__(self, session, request):
121 WebScreen.__init__(self, session, request)
122 from WebComponents.Sources.SubServices import SubServices
124 self["StreamSubServices"] = SubServices(session, streamingScreens)
126 class ServiceWebScreen(WebScreen):
127 def __init__(self, session, request):
128 WebScreen.__init__(self, session, request)
129 from WebComponents.Sources.ServiceListRecursive import ServiceListRecursive
130 from Components.Sources.ServiceList import ServiceList
131 from Screens.ChannelSelection import service_types_tv
133 fav = eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
134 self["SwitchService"] = ServiceList(fav, command_func=self.zapTo, validate_commands=False)
135 self["ServiceList"] = ServiceList(fav, command_func=self.getServiceList, validate_commands=False)
136 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
137 self["localip"] = RequestData(request, what=RequestData.HOST)
139 def getServiceList(self, sRef):
140 self["ServiceList"].root = sRef
142 def zapTo(self, reftozap):
143 from Components.config import config
144 pc = config.ParentalControl.configured.value
146 config.ParentalControl.configured.value = False
147 if config.plugins.Webinterface.allowzapping.value:
148 self.session.nav.playService(reftozap)
150 config.ParentalControl.configured.value = pc
152 switching config.ParentalControl.configured.value
153 ugly, but necessary :(
156 class LocationsAndTagsWebScreen(WebScreen):
157 def __init__(self, session, request):
158 WebScreen.__init__(self, session, request)
159 from WebComponents.Sources.LocationsAndTags import LocationsAndTags
161 self["CurrentLocation"] = LocationsAndTags(session, LocationsAndTags.CURRLOCATION)
162 self["Locations"] = LocationsAndTags(session, LocationsAndTags.LOCATIONS)
163 self["Tags"] = LocationsAndTags(session, LocationsAndTags.TAGS)
165 class EPGWebScreen(WebScreen):
166 def __init__(self, session, request):
167 WebScreen.__init__(self, session, request)
168 from WebComponents.Sources.EPG import EPG
170 self["EPGTITLE"] = EPG(session, func=EPG.TITLE)
171 self["EPGSERVICE"] = EPG(session, func=EPG.SERVICE)
172 self["EPGBOUQUETNOW"] = EPG(session, func=EPG.BOUQUETNOW)
173 self["EPGBOUQUETNEXT"] = EPG(session, func=EPG.BOUQUETNEXT)
174 self["EPGSERVICENOW"] = EPG(session, func=EPG.SERVICENOW)
175 self["EPGSERVICENEXT"] = EPG(session, func=EPG.SERVICENEXT)
176 self["EPGBOUQUET"] = EPG(session, func=EPG.BOUQUET)
177 self["localip"] = RequestData(request, what=RequestData.HOST)
179 self["EPGSERVICEWAP"] = EPG(session, func=EPG.SERVICE, endtm=True)
181 def getServiceList(self, sRef):
182 self["ServiceList"].root = sRef
184 class MovieWebScreen(WebScreen):
185 def __init__(self, session, request):
186 WebScreen.__init__(self, session, request)
187 from Components.MovieList import MovieList
188 from Tools.Directories import resolveFilename, SCOPE_HDD
189 from WebComponents.Sources.Movie import Movie
191 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
192 self["MovieList"] = Movie(session, movielist, func=Movie.LIST)
193 self["MovieFileDel"] = Movie(session, movielist, func=Movie.DEL)
194 self["localip"] = RequestData(request, what=RequestData.HOST)
196 class MediaPlayerWebScreen(WebScreen):
197 def __init__(self, session, request):
198 WebScreen.__init__(self, session, request)
199 from WebComponents.Sources.MP import MP
201 self["FileList"] = MP(session, func=MP.LIST)
202 self["PlayFile"] = MP(session, func=MP.PLAY)
203 self["Command"] = MP(session, func=MP.COMMAND)
204 self["WritePlaylist"] = MP(session, func=MP.WRITEPLAYLIST)
206 class AutoTimerWebScreen(WebScreen):
207 def __init__(self, session, request):
208 WebScreen.__init__(self, session, request)
209 from WebComponents.Sources.AT import AT
211 self["AutoTimerList"] = AT(session, func=AT.LIST)
212 self["AutoTimerWrite"] = AT(session, func=AT.WRITE)
214 class TimerWebScreen(WebScreen):
215 def __init__(self, session, request):
216 WebScreen.__init__(self, session, request)
217 from WebComponents.Sources.Timer import Timer
219 self["TimerList"] = Timer(session, func=Timer.LIST)
220 self["TimerAddEventID"] = Timer(session, func=Timer.ADDBYID)
221 self["TimerAdd"] = Timer(session, func=Timer.ADD)
222 self["TimerDel"] = Timer(session, func=Timer.DEL)
223 self["TimerChange"] = Timer(session, func=Timer.CHANGE)
224 self["TimerListWrite"] = Timer(session, func=Timer.WRITE)
225 self["TVBrowser"] = Timer(session, func=Timer.TVBROWSER)
226 self["RecordNow"] = Timer(session, func=Timer.RECNOW)
227 self["TimerCleanup"] = Timer(session, func=Timer.CLEANUP)
229 class RemoteWebScreen(WebScreen):
230 def __init__(self, session, request):
231 WebScreen.__init__(self, session, request)
232 from WebComponents.Sources.RemoteControl import RemoteControl
234 self["RemoteControl"] = RemoteControl(session)
236 class PowerWebScreen(WebScreen):
237 def __init__(self, session, request):
238 WebScreen.__init__(self, session, request)
239 from WebComponents.Sources.PowerState import PowerState
241 self["PowerState"] = PowerState(session)
243 class ParentControlWebScreen(WebScreen):
244 def __init__(self, session, request):
245 WebScreen.__init__(self, session, request)
246 from WebComponents.Sources.ParentControl import ParentControl
248 self["ParentControlList"] = ParentControl(session)
250 class WAPWebScreen(WebScreen):
251 def __init__(self, session, request):
252 WebScreen.__init__(self, session, request)
253 from WebComponents.Sources.WAPfunctions import WAPfunctions
255 self["WAPFillOptionListYear"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
256 self["WAPFillOptionListDay"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
257 self["WAPFillOptionListMonth"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
258 self["WAPFillOptionListShour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
259 self["WAPFillOptionListSmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
260 self["WAPFillOptionListEhour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
261 self["WAPFillOptionListEmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
263 self["WAPFillOptionListRecord"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
264 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
266 self["WAPFillValueName"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
267 self["WAPFillValueDescr"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
268 self["WAPFillLocation"] = WAPfunctions(session, func=WAPfunctions.LOCATIONLIST)
269 self["WAPFillTags"] = WAPfunctions(session, func=WAPfunctions.TAGLIST)
271 self["WAPFillOptionListRepeated"] = WAPfunctions(session, func=WAPfunctions.REPEATED)
272 self["WAPServiceList"] = WAPfunctions(session, func=WAPfunctions.SERVICELIST)
274 self["WAPdeleteOldOnSave"] = WAPfunctions(session, func=WAPfunctions.DELETEOLD)
276 streamingScreens = []
278 class StreamingWebScreen(WebScreen):
279 def __init__(self, session, request):
280 WebScreen.__init__(self, session, request)
281 from Components.Sources.StreamService import StreamService
282 self["StreamService"] = StreamService(self.session.nav)
283 streamingScreens.append(self)
284 self.screenIndex = len(streamingScreens) - 1
286 def getRecordService(self):
287 if self.has_key("StreamService"):
288 return self["StreamService"].getService()
291 def getRecordServiceRef(self):
292 if self.has_key("StreamService"):
293 return self["StreamService"].ref
296 class M3UStreamingWebScreen(WebScreen):
297 def __init__(self, session, request):
298 WebScreen.__init__(self, session, request)
299 from Components.Sources.StaticText import StaticText
300 from Components.Sources.Config import Config
301 from Components.config import config
302 self["ref"] = StaticText()
303 self["localip"] = RequestData(request, what=RequestData.HOST)
305 class M3UStreamingCurrentServiceWebScreen(WebScreen):
306 def __init__(self, session, request):
307 WebScreen.__init__(self, session, request)
308 from WebComponents.Sources.CurrentService import CurrentService
310 self["CurrentService"] = CurrentService(session)
311 self["localip"] = RequestData(request, what=RequestData.HOST)
313 class TsM3U(WebScreen):
314 def __init__(self, session, request):
315 WebScreen.__init__(self, session, request)
316 from Components.Sources.StaticText import StaticText
317 from Components.Sources.Config import Config
318 from Components.config import config
319 self["file"] = StaticText()
320 self["localip"] = RequestData(request, what=RequestData.HOST)
322 class RestartWebScreen(WebScreen):
323 def __init__(self, session, request):
324 WebScreen.__init__(self, session, request)
326 plugin.restartWebserver(session)
328 class GetPid(WebScreen):
329 def __init__(self, session, request):
330 WebScreen.__init__(self, session, request)
331 from Components.Sources.StaticText import StaticText
332 from enigma import iServiceInformation
333 pids = self.session.nav.getCurrentService()
335 pidinfo = pids.info()
336 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
337 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
338 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
339 self["pids"] = StaticText("%s,%s,%s" % (PPID.lstrip("0x"), VPID.lstrip("0x"), APID.lstrip("0x")))
341 self["pids"] = StaticText("0x,0x,0x")
343 self["localip"] = RequestData(request, what=RequestData.HOST)
345 class DeviceInfo(WebScreen):
346 def __init__(self, session, request):
347 WebScreen.__init__(self, session, request)
348 from WebComponents.Sources.Network import Network
349 from WebComponents.Sources.Hdd import Hdd
350 from WebComponents.Sources.Frontend import Frontend
351 from Components.config import config
352 from Components.About import about
353 from Components.Sources.StaticText import StaticText
354 from Tools.DreamboxHardware import getFPVersion
355 from Tools.HardwareInfo import HardwareInfo
359 self["Network"] = Network()
361 self["Frontends"] = Frontend()
362 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
363 self["ImageVersion"] = StaticText(about.getVersionString())
364 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
365 self["FpVersion"] = StaticText(str(getFPVersion()))
366 self["DeviceName"] = StaticText(hw.get_device_name())
368 # implements the 'render'-call.
369 # this will act as a downstream_element, like a renderer.
370 class OneTimeElement(Element):
371 def __init__(self, id):
372 Element.__init__(self)
375 # CHECKME: is this ok performance-wise?
376 def handleCommand(self, args):
377 if self.source_id.find(",") >= 0:
378 paramlist = self.source_id.split(",")
380 for key in paramlist:
381 arg = args.get(key, [])
385 list[key] = "".join(arg)
388 self.source.handleCommand(list)
390 for c in args.get(self.source_id, []):
391 self.source.handleCommand(c)
393 def render(self, stream):
394 t = self.source.getHTML(self.source_id)
398 self.suspended = False
401 self.suspended = True
412 class MacroElement(OneTimeElement):
413 def __init__(self, id, macro_dict, macro_name):
414 OneTimeElement.__init__(self, id)
415 self.macro_dict = macro_dict
416 self.macro_name = macro_name
418 def render(self, stream):
419 self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
421 class StreamingElement(OneTimeElement):
422 def __init__(self, id):
423 OneTimeElement.__init__(self, id)
426 def changed(self, what):
428 self.render(self.stream)
430 def setStream(self, stream):
433 # a to-be-filled list item
435 def __init__(self, name, filternum):
437 self.filternum = filternum
440 def __init__(self, macrodict, macroname):
441 self.macrodict = macrodict
442 self.macroname = macroname
444 class TextToHTML(Converter):
445 def __init__(self, arg):
446 Converter.__init__(self, arg)
448 def getHTML(self, id):
449 return self.source.text # encode & etc. here!
451 class TextToXML(Converter):
452 def __init__(self, arg):
453 Converter.__init__(self, arg)
455 def getHTML(self, id):
456 return escape_xml(self.source.text).replace("\x19", "").replace("\x1c", "").replace("\x1e", "")
458 class TextToURL(Converter):
459 def __init__(self, arg):
460 Converter.__init__(self, arg)
462 def getHTML(self, id):
463 return self.source.text.replace(" ", "%20")
465 class ReturnEmptyXML(Converter):
466 def __init__(self, arg):
467 Converter.__init__(self, arg)
469 def getHTML(self, id):
470 return "<rootElement></rootElement>"
472 # a null-output. Useful if you only want to issue a command.
473 class Null(Converter):
474 def __init__(self, arg):
475 Converter.__init__(self, arg)
477 def getHTML(self, id):
480 class JavascriptUpdate(Converter):
481 def __init__(self, arg):
482 Converter.__init__(self, arg)
484 def getHTML(self, id):
485 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
486 # all other will replace this in JS
487 return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
489 # the performant 'one-dimensonial listfiller' engine (podlfe)
490 class SimpleListFiller(Converter):
491 def __init__(self, arg):
492 Converter.__init__(self, arg)
495 l = self.source.simplelist
496 conv_args = self.converter_arguments
499 for element in conv_args:
500 if isinstance(element, basestring):
501 list.append((element, None))
502 elif isinstance(element, ListItem):
503 list.append((element, element.filternum))
504 elif isinstance(element, ListMacroItem):
505 list.append(element.macrodict[element.macroname], None)
507 raise Exception("neither string, ListItem nor ListMacroItem")
510 append = strlist.append
515 for (element, filternum) in list:
519 append(str(item).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
521 append(escape_xml(str(item)))
523 append(str(item).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
525 append(quote(str(item)))
527 time = parseint(item) or 0
529 append("%02d:%02d" % (t.tm_hour, t.tm_min))
531 time = parseint(item) or 0
533 append("%d min" % (time / 60))
536 # (this will be done in c++ later!)
538 return ''.join(strlist)
540 text = property(getText)
544 # the performant 'listfiller'-engine (plfe)
545 class ListFiller(Converter):
546 def __init__(self, arg):
547 Converter.__init__(self, arg)
548 # print "ListFiller-arg: ",arg
552 lut = self.source.lut
553 conv_args = self.converter_arguments
555 # now build a ["string", 1, "string", 2]-styled list, with indices into the
556 # list to avoid lookup of item name for each entry
558 for element in conv_args:
559 if isinstance(element, basestring):
560 lutlist.append((element, None))
561 elif isinstance(element, ListItem):
562 lutlist.append((lut[element.name], element.filternum))
563 elif isinstance(element, ListMacroItem):
564 lutlist.append((element.macrodict[element.macroname], None))
566 raise Exception("neither string, ListItem nor ListMacroItem")
568 # now, for the huge list, do:
570 append = strlist.append
572 for (element, filternum) in lutlist:
576 curitem = item[element]
586 append(str(curitem).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
588 append(escape_xml(str(curitem)))
590 append(str(curitem).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
592 append(quote(str(curitem)))
594 from time import localtime
595 time = int(float(curitem)) or 0
597 append("%02d:%02d" % (t.tm_hour, t.tm_min))
599 from time import localtime
600 time = int(float(curitem)) or 0
602 append("%d min" % (time / 60))
605 # (this will be done in c++ later!)
607 return ''.join(strlist)
609 text = property(getText)
611 class webifHandler(ContentHandler):
612 def __init__(self, session, request):
616 self.session = session
618 self.request = request
621 def start_element(self, attrs):
624 wsource = attrs["source"]
626 path = wsource.split('.')
628 scr = self.screen.getRelatedScreen(path[0])
630 print "[webif.py] Parent Screen not found!"
634 source = scr.get(path[0])
636 if isinstance(source, ObsoleteSource):
637 # however, if we found an "obsolete source", issue warning, and resolve the real source.
638 print "WARNING: WEBIF '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
639 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
640 if source.description:
641 print source.description
643 wsource = source.new_source
646 # otherwise, use that source.
649 self.source_id = str(attrs.get("id", wsource))
650 self.is_streaming = "streaming" in attrs
651 self.macro_name = attrs.get("macro") or None
653 def end_element(self):
654 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
655 if not self.is_streaming:
656 if self.macro_name is None:
657 c = OneTimeElement(self.source_id)
659 c = MacroElement(self.source_id, self.macros, self.macro_name)
661 assert self.macro_name is None
662 c = StreamingElement(self.source_id)
664 c.connect(self.source)
666 self.screen.renderer.append(c)
669 def start_convert(self, attrs):
670 ctype = attrs["type"]
672 # TODO: we need something better here
673 if ctype[:4] == "web:": # for now
674 self.converter = eval(ctype[4:])
677 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
679 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
682 def end_convert(self):
683 if len(self.sub) == 1:
684 self.sub = self.sub[0]
685 c = self.converter(self.sub)
686 c.connect(self.source)
690 def parse_item(self, attrs):
692 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4, "urlencode": 5, "time": 6, "minutes": 7}[attrs.get("filter", "")]
693 self.sub.append(ListItem(attrs["name"], filter))
695 assert "macro" in attrs, "e2:item must have a name= or macro= attribute!"
696 self.sub.append(ListMacroItem(self.macros, attrs["macro"]))
698 def startElement(self, name, attrs):
699 if name == "e2:screen":
700 self.screen = eval(attrs["name"])(self.session, self.request) # fixme
701 self.screens.append(self.screen)
704 if name[:3] == "e2:":
707 tag = [' %s="%s"' % (key, val) for (key, val) in attrs.items()]
711 tag = ''.join(tag)#.encode('utf-8')
715 elif self.mode == 1: # expect "<e2:element>"
716 assert name == "e2:element", "found %s instead of e2:element" % name
717 self.start_element(attrs)
718 elif self.mode == 2: # expect "<e2:convert>"
719 if name[:3] == "e2:":
720 assert name == "e2:convert"
721 self.start_convert(attrs)
725 assert name == "e2:item", "found %s instead of e2:item!" % name
727 self.parse_item(attrs)
729 def endElement(self, name):
730 if name == "e2:screen":
734 tag = "</" + name + ">"
737 elif self.mode == 2 and name[:3] != "e2:":
739 elif self.mode == 2: # closed 'convert' -> sub
741 elif self.mode == 1: # closed 'element'
743 if name[:3] == "e2:":
746 def processingInstruction(self, target, data):
747 self.res.append('<?' + target + ' ' + data + '>')
749 def characters(self, ch):
750 ch = ch.encode('utf-8')
756 def startEntity(self, name):
757 self.res.append('&' + name + ';');
760 for screen in self.screens:
764 print "screen cleanup!"
765 for screen in self.screens:
770 def renderPage(stream, path, req, session):
771 # read in the template, create required screens
772 # we don't have persistense yet.
773 # if we had, this first part would only be done once.
774 handler = webifHandler(session, req)
775 parser = make_parser()
776 parser.setFeature(feature_namespaces, 0)
777 parser.setContentHandler(handler)
778 parser.parse(open(util.sibpath(__file__, path)))
780 # by default, we have non-streaming pages
783 # first, apply "commands" (aka. URL argument)
784 for x in handler.res:
785 if isinstance(x, Element):
786 x.handleCommand(req.args)
790 # now, we have a list with static texts mixed
791 # with non-static Elements.
792 # flatten this list, write into the stream.
793 for x in handler.res:
794 if isinstance(x, Element):
795 if isinstance(x, StreamingElement):
803 from twisted.internet import reactor
805 reactor.callLater(3, ping, s)
807 # if we met a "StreamingElement", there is at least one
808 # element which wants to output data more than once,
809 # i.e. on host-originated changes.
810 # in this case, don't finish yet, don't cleanup yet,
811 # but instead do that when the client disconnects.
813 streamFinish(handler, stream)
816 # you *need* something which constantly sends something in a regular interval,
817 # in order to detect disconnected clients.
818 # i agree that this "ping" sucks terrible, so better be sure to have something
819 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
821 stream.closed_callback = lambda : streamFinish(handler, stream)
823 def streamFinish(handler, stream):