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