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
20 from Components.Sources.Clock import Clock
21 from Components.Sources.ServiceList import ServiceList
23 from WebComponents.Sources.ServiceListRecursive import ServiceListRecursive
24 from WebComponents.Sources.Volume import Volume
25 from WebComponents.Sources.EPG import EPG
26 from WebComponents.Sources.Timer import Timer
27 from WebComponents.Sources.Movie import Movie
28 from WebComponents.Sources.Message import Message
29 from WebComponents.Sources.PowerState import PowerState
30 from WebComponents.Sources.RemoteControl import RemoteControl
31 from WebComponents.Sources.Settings import Settings
32 from WebComponents.Sources.SubServices import SubServices
33 from WebComponents.Sources.ParentControl import ParentControl
34 from WebComponents.Sources.About import About
35 from WebComponents.Sources.RequestData import RequestData
36 from WebComponents.Sources.AudioTracks import AudioTracks
37 from WebComponents.Sources.WAPfunctions import WAPfunctions
38 from WebComponents.Sources.MP import MP
39 from WebComponents.Sources.ServiceListReload import ServiceListReload
40 from WebComponents.Sources.AT import AT
41 from WebComponents.Sources.CurrentService import CurrentService
42 from WebComponents.Sources.LocationsAndTags import LocationsAndTags
44 from Components.Sources.FrontendStatus import FrontendStatus
46 from Components.Converter.Converter import Converter
48 from Components.Element import Element
50 from xml.sax import make_parser
51 from xml.sax.handler import ContentHandler, feature_namespaces
52 from xml.sax.saxutils import escape as escape_xml
53 from twisted.python import util
54 from urllib2 import quote
56 # prototype of the new web frontend template system.
58 class WebScreen(Screen):
59 def __init__(self, session, request):
60 Screen.__init__(self, session)
61 self.stand_alone = True
62 self.request = request
65 class DummyWebScreen(WebScreen):
66 #use it, if you dont need any source, just to can do a static file with an xml-file
67 def __init__(self, session, request):
68 WebScreen.__init__(self, session, request)
70 class UpdateWebScreen(WebScreen):
71 def __init__(self, session, request):
72 WebScreen.__init__(self, session, request)
73 self["CurrentTime"] = Clock()
75 class MessageWebScreen(WebScreen):
76 def __init__(self, session, request):
77 WebScreen.__init__(self, session, request)
78 self["Message"] = Message(session, func=Message.PRINT)
79 self["GetAnswer"] = Message(session, func=Message.ANSWER)
81 class ServiceListReloadWebScreen(WebScreen):
82 def __init__(self, session, request):
83 WebScreen.__init__(self, session, request)
84 self["ServiceListReload"] = ServiceListReload(session)
86 class AudioWebScreen(WebScreen):
87 def __init__(self, session, request):
88 WebScreen.__init__(self, session, request)
89 self["AudioTracks"] = AudioTracks(session, func=AudioTracks.GET)
90 self["SelectAudioTrack"] = AudioTracks(session, func=AudioTracks.SET)
92 class AboutWebScreen(WebScreen):
93 def __init__(self, session, request):
94 WebScreen.__init__(self, session, request)
96 from WebComponents.Sources.Network import Network
97 from WebComponents.Sources.Hdd import Hdd
98 from WebComponents.Sources.Frontend import Frontend
99 from Components.config import config
100 from Components.About import about
101 from Components.Sources.StaticText import StaticText
102 from Tools.DreamboxHardware import getFPVersion
103 from Tools.HardwareInfo import HardwareInfo
107 self["About"] = About(session)
109 self["Network"] = Network()
111 self["Frontends"] = Frontend()
112 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
113 self["ImageVersion"] = StaticText(about.getVersionString())
114 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
115 self["FpVersion"] = StaticText(str(getFPVersion()))
116 self["DeviceName"] = StaticText(hw.get_device_name())
118 class VolumeWebScreen(WebScreen):
119 def __init__(self, session, request):
120 WebScreen.__init__(self, session, request)
121 self["Volume"] = Volume(session)
123 class SettingsWebScreen(WebScreen):
124 def __init__(self, session, request):
125 WebScreen.__init__(self, session, request)
126 self["Settings"] = Settings(session)
128 class SubServiceWebScreen(WebScreen):
129 def __init__(self, session, request):
130 WebScreen.__init__(self, session, request)
131 self["SubServices"] = SubServices(session)
133 class StreamSubServiceWebScreen(WebScreen):
134 def __init__(self, session, request):
135 WebScreen.__init__(self, session, request)
136 self["StreamSubServices"] = SubServices(session, streamingScreens)
138 class ServiceWebScreen(WebScreen):
139 def __init__(self, session, request):
140 WebScreen.__init__(self, session, request)
142 fav = eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
143 self["SwitchService"] = ServiceList(fav, command_func=self.zapTo, validate_commands=False)
144 self["ServiceList"] = ServiceList(fav, command_func=self.getServiceList, validate_commands=False)
145 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
146 self["localip"] = RequestData(request, what=RequestData.HOST)
148 def getServiceList(self, sRef):
149 self["ServiceList"].root = sRef
151 def zapTo(self, reftozap):
152 from Components.config import config
153 pc = config.ParentalControl.configured.value
155 config.ParentalControl.configured.value = False
156 if config.plugins.Webinterface.allowzapping.value:
157 self.session.nav.playService(reftozap)
159 config.ParentalControl.configured.value = pc
161 switching config.ParentalControl.configured.value
162 ugly, but necessary :(
165 class LocationsAndTagsWebScreen(WebScreen):
166 def __init__(self, session, request):
167 WebScreen.__init__(self, session, request)
168 self["CurrentLocation"] = LocationsAndTags(session, LocationsAndTags.CURRLOCATION)
169 self["Locations"] = LocationsAndTags(session, LocationsAndTags.LOCATIONS)
170 self["Tags"] = LocationsAndTags(session, LocationsAndTags.TAGS)
172 class EPGWebScreen(WebScreen):
173 def __init__(self, session, request):
174 WebScreen.__init__(self, session, request)
176 self["EPGTITLE"] = EPG(session, func=EPG.TITLE)
177 self["EPGSERVICE"] = EPG(session, func=EPG.SERVICE)
178 self["EPGBOUQUETNOW"] = EPG(session, func=EPG.BOUQUETNOW)
179 self["EPGBOUQUETNEXT"] = EPG(session, func=EPG.BOUQUETNEXT)
180 self["EPGSERVICENOW"] = EPG(session, func=EPG.SERVICENOW)
181 self["EPGSERVICENEXT"] = EPG(session, func=EPG.SERVICENEXT)
182 self["EPGBOUQUET"] = EPG(session, func=EPG.BOUQUET)
183 self["localip"] = RequestData(request, what=RequestData.HOST)
185 self["EPGSERVICEWAP"] = EPG(session, func=EPG.SERVICE, endtm=True)
187 def getServiceList(self, sRef):
188 self["ServiceList"].root = sRef
190 class MovieWebScreen(WebScreen):
191 def __init__(self, session, request):
192 WebScreen.__init__(self, session, request)
193 from Components.MovieList import MovieList
194 from Tools.Directories import resolveFilename, SCOPE_HDD
195 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
196 self["MovieList"] = Movie(session, movielist, func=Movie.LIST)
197 self["MovieFileDel"] = Movie(session, movielist, func=Movie.DEL)
198 self["localip"] = RequestData(request, what=RequestData.HOST)
200 class MediaPlayerWebScreen(WebScreen):
201 def __init__(self, session, request):
202 WebScreen.__init__(self, session, request)
203 self["FileList"] = MP(session, func=MP.LIST)
204 self["PlayFile"] = MP(session, func=MP.PLAY)
205 self["Command"] = MP(session, func=MP.COMMAND)
206 self["WritePlaylist"] = MP(session, func=MP.WRITEPLAYLIST)
208 class AutoTimerWebScreen(WebScreen):
209 def __init__(self, session, request):
210 WebScreen.__init__(self, session, request)
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 self["TimerList"] = Timer(session, func=Timer.LIST)
218 self["TimerAddEventID"] = Timer(session, func=Timer.ADDBYID)
219 self["TimerAdd"] = Timer(session, func=Timer.ADD)
220 self["TimerDel"] = Timer(session, func=Timer.DEL)
221 self["TimerChange"] = Timer(session, func=Timer.CHANGE)
222 self["TimerListWrite"] = Timer(session, func=Timer.WRITE)
223 self["TVBrowser"] = Timer(session, func=Timer.TVBROWSER)
224 self["RecordNow"] = Timer(session, func=Timer.RECNOW)
225 self["TimerCleanup"] = Timer(session, func=Timer.CLEANUP)
227 class RemoteWebScreen(WebScreen):
228 def __init__(self, session, request):
229 WebScreen.__init__(self, session, request)
230 self["RemoteControl"] = RemoteControl(session)
232 class PowerWebScreen(WebScreen):
233 def __init__(self, session, request):
234 WebScreen.__init__(self, session, request)
235 self["PowerState"] = PowerState(session)
237 class ParentControlWebScreen(WebScreen):
238 def __init__(self, session, request):
239 WebScreen.__init__(self, session, request)
240 self["ParentControlList"] = ParentControl(session)
242 class WAPWebScreen(WebScreen):
243 def __init__(self, session, request):
244 WebScreen.__init__(self, session, request)
245 self["WAPFillOptionListYear"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
246 self["WAPFillOptionListDay"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
247 self["WAPFillOptionListMonth"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
248 self["WAPFillOptionListShour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
249 self["WAPFillOptionListSmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
250 self["WAPFillOptionListEhour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
251 self["WAPFillOptionListEmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
253 self["WAPFillOptionListRecord"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
254 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
256 self["WAPFillValueName"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
257 self["WAPFillValueDescr"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
258 self["WAPFillLocation"] = WAPfunctions(session, func=WAPfunctions.LOCATIONLIST)
259 self["WAPFillTags"] = WAPfunctions(session, func=WAPfunctions.TAGLIST)
261 self["WAPFillOptionListRepeated"] = WAPfunctions(session, func=WAPfunctions.REPEATED)
262 self["WAPServiceList"] = WAPfunctions(session, func=WAPfunctions.SERVICELIST)
264 self["WAPdeleteOldOnSave"] = WAPfunctions(session, func=WAPfunctions.DELETEOLD)
266 streamingScreens = []
268 class StreamingWebScreen(WebScreen):
269 def __init__(self, session, request):
270 WebScreen.__init__(self, session, request)
271 from Components.Sources.StreamService import StreamService
272 self["StreamService"] = StreamService(self.session.nav)
273 streamingScreens.append(self)
274 self.screenIndex = len(streamingScreens) - 1
276 def getRecordService(self):
277 if self.has_key("StreamService"):
278 return self["StreamService"].getService()
281 def getRecordServiceRef(self):
282 if self.has_key("StreamService"):
283 return self["StreamService"].ref
286 class M3UStreamingWebScreen(WebScreen):
287 def __init__(self, session, request):
288 WebScreen.__init__(self, session, request)
289 from Components.Sources.StaticText import StaticText
290 from Components.Sources.Config import Config
291 from Components.config import config
292 self["ref"] = StaticText()
293 self["localip"] = RequestData(request, what=RequestData.HOST)
295 class M3UStreamingCurrentServiceWebScreen(WebScreen):
296 def __init__(self, session, request):
297 WebScreen.__init__(self, session, request)
298 self["CurrentService"] = CurrentService(session)
299 self["localip"] = RequestData(request, what=RequestData.HOST)
301 class TsM3U(WebScreen):
302 def __init__(self, session, request):
303 WebScreen.__init__(self, session, request)
304 from Components.Sources.StaticText import StaticText
305 from Components.Sources.Config import Config
306 from Components.config import config
307 self["file"] = StaticText()
308 self["localip"] = RequestData(request, what=RequestData.HOST)
310 class RestartWebScreen(WebScreen):
311 def __init__(self, session, request):
312 WebScreen.__init__(self, session, request)
314 plugin.restartWebserver(session)
316 class GetPid(WebScreen):
317 def __init__(self, session, request):
318 WebScreen.__init__(self, session, request)
319 from Components.Sources.StaticText import StaticText
320 from enigma import iServiceInformation
321 pids = self.session.nav.getCurrentService()
323 pidinfo = pids.info()
324 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
325 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
326 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
327 self["pids"] = StaticText("%s,%s,%s" % (PPID.lstrip("0x"), VPID.lstrip("0x"), APID.lstrip("0x")))
329 self["pids"] = StaticText("0x,0x,0x")
331 self["localip"] = RequestData(request, what=RequestData.HOST)
333 class DeviceInfo(WebScreen):
334 def __init__(self, session, request):
335 WebScreen.__init__(self, session, request)
336 from WebComponents.Sources.Network import Network
337 from WebComponents.Sources.Hdd import Hdd
338 from WebComponents.Sources.Frontend import Frontend
339 from Components.config import config
340 from Components.About import about
341 from Components.Sources.StaticText import StaticText
342 from Tools.DreamboxHardware import getFPVersion
343 from Tools.HardwareInfo import HardwareInfo
347 self["Network"] = Network()
349 self["Frontends"] = Frontend()
350 self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
351 self["ImageVersion"] = StaticText(about.getVersionString())
352 self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
353 self["FpVersion"] = StaticText(str(getFPVersion()))
354 self["DeviceName"] = StaticText(hw.get_device_name())
356 # implements the 'render'-call.
357 # this will act as a downstream_element, like a renderer.
358 class OneTimeElement(Element):
359 def __init__(self, id):
360 Element.__init__(self)
363 # CHECKME: is this ok performance-wise?
364 def handleCommand(self, args):
365 if self.source_id.find(",") >= 0:
366 paramlist = self.source_id.split(",")
368 for key in paramlist:
369 arg = args.get(key, [])
373 list[key] = "".join(arg)
376 self.source.handleCommand(list)
378 for c in args.get(self.source_id, []):
379 self.source.handleCommand(c)
381 def render(self, stream):
382 t = self.source.getHTML(self.source_id)
386 self.suspended = False
389 self.suspended = True
400 class MacroElement(OneTimeElement):
401 def __init__(self, id, macro_dict, macro_name):
402 OneTimeElement.__init__(self, id)
403 self.macro_dict = macro_dict
404 self.macro_name = macro_name
406 def render(self, stream):
407 self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
409 class StreamingElement(OneTimeElement):
410 def __init__(self, id):
411 OneTimeElement.__init__(self, id)
414 def changed(self, what):
416 self.render(self.stream)
418 def setStream(self, stream):
421 # a to-be-filled list item
423 def __init__(self, name, filternum):
425 self.filternum = filternum
428 def __init__(self, macrodict, macroname):
429 self.macrodict = macrodict
430 self.macroname = macroname
432 class TextToHTML(Converter):
433 def __init__(self, arg):
434 Converter.__init__(self, arg)
436 def getHTML(self, id):
437 return self.source.text # encode & etc. here!
439 class TextToXML(Converter):
440 def __init__(self, arg):
441 Converter.__init__(self, arg)
443 def getHTML(self, id):
444 return escape_xml(self.source.text).replace("\x19", "").replace("\x1c", "").replace("\x1e", "")
446 class TextToURL(Converter):
447 def __init__(self, arg):
448 Converter.__init__(self, arg)
450 def getHTML(self, id):
451 return self.source.text.replace(" ", "%20")
453 class ReturnEmptyXML(Converter):
454 def __init__(self, arg):
455 Converter.__init__(self, arg)
457 def getHTML(self, id):
458 return "<rootElement></rootElement>"
460 # a null-output. Useful if you only want to issue a command.
461 class Null(Converter):
462 def __init__(self, arg):
463 Converter.__init__(self, arg)
465 def getHTML(self, id):
468 class JavascriptUpdate(Converter):
469 def __init__(self, arg):
470 Converter.__init__(self, arg)
472 def getHTML(self, id):
473 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
474 # all other will replace this in JS
475 return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
477 # the performant 'one-dimensonial listfiller' engine (podlfe)
478 class SimpleListFiller(Converter):
479 def __init__(self, arg):
480 Converter.__init__(self, arg)
483 l = self.source.simplelist
484 conv_args = self.converter_arguments
487 for element in conv_args:
488 if isinstance(element, basestring):
489 list.append((element, None))
490 elif isinstance(element, ListItem):
491 list.append((element, element.filternum))
492 elif isinstance(element, ListMacroItem):
493 list.append(element.macrodict[element.macroname], None)
495 raise Exception("neither string, ListItem nor ListMacroItem")
498 append = strlist.append
503 for (element, filternum) in list:
507 append(str(item).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
509 append(escape_xml(str(item)))
511 append(str(item).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
513 append(quote(str(item)))
515 time = parseint(item) or 0
517 append("%02d:%02d" % (t.tm_hour, t.tm_min))
519 time = parseint(item) or 0
521 append("%d min" % (time / 60))
524 # (this will be done in c++ later!)
526 return ''.join(strlist)
528 text = property(getText)
532 # the performant 'listfiller'-engine (plfe)
533 class ListFiller(Converter):
534 def __init__(self, arg):
535 Converter.__init__(self, arg)
536 # print "ListFiller-arg: ",arg
540 lut = self.source.lut
541 conv_args = self.converter_arguments
543 # now build a ["string", 1, "string", 2]-styled list, with indices into the
544 # list to avoid lookup of item name for each entry
546 for element in conv_args:
547 if isinstance(element, basestring):
548 lutlist.append((element, None))
549 elif isinstance(element, ListItem):
550 lutlist.append((lut[element.name], element.filternum))
551 elif isinstance(element, ListMacroItem):
552 lutlist.append((element.macrodict[element.macroname], None))
554 raise Exception("neither string, ListItem nor ListMacroItem")
556 # now, for the huge list, do:
558 append = strlist.append
560 for (element, filternum) in lutlist:
564 curitem = item[element]
574 append(str(curitem).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
576 append(escape_xml(str(curitem)))
578 append(str(curitem).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
580 append(quote(str(curitem)))
582 from time import localtime
583 time = int(float(curitem)) or 0
585 append("%02d:%02d" % (t.tm_hour, t.tm_min))
587 from time import localtime
588 time = int(float(curitem)) or 0
590 append("%d min" % (time / 60))
593 # (this will be done in c++ later!)
595 return ''.join(strlist)
597 text = property(getText)
599 class webifHandler(ContentHandler):
600 def __init__(self, session, request):
604 self.session = session
606 self.request = request
609 def start_element(self, attrs):
612 wsource = attrs["source"]
614 path = wsource.split('.')
616 scr = self.screen.getRelatedScreen(path[0])
618 print "[webif.py] Parent Screen not found!"
622 source = scr.get(path[0])
624 if isinstance(source, ObsoleteSource):
625 # however, if we found an "obsolete source", issue warning, and resolve the real source.
626 print "WARNING: WEBIF '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
627 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
628 if source.description:
629 print source.description
631 wsource = source.new_source
634 # otherwise, use that source.
637 self.source_id = str(attrs.get("id", wsource))
638 self.is_streaming = "streaming" in attrs
639 self.macro_name = attrs.get("macro") or None
641 def end_element(self):
642 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
643 if not self.is_streaming:
644 if self.macro_name is None:
645 c = OneTimeElement(self.source_id)
647 c = MacroElement(self.source_id, self.macros, self.macro_name)
649 assert self.macro_name is None
650 c = StreamingElement(self.source_id)
652 c.connect(self.source)
654 self.screen.renderer.append(c)
657 def start_convert(self, attrs):
658 ctype = attrs["type"]
660 # TODO: we need something better here
661 if ctype[:4] == "web:": # for now
662 self.converter = eval(ctype[4:])
665 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
667 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
670 def end_convert(self):
671 if len(self.sub) == 1:
672 self.sub = self.sub[0]
673 c = self.converter(self.sub)
674 c.connect(self.source)
678 def parse_item(self, attrs):
680 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4, "urlencode": 5, "time": 6, "minutes": 7}[attrs.get("filter", "")]
681 self.sub.append(ListItem(attrs["name"], filter))
683 assert "macro" in attrs, "e2:item must have a name= or macro= attribute!"
684 self.sub.append(ListMacroItem(self.macros, attrs["macro"]))
686 def startElement(self, name, attrs):
687 if name == "e2:screen":
688 self.screen = eval(attrs["name"])(self.session, self.request) # fixme
689 self.screens.append(self.screen)
692 if name[:3] == "e2:":
695 tag = [' %s="%s"' % (key, val) for (key, val) in attrs.items()]
699 tag = ''.join(tag)#.encode('utf-8')
703 elif self.mode == 1: # expect "<e2:element>"
704 assert name == "e2:element", "found %s instead of e2:element" % name
705 self.start_element(attrs)
706 elif self.mode == 2: # expect "<e2:convert>"
707 if name[:3] == "e2:":
708 assert name == "e2:convert"
709 self.start_convert(attrs)
713 assert name == "e2:item", "found %s instead of e2:item!" % name
715 self.parse_item(attrs)
717 def endElement(self, name):
718 if name == "e2:screen":
722 tag = "</" + name + ">"
725 elif self.mode == 2 and name[:3] != "e2:":
727 elif self.mode == 2: # closed 'convert' -> sub
729 elif self.mode == 1: # closed 'element'
731 if name[:3] == "e2:":
734 def processingInstruction(self, target, data):
735 self.res.append('<?' + target + ' ' + data + '>')
737 def characters(self, ch):
738 ch = ch.encode('utf-8')
744 def startEntity(self, name):
745 self.res.append('&' + name + ';');
748 for screen in self.screens:
752 print "screen cleanup!"
753 for screen in self.screens:
758 def renderPage(stream, path, req, session):
759 # read in the template, create required screens
760 # we don't have persistense yet.
761 # if we had, this first part would only be done once.
762 handler = webifHandler(session, req)
763 parser = make_parser()
764 parser.setFeature(feature_namespaces, 0)
765 parser.setContentHandler(handler)
766 parser.parse(open(util.sibpath(__file__, path)))
768 # by default, we have non-streaming pages
771 # first, apply "commands" (aka. URL argument)
772 for x in handler.res:
773 if isinstance(x, Element):
774 x.handleCommand(req.args)
778 # now, we have a list with static texts mixed
779 # with non-static Elements.
780 # flatten this list, write into the stream.
781 for x in handler.res:
782 if isinstance(x, Element):
783 if isinstance(x, StreamingElement):
791 from twisted.internet import reactor
793 reactor.callLater(3, ping, s)
795 # if we met a "StreamingElement", there is at least one
796 # element which wants to output data more than once,
797 # i.e. on host-originated changes.
798 # in this case, don't finish yet, don't cleanup yet,
799 # but instead do that when the client disconnects.
801 streamFinish(handler, stream)
804 # you *need* something which constantly sends something in a regular interval,
805 # in order to detect disconnected clients.
806 # i agree that this "ping" sucks terrible, so better be sure to have something
807 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
809 stream.closed_callback = lambda : streamFinish(handler, stream)
811 def streamFinish(handler, stream):