nothing
[vuplus_dvbapp-plugin] / webinterface / src / webif.py
index fd607f1..97e4bdd 100644 (file)
@@ -1,50 +1,14 @@
 # -*- coding: UTF-8 -*-
 Version = '$Header$';
 
-# OK, this is more than a proof of concept
 # things to improve:
-#      - nicer code
-#      - screens need to be defined somehow else.
-#      I don't know how, yet. Probably each in an own file.
-#      - more components, like the channellist
 #      - better error handling
 #      - use namespace parser
-from enigma import eServiceReference
 
-from Screens.Screen import Screen
-from Screens.ChannelSelection import service_types_tv, service_types_radio
 from Tools.Import import my_import
 
 from Components.Sources.Source import ObsoleteSource
-
-from Components.Sources.Clock import Clock
-from Components.Sources.ServiceList import ServiceList
-
-from WebComponents.Sources.ServiceListRecursive import ServiceListRecursive
-from WebComponents.Sources.Volume import Volume
-from WebComponents.Sources.EPG import EPG
-from WebComponents.Sources.Timer import Timer
-from WebComponents.Sources.Movie import Movie
-from WebComponents.Sources.Message import Message
-from WebComponents.Sources.PowerState import PowerState
-from WebComponents.Sources.RemoteControl import RemoteControl
-from WebComponents.Sources.Settings import Settings
-from WebComponents.Sources.SubServices import SubServices
-from WebComponents.Sources.ParentControl import ParentControl
-from WebComponents.Sources.About import About
-from WebComponents.Sources.RequestData import RequestData
-from WebComponents.Sources.AudioTracks import AudioTracks
-from WebComponents.Sources.WAPfunctions import WAPfunctions
-from WebComponents.Sources.MP import MP
-from WebComponents.Sources.ServiceListReload import ServiceListReload
-from WebComponents.Sources.AT import AT
-from WebComponents.Sources.CurrentService import CurrentService
-from WebComponents.Sources.LocationsAndTags import LocationsAndTags
-
-from Components.Sources.FrontendStatus import FrontendStatus
-
 from Components.Converter.Converter import Converter
-
 from Components.Element import Element
 
 from xml.sax import make_parser
@@ -53,334 +17,51 @@ from xml.sax.saxutils import escape as escape_xml
 from twisted.python import util
 from urllib2 import quote
 
-# prototype of the new web frontend template system.
-
-class WebScreen(Screen):
-       def __init__(self, session, request):
-               Screen.__init__(self, session)
-               self.stand_alone = True
-               self.request = request
-               self.instance = None            
-
-class DummyWebScreen(WebScreen):
-       #use it, if you dont need any source, just to can do a static file with an xml-file
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-
-class UpdateWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["CurrentTime"] = Clock()
-
-class MessageWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["Message"] = Message(session, func=Message.PRINT)
-               self["GetAnswer"] = Message(session, func=Message.ANSWER)
-
-class ServiceListReloadWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["ServiceListReload"] = ServiceListReload(session)
-
-class AudioWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["AudioTracks"] = AudioTracks(session, func=AudioTracks.GET)
-               self["SelectAudioTrack"] = AudioTracks(session, func=AudioTracks.SET)
-
-class AboutWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               
-               from WebComponents.Sources.Network import Network
-               from WebComponents.Sources.Hdd import Hdd
-               from WebComponents.Sources.Frontend import Frontend
-               from Components.config import config
-               from Components.About import about
-               from Components.Sources.StaticText import StaticText
-               from Tools.DreamboxHardware import getFPVersion
-               from Tools.HardwareInfo import HardwareInfo
-               
-               hw = HardwareInfo()
-               
-               self["About"] = About(session)          
-               
-               self["Network"] = Network()
-               self["Hdd"] = Hdd()
-               self["Frontends"] = Frontend()                                  
-               self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
-               self["ImageVersion"] = StaticText(about.getVersionString())
-               self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
-               self["FpVersion"] = StaticText(str(getFPVersion()))
-               self["DeviceName"] = StaticText(hw.get_device_name())           
-
-class VolumeWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["Volume"] = Volume(session)
-
-class SettingsWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["Settings"] = Settings(session)
+#DO NOT REMOVE THIS IMPORT
+#It IS used (dynamically)
+from WebScreens import *
+#DO NOT REMOVE THIS IMPORT
 
-class SubServiceWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["SubServices"] = SubServices(session)
 
-class StreamSubServiceWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["StreamSubServices"] = SubServices(session, streamingScreens)
+global screen_cache
+screen_cache = {}
 
-class ServiceWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-
-               fav = eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
-               self["SwitchService"] = ServiceList(fav, command_func=self.zapTo, validate_commands=False)
-               self["ServiceList"] = ServiceList(fav, command_func=self.getServiceList, validate_commands=False)
-               self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-
-       def getServiceList(self, sRef):
-               self["ServiceList"].root = sRef
-
-       def zapTo(self, reftozap):
-               from Components.config import config
-               pc = config.ParentalControl.configured.value
-               if pc:
-                       config.ParentalControl.configured.value = False
-               if config.plugins.Webinterface.allowzapping.value:
-                       self.session.nav.playService(reftozap)
-               if pc:
-                       config.ParentalControl.configured.value = pc
-               """
-               switching config.ParentalControl.configured.value
-               ugly, but necessary :(
-               """
-
-class LocationsAndTagsWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["CurrentLocation"] = LocationsAndTags(session, LocationsAndTags.CURRLOCATION)
-               self["Locations"] = LocationsAndTags(session, LocationsAndTags.LOCATIONS)
-               self["Tags"] = LocationsAndTags(session, LocationsAndTags.TAGS)
-
-class EPGWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-
-               self["EPGTITLE"] = EPG(session, func=EPG.TITLE)
-               self["EPGSERVICE"] = EPG(session, func=EPG.SERVICE)             
-               self["EPGBOUQUETNOW"] = EPG(session, func=EPG.BOUQUETNOW)
-               self["EPGBOUQUETNEXT"] = EPG(session, func=EPG.BOUQUETNEXT)
-               self["EPGSERVICENOW"] = EPG(session, func=EPG.SERVICENOW)
-               self["EPGSERVICENEXT"] = EPG(session, func=EPG.SERVICENEXT)
-               self["EPGBOUQUET"] = EPG(session, func=EPG.BOUQUET)
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-               
-               self["EPGSERVICEWAP"] = EPG(session, func=EPG.SERVICE, endtm=True)
+# The classes and Function in File handle all ScreenPage-based requests
+# ScreenPages use enigma2 standard functionality to bring contents to a webfrontend
+#
+# Like Skins a ScreenPage can consist of several Elements and Converters
 
-       def getServiceList(self, sRef):
-               self["ServiceList"].root = sRef
-
-class MovieWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               from Components.MovieList import MovieList
-               from Tools.Directories import resolveFilename, SCOPE_HDD
-               movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
-               self["MovieList"] = Movie(session, movielist, func=Movie.LIST)
-               self["MovieFileDel"] = Movie(session, movielist, func=Movie.DEL)
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-
-class MediaPlayerWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["FileList"] = MP(session, func=MP.LIST)
-               self["PlayFile"] = MP(session, func=MP.PLAY)
-               self["Command"] = MP(session, func=MP.COMMAND)
-               self["WritePlaylist"] = MP(session, func=MP.WRITEPLAYLIST)
-
-class AutoTimerWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["AutoTimerList"] = AT(session, func=AT.LIST)
-               self["AutoTimerWrite"] = AT(session, func=AT.WRITE)
-
-class TimerWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["TimerList"] = Timer(session, func=Timer.LIST)
-               self["TimerAddEventID"] = Timer(session, func=Timer.ADDBYID)
-               self["TimerAdd"] = Timer(session, func=Timer.ADD)
-               self["TimerDel"] = Timer(session, func=Timer.DEL)
-               self["TimerChange"] = Timer(session, func=Timer.CHANGE)
-               self["TimerListWrite"] = Timer(session, func=Timer.WRITE)
-               self["TVBrowser"] = Timer(session, func=Timer.TVBROWSER)
-               self["RecordNow"] = Timer(session, func=Timer.RECNOW)
-               self["TimerCleanup"] = Timer(session, func=Timer.CLEANUP)
-
-class RemoteWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["RemoteControl"] = RemoteControl(session)
-
-class PowerWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["PowerState"] = PowerState(session)
-
-class ParentControlWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["ParentControlList"] = ParentControl(session)
-
-class WAPWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["WAPFillOptionListYear"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListDay"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListMonth"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListShour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListSmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListEhour"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-               self["WAPFillOptionListEmin"] = WAPfunctions(session, func=WAPfunctions.LISTTIME)
-
-               self["WAPFillOptionListRecord"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
-               self["WAPFillOptionListAfterEvent"] = WAPfunctions(session, func=WAPfunctions.OPTIONLIST)
-
-               self["WAPFillValueName"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
-               self["WAPFillValueDescr"] = WAPfunctions(session, func=WAPfunctions.FILLVALUE)
-               self["WAPFillLocation"] = WAPfunctions(session, func=WAPfunctions.LOCATIONLIST)
-               self["WAPFillTags"] = WAPfunctions(session, func=WAPfunctions.TAGLIST)
-
-               self["WAPFillOptionListRepeated"] = WAPfunctions(session, func=WAPfunctions.REPEATED)
-               self["WAPServiceList"] = WAPfunctions(session, func=WAPfunctions.SERVICELIST)
-
-               self["WAPdeleteOldOnSave"] = WAPfunctions(session, func=WAPfunctions.DELETEOLD)
-
-streamingScreens = []
-
-class StreamingWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               from Components.Sources.StreamService import StreamService
-               self["StreamService"] = StreamService(self.session.nav)         
-               streamingScreens.append(self)
-               self.screenIndex = len(streamingScreens) - 1
-       
-       def getRecordService(self):
-               if self.has_key("StreamService"):
-                       return self["StreamService"].getService()
-               return None
-       
-       def getRecordServiceRef(self):
-               if self.has_key("StreamService"):
-                       return self["StreamService"].ref
-               return None
-
-class M3UStreamingWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               from Components.Sources.StaticText import StaticText
-               from Components.Sources.Config import Config
-               from Components.config import config
-               self["ref"] = StaticText()
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-
-class M3UStreamingCurrentServiceWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               self["CurrentService"] = CurrentService(session)
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-
-class TsM3U(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               from Components.Sources.StaticText import StaticText
-               from Components.Sources.Config import Config
-               from Components.config import config
-               self["file"] = StaticText()
-               self["localip"] = RequestData(request, what=RequestData.HOST)
-
-class RestartWebScreen(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               import plugin
-               plugin.restartWebserver(session)
-
-class GetPid(WebScreen):
-       def __init__(self, session, request):
-                WebScreen.__init__(self, session, request)
-                from Components.Sources.StaticText import StaticText
-                from enigma import iServiceInformation
-                pids = self.session.nav.getCurrentService()
-                if pids is not None:
-                       pidinfo = pids.info()
-                       VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
-                       APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
-                       PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
-                       self["pids"] = StaticText("%s,%s,%s" % (PPID.lstrip("0x"), VPID.lstrip("0x"), APID.lstrip("0x")))
-                else:
-                       self["pids"] = StaticText("0x,0x,0x")
-
-                self["localip"] = RequestData(request, what=RequestData.HOST)
-
-class DeviceInfo(WebScreen):
-       def __init__(self, session, request):
-               WebScreen.__init__(self, session, request)
-               from WebComponents.Sources.Network import Network
-               from WebComponents.Sources.Hdd import Hdd
-               from WebComponents.Sources.Frontend import Frontend             
-               from Components.config import config
-               from Components.About import about
-               from Components.Sources.StaticText import StaticText
-               from Tools.DreamboxHardware import getFPVersion
-               from Tools.HardwareInfo import HardwareInfo
-               
-               hw = HardwareInfo()
-               
-               self["Network"] = Network()
-               self["Hdd"] = Hdd()
-               self["Frontends"] = Frontend()                          
-               self["EnigmaVersion"] = StaticText(about.getEnigmaVersionString())
-               self["ImageVersion"] = StaticText(about.getVersionString())
-               self["WebIfVersion"] = StaticText(config.plugins.Webinterface.version.value)
-               self["FpVersion"] = StaticText(str(getFPVersion()))
-               self["DeviceName"] = StaticText(hw.get_device_name())
-               
-# implements the 'render'-call.
-# this will act as a downstream_element, like a renderer.
+#===============================================================================
+# OneTimeElement
+#
+# This is the Standard Element for Rendering a "standard" WebElement
+#===============================================================================
 class OneTimeElement(Element):
        def __init__(self, id):
                Element.__init__(self)
                self.source_id = id
 
-       # CHECKME: is this ok performance-wise?
        def handleCommand(self, args):
-               if self.source_id.find(",") >= 0:
+               if ',' in self.source_id:
                        paramlist = self.source_id.split(",")
                        list = {}
                        for key in paramlist:
-                               arg = args.get(key, [])
-                               if len(arg) == 0:
+                               arg = args.get(key, ())
+                               Len = len(arg)
+                               if Len == 0:
                                        list[key] = None
-                               elif len(arg) == 1:
+                               elif Len == 1:
                                        list[key] = "".join(arg)
-                               elif len(arg) == 2:
+                               elif Len == 2:
                                        list[key] = arg[0]
                        self.source.handleCommand(list)
                else:
-                       for c in args.get(self.source_id, []):
+                       for c in args.get(self.source_id, ()):
                                self.source.handleCommand(c)
 
-       def render(self, stream):
+       def render(self, request):
                t = self.source.getHTML(self.source_id)
-               stream.write(t)
+               request.write(t)
 
        def execBegin(self):
                self.suspended = False
@@ -397,67 +78,112 @@ class OneTimeElement(Element):
        def destroy(self):
                pass
 
+#===============================================================================
+# MacroElement
+#
+# A MacroElement helps using OneTimeElements inside a (Simple)ListFiller Loop
+#===============================================================================
 class MacroElement(OneTimeElement):
        def __init__(self, id, macro_dict, macro_name):
                OneTimeElement.__init__(self, id)
                self.macro_dict = macro_dict
                self.macro_name = macro_name
 
-       def render(self, stream):
+       def render(self, request):
                self.macro_dict[self.macro_name] = self.source.getHTML(self.source_id)
 
+#===============================================================================
+# StreamingElement
+#
+# In difference to an OneTimeElement a StreamingElement sends an ongoing Stream
+# of Data. The end of the Streaming is usually when the client disconnects
+#===============================================================================
 class StreamingElement(OneTimeElement):
        def __init__(self, id):
                OneTimeElement.__init__(self, id)
-               self.stream = None
+               self.request = None
 
        def changed(self, what):
-               if self.stream:
-                       self.render(self.stream)
+               if self.request:
+                       self.render(self.request)
 
-       def setStream(self, stream):
-               self.stream = stream
+       def setRequest(self, request):
+               self.request = request
 
+#===============================================================================
+# ListItem
+#
 # a to-be-filled list item
+#===============================================================================
 class ListItem:
        def __init__(self, name, filternum):
                self.name = name
                self.filternum = filternum
 
+#===============================================================================
+# ListMacroItem
+#
+# MacroItem inside a (Simple)ListFiller
+#===============================================================================
 class ListMacroItem:
        def __init__(self, macrodict, macroname):
                self.macrodict = macrodict
                self.macroname = macroname
 
+
+#===============================================================================
+# TextToHTML
+#
+# Returns the String as is
+#===============================================================================
 class TextToHTML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return self.source.text # encode & etc. here!
+               return self.source.text.replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") # encode & etc. here!
 
+#===============================================================================
+# TextToXML
+#
+# Escapes the given Text to be XML conform
+#===============================================================================
 class TextToXML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return escape_xml(self.source.text).replace("\x19", "").replace("\x1c", "").replace("\x1e", "")
+               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")
 
+#===============================================================================
+# TextToURL
+#
+# Escapes the given Text so it can be used inside a URL
+#===============================================================================
 class TextToURL(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return self.source.text.replace(" ", "%20")
+               return self.source.text.replace(" ", "%20").replace("+", "%2b").replace("&", "%26").replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8")
 
+#===============================================================================
+# ReturnEmptyXML
+# 
+# Returns a XML only consisting of <rootElement />
+#===============================================================================
 class ReturnEmptyXML(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
 
        def getHTML(self, id):
-               return "<rootElement></rootElement>"
+               return "<rootElement />"
 
-# a null-output. Useful if you only want to issue a command.
+#===============================================================================
+# Null
+# Return simply NOTHING
+# Useful if you only want to issue a command.
+#===============================================================================
 class Null(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -465,6 +191,12 @@ class Null(Converter):
        def getHTML(self, id):
                return ""
 
+
+#===============================================================================
+# JavascriptUpdate
+#
+# Transforms a string into a javascript update pattern
+#===============================================================================
 class JavascriptUpdate(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -474,15 +206,18 @@ class JavascriptUpdate(Converter):
                #                all other will replace this in JS
                return '<script>parent.set("%s", "%s");</script>\n' % (id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '&deg;'))
 
-# the performant 'one-dimensonial listfiller' engine (podlfe)
+#===============================================================================
+# SimpleListFiller
+#
+# The performant 'one-dimensonial listfiller' engine (podlfe)
+#===============================================================================
 class SimpleListFiller(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
                
        def getText(self):
                l = self.source.simplelist
-               conv_args = self.converter_arguments
-               print "[webif.py] conv_args: " % conv_args
+               conv_args = self.converter_arguments            
                
                list = [ ]
                for element in conv_args:
@@ -498,17 +233,23 @@ class SimpleListFiller(Converter):
                strlist = [ ]
                append = strlist.append
                for item in l:
+                       if item is None:
+                               item = ""
+                               
                        for (element, filternum) in list:
+                               #filter out "non-displayable" Characters - at the very end, do it the hard way...
+                               item = str(item).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
+                               
                                if not filternum:
                                        append(element)
                                elif filternum == 2:
-                                       append(str(item).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
-                               elif filternum == 3:
-                                       append(escape_xml(str(item)))
+                                       append(item.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
+                               elif filternum == 3:                                    
+                                       append(escape_xml(item))
                                elif filternum == 4:
-                                       append(str(item).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
+                                       append(item.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
                                elif filternum == 5:
-                                       append(quote(str(item)))
+                                       append(quote(item))
                                elif filternum == 6:
                                        time = parseint(item) or 0
                                        t = localtime(time)
@@ -518,16 +259,16 @@ class SimpleListFiller(Converter):
                                        t = localtime(time)
                                        append("%d min" % (time / 60))
                                else:
-                                       append(str(item))
+                                       append(item)
                # (this will be done in c++ later!)
 
                return ''.join(strlist)         
        
        text = property(getText)
-               
-                               
-
+                       
+#===============================================================================
 # the performant 'listfiller'-engine (plfe)
+#===============================================================================
 class ListFiller(Converter):
        def __init__(self, arg):
                Converter.__init__(self, arg)
@@ -555,35 +296,52 @@ class ListFiller(Converter):
                strlist = [ ]
                append = strlist.append
                for item in l:
-                       for (element, filternum) in lutlist:
+                       for (element, filternum) in lutlist:                    
+                               #None becomes ""
+                               curitem = ""
+                               if filternum:
+                                       #filter out "non-displayable" Characters - at the very end, do it the hard way...
+                                       curitem = str(item[element]).replace('\xc2\x86', '').replace('\xc2\x87', '').replace("\x19", "").replace("\x1c", "").replace("\x1e", "").decode("utf-8", "ignore").encode("utf-8")
+                                       if curitem is None:
+                                               curitem = ""
+                               else:
+                                       if element is None:
+                                               element = ""
+                                               
                                if not filternum:
                                        append(element)
                                elif filternum == 2:
-                                       append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
+                                       append(curitem.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
                                elif filternum == 3:
-                                       append(escape_xml(str(item[element])))
+                                       append( escape_xml( curitem ))
                                elif filternum == 4:
-                                       append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
+                                       append(curitem.replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
                                elif filternum == 5:
-                                       append(quote(str(item[element])))
+                                       append(quote(curitem))
                                elif filternum == 6:
                                        from time import localtime
-                                       time = int(float(item[element])) or 0
+                                       time = int(float(curitem)) or 0
                                        t = localtime(time)
                                        append("%02d:%02d" % (t.tm_hour, t.tm_min))
                                elif filternum == 7:
                                        from time import localtime
-                                       time = int(float(item[element])) or 0
+                                       time = int(float(curitem)) or 0
                                        t = localtime(time)
                                        append("%d min" % (time / 60))                                  
                                else:
-                                       append(str(item[element]))
+                                       append(curitem)
                # (this will be done in c++ later!)
 
                return ''.join(strlist)
 
        text = property(getText)
 
+#===============================================================================
+# webifHandler
+#
+# Handles the Content of a Web-Request
+# It looks up the source, instantiates the Element and Calls the Converter
+#===============================================================================
 class webifHandler(ContentHandler):
        def __init__(self, session, request):
                self.res = [ ]
@@ -650,9 +408,9 @@ class webifHandler(ContentHandler):
                        self.converter = eval(ctype[4:])
                else:
                        try:
-                               self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
+                               self.converter = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
                        except ImportError:
-                               self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
+                               self.converter = my_import('.'.join(("Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype))).__dict__.get(ctype)
                self.sub = [ ]
 
        def end_convert(self):
@@ -680,11 +438,8 @@ class webifHandler(ContentHandler):
                if name[:3] == "e2:":
                        self.mode += 1
 
-               tag = [' %s="%s"' % (key, val) for (key, val) in attrs.items()]
-               tag.insert(0, name)
-               tag.insert(0, '<')
-               tag.append('>')
-               tag = ''.join(tag)#.encode('utf-8')
+               tag = '<' + name + ''.join([' %s="%s"' % x for x in attrs.items()]) + '>'
+               #tag = tag.encode('utf-8')
 
                if self.mode == 0:
                        self.res.append(tag)
@@ -743,11 +498,17 @@ class webifHandler(ContentHandler):
                        screen.doClose()
                self.screens = [ ]
 
-def renderPage(stream, path, req, session):
+#===============================================================================
+# renderPage
+#
+# Creates the Handler for a Request and calls it
+# Also ensures that the Handler is finished after the Request is done
+#===============================================================================
+def renderPage(request, path, session):
        # read in the template, create required screens
        # we don't have persistense yet.
        # if we had, this first part would only be done once.
-       handler = webifHandler(session, req)
+       handler = webifHandler(session, request)
        parser = make_parser()
        parser.setFeature(feature_namespaces, 0)
        parser.setContentHandler(handler)
@@ -759,26 +520,21 @@ def renderPage(stream, path, req, session):
        # first, apply "commands" (aka. URL argument)
        for x in handler.res:
                if isinstance(x, Element):
-                       x.handleCommand(req.args)
+                       x.handleCommand(request.args)
 
        handler.execBegin()
 
        # now, we have a list with static texts mixed
        # with non-static Elements.
-       # flatten this list, write into the stream.
+       # flatten this list, write into the request.
        for x in handler.res:
                if isinstance(x, Element):
                        if isinstance(x, StreamingElement):
                                finish = False
-                               x.setStream(stream)
-                       x.render(stream)
+                               x.setRequest(request)
+                       x.render(request)
                else:
-                       stream.write(str(x))
-
-       def ping(s):
-               from twisted.internet import reactor
-               s.write("\n");
-               reactor.callLater(3, ping, s)
+                       request.write(str(x))
 
        # if we met a "StreamingElement", there is at least one
        # element which wants to output data more than once,
@@ -786,18 +542,24 @@ def renderPage(stream, path, req, session):
        # in this case, don't finish yet, don't cleanup yet,
        # but instead do that when the client disconnects.
        if finish:
-               streamFinish(handler, stream)
-       else:
-               # ok.
-               # you *need* something which constantly sends something in a regular interval,
-               # in order to detect disconnected clients.
-               # i agree that this "ping" sucks terrible, so better be sure to have something
-               # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
-               ping(stream)
-               stream.closed_callback = lambda : streamFinish(handler, stream)
-
-def streamFinish(handler, stream):
+               requestFinish(handler, request)
+       
+       else:   
+               def requestFinishDeferred(nothing, handler, request):
+                       from twisted.internet import reactor
+                       reactor.callLater(0, requestFinish, handler, request)                           
+               
+               d = request.notifyFinish()
+
+               d.addBoth( requestFinishDeferred, handler, request )
+                                                       
+#===============================================================================
+# requestFinish
+#
+# This has to be/is called at the end of every ScreenPage-based Request
+#===============================================================================
+def requestFinish(handler, request):
        handler.cleanup()
-       stream.finish()
+       request.finish()        
+       
        del handler
-       del stream