1 # -*- coding: UTF-8 -*-
4 # OK, this is more than a proof of concept
7 # - screens need to be defined somehow else.
8 # I don't know how, yet. Probably each in an own file.
9 # - more components, like the channellist
10 # - better error handling
11 # - use namespace parser
12 from enigma import eServiceReference
14 from Screens.Screen import Screen
15 from Screens.ChannelSelection import service_types_tv, service_types_radio
16 from Tools.Import import my_import
18 from Components.Sources.Source import ObsoleteSource
19 from Components.Converter.Converter import Converter
20 from Components.Element import Element
21 from WebComponents.Sources.RequestData import RequestData
23 from xml.sax import make_parser
24 from xml.sax.handler import ContentHandler, feature_namespaces
25 from xml.sax.saxutils import escape as escape_xml
26 from twisted.python import util
27 from urllib2 import quote
29 # prototype of the new web frontend template system.
31 class WebScreen(Screen):
32 def __init__(self, session, request):
33 Screen.__init__(self, session)
34 self.stand_alone = True
35 self.request = request
38 class DummyWebScreen(WebScreen):
39 #use it, if you dont need any source, just to can do a static file with an xml-file
40 def __init__(self, session, request):
41 WebScreen.__init__(self, session, request)
43 class UpdateWebScreen(WebScreen):
44 def __init__(self, session, request):
45 WebScreen.__init__(self, session, request)
46 from Components.Sources.Clock import Clock
48 self["CurrentTime"] = Clock()
51 class MessageWebScreen(WebScreen):
52 def __init__(self, session, request):
53 WebScreen.__init__(self, session, request)
54 from WebComponents.Sources.Message import Message
56 self["Message"] = Message(session, func=Message.PRINT)
57 self["GetAnswer"] = Message(session, func=Message.ANSWER)
59 class ServiceListReloadWebScreen(WebScreen):
60 def __init__(self, session, request):
61 WebScreen.__init__(self, session, request)
62 from WebComponents.Sources.ServiceListReload import ServiceListReload
64 self["ServiceListReload"] = ServiceListReload(session)
66 class AudioWebScreen(WebScreen):
67 def __init__(self, session, request):
68 WebScreen.__init__(self, session, request)
69 from WebComponents.Sources.AudioTracks import AudioTracks
71 self["AudioTracks"] = AudioTracks(session, func=AudioTracks.GET)
72 self["SelectAudioTrack"] = AudioTracks(session, func=AudioTracks.SET)
74 class AboutWebScreen(WebScreen):
75 def __init__(self, session, request):
76 WebScreen.__init__(self, session, request)
77 from WebComponents.Sources.About import About
78 from WebComponents.Sources.Frontend import Frontend
79 from WebComponents.Sources.Hdd import Hdd
80 from WebComponents.Sources.Network import Network
81 from Components.config import config
82 from Components.About import about
83 from Components.Sources.StaticText import StaticText
84 from Tools.DreamboxHardware import getFPVersion
85 from Tools.HardwareInfo import HardwareInfo
89 self["About"] = About(session)
91 self["Network"] = Network()
93 self["Frontends"] = Frontend()
94 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
95 self["ImageVersion"] = StaticText(about.getVersionString())
96 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
97 self["FpVersion"] = StaticText(str(getFPVersion()))
98 self["DeviceName"] = StaticText(hw.get_device_name())
100 class VolumeWebScreen(WebScreen):
101 def __init__(self, session, request):
102 WebScreen.__init__(self, session, request)
104 from WebComponents.Sources.Volume import Volume
105 self["Volume"] = Volume(session)
107 class SettingsWebScreen(WebScreen):
108 def __init__(self, session, request):
109 WebScreen.__init__(self, session, request)
110 from WebComponents.Sources.Settings import Settings
112 self["Settings"] = Settings(session)
114 class SubServiceWebScreen(WebScreen):
115 def __init__(self, session, request):
116 WebScreen.__init__(self, session, request)
117 from WebComponents.Sources.SubServices import SubServices
119 self["SubServices"] = SubServices(session)
121 class StreamSubServiceWebScreen(WebScreen):
122 def __init__(self, session, request):
123 WebScreen.__init__(self, session, request)
124 from WebComponents.Sources.SubServices import SubServices
126 self["StreamSubServices"] = SubServices(session, streamingScreens)
128 class ServiceWebScreen(WebScreen):
129 def __init__(self, session, request):
130 WebScreen.__init__(self, session, request)
131 from WebComponents.Sources.ServiceListRecursive import ServiceListRecursive
132 from Components.Sources.ServiceList import ServiceList
134 fav = eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
135 self["SwitchService"] = ServiceList(fav, command_func=self.zapTo, validate_commands=False)
136 self["ServiceList"] = ServiceList(fav, command_func=self.getServiceList, validate_commands=False)
137 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
138 self["localip"] = RequestData(request, what=RequestData.HOST)
140 def getServiceList(self, sRef):
141 self["ServiceList"].root = sRef
143 def zapTo(self, reftozap):
144 from Components.config import config
145 pc = config.ParentalControl.configured.value
147 config.ParentalControl.configured.value = False
148 if config.plugins.Webinterface.allowzapping.value:
149 self.session.nav.playService(reftozap)
151 config.ParentalControl.configured.value = pc
153 switching config.ParentalControl.configured.value
154 ugly, but necessary :(
157 class LocationsAndTagsWebScreen(WebScreen):
158 def __init__(self, session, request):
159 WebScreen.__init__(self, session, request)
160 from WebComponents.Sources.LocationsAndTags import LocationsAndTags
162 self["CurrentLocation"] = LocationsAndTags(session, LocationsAndTags.CURRLOCATION)
163 self["Locations"] = LocationsAndTags(session, LocationsAndTags.LOCATIONS)
164 self["Tags"] = LocationsAndTags(session, LocationsAndTags.TAGS)
166 class EPGWebScreen(WebScreen):
167 def __init__(self, session, request):
168 WebScreen.__init__(self, session, request)
169 from WebComponents.Sources.EPG import EPG
171 self["EPGTITLE"] = EPG(session, func=EPG.TITLE)
172 self["EPGSERVICE"] = EPG(session, func=EPG.SERVICE)
173 self["EPGBOUQUETNOW"] = EPG(session, func=EPG.BOUQUETNOW)
174 self["EPGBOUQUETNEXT"] = EPG(session, func=EPG.BOUQUETNEXT)
175 self["EPGSERVICENOW"] = EPG(session, func=EPG.SERVICENOW)
176 self["EPGSERVICENEXT"] = EPG(session, func=EPG.SERVICENEXT)
177 self["EPGBOUQUET"] = EPG(session, func=EPG.BOUQUET)
178 self["localip"] = RequestData(request, what=RequestData.HOST)
180 self["EPGSERVICEWAP"] = EPG(session, func=EPG.SERVICE, endtm=True)
182 def getServiceList(self, sRef):
183 self["ServiceList"].root = sRef
185 class MovieWebScreen(WebScreen):
186 def __init__(self, session, request):
187 WebScreen.__init__(self, session, request)
188 from Components.MovieList import MovieList
189 from Tools.Directories import resolveFilename, SCOPE_HDD
190 from WebComponents.Sources.Movie import Movie
192 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
193 self["MovieList"] = Movie(session, movielist, func=Movie.LIST)
194 self["MovieFileDel"] = Movie(session, movielist, func=Movie.DEL)
195 self["localip"] = RequestData(request, what=RequestData.HOST)
197 class MediaPlayerWebScreen(WebScreen):
198 def __init__(self, session, request):
199 WebScreen.__init__(self, session, request)
200 from WebComponents.Sources.MP import MP
202 self["FileList"] = MP(session, func=MP.LIST)
203 self["PlayFile"] = MP(session, func=MP.PLAY)
204 self["Command"] = MP(session, func=MP.COMMAND)
205 self["WritePlaylist"] = MP(session, func=MP.WRITEPLAYLIST)
207 class AutoTimerWebScreen(WebScreen):
208 def __init__(self, session, request):
209 WebScreen.__init__(self, session, request)
210 from WebComponents.Sources.AT import AT
212 self["AutoTimerList"] = AT(session, func=AT.LIST)
213 self["AutoTimerWrite"] = AT(session, func=AT.WRITE)
215 class TimerWebScreen(WebScreen):
216 def __init__(self, session, request):
217 WebScreen.__init__(self, session, request)
218 from WebComponents.Sources.Timer import Timer
220 self["TimerList"] = Timer(session, func=Timer.LIST)
221 self["TimerAddEventID"] = Timer(session, func=Timer.ADDBYID)
222 self["TimerAdd"] = Timer(session, func=Timer.ADD)
223 self["TimerDel"] = Timer(session, func=Timer.DEL)
224 self["TimerChange"] = Timer(session, func=Timer.CHANGE)
225 self["TimerListWrite"] = Timer(session, func=Timer.WRITE)
226 self["TVBrowser"] = Timer(session, func=Timer.TVBROWSER)
227 self["RecordNow"] = Timer(session, func=Timer.RECNOW)
228 self["TimerCleanup"] = Timer(session, func=Timer.CLEANUP)
230 class RemoteWebScreen(WebScreen):
231 def __init__(self, session, request):
232 WebScreen.__init__(self, session, request)
233 from WebComponents.Sources.RemoteControl import RemoteControl
235 self["RemoteControl"] = RemoteControl(session)
237 class PowerWebScreen(WebScreen):
238 def __init__(self, session, request):
239 WebScreen.__init__(self, session, request)
240 from WebComponents.Sources.PowerState import PowerState
242 self["PowerState"] = PowerState(session)
244 class ParentControlWebScreen(WebScreen):
245 def __init__(self, session, request):
246 WebScreen.__init__(self, session, request)
247 from WebComponents.Sources.ParentControl import ParentControl
249 self["ParentControlList"] = ParentControl(session)
251 class WAPWebScreen(WebScreen):
252 def __init__(self, session, request):
253 WebScreen.__init__(self, session, request)
254 from WebComponents.Sources.WAPfunctions import WAPfunctions
256 self["WAPFillOptionListYear"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
257 self["WAPFillOptionListDay"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
258 self["WAPFillOptionListMonth"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
259 self["WAPFillOptionListShour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
260 self["WAPFillOptionListSmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
261 self["WAPFillOptionListEhour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
262 self["WAPFillOptionListEmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
264 self["WAPFillOptionListRecord"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
265 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
267 self["WAPFillValueName"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
268 self["WAPFillValueDescr"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
269 self["WAPFillLocation"] = WAPfunctions(session, func=WAPfunctions.LOCATIONLIST)
270 self["WAPFillTags"] = WAPfunctions(session, func=WAPfunctions.TAGLIST)
272 self["WAPFillOptionListRepeated"] = WAPfunctions(session, func=WAPfunctions.REPEATED)
273 self["WAPServiceList"] = WAPfunctions(session, func=WAPfunctions.SERVICELIST)
275 self["WAPdeleteOldOnSave"] = WAPfunctions(session, func=WAPfunctions.DELETEOLD)
277 streamingScreens = []
279 class StreamingWebScreen(WebScreen):
280 def __init__(self, session, request):
281 WebScreen.__init__(self, session, request)
282 from Components.Sources.StreamService import StreamService
283 self["StreamService"] = StreamService(self.session.nav)
284 streamingScreens.append(self)
285 self.screenIndex = len(streamingScreens) - 1
287 def getRecordService(self):
288 if self.has_key("StreamService"):
289 return self["StreamService"].getService()
292 def getRecordServiceRef(self):
293 if self.has_key("StreamService"):
294 return self["StreamService"].ref
297 class M3UStreamingWebScreen(WebScreen):
298 def __init__(self, session, request):
299 WebScreen.__init__(self, session, request)
300 from Components.Sources.StaticText import StaticText
301 from Components.Sources.Config import Config
302 from Components.config import config
303 self["ref"] = StaticText()
304 self["localip"] = RequestData(request, what=RequestData.HOST)
306 class M3UStreamingCurrentServiceWebScreen(WebScreen):
307 def __init__(self, session, request):
308 WebScreen.__init__(self, session, request)
309 from WebComponents.Sources.CurrentService import CurrentService
311 self["CurrentService"] = CurrentService(session)
312 self["localip"] = RequestData(request, what=RequestData.HOST)
314 class TsM3U(WebScreen):
315 def __init__(self, session, request):
316 WebScreen.__init__(self, session, request)
317 from Components.Sources.StaticText import StaticText
318 from Components.Sources.Config import Config
319 from Components.config import config
320 self["file"] = StaticText()
321 self["localip"] = RequestData(request, what=RequestData.HOST)
323 class RestartWebScreen(WebScreen):
324 def __init__(self, session, request):
325 WebScreen.__init__(self, session, request)
327 plugin.restartWebserver(session)
329 class GetPid(WebScreen):
330 def __init__(self, session, request):
331 WebScreen.__init__(self, session, request)
332 from Components.Sources.StaticText import StaticText
333 from enigma import iServiceInformation
334 pids = self.session.nav.getCurrentService()
336 pidinfo = pids.info()
337 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
338 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
339 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
340 self["pids"] = StaticText("%s,%s,%s" % (PPID.lstrip("0x"), VPID.lstrip("0x"), APID.lstrip("0x")))
342 self["pids"] = StaticText("0x,0x,0x")
344 self["localip"] = RequestData(request, what=RequestData.HOST)
346 class DeviceInfo(WebScreen):
347 def __init__(self, session, request):
348 WebScreen.__init__(self, session, request)
349 from WebComponents.Sources.Network import Network
350 from WebComponents.Sources.Hdd import Hdd
351 from WebComponents.Sources.Frontend import Frontend
352 from Components.config import config
353 from Components.About import about
354 from Components.Sources.StaticText import StaticText
355 from Tools.DreamboxHardware import getFPVersion
356 from Tools.HardwareInfo import HardwareInfo
360 self["Network"] = Network()
362 self["Frontends"] = Frontend()
363 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
364 self["ImageVersion"] = StaticText(about.getVersionString())
365 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
366 self["FpVersion"] = StaticText(str(getFPVersion()))
367 self["DeviceName"] = StaticText(hw.get_device_name())
369 # implements the 'render'-call.
370 # this will act as a downstream_element, like a renderer.
371 class OneTimeElement(Element):
372 def __init__(self, id):
373 Element.__init__(self)
376 # CHECKME: is this ok performance-wise?
377 def handleCommand(self, args):
378 if self.source_id.find(",") >= 0:
379 paramlist = self.source_id.split(",")
381 for key in paramlist:
382 arg = args.get(key, [])
386 list[key] = "".join(arg)
389 self.source.handleCommand(list)
391 for c in args.get(self.source_id, []):
392 self.source.handleCommand(c)
394 def render(self, stream):
395 t = self.source.getHTML(self.source_id)
399 self.suspended = False
402 self.suspended = True
413 class MacroElement(OneTimeElement):
414 def __init__(self, id, macro_dict, macro_name):
415 OneTimeElement.__init__(self, id)
416 self.macro_dict = macro_dict
417 self.macro_name = macro_name
419 def render(self, stream):
420 self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
422 class StreamingElement(OneTimeElement):
423 def __init__(self, id):
424 OneTimeElement.__init__(self, id)
427 def changed(self, what):
429 self.render(self.stream)
431 def setStream(self, stream):
434 # a to-be-filled list item
436 def __init__(self, name, filternum):
438 self.filternum = filternum
441 def __init__(self, macrodict, macroname):
442 self.macrodict = macrodict
443 self.macroname = macroname
445 class TextToHTML(Converter):
446 def __init__(self, arg):
447 Converter.__init__(self, arg)
449 def getHTML(self, id):
450 return self.source.text # encode & etc. here!
452 class TextToXML(Converter):
453 def __init__(self, arg):
454 Converter.__init__(self, arg)
456 def getHTML(self, id):
457 return escape_xml(self.source.text).replace("\x19", "").replace("\x1c", "").replace("\x1e", "")
459 class TextToURL(Converter):
460 def __init__(self, arg):
461 Converter.__init__(self, arg)
463 def getHTML(self, id):
464 return self.source.text.replace(" ", "%20")
466 class ReturnEmptyXML(Converter):
467 def __init__(self, arg):
468 Converter.__init__(self, arg)
470 def getHTML(self, id):
471 return "<rootElement></rootElement>"
473 # a null-output. Useful if you only want to issue a command.
474 class Null(Converter):
475 def __init__(self, arg):
476 Converter.__init__(self, arg)
478 def getHTML(self, id):
481 class JavascriptUpdate(Converter):
482 def __init__(self, arg):
483 Converter.__init__(self, arg)
485 def getHTML(self, id):
486 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
487 # all other will replace this in JS
488 return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
490 # the performant 'one-dimensonial listfiller' engine (podlfe)
491 class SimpleListFiller(Converter):
492 def __init__(self, arg):
493 Converter.__init__(self, arg)
496 l = self.source.simplelist
497 conv_args = self.converter_arguments
500 for element in conv_args:
501 if isinstance(element, basestring):
502 list.append((element, None))
503 elif isinstance(element, ListItem):
504 list.append((element, element.filternum))
505 elif isinstance(element, ListMacroItem):
506 list.append(element.macrodict[element.macroname], None)
508 raise Exception("neither string, ListItem nor ListMacroItem")
511 append = strlist.append
516 for (element, filternum) in list:
520 append(str(item).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
522 append(escape_xml(str(item)))
524 append(str(item).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
526 append(quote(str(item)))
528 time = parseint(item) or 0
530 append("%02d:%02d" % (t.tm_hour, t.tm_min))
532 time = parseint(item) or 0
534 append("%d min" % (time / 60))
537 # (this will be done in c++ later!)
539 return ''.join(strlist)
541 text = property(getText)
545 # the performant 'listfiller'-engine (plfe)
546 class ListFiller(Converter):
547 def __init__(self, arg):
548 Converter.__init__(self, arg)
549 # print "ListFiller-arg: ",arg
553 lut = self.source.lut
554 conv_args = self.converter_arguments
556 # now build a ["string", 1, "string", 2]-styled list, with indices into the
557 # list to avoid lookup of item name for each entry
559 for element in conv_args:
560 if isinstance(element, basestring):
561 lutlist.append((element, None))
562 elif isinstance(element, ListItem):
563 lutlist.append((lut[element.name], element.filternum))
564 elif isinstance(element, ListMacroItem):
565 lutlist.append((element.macrodict[element.macroname], None))
567 raise Exception("neither string, ListItem nor ListMacroItem")
569 # now, for the huge list, do:
571 append = strlist.append
573 for (element, filternum) in lutlist:
577 curitem = item[element]
587 append(str(curitem).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
589 append(escape_xml(str(curitem)))
591 append(str(curitem).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
593 append(quote(str(curitem)))
595 from time import localtime
596 time = int(float(curitem)) or 0
598 append("%02d:%02d" % (t.tm_hour, t.tm_min))
600 from time import localtime
601 time = int(float(curitem)) or 0
603 append("%d min" % (time / 60))
606 # (this will be done in c++ later!)
608 return ''.join(strlist)
610 text = property(getText)
612 class webifHandler(ContentHandler):
613 def __init__(self, session, request):
617 self.session = session
619 self.request = request
622 def start_element(self, attrs):
625 wsource = attrs["source"]
627 path = wsource.split('.')
629 scr = self.screen.getRelatedScreen(path[0])
631 print "[webif.py] Parent Screen not found!"
635 source = scr.get(path[0])
637 if isinstance(source, ObsoleteSource):
638 # however, if we found an "obsolete source", issue warning, and resolve the real source.
639 print "WARNING: WEBIF '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
640 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
641 if source.description:
642 print source.description
644 wsource = source.new_source
647 # otherwise, use that source.
650 self.source_id = str(attrs.get("id", wsource))
651 self.is_streaming = "streaming" in attrs
652 self.macro_name = attrs.get("macro") or None
654 def end_element(self):
655 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
656 if not self.is_streaming:
657 if self.macro_name is None:
658 c = OneTimeElement(self.source_id)
660 c = MacroElement(self.source_id, self.macros, self.macro_name)
662 assert self.macro_name is None
663 c = StreamingElement(self.source_id)
665 c.connect(self.source)
667 self.screen.renderer.append(c)
670 def start_convert(self, attrs):
671 ctype = attrs["type"]
673 # TODO: we need something better here
674 if ctype[:4] == "web:": # for now
675 self.converter = eval(ctype[4:])
678 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
680 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
683 def end_convert(self):
684 if len(self.sub) == 1:
685 self.sub = self.sub[0]
686 c = self.converter(self.sub)
687 c.connect(self.source)
691 def parse_item(self, attrs):
693 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4, "urlencode": 5, "time": 6, "minutes": 7}[attrs.get("filter", "")]
694 self.sub.append(ListItem(attrs["name"], filter))
696 assert "macro" in attrs, "e2:item must have a name= or macro= attribute!"
697 self.sub.append(ListMacroItem(self.macros, attrs["macro"]))
699 def startElement(self, name, attrs):
700 if name == "e2:screen":
701 self.screen = eval(attrs["name"])(self.session, self.request) # fixme
702 self.screens.append(self.screen)
705 if name[:3] == "e2:":
708 tag = [' %s="%s"' % (key, val) for (key, val) in attrs.items()]
712 tag = ''.join(tag)#.encode('utf-8')
716 elif self.mode == 1: # expect "<e2:element>"
717 assert name == "e2:element", "found %s instead of e2:element" % name
718 self.start_element(attrs)
719 elif self.mode == 2: # expect "<e2:convert>"
720 if name[:3] == "e2:":
721 assert name == "e2:convert"
722 self.start_convert(attrs)
726 assert name == "e2:item", "found %s instead of e2:item!" % name
728 self.parse_item(attrs)
730 def endElement(self, name):
731 if name == "e2:screen":
735 tag = "</" + name + ">"
738 elif self.mode == 2 and name[:3] != "e2:":
740 elif self.mode == 2: # closed 'convert' -> sub
742 elif self.mode == 1: # closed 'element'
744 if name[:3] == "e2:":
747 def processingInstruction(self, target, data):
748 self.res.append('<?' + target + ' ' + data + '>')
750 def characters(self, ch):
751 ch = ch.encode('utf-8')
757 def startEntity(self, name):
758 self.res.append('&' + name + ';');
761 for screen in self.screens:
765 print "screen cleanup!"
766 for screen in self.screens:
771 def renderPage(stream, path, req, session):
772 # read in the template, create required screens
773 # we don't have persistense yet.
774 # if we had, this first part would only be done once.
775 handler = webifHandler(session, req)
776 parser = make_parser()
777 parser.setFeature(feature_namespaces, 0)
778 parser.setContentHandler(handler)
779 parser.parse(open(util.sibpath(__file__, path)))
781 # by default, we have non-streaming pages
784 # first, apply "commands" (aka. URL argument)
785 for x in handler.res:
786 if isinstance(x, Element):
787 x.handleCommand(req.args)
791 # now, we have a list with static texts mixed
792 # with non-static Elements.
793 # flatten this list, write into the stream.
794 for x in handler.res:
795 if isinstance(x, Element):
796 if isinstance(x, StreamingElement):
804 from twisted.internet import reactor
806 reactor.callLater(3, ping, s)
808 # if we met a "StreamingElement", there is at least one
809 # element which wants to output data more than once,
810 # i.e. on host-originated changes.
811 # in this case, don't finish yet, don't cleanup yet,
812 # but instead do that when the client disconnects.
814 streamFinish(handler, stream)
817 # you *need* something which constantly sends something in a regular interval,
818 # in order to detect disconnected clients.
819 # i agree that this "ping" sucks terrible, so better be sure to have something
820 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
822 stream.closed_callback = lambda : streamFinish(handler, stream)
824 def streamFinish(handler, stream):