NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

htmlwriter.py (190686B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\script\util\htmlwriter.py
      2 from __future__ import print_function
      3 import sys
      4 import time
      5 from datetime import datetime
      6 import blue
      7 import copy
      8 import eve.common.lib.appConst as const
      9 import functools
     10 import json
     11 import localization
     12 import log
     13 import numbers
     14 import random
     15 import re
     16 import scriber
     17 import types
     18 import urllib
     19 import urllib2
     20 from carbon.common.lib import iocp
     21 from carbon.common.script.net import http, machoNet
     22 from carbon.common.script.util.format import FmtDate, FmtDateEng
     23 from datetimeutils import deltastr
     24 from eveexceptions import RoleNotAssignedError
     25 from eveprefs import prefs, boot
     26 from localization.const import LANGUAGE_BY_LANGUAGE_ID
     27 from stringutil import strx
     28 from urllib import unquote_plus
     29 from uuid import uuid4
     30 scriber.init([blue.paths.ResolvePath('res:/scriber')])
     31 reserved = {';': '%3B',
     32  '/': '%2F',
     33  '?': '%3F',
     34  ':': '%3A',
     35  '@': '%40',
     36  '&': '%26',
     37  '=': '%3D',
     38  '+': '%2B',
     39  '$': '%24',
     40  ',': '%2C',
     41  ' ': '%20',
     42  '#': '%23'}
     43 
     44 def multiple_replace(text, adict):
     45     rx = re.compile('|'.join(map(re.escape, adict)))
     46 
     47     def one_xlat(match):
     48         return adict[match.group(0)]
     49 
     50     return rx.sub(one_xlat, text)
     51 
     52 
     53 def multiple_replacer(adict):
     54     rx = re.compile('|'.join(map(re.escape, adict)))
     55 
     56     def one_xlat(match):
     57         return adict[match.group(0)]
     58 
     59     def replacer(text):
     60         return rx.sub(one_xlat, text)
     61 
     62     return replacer
     63 
     64 
     65 swing = multiple_replacer({'&': '&',
     66  '<': '&lt;',
     67  '>': '&gt;'})
     68 
     69 def Swing(*args, **kwargs):
     70     return swing(*args, **kwargs)
     71 
     72 
     73 def Canonicalize(text):
     74     text = text.replace('%', '%25')
     75     for k, v in reserved.iteritems():
     76         text = text.replace(k, v)
     77 
     78     return text
     79 
     80 
     81 def HTMLEncode(text):
     82     t = ''
     83     text = unicode(text)
     84     for i in range(len(text)):
     85         tord = ord(text[i])
     86         if tord == 38:
     87             testStr = text[i + 1:i + text[i:].find(';')]
     88             if testStr in ('quot', 'amp', 'lt', 'gt'):
     89                 t = t + text[i]
     90                 continue
     91         if tord == 34:
     92             t = t + '&quot;'
     93         elif tord == 38:
     94             t = t + '&amp;'
     95         elif tord == 60:
     96             t = t + '&lt;'
     97         elif tord == 62:
     98             t = t + '&gt;'
     99         else:
    100             t = t + text[i]
    101 
    102     return t
    103 
    104 
    105 def Link(url, title, args = None, props = '', target = None, bolConfirm = 0):
    106     if title is None:
    107         return ''
    108     if bolConfirm:
    109         onClick = ' onClick="return confirm(\'Are you sure?\')"'
    110     else:
    111         onClick = ''
    112     if args:
    113         realArgs = args
    114         if isinstance(args, list):
    115             realArgs = {}
    116             for a in args:
    117                 realArgs.update(a)
    118 
    119         stuff = url.split('#')
    120         if len(stuff) > 1:
    121             url = stuff[0]
    122         url += '?'
    123         for k, v in realArgs.iteritems():
    124             if isinstance(v, list):
    125                 for i in v:
    126                     url += unicode(k) + '=' + unicode(i) + '&'
    127 
    128             else:
    129                 url += unicode(k) + '=' + Canonicalize(unicode(v)) + '&'
    130 
    131         if len(stuff) > 1:
    132             url += '#' + stuff[1]
    133     if '&' in url:
    134         url = url.replace('&', '&amp;')
    135     if iocp.UsingHTTPS():
    136         url = ConvertLinkForHTTPS(url)
    137     if target:
    138         return '<a href="%s" %s target="%s"%s>%s</a>' % (url,
    139          props,
    140          target,
    141          onClick,
    142          title)
    143     else:
    144         return '<a href="%s" %s%s>%s</a>' % (url,
    145          props,
    146          onClick,
    147          title)
    148 
    149 
    150 def Button(url, title, args = None, color = 'black', bolConfirm = 0):
    151     props = 'class="button button%s"' % color
    152     return Link(url, title, args, props, None, bolConfirm)
    153 
    154 
    155 def ConvertLinkForHTTPS(url):
    156     if url.startswith('http://'):
    157         host = url[7:].split('/', 1)[0]
    158         if ':' in host:
    159             tcpProxy = sm.services.get('tcpRawProxyService', None)
    160             if tcpProxy:
    161                 tunnels = tcpProxy.GetTunnelsByType('esp')
    162                 if host in (tunnelHost for name, port, tunnelHost in tunnels):
    163                     return url.replace('http://', 'https://')
    164                 ourHostIP, tundict = tcpProxy.GetESPTunnelingAddressByNodeID()
    165                 potentialTunnelingAddresses = (ourHostIP + ':' + str(port) for port in tundict.itervalues())
    166                 if host in potentialTunnelingAddresses:
    167                     return url.replace('http://', 'https://')
    168             machoNet = sm.GetService('machoNet')
    169             socket = machoNet.GetTransport('tcp:raw:http')
    170             espAddress = socket.address
    171             if host == espAddress:
    172                 return url.replace('http://', 'https://')
    173     return url
    174 
    175 
    176 def Image(url, props = 'border=0'):
    177     return '<img src="%s" %s />' % (url, props)
    178 
    179 
    180 def Table(headers, lines, props = 'border=0', rowprops = 'valign=top'):
    181     tbl = ''
    182     tbl = tbl + '<table %s>\n' % (props,)
    183     if len(headers):
    184         tbl = tbl + '<tr>'
    185         for i in headers:
    186             tbl = tbl + '<td><b>%s</b></td>' % (i,)
    187 
    188         tbl = tbl + '</tr>\n'
    189     for row in lines:
    190         tbl = tbl + '<tr>'
    191         for col in row:
    192             tbl = tbl + '<td %s>%s</td>' % (rowprops, col)
    193 
    194         tbl = tbl + '</tr>\n'
    195 
    196     tbl = tbl + '</table>\n'
    197     return tbl
    198 
    199 
    200 class UnicodeMemStream(object):
    201     __slots__ = ['ms']
    202 
    203     def __init__(self):
    204         self.ms = blue.MemStream()
    205 
    206     def Write(self, s):
    207         if s is not None:
    208             u = unicode(s)
    209             b = buffer(u)
    210             self.ms.Write(b)
    211 
    212     def Read(self):
    213         return self.ms.Read().decode('utf-16', errors='replace')
    214 
    215     def getpos(self):
    216         return self.ms.pos / 2
    217 
    218     def getsize(self):
    219         return self.ms.size / 2
    220 
    221     size = property(getsize)
    222     pos = property(getpos)
    223 
    224     def Seek(self, x):
    225         self.ms.Seek(2 * x)
    226 
    227 
    228 def JsonFunction(func):
    229 
    230     @functools.wraps(func)
    231     def Wrapper(*args, **kwargs):
    232         args[0].Write(json.dumps(func(*args, **kwargs)))
    233 
    234     return Wrapper
    235 
    236 
    237 def HtmlFunction(func):
    238 
    239     @functools.wraps(func)
    240     def Wrapper(*args, **kwargs):
    241         args[0].Write(func(*args, **kwargs))
    242 
    243     return Wrapper
    244 
    245 
    246 def XmlFunction(func):
    247 
    248     @functools.wraps(func)
    249     def Wrapper(*args, **kwargs):
    250         response = args[0].response
    251         headers = [('Content-type', 'text/xml'), ('Content-Disposition', 'attachment; filename=' + 'file.xml'), ('Cache-Control', 'no-cache, must-revalidate, max_age=0, no-store , private')]
    252         for h in headers:
    253             if hasattr(response, 'AddHeader'):
    254                 response.AddHeader(h[0], h[1])
    255             else:
    256                 http.AddHeader(h[0], h[1])
    257 
    258         response.WriteBinary(func(*args, **kwargs))
    259         response.streamMode = 'b'
    260         if hasattr(response, 'Flush'):
    261             response.Flush()
    262 
    263     return Wrapper
    264 
    265 
    266 def StaticImage(func):
    267 
    268     @functools.wraps(func)
    269     def Wrapper(*args, **kwargs):
    270         fileName = func(*args, **kwargs)
    271         fileName = '%swwwroot/%s' % (blue.paths.ResolvePath(u'script:/').replace('eve/server', 'carbon/common'), fileName)
    272         response = args[0].response
    273         if hasattr(response, 'AddHeader'):
    274             response.AddHeader('Content-type', 'image/%s' % fileName.split('.')[-1])
    275         else:
    276             http.AddHeader('Content-type', 'image/%s' % fileName.split('.')[-1])
    277         with file(fileName, 'rb') as f:
    278             response.WriteBinary(f.read())
    279             if hasattr(response, 'Flush'):
    280                 response.Flush()
    281 
    282     return Wrapper
    283 
    284 
    285 def ZipFunction(func):
    286 
    287     @functools.wraps(func)
    288     def Wrapper(*args, **kwargs):
    289         response = args[0].response
    290         headers = [('Content-type', 'application/octet-stream'), ('Content-Disposition', 'attachment; filename=' + 'file.zip'), ('Cache-Control', 'no-cache, must-revalidate, max_age=0, no-store , private')]
    291         for h in headers:
    292             if hasattr(response, 'AddHeader'):
    293                 response.AddHeader(h[0], h[1])
    294             else:
    295                 http.AddHeader(h[0], h[1])
    296 
    297         response.WriteBinary(func(*args, **kwargs))
    298         response.streamMode = 'b'
    299         if hasattr(response, 'Flush'):
    300             response.Flush()
    301 
    302     return Wrapper
    303 
    304 
    305 def GenerateCSRFToken(request):
    306     if not hasattr(request.session, 'CSRFToken'):
    307         request.session.CSRFToken = str(uuid4())
    308     return request.session.CSRFToken
    309 
    310 
    311 def CSRFFormField(request):
    312     return '<input type="hidden" name="csrf_token" autocomplete="off" value="%s" />' % GenerateCSRFToken(request)
    313 
    314 
    315 def WaitingMessage(waitingMessage):
    316     return '<input type="hidden" name="__waiting_message__" value="%s" />' % waitingMessage
    317 
    318 
    319 def OutlineDictTable(lines):
    320     s = UnicodeMemStream()
    321     s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
    322     s.Write('<tr>\n')
    323     s.Write('<td class="tableOutline" valign="top">\n')
    324     s.Write('<table border="0" cellpadding="7" cellspacing="1" width="100%">\n')
    325     s.Write('<tr>\n')
    326     s.Write('<tr>\n')
    327     headers = []
    328     if len(lines) == 0:
    329         headers = lines.iterkeys()
    330     else:
    331         headers = lines[0].iterkeys()
    332     for i in headers:
    333         s.Write('    <td class="tableHeader"><b>%s</b></td>\n' % header)
    334 
    335     s.Write('</tr>\n')
    336     for i in lines:
    337         for row in i.itervalues():
    338             s.Write('<tr>\n')
    339             for col in row:
    340                 s.Write('    <td class="tC" valign=top>%s</td>\n' % unicode(col))
    341 
    342             s.Write('</tr>\n')
    343 
    344     s.Write('</tr>\n')
    345     s.Write('</table>\n')
    346     s.Write('</td>\n')
    347     s.Write('</tr>\n')
    348     s.Write('</table>\n')
    349     s.Seek(0)
    350     return s.Read()
    351 
    352 
    353 def SortOSST(i, a, b):
    354     try:
    355         ai = long(a[i])
    356         bi = long(b[i])
    357         return [1, -1][ai < bi]
    358     except:
    359         sys.exc_clear()
    360         return [1, -1][a[i].lower() < b[i].lower()]
    361 
    362 
    363 def OutlineSortedSegmentedTable(lname, ldict, hd, li, idstr, idx, num, displayNameIDX = None, firstIDX = 0, firstNUM = None):
    364     li.sort(lambda a, b, d = displayNameIDX: SortOSST(d, a, b))
    365     return OutlineSegmentedTable(lname, ldict, hd, li, idstr, idx, num, displayNameIDX, firstIDX, firstNUM)
    366 
    367 
    368 def OutlineSegmentedTable(lname, ldict, hd, li, idstr, idx, num, displayNameIDX = None, firstIDX = 0, firstNUM = None):
    369     nTotal = len(li)
    370     if firstNUM is None:
    371         if nTotal > 10000:
    372             firstNUM = 1000
    373         elif nTotal > 1000:
    374             firstNUM = 250
    375         elif nTotal > 500:
    376             firstNUM = 100
    377         elif nTotal > 250:
    378             firstNUM = 50
    379         elif nTotal > 100:
    380             firstNUM = 25
    381         else:
    382             firstNUM = 10
    383     segmentIDstring = Swing(lname + '/' + ldict['action'] + '/' + str(hd))
    384     if idx is not None and idstr == segmentIDstring:
    385         session.SetSessionVariable(segmentIDstring + '.idx', idx)
    386     else:
    387         idx = firstIDX
    388     if num is not None and idstr == segmentIDstring:
    389         session.SetSessionVariable(segmentIDstring + '.num', num)
    390     else:
    391         num = firstNUM
    392     if displayNameIDX is not None and idstr == segmentIDstring:
    393         session.SetSessionVariable(segmentIDstring + '.what', displayNameIDX)
    394     elif displayNameIDX is None:
    395         displayNameIDX = 0
    396     nStart, nCount, nWhat = session.GetSessionVariable(segmentIDstring + '.idx', firstIDX), session.GetSessionVariable(segmentIDstring + '.num', firstNUM), session.GetSessionVariable(segmentIDstring + '.what', displayNameIDX)
    397     if li and nWhat >= len(li[0]):
    398         nWhat = len(li[0]) - 1
    399     elif nWhat < 0:
    400         nWhat = 0
    401     l = ''
    402     ldict = copy.deepcopy(ldict)
    403     ldict['segmentedTableID'] = segmentIDstring
    404     ldict['segmentedTableNUM'] = nCount
    405     ldict['segmentedTableWHAT'] = nWhat
    406     for i in range(0, nTotal, nCount):
    407         if nStart < i or nStart >= i + nCount:
    408             ldict['segmentedTableIDX'] = i
    409             t = i + nCount - 1
    410             if t >= len(li):
    411                 t = len(li) - 1
    412             l += Link(lname, Swing(str(li[i][nWhat])) + '..' + Swing(str(li[t][nWhat])), ldict) + '&nbsp;&nbsp;&nbsp;'
    413 
    414     li = li[nStart:nStart + nCount]
    415     ldict['segmentedTableIDX'] = nStart
    416     for i in range(len(hd)):
    417         if i != nWhat:
    418             ldict['segmentedTableWHAT'] = i
    419             hd[i] = Link(lname, hd[i], ldict)
    420 
    421     if l:
    422         if li:
    423             current = '<br/>Now viewing: ' + Swing(str(li[0][nWhat])) + '..' + Swing(str(li[-1][nWhat]))
    424         else:
    425             current = ''
    426         selected = '<br/>Select: ' + l + '<br/>'
    427         return GetTable(hd, li) + current + selected
    428     else:
    429         return GetTable(hd, li)
    430 
    431 
    432 def RemoveHtmlTag(tagName = 'font', stringWithTags = None):
    433     tagCompiled = re.compile('(\\<%(tag)s[^\\>]+\\>)|(</%(tag)s>)' % {'tag': tagName})
    434     while True:
    435         match = tagCompiled.search(stringWithTags)
    436         if match is None:
    437             break
    438         stringWithTags = stringWithTags.replace(match.group(), '')
    439 
    440     return stringWithTags
    441 
    442 
    443 def _GetElementBegin(elementName, attributes):
    444     return '<%s%s>' % (elementName, attributes)
    445 
    446 
    447 def _GetElementEnd(elementName):
    448     return '</%s>' % elementName
    449 
    450 
    451 def _GetElement(ctrlName, ctrlValue, ctrlID = None, className = None, style = None):
    452     controlID = CheckValue('id', ctrlID)
    453     controlName = CheckValue('id', ctrlID)
    454     className = CheckValue('class', className)
    455     style = CheckValue('style', style)
    456     return '\n'.join([_GetElementBegin(ctrlName, '%s%s%s%s' % (controlID,
    457       controlName,
    458       className,
    459       style)), unicode(ctrlValue), _GetElementEnd(ctrlName)])
    460 
    461 
    462 def _GetElements(elements = None, ctrlName = 'div', ctrlID = None, className = None, style = None, title = None, dataBind = None):
    463     ctrlID = CheckValue('id', ctrlID)
    464     className = CheckValue('class', className)
    465     style = CheckValue('style', style)
    466     title = CheckValue('title', title)
    467     dataBind = CheckValue('data-bind', dataBind)
    468     res = UnicodeMemStream()
    469     res.Write(_GetElementBegin(ctrlName, '%s%s%s%s%s' % (ctrlID,
    470      className,
    471      style,
    472      title,
    473      dataBind)))
    474     if isinstance(elements, list):
    475         if elements and len(elements) > 0:
    476             for n in elements:
    477                 res.Write(unicode(n))
    478 
    479     elif elements is not None:
    480         res.Write(unicode(elements))
    481     res.Write(_GetElementEnd(ctrlName))
    482     res.Seek(0)
    483     return res.Read()
    484 
    485 
    486 def GetA(innerText, href, ctrlID = None, className = None, style = None, onClick = None, title = None, rel = None, rev = None, target = None):
    487     ctrlID = CheckValue('id', ctrlID)
    488     className = CheckValue('class', className)
    489     style = CheckValue('style', style)
    490     onClick = CheckValue('onclick', onClick)
    491     title = CheckValue('title', HTMLEncode(title) if title is not None and len(title) > 0 else '')
    492     rel = CheckValue('rel', rel)
    493     rev = CheckValue('rev', rev)
    494     target = CheckValue('target', target)
    495     return '<a href="%s"%s%s%s%s%s%s%s%s>%s</a>' % (href,
    496      ctrlID,
    497      className,
    498      style,
    499      onClick,
    500      title,
    501      rel,
    502      rev,
    503      target,
    504      innerText)
    505 
    506 
    507 def GetInput(ctrlID, labelCaption, className = None, minLength = None, width = None, value = None, title = ''):
    508     minLength = CheckValue('minlength', minLength)
    509     if width:
    510         width = ' style="width:%spx"' % width
    511     else:
    512         width = ''
    513     className = CheckValue('class', className)
    514     value = CheckValue('value', value)
    515     title = CheckValue('title', title)
    516     res = []
    517     if labelCaption is not None:
    518         res.append('<label class="form-label" for="%s">%s</label>' % (ctrlID, labelCaption))
    519     res.append('<input id="%s" name="%s"%s%s%s%s%s/>' % (ctrlID,
    520      ctrlID,
    521      className,
    522      minLength,
    523      width,
    524      value,
    525      title))
    526     return '\n'.join(res)
    527 
    528 
    529 def GetSelect(elements, controlID, labelCaption = None, elementClass = None, selectedValue = None, attributes = ''):
    530     elementClass = CheckValue('class', elementClass)
    531     if controlID:
    532         controlID = ' id="%s" name="%s"' % (controlID, controlID)
    533     opt0 = lambda row: (' selected="selected"' if row[0] == selectedValue else '')
    534     opt1 = lambda row: '<option value="%s"%s>%s</option>\n' % (row[0], CheckValue('selected', row[2]) if len(row) > 2 else opt0(row), row[1])
    535     res = []
    536     if labelCaption is not None:
    537         res.append('<label class="form-label" for="%s">%s</label>' % (controlID, labelCaption))
    538     res.append(_GetElementBegin('select', '%s%s %s' % (controlID, elementClass, attributes)))
    539     res.append(''.join(map(opt1, elements.items() if isinstance(elements, dict) else elements)))
    540     res.append(_GetElementEnd('select'))
    541     return ''.join(res)
    542 
    543 
    544 def GetSpan(elements, ctrlID = None, className = None, style = None, title = None, dataBind = None):
    545     return _GetElements(elements, 'span', ctrlID, className, style, title, dataBind)
    546 
    547 
    548 def GetImage(src, ctrlID = None, className = None, style = None, title = None, rel = None):
    549     ctrlID = CheckValue('id', ctrlID)
    550     className = CheckValue('class', className)
    551     style = CheckValue('style', style)
    552     title = CheckValue('title', title)
    553     rel = CheckValue('rel', rel)
    554     return '<img src="%s" %s%s%s%s%s/>' % (src,
    555      ctrlID,
    556      className,
    557      style,
    558      title,
    559      rel)
    560 
    561 
    562 def GetTextArea(ctrlID, labelCaption, className, cols, rows, innerHtml = None):
    563     className = CheckValue('class', className)
    564     cols = CheckValue('cols', cols)
    565     rows = CheckValue('rows', rows)
    566     ctrlFor = CheckValue('for', ctrlID)
    567     ctrlID2 = CheckValue('id', ctrlID)
    568     ctrlName = CheckValue('name', ctrlID)
    569     if innerHtml is None:
    570         innerHtml = ''
    571     lines = []
    572     if labelCaption:
    573         lines.append('<label class="form-label"%(ctrlID)s>%(labelCaption)s</label>' % {'ctrlID': ctrlFor,
    574          'labelCaption': labelCaption})
    575     lines += [_GetElementBegin('textarea', '%(ctrlID)s%(className)s%(cols)s%(rows)s%(ctrlName)s' % {'ctrlID': ctrlID2,
    576       'className': className,
    577       'cols': cols,
    578       'rows': rows,
    579       'ctrlName': ctrlName}), innerHtml, _GetElementEnd('textarea')]
    580     return ''.join(lines)
    581 
    582 
    583 def GetTable(header = None, lines = None, className = 'tablesorter', enableEdit = False, editUrl = None, enableDelete = False, deleteUrl = None, useFilter = False, invertSelection = False, paging = None, customActions = None, serverOrder = False, baseUrl = None, keyFields = None, readOnlyFields = None, showCount = True, cssByRow = None):
    584     elementSep = '<span class="toolbar-separator">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>'
    585     customActions2 = []
    586     if customActions is not None:
    587         for ca in customActions:
    588             customActions2.append('&nbsp;' * 5)
    589             customActions2.append(ca)
    590 
    591     if keyFields is None:
    592         keyFields = []
    593     if readOnlyFields is None:
    594         readOnlyFields = []
    595     className = CheckValue('class', className)
    596     res = UnicodeMemStream()
    597     r = random.randint(1, 1000)
    598     id = 'table_%i' % r
    599     headerIndexControl = []
    600     xLoop = 0
    601     for x in header or []:
    602         if '<' in unicode(x):
    603             headerIndexControl.append(xLoop)
    604         xLoop += 1
    605 
    606     if enableDelete or enableEdit:
    607         headerIndexControl.append(len(header))
    608     if header is not None and len(header) > 0:
    609         res.Write('\n        <script type="text/javascript">\n            $(document).ready(function() {\n                $("#%s").tablesorter(' % id)
    610         if header is not None and serverOrder is True:
    611             res.Write('{\n                    headers: {')
    612             t_res = []
    613             for n in xrange(len(header)):
    614                 t_res.append('%i: {sorter: false}' % n)
    615 
    616             res.Write(', '.join(t_res))
    617             res.Write('}\n}')
    618         elif header is not None and len(headerIndexControl) > 0:
    619             res.Write('{\n                    headers: {')
    620             t_res = []
    621             for n in headerIndexControl:
    622                 t_res.append('%i: {sorter: false}' % n)
    623 
    624             res.Write(', '.join(t_res))
    625             res.Write('}\n}')
    626         res.Write(');\n        });\n        </script>')
    627     ctrlID = ' id="%s"' % id
    628     invertText = '<li><a class="csp-invert button buttongray">Invert</a></li>' if invertSelection else ''
    629     filterBar = '\n        <div class="csp-tool-strip">\n            <a class="button buttonwhite excel" onclick="exportToExcel(\'%(ctrlID)s\');">Excel</a>\n            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n            <input type="text" size="25" maxlength="30" value="" id="filter_%(ctrlID)s" name="filter_%(ctrlID)s"/>\n        </div>\n    ' % {'ctrlID': id}
    630     toolbar = '\n        <div class="csp-tool">\n            <div style="float:left;">\n                %(leftbar)s\n            </div>\n            <div style="float:right;">\n                <div class="csp-tool-strip">\n                    <ul>\n                        %(customActions)s\n                        %(sep1)s\n                        %(paging)s\n                        %(sep2)s\n                        %(invert)s\n                    </ul>\n                </div>\n            </div>\n            <div style="clear:both;"></div>\n        </div>\n        '
    631     res.Write('<div>')
    632     if useFilter or invertSelection:
    633         res.Write('\n            <script type="text/javascript">\n                $(function() {\n                    var $%(ctrlID)s = $("#%(ctrlID)s")\n                    $("#filter_%(ctrlID)s").keyup(function() {\n                      $.uiTableFilter( $%(ctrlID)s, this.value );\n                    });\n                    $(".csp-invert").click(function(){\n                        $.each($("#%(ctrlID)s input").not(":disabled"), function(idx,val) {\n                            if ($(val).is(":visible")) {\n                                val.checked = !val.checked;\n                            }\n                        });\n                    });\n                });\n            </script>\n        ' % {'ctrlID': id})
    634     if useFilter or paging is not None or len(customActions2) > 0:
    635         res.Write(toolbar % {'leftbar': filterBar if useFilter else '',
    636          'invert': invertText,
    637          'paging': '' if paging is None else '<li>%s</li>' % paging,
    638          'customActions': u''.join([ u'<li>%s</li>' % x for x in customActions2 ]),
    639          'sep1': '' if paging is None else '<li>%s</li>' % elementSep,
    640          'sep2': '<li>%s</li>' % elementSep if len(invertText) > 0 else ''})
    641     res.Write('<table %(className)s%(ctrlID)s>' % {'className': className,
    642      'ctrlID': ctrlID})
    643     columnRules = []
    644     if header:
    645         res.Write('<thead><tr>')
    646         h_idx = 0
    647         for h in header or []:
    648             th_style = ''
    649             w = 0
    650             columnRules.append('')
    651             if isinstance(h, list):
    652                 if isinstance(h[1], list):
    653                     w = h[1][0]
    654                     if len(h[1]) > 1:
    655                         columnRules[h_idx] = h[1][1]
    656                 elif isinstance(h[1], str):
    657                     columnRules[h_idx] = h[1]
    658                 elif isinstance(h[1], int):
    659                     w = h[1]
    660                 th_style = ' style="width: %spx"' % w if w > 0 else ''
    661                 h = h[0]
    662             if keyFields is not None and h in keyFields:
    663                 k = ' class="csp-table-key"'
    664             elif readOnlyFields is not None and h in readOnlyFields:
    665                 k = ' class="csp-table-readonly"'
    666             else:
    667                 k = ''
    668             if serverOrder is True:
    669                 res.Write('<th%s%s>%s</a></th>' % (k, th_style, GetA(h, '%s&sortcolumn=%i' % (baseUrl, h_idx))))
    670             else:
    671                 res.Write('<th%s>%s</a></th>' % (th_style, h))
    672             h_idx += 1
    673 
    674         if enableEdit or enableDelete:
    675             res.Write('<th></th>')
    676         res.Write('</tr></thead>')
    677     if lines:
    678         rowNumber = 0
    679         for r in lines:
    680             blue.pyos.BeNice()
    681             if isinstance(cssByRow, dict) and rowNumber in cssByRow:
    682                 rowCss = cssByRow[rowNumber]
    683                 res.Write('<tr style="%s">' % rowCss)
    684             else:
    685                 res.Write('<tr>')
    686             lnr = 0
    687             for c in r:
    688                 td_style = ''
    689                 if len(columnRules) and len(columnRules) > lnr:
    690                     td_style = ' class="csp-%s"' % columnRules[lnr]
    691                 res.Write('<td%s>' % td_style)
    692                 if len(columnRules) and len(columnRules) > lnr and columnRules[lnr] in ('number', 'numberleft'):
    693                     res.Write(AddThousandSeparator(c))
    694                 else:
    695                     res.Write(c)
    696                 res.Write('</td>')
    697                 lnr += 1
    698 
    699             if enableEdit or enableDelete:
    700                 actions = []
    701                 if enableEdit:
    702                     actions.append(GetA('Edit', href='#', className='edit-row', rev=editUrl))
    703                 if enableDelete:
    704                     actions.append(GetA('Delete', href='#', className='delete-row', rev=deleteUrl))
    705                 res.Write('<td>%s</td>' % '&nbsp;|&nbsp;'.join(actions))
    706             res.Write('</tr>')
    707             rowNumber += 1
    708 
    709     else:
    710         res.Write('<tr>')
    711         for h in header or []:
    712             res.Write('<td></td>')
    713 
    714         res.Write('</tr>')
    715     res.Write('</table>')
    716     if len(lines) > 30:
    717         if useFilter or paging is not None or len(customActions2) > 0:
    718             res.Write(toolbar % {'leftbar': '%i rows' % len(lines) if showCount else '',
    719              'invert': invertText,
    720              'paging': '' if paging is None else '<li>%s</li>' % paging,
    721              'customActions': u''.join([ u'<li>%s</li>' % x for x in customActions2 ]),
    722              'sep1': '' if paging is None else '<li>%s</li>' % elementSep,
    723              'sep2': '<li>%s</li>' % elementSep if len(invertText) > 0 else ''})
    724         elif showCount:
    725             res.Write('<div>%i rows</div>' % len(lines))
    726     elif showCount and len(lines) > 5:
    727         res.Write('<div>%i rows</div>' % len(lines))
    728     res.Write('</div>')
    729     res.Seek(0)
    730     return res.Read()
    731 
    732 
    733 def GetMessageEnglishByID(messageID, defaultValue):
    734     return localization.GetByMessageID(messageID) or defaultValue
    735 
    736 
    737 def GetLocalizationLabel(labelText, messageID):
    738     msgSpan = GetSpan([GetMessageEnglishByID(messageID, labelText)], className='')
    739     return msgSpan
    740 
    741 
    742 def GetLanguagesForSelector():
    743     languages = []
    744     for languageID, language in LANGUAGE_BY_LANGUAGE_ID.iteritems():
    745         languages.append([localization.util.ConvertLanguageIDToMLS(languageID), language])
    746 
    747     return languages
    748 
    749 
    750 def OutlineTable(headers, lines, opLink = '', opAct = '', opWidth = None, opHeadClass = 'tableHeader', nobreak = 0, opColClass = None, opHeaderArgs = None, opTDArgs = None):
    751     tableStyle = ''
    752     for headerValue in headers:
    753         if isinstance(headerValue, list) or isinstance(headerValue, tuple):
    754             tableStyle = "STYLE='table-layout:fixed'"
    755             break
    756 
    757     s = UnicodeMemStream()
    758     if opWidth is not None:
    759         if opWidth != '':
    760             opWidth = 'width="%s"' % opWidth
    761         s.Write('<table border="0" cellpadding="0" cellspacing="0" %s %s>\n' % (opWidth, tableStyle))
    762     else:
    763         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%%" %s>\n' % tableStyle)
    764     s.Write('<tr>\n')
    765     s.Write('<td class="tableOutline" valign="top">\n')
    766     s.Write('<table border="0" cellpadding="7" cellspacing="0" width="100%%" %s>\n' % tableStyle)
    767     if len(headers):
    768         s.Write('<tr>\n')
    769         for headerIndex, headerValue in enumerate(headers):
    770             headerTDArgs = ''
    771             if opHeaderArgs is not None and headerIndex in opHeaderArgs:
    772                 headerTDArgs = ' '.join([ '%s="%s"' % (attribute, value) for attribute, value in opHeaderArgs[headerIndex] ])
    773             if isinstance(headerValue, list) or isinstance(headerValue, tuple):
    774                 headerLabel, width = headerValue
    775                 s.Write('    <td width=%d class="%s"%s>%s</td>\n' % (width,
    776                  opHeadClass,
    777                  headerTDArgs,
    778                  headerLabel))
    779             else:
    780                 s.Write('    <td class="%s"%s>%s</td>\n' % (opHeadClass, headerTDArgs, headerValue))
    781 
    782         s.Write('</tr>\n')
    783     c = 1
    784     for rowIndex, rowValue in enumerate(lines):
    785         if len(rowValue) == 1 and unicode(rowValue[0]).lower().find('<tr') == 0:
    786             s.Write(rowValue[0] + '\n')
    787             continue
    788         klass = 'class="tC"'
    789         if c:
    790             c = 0
    791         else:
    792             klass = 'class="tCO"'
    793             c = 1
    794         if opColClass:
    795             klass = 'class="%s"' % opColClass
    796         s.Write('<tr>\n')
    797         for colIndex, colValue in enumerate(rowValue):
    798             tdArgs = ''
    799             if opTDArgs is not None and (rowIndex, colIndex) in opTDArgs:
    800                 tdArgs = ' '.join([ '%s="%s"' % (attribute, value) for attribute, value in opTDArgs[rowIndex, colIndex] ])
    801             if colValue == rowValue[0] and opLink != '':
    802                 link = opLink % (colValue, colValue)
    803                 s.Write('<td %s%s>%s</td>\n' % (klass, tdArgs, link))
    804                 continue
    805             s.Write('<td %s%s>' % (klass, tdArgs))
    806             if nobreak:
    807                 s.Write('<nobr>')
    808             s.Write(colValue)
    809             if nobreak:
    810                 s.Write('</nobr>')
    811             s.Write('</td>\n')
    812 
    813         s.Write('</tr>\n')
    814 
    815     s.Write('</table>\n')
    816     s.Write('</td>\n')
    817     s.Write('</tr>\n')
    818     s.Write('</table>\n')
    819     s.Seek(0)
    820     return s.Read()
    821 
    822 
    823 def OutlinePropertyTable(keyvaldict, header = None):
    824     s = UnicodeMemStream()
    825     s.Write('<table border="0" cellpadding="0" cellspacing="0">\n')
    826     s.Write('<tr>\n')
    827     s.Write('<td class="tableOutline" valign="top">\n')
    828     s.Write('<table border="0" cellpadding="7" cellspacing="1">\n')
    829     s.Write('<tr>\n')
    830     it = keyvaldict.items()
    831     if header and len(header):
    832         s.Write('<tr>')
    833         for i in header:
    834             s.Write('<td ')
    835             if len(header) == 1:
    836                 s.Write('colspan = 2 ')
    837             s.Write('class="tableHeader"><b>%s</b></td>\n' % (i,))
    838 
    839         s.Write('</tr>\n')
    840     for i in it:
    841         s.Write('<tr>\n')
    842         s.Write('<td class="tC"><b>%s</b></td>\n' % i[0])
    843         s.Write('<td class="tC">%s</td>\n' % i[1])
    844         s.Write('</tr>\n')
    845 
    846     s.Write('</tr>\n')
    847     s.Write('</table>\n')
    848     s.Write('</td>\n')
    849     s.Write('</tr>\n')
    850     s.Write('</table>\n')
    851     s.Seek(0)
    852     return s.Read()
    853 
    854 
    855 def OutlineKeyValTable(keyval):
    856     s = UnicodeMemStream()
    857     s.Write('<table border="0" cellpadding="0" cellspacing="0">\n')
    858     s.Write('<tr>\n')
    859     s.Write('<td class="tableOutline" valign="top">\n')
    860     s.Write('<table border="0" cellpadding="7" cellspacing="1">\n')
    861     s.Write('<tr>\n')
    862     for i in keyval:
    863         s.Write('<tr>\n')
    864         s.Write('<td class="tC" valign=top><b>%s</b></td>\n' % unicode(i[0]))
    865         s.Write('<td class="tC" valign=top>%s</td>\n' % unicode(i[1]))
    866         s.Write('</tr>\n')
    867 
    868     s.Write('</tr>\n')
    869     s.Write('</table>\n')
    870     s.Write('</td>\n')
    871     s.Write('</tr>\n')
    872     s.Write('</table>\n')
    873     s.Seek(0)
    874     return s.Read()
    875 
    876 
    877 def BarChart(name = '', data = {}, width = 500, height = 500):
    878     Tot = 0
    879     Max = 0
    880     sortedDataKeys = data.keys()
    881     sortedDataKeys.sort()
    882     for k in sortedDataKeys:
    883         Tot = Tot + data[k]
    884         if data[k] > Max:
    885             Max = data[k]
    886 
    887     strHTML = '\n\n<table border=0 cellspacing=2 width="100%%">\n'
    888     for k in sortedDataKeys:
    889         strHTML = strHTML + '<tr><td width="50"><b>%s</b><br/>%s times</td><td><table height="100%%"  Width="%s%%" border=0 cellpadding=0 bgcolor="#6699cc"><tr><td>&nbsp;</td></tr></table></td></tr>\n' % (k, data[k], int(float(data[k]) / float(Tot) * 100))
    890 
    891     strHTML = strHTML + '</table>'
    892     return strHTML
    893 
    894 
    895 def HtmlFormatStack(stack):
    896     ret = ''
    897     for each in stack:
    898         ret += Swing(each) + '<br/>'
    899 
    900     return ret
    901 
    902 
    903 def BrowserCheck(browser):
    904     valids = ['MSIE 6.0', 'MSIE 5.5']
    905     ret = 0
    906     for valid in valids:
    907         if browser.find(valid) > 0:
    908             ret = 1
    909 
    910     return ret
    911 
    912 
    913 def WebPart(title, content, ID = '4', width = '100%', headerClass = '', bodyClass = ''):
    914     s = UnicodeMemStream()
    915     s.Write('<table class=WebPartTable cellpadding="0" cellspacing="0" width="' + str(width) + '" border="0" id="' + unicode(ID) + '">\n')
    916     s.Write('<tr>\n')
    917     s.Write('    <td valign="middle" align="left" class="WebPartHeader %s">\n' % headerClass)
    918     s.Write('    ' + title + '\n')
    919     s.Write('    </td>\n')
    920     s.Write('</tr>\n')
    921     s.Write('<tr>\n')
    922     s.Write('    <td>\n')
    923     s.Write('    <table bgcolor="#ffffff" width="100%" cellpadding="0" cellspacing="0" border="0">\n')
    924     s.Write('    <tr>\n')
    925     s.Write('        <td valign="top" class="WebPartContent %s">\n' % bodyClass)
    926     s.Write(content)
    927     s.Write('        </td>\n')
    928     s.Write('     </tr>\n')
    929     s.Write('     </table>\n')
    930     s.Write('     </td>\n')
    931     s.Write('</tr>\n')
    932     s.Write('</table>\n')
    933     s.Seek(0)
    934     return s.Read()
    935 
    936 
    937 def GetFrameOf(what):
    938     import stackless
    939     first = stackless.getcurrent()
    940     if type(what) == type(stackless.getcurrent().frame):
    941         return what
    942     current = first
    943     f = None
    944     while 1:
    945         q = current.frame
    946         while 1:
    947             if q is not None and not hasattr(q, 'f_locals'):
    948                 q = None
    949             if q is None:
    950                 break
    951             if q.f_locals is what:
    952                 return q
    953             for each in q.f_locals:
    954                 if each is what:
    955                     return q
    956 
    957             q = q.f_back
    958 
    959         current = current.next
    960         if current == first:
    961             break
    962 
    963 
    964 def FrameIsCallingFrame(f, current = None):
    965     import stackless
    966     if current is None:
    967         current = stackless.getcurrent().frame
    968     while 1:
    969         if f is current:
    970             return 1
    971         if current.f_back is None or not hasattr(current.f_back, 'f_locals'):
    972             if f is not None:
    973                 f = f.f_back
    974             if f is not None and hasattr(f, 'f_locals'):
    975                 return FrameIsCallingFrame(f, current)
    976             return 0
    977         current = current.f_back
    978 
    979 
    980 def Kapling(s):
    981     s = Swing(s)
    982     if len(s) > 500:
    983         s = 'huge mofu (%d)' % len(s) + s[:500]
    984     return s
    985 
    986 
    987 def GetXyzzyReferrerInfo(each, inst):
    988     ok = '<b>' + Kapling(str(type(each))) + '</b>:  ' + Kapling(str(each))
    989     done = 0
    990     if type(each) == type({}):
    991         for yetanother in inst:
    992             if yetanother.__dict__ is each:
    993                 done = 1
    994                 ok += '<br/><b>This ref is owned by this object: ' + Kapling(str(yetanother)) + '</b>'
    995                 break
    996             for k, attr in yetanother.__dict__.iteritems():
    997                 if attr is each:
    998                     ok += '<br/><b>This ref is ' + Kapling(str(yetanother)) + '.' + k + '</b>'
    999                     done = 1
   1000                 elif type(attr) in (types.TupleType, types.ListType):
   1001                     for i in attr:
   1002                         if i is each:
   1003                             ok += '<br/><b>This ref is contained in ' + Kapling(str(yetanother)) + '.' + k + '</b>'
   1004                             done = 1
   1005 
   1006                 elif type(attr) == types.DictType:
   1007                     for i in attr.itervalues():
   1008                         if i is each:
   1009                             ok += '<br/><b>This ref is contained in ' + Kapling(str(yetanother)) + '.' + k + '</b>'
   1010                             done = 1
   1011 
   1012     return (ok, done)
   1013 
   1014 
   1015 def GetXyzzyReferrers(s, inst = None):
   1016     import traceback
   1017     import gc
   1018     ok = ''
   1019     gc.collect()
   1020     referrers = gc.get_referrers(s)
   1021     if inst is None:
   1022         inst = [ o for o in gc.get_objects() if type(o) is types.InstanceType ]
   1023     for each in referrers:
   1024         f = GetFrameOf(each)
   1025         ok += '<br/>'
   1026         if FrameIsCallingFrame(f):
   1027             ok += '<br/><b>It looks like this ref might belong to this web page:</b>'
   1028         if f is not None:
   1029             ok += '<br/><b>Frame: ' + Kapling(str(f)) + '</b>'
   1030             for other in traceback.extract_stack(f, 40):
   1031                 ok += '<br/>' + Kapling(str(other))
   1032 
   1033         ri, done = GetXyzzyReferrerInfo(each, inst)
   1034         ok += ri
   1035 
   1036     gc.collect()
   1037     return ok
   1038 
   1039 
   1040 def Convert(var):
   1041     try:
   1042         if isinstance(var, (str, unicode)):
   1043             if len(var) > 1:
   1044                 if var == 'None':
   1045                     return None
   1046                 if var.startswith('-'):
   1047                     rest = var[1:]
   1048                     if rest.isdigit():
   1049                         if rest.startswith('0'):
   1050                             return var
   1051                         else:
   1052                             return int(var)
   1053                     elif '.' in rest and rest.replace('.', '').isdigit():
   1054                         if rest.startswith('0'):
   1055                             return var
   1056                         else:
   1057                             return float(var)
   1058                 elif var.isdigit():
   1059                     if var.startswith('0'):
   1060                         return var
   1061                     else:
   1062                         return int(var)
   1063                 elif '.' in var and var.replace('.', '').isdigit():
   1064                     if var.startswith('0'):
   1065                         return var
   1066                     else:
   1067                         return float(var)
   1068             elif var.isdigit():
   1069                 return int(var)
   1070     except:
   1071         sys.exc_clear()
   1072 
   1073     return var
   1074 
   1075 
   1076 def Pythonize(var):
   1077     if type(var) == type([]):
   1078         ret = []
   1079         for i in var:
   1080             ret.append(Convert(i))
   1081 
   1082         return ret
   1083     return Convert(var)
   1084 
   1085 
   1086 def Decanonicalize(text):
   1087     ix = 0
   1088     text = text.replace('+', ' ')
   1089     ret = ''
   1090     while 1:
   1091         newix = text.find('%', ix)
   1092         if newix == -1:
   1093             break
   1094         ret = ret + text[ix:newix]
   1095         ix = newix
   1096         h2, h1 = text[ix + 1:ix + 3]
   1097         ch = 0
   1098         if h1 >= '0' and h1 <= '9':
   1099             ch = ord(h1) - ord('0')
   1100         else:
   1101             ch = ord(h1) - ord('A') + 10
   1102         if h2 >= '0' and h2 <= '9':
   1103             ch = ch + (ord(h2) - ord('0')) * 16
   1104         else:
   1105             ch = ch + (ord(h2) - ord('A') + 10) * 16
   1106         ret = ret + chr(ch)
   1107         ix = ix + 3
   1108 
   1109     return ret + text[ix:]
   1110 
   1111 
   1112 def SplitArgs(args, dest = None):
   1113     args = str(args)
   1114     if dest is None:
   1115         dest = {}
   1116     arg = args.split('&')
   1117     for i in arg:
   1118         kv = i.split('=', 1)
   1119         if len(kv) == 2:
   1120             s = unquote_plus(kv[1])
   1121             if kv[0] in dest:
   1122                 if type(dest[kv[0]]) != type([]):
   1123                     dest[kv[0]] = [dest[kv[0]]]
   1124                 dest[kv[0]].append(s)
   1125             else:
   1126                 dest[kv[0]] = s
   1127         else:
   1128             dest[i] = ''
   1129 
   1130     return dest
   1131 
   1132 
   1133 def SplitMIMEArgs(fields, dest = None):
   1134     if dest is None:
   1135         dest = {}
   1136     for field in fields.list:
   1137         if field.name in dest:
   1138             if type(dest[field.name]) != type([]):
   1139                 dest[field.name] = [dest[field.name]]
   1140             if field.type != 'text/plain':
   1141                 dest[field.name].append(field.value)
   1142             else:
   1143                 dest[field.name].append(unquote_plus(field.value))
   1144         elif field.type != 'text/plain':
   1145             dest[field.name] = field.value
   1146         else:
   1147             dest[field.name] = urllib2.unquote(field.value)
   1148 
   1149     return dest
   1150 
   1151 
   1152 def PythonizeArgs(args):
   1153     dest = SplitArgs(str(args))
   1154     for k, v in dest.iteritems():
   1155         dest[k] = Pythonize(v)
   1156 
   1157     return dest
   1158 
   1159 
   1160 def PackModalDialogueResult(result):
   1161     r = ''
   1162     for k, v in result.iteritems():
   1163         r += str(k) + '=' + str(v) + '&'
   1164 
   1165     r = r[:-1]
   1166     return r
   1167 
   1168 
   1169 def UnpackModalDialogueResult(result):
   1170     ret = {}
   1171     if result:
   1172         for each in result.split('&'):
   1173             k, v = each.split('=')
   1174             v = Pythonize(v)
   1175             if k in ret:
   1176                 if type(ret[k]) != types.ListType:
   1177                     ret[k] = [ret[k]]
   1178                 ret[k].append(v)
   1179             else:
   1180                 ret[k] = v
   1181 
   1182     return ret
   1183 
   1184 
   1185 def AddThousandSeparator(s):
   1186     res = []
   1187     for i, c in enumerate(reversed(str(s))):
   1188         if i and not i % 3:
   1189             res.insert(0, '.')
   1190         res.insert(0, c)
   1191 
   1192     return ''.join(res)
   1193 
   1194 
   1195 def CheckValue(id, idValue):
   1196     if idValue is not None and idValue is not '':
   1197         return ' %s="%s"' % (id, idValue)
   1198     return ''
   1199 
   1200 
   1201 class CSRFException(Exception):
   1202 
   1203     def __init__(self, *args, **kwargs):
   1204         Exception.__init__(self, *args, **kwargs)
   1205 
   1206 
   1207 class HtmlWriter():
   1208 
   1209     def __init__(self, template = 'script:/wwwroot/lib/template/emptybase.html'):
   1210         self.baseTemplate = template
   1211         self.Clear()
   1212         from carbon.common.script.sys.basesession import GetServiceSession
   1213         self.session = GetServiceSession('HtmlWriter')
   1214         self.ajaxDefaultURL = None
   1215         self.ajaxCSRF = False
   1216 
   1217     def LogInfo(self, *args, **keywords):
   1218         self.Log(log.LGINFO, *args, **keywords)
   1219 
   1220     def LogNotice(self, *args, **keywords):
   1221         self.Log(log.LGNOTICE, *args, **keywords)
   1222 
   1223     def LogWarn(self, *args, **keywords):
   1224         self.Log(log.LGWARN, *args, **keywords)
   1225 
   1226     def LogError(self, *args, **keywords):
   1227         self.Log(log.LGERR, *args, **keywords)
   1228 
   1229     def Log(self, severity, *args, **keywords):
   1230         try:
   1231             if len(args) == 1:
   1232                 s = strx(args[0])
   1233             else:
   1234                 s = ' '.join(map(strx, args))
   1235             log.sp.Log(s, severity, 1)
   1236         except Exception as e:
   1237             log.sp.Log('*** Error logging out logline: %s' % e, severity, 1)
   1238 
   1239     def EspIcon(self, filename = 'none.gif', size = 32):
   1240         iconUrl = '/img/' + filename
   1241         return self.Image(iconUrl, 'width=%s height=%s border=0 align=absmiddle' % (size, size))
   1242 
   1243     def Clear(self):
   1244         self.redirect = 'mainhtml'
   1245         self.inserts = {}
   1246         self.visualization = []
   1247         self.scripts = []
   1248         self.cssFiles = []
   1249         self.jsFiles = []
   1250         self.headjsScripts = []
   1251         self.inserts['title'] = 'HTML Writer Title'
   1252         self.inserts['heading'] = 'HTML Writer Heading'
   1253         self.WriteDirect('icon', self.EspIcon('none.gif', 32))
   1254         self.WriteDirect('stylesheet', '/lib/std.css')
   1255         self.start = blue.os.GetCycles()[0]
   1256         self.WriteDirect('bottom_left', '')
   1257         self.inserts['navigation'] = ''
   1258         self.WriteDirect('jscript', '')
   1259         self.WriteDirect('head', '')
   1260         self.WriteDirect('scripts', '')
   1261         self.WriteDirect('visualscripts', '')
   1262         self.WriteDirect('trackpage', '')
   1263         self.WriteDirect('treeMenu', '')
   1264         self.WriteDirect('headjs', '')
   1265         self.WriteDirect('footer', '')
   1266         self.visualcallbacks = []
   1267         self.enabledJscript = set()
   1268         self.current_form_rules = None
   1269         self.prettify = False
   1270 
   1271     def CSRFFormField(self):
   1272         return '<input type="hidden" name="csrf_token" value="%s" />' % GenerateCSRFToken(self.request)
   1273 
   1274     def CSRF(self):
   1275         self.ajaxCSRF = True
   1276 
   1277     def ValidateCSRF(self):
   1278         csrf_token = self.request.form['csrf_token'] if self.request.form is not None and 'csrf_token' in self.request.form else ''
   1279         if csrf_token is None or len(csrf_token) == 0 or csrf_token != GenerateCSRFToken(self.request):
   1280             return False
   1281         return True
   1282 
   1283     def toJavascriptDict(self, d = {}):
   1284         map_fun = lambda x: ('%s' % x if x.startswith('function') or x.startswith('[') else "'%s'" % x)
   1285         map_outer = lambda x: ('%s: %s' % (x, self.toJavascriptDict(d[x])) if isinstance(d[x], types.DictType) else '%s: %s' % (x, str(d[x]).lower()))
   1286         map_key = lambda x: ('%s: %s' % (x, map_fun(d[x])) if isinstance(d[x], types.StringType) else map_outer(x))
   1287         return ''.join(['{', ', '.join(map(map_key, d.keys())), '}'])
   1288 
   1289     def GetLocalizationLabel(self, labelText, messageID):
   1290         return GetLocalizationLabel(labelText, messageID)
   1291 
   1292     def Swing(self, text):
   1293         return Swing(text)
   1294 
   1295     def AddScript(self, src):
   1296         self.AddJsFile(src)
   1297 
   1298     def AddScriptRaw(self, source):
   1299         self.scripts.append(source)
   1300 
   1301     def AddHeadScript(self, src):
   1302         self.headjsScripts.append('<script>head.js("%s")</script>' % src)
   1303 
   1304     def AddJsFile(self, src):
   1305         if src not in self.jsFiles:
   1306             self.jsFiles.append(src)
   1307 
   1308     def AddCss(self, src):
   1309         self.AddCssFile(src)
   1310 
   1311     def AddCssFile(self, src):
   1312         if src not in self.cssFiles:
   1313             self.cssFiles.append(src)
   1314 
   1315     def AddSyntaxHighlighting(self, element, language = 'python'):
   1316         if not self.prettify:
   1317             self.AddScript('/js/prettify.js')
   1318             self.AddCss('/css/prettify.css')
   1319             self.WriteDirect('jscript', '$(document).ready(function(){ prettyPrint(); });')
   1320             self.prettify = True
   1321         return '<code class="prettyprint lang-%(language)s">%(element)s</code>' % locals()
   1322 
   1323     def AddVisualJScript(self, package, source):
   1324         self.EnableVisualTool(package=package)
   1325         self.WriteDirect('visualscripts', source)
   1326 
   1327     def AddVisualQueryJScript(self, package, visualTool, ajax, ctrlID, options = None, query = None, reloadData = None):
   1328         self.EnableVisualTool(package=package)
   1329         temp_reloadData = '\nsetTimeout(doVisual,%i);\n' % reloadData if reloadData is not None else None
   1330         temp_script = []
   1331         temp_script.append("var query_%(ctrlID)s = new google.visualization.Query('%(ajax)s');" % {'ctrlID': ctrlID,
   1332          'ajax': ajax})
   1333         if query is not None:
   1334             temp_script.append("query_%(ctrlID)s.setQuery('%(query)s');" % {'ctrlID': ctrlID,
   1335              'query': query})
   1336         temp_script.append('query_%(ctrlID)s.send(handle%(ctrlID)sResponse);' % {'ctrlID': ctrlID})
   1337         if temp_reloadData:
   1338             temp_script.append(temp_reloadData)
   1339         self.WriteDirect('visualscripts', '\n'.join(temp_script))
   1340         handleResponse = "\n        var visual%(ctrlID)s;\n        function handle%(ctrlID)sResponse(response) {\n            if (response.isError()) {\n                alert('Error in query: '+ response.getMessage() + ' ' + response.getDetailedMessage());\n            }\n            var data = response.getDataTable();\n            if (!visual%(ctrlID)s) {\n                visual%(ctrlID)s = new google.visualization.%(visualTool)s(document.getElementById('%(ctrlID)s'));\n            }\n            visual%(ctrlID)s.draw(data, %(opt)s);\n            try {\n                if (graphLoaded) {\n                    graphLoaded();\n                }\n            }\n            catch(e) {}\n        }\n        " % {'ctrlID': ctrlID,
   1341          'visualTool': visualTool,
   1342          'opt': 'null' if options is None else self.toJavascriptDict(d=options)}
   1343         self.visualcallbacks.append(handleResponse)
   1344 
   1345     def _GoogleVisualization(self, ctrlID, header, lines, tool, style = None, options = None, events = None):
   1346         if options is None:
   1347             options = {}
   1348         ms = UnicodeMemStream()
   1349         ms.Write('\n    <script type="text/javascript">\n        function draw%(ctrlID)s() {\n            var data = new google.visualization.DataTable();\n' % {'ctrlID': ctrlID})
   1350         ms.Write(''.join([ "            data.addColumn('%s', '%s');\n" % (row[0], row[1]) for row in header ]))
   1351         lineCount = len(lines)
   1352         if lineCount > 0:
   1353             ms.Write('            ')
   1354             ms.Write('data.addRows([\n')
   1355             rowIndex = 0
   1356             for row in lines:
   1357                 cellIndex = 0
   1358                 ms.Write('              ')
   1359                 ms.Write('[')
   1360                 for h in header:
   1361                     if row[cellIndex] is not None:
   1362                         if h[0] in ('datetime', 'date'):
   1363                             ms.Write("Date.parse('%s')" % row[cellIndex])
   1364                         elif h[0] in ('number',):
   1365                             ms.Write(str(row[cellIndex]).replace('L,', ',').replace('L]', ']'))
   1366                         else:
   1367                             ms.Write("'%s'" % unicode(row[cellIndex]).replace("u'", "'"))
   1368                     if len(header) > cellIndex + 1:
   1369                         ms.Write(',')
   1370                     cellIndex += 1
   1371 
   1372                 ms.Write(']')
   1373                 if lineCount > rowIndex + 1:
   1374                     ms.Write(',\n')
   1375                 rowIndex += 1
   1376 
   1377             ms.Write('\n')
   1378             ms.Write('            ')
   1379             ms.Write(']);\n')
   1380         ms.Write("\n            var %(ctrlID)sGraph = new google.visualization.%(tool)s(document.getElementById('%(ctrlID)s'));\n        " % {'ctrlID': ctrlID,
   1381          'tool': tool})
   1382         if events is not None:
   1383             for key in events.iterkeys():
   1384                 ms.Write("      google.visualization.events.addListener(%(ctrlID)sGraph, '%(event)s', function() {\n" % {'ctrlID': ctrlID,
   1385                  'event': key})
   1386                 ms.Write(events[key])
   1387                 ms.Write('      });\n')
   1388 
   1389         ms.Write("\n            %(ctrlID)sGraph.draw(data, %(options)s);\n    }\n    google.charts.load('current', {'packages':['corechart', 'annotationchart']});\n    google.setOnLoadCallback(draw%(ctrlID)s);\n  </script>\n        " % {'ctrlID': ctrlID,
   1390          'options': str(options).replace('True', 'true').replace('False', 'false') if options else 'null',
   1391          'tool': tool})
   1392         self.WriteDirect('scripts', ms)
   1393         return self.GetDiv(['<img alt="Loading..." src="/static/img/ajax-loader-small.gif" /> '], ctrlID=ctrlID, style=style)
   1394 
   1395     def GetAnnotatedTimeline(self, ctrlID, header, lines, style = None, options = None, events = None):
   1396         self.EnableVisualTool('annotatedtimeline')
   1397         return self._GoogleVisualization(ctrlID, header, lines, 'AnnotatedTimeLine', style, options, events)
   1398 
   1399     def GetAnnotationChart(self, ctrlID, header, lines, style = None, options = None, events = None):
   1400         self.EnableVisualTool('annotationchart')
   1401         return self._GoogleVisualization(ctrlID, header, lines, 'AnnotationChart', style, options, events)
   1402 
   1403     def WriteAnnotationChart(self, ctrlID, header, lines, style = None, options = None, events = None):
   1404         self.Write(self.GetAnnotationChart(ctrlID, header, lines, style, options, events))
   1405 
   1406     def WriteAnnotatedTimeline(self, ctrlID, header, lines, style = None, options = None, events = None):
   1407         self.Write(self.GetAnnotatedTimeline(ctrlID, header, lines, style, options, events))
   1408 
   1409     def _SetDefaultChartOptions(self, options, showLegend = True):
   1410         if options is None:
   1411             options = {}
   1412         if 'fontSize' not in options:
   1413             options['fontSize'] = '9'
   1414         if 'fontName' not in options:
   1415             options['fontName'] = 'Verdana'
   1416         if 'chartArea' not in options:
   1417             options['chartArea'] = {'left': 40,
   1418              'top': 25,
   1419              'height': '75%'}
   1420         if 'legend' not in options and showLegend == False:
   1421             options['legend'] = {'position': 'none'}
   1422 
   1423     def GetLineChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1424         self.EnableVisualTool('corechart')
   1425         self._SetDefaultChartOptions(options, showLegend)
   1426         return self._GoogleVisualization(ctrlID, header, lines, 'LineChart', style, options, events)
   1427 
   1428     def GetAreaChart(self, ctrlID, header, lines, style = None, options = None, events = None):
   1429         self.EnableVisualTool('corechart')
   1430         self._SetDefaultChartOptions(options, showLegend)
   1431         return self._GoogleVisualization(ctrlID, header, lines, 'AreaChart', style, options, events)
   1432 
   1433     def GetComboChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1434         self.EnableVisualTool('corechart')
   1435         self._SetDefaultChartOptions(options, showLegend)
   1436         return self._GoogleVisualization(ctrlID, header, lines, 'ComboChart', style, options, events)
   1437 
   1438     def WriteLineChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1439         self.Write(self.GetLineChart(ctrlID, header, lines, style, options, events, showLegend))
   1440 
   1441     def WriteAreaChart(self, ctrlID, header, lines, style = None, options = None, events = None):
   1442         self.Write(self.GetAreaChart(ctrlID, header, lines, style, options, events))
   1443 
   1444     def WriteComboChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1445         self.Write(self.GetComboChart(ctrlID, header, lines, style, options, events, showLegend))
   1446 
   1447     def GetColumnChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1448         self.EnableVisualTool('corechart')
   1449         self._SetDefaultChartOptions(options, showLegend)
   1450         return self._GoogleVisualization(ctrlID, header, lines, 'ColumnChart', style, options, events)
   1451 
   1452     def WriteColumnChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1453         self.Write(self.GetColumnChart(ctrlID, header, lines, style, options, events, showLegend))
   1454 
   1455     def GetAreaChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1456         self.EnableVisualTool('corechart')
   1457         self._SetDefaultChartOptions(options, showLegend)
   1458         return self._GoogleVisualization(ctrlID, header, lines, 'AreaChart', style, options, events)
   1459 
   1460     def WriteAreaChart(self, ctrlID, header, lines, style = None, options = None, events = None, showLegend = True):
   1461         self.Write(self.GetAreaChart(ctrlID, header, lines, style, options, events, showLegend))
   1462 
   1463     def Reloader(self, url, sec, caption, className = None):
   1464         randNumber = random.randint(1, 1000)
   1465         timerID = 'timerSpan%i' % randNumber
   1466         reloadID = 'reloadDiv%i' % randNumber
   1467         script = '\n        <script type="text/javascript">\n            function func%(id)s() {\n                $("#%(id)s").load(\'%(url)s\');\n            }\n            func%(id)s();\n            var countdown%(rand)i = %(sec)i;\n            var countdown%(rand)i_org = %(sec)i;\n            function reset%(id)s() {\n                countdown%(rand)i = 0;\n            }\n            function coundownc%(id)s() {\n                setTimeout(coundownc%(id)s, 1000);\n                var date = new Date(null);\n                date.setSeconds(countdown%(rand)i);\n                $("#%(timerID)s .csp-sec").html( date.toTimeString().substr(0, 8) );\n                countdown%(rand)i = countdown%(rand)i - 1;\n                if ( countdown%(rand)i<0 ) {\n                    countdown%(rand)i = countdown%(rand)i_org;\n                    func%(id)s();\n                }\n            }\n            $("#%(timerID)s").html("<span class=\'csp-counter\'><img src=\'/static/img/ctrl_end.png\' onclick=\'reset%(id)s();\'/><span class=\'csp-sec\'></span></span>");\n            coundownc%(id)s();\n        </script>\n        ' % {'id': reloadID,
   1468          'url': url,
   1469          'timerID': timerID,
   1470          'rand': randNumber,
   1471          'sec': sec}
   1472         return self.GetDiv([self.GetDiv([self.GetSpan([caption], className='csp-caption'), self.GetSpan([], timerID)]), self.GetDiv([], reloadID), script], className=className)
   1473 
   1474     def WriteReloader(self, url, sec, caption, className = None):
   1475         self.Write(self.Reloader(url, sec, caption, className))
   1476 
   1477     def WriteJson(self, jsonName, jsonData):
   1478         self.Write(['<script type="text/javascript">\nvar ',
   1479          jsonName,
   1480          ' = ',
   1481          json.dumps(jsonData),
   1482          ';\n',
   1483          '</script>'])
   1484 
   1485     def Canvas(self, ctrlID, width, height):
   1486         return '<canvas id="%s" width="%i" height="%i"></canvas>' % (ctrlID, width, height)
   1487 
   1488     def WriteCanvas(self, ctrlID, width, height):
   1489         self.Write(self.Canvas(ctrlID, width, height))
   1490 
   1491     def IFrame(self, src, ctrlID = None, width = None, height = None):
   1492         params = (src,
   1493          CheckValue('id', ctrlID),
   1494          CheckValue('width', width),
   1495          CheckValue('height', height),
   1496          ' scrolling="no" frameborder="0" marginheight="0" marginwidth="0"')
   1497         return '<iframe src="%s"%s%s%s%s></iframe>' % params
   1498 
   1499     def WriteIFrame(self, src, ctrlID = None, width = None, height = None):
   1500         self.Write(self.IFrame(src, ctrlID, width, height))
   1501 
   1502     def DrawList(self, drawList, width, height):
   1503         self.AddScript(src='/static/js/libs/excanvas.js')
   1504         ctrlID = 'drawListCanvas'
   1505         self.WriteJson('drawList', drawList)
   1506         self.WriteCanvas(ctrlID, width, height)
   1507         self.Write('\n        <script type="text/javascript">\n            var canvas = document.getElementById("%(ctrlID)s");\n            if (typeof(G_vmlCanvasManager) != \'undefined\') {\n                canvas = G_vmlCanvasManager.initElement(canvas);\n            }\n            var ctx = canvas.getContext("2d");\n            $.each(drawList, function(index, element) {\n                console.log(element);\n                if (typeof element.area != \'undefined\') {\n                    ctx.fillStyle = element.area.color;\n                    ctx.fillRect(element.area.x, element.area.y, element.area.w, element.area.h);\n                }\n                else if (typeof element.label != \'undefined\') {\n                    ctx.fillStyle = element.label.color;\n                    ctx.font = "bold 36px sans-serif";\n                    ctx.fillText(element.label.title, element.label.x, element.label.y);\n                }\n                else if (typeof element.circle != \'undefined\') {\n                    ctx.fillStyle = element.circle.color;\n                    ctx.beginPath();\n                    ctx.arc(element.circle.x, element.circle.y, element.circle.r, 2*Math.PI, false);\n                    ctx.fill();\n                    ctx.closePath();\n                }\n                else if (typeof element.line != \'undefined\') {\n                    ctx.fillStyle = element.line.color;\n                    ctx.lineWidth = 2;\n                    ctx.beginPath();\n                    ctx.moveTo(element.line.x1, element.line.y1);\n                    ctx.lineTo(element.line.x2, element.line.y2);\n                    ctx.stroke();\n                }\n            });\n        </script>\n        ' % {'ctrlID': ctrlID})
   1508 
   1509     def FormatDate(self, dateString):
   1510         if dateString is not None:
   1511             date_part = [ int(x) for x in dateString.split('.') ]
   1512             return blue.os.GetTimeFromParts(date_part[0], date_part[1], date_part[2], 0, 0, 0, 0)
   1513 
   1514     def _Host(self):
   1515         try:
   1516             func = lambda x: (True if x.lower().startswith('host') else False)
   1517             host = filter(func, self.request.raw.split('\r\n'))[0].split(':')
   1518             return (host[1].strip(), host[2].strip())
   1519         except:
   1520             return ('', '')
   1521 
   1522     def GetExcelWebQuery(self, q, label):
   1523         host = self._Host()
   1524         url = 'http://%s:%s%s' % (host[0], host[1], q)
   1525         return self.GetA(innerText=label, href='/info/iqy.py?q=%s' % url)
   1526 
   1527     def EnableInfoVisToolkit(self, src = None):
   1528         self.AddScript('/static/js/libs/excanvas.js')
   1529         self.AddScript('/static/js/libs/jit.js')
   1530         if src is not None:
   1531             self.AddScript(src)
   1532 
   1533     def EnableVisualTool(self, package):
   1534         allowedTools = ['annotatedtimeline',
   1535          'annotationchart',
   1536          'areachart',
   1537          'barchart',
   1538          'columnchart',
   1539          'corechart',
   1540          'gauge',
   1541          'geomap',
   1542          'imageareachart',
   1543          'imagebarchart',
   1544          'imagechart',
   1545          'imagelinechart',
   1546          'imagepiechart',
   1547          'imagesparkline',
   1548          'intensitymap',
   1549          'linechart',
   1550          'map',
   1551          'motionchart',
   1552          'orgchart',
   1553          'piechart',
   1554          'scatterchart',
   1555          'table',
   1556          'treemap',
   1557          'areachart']
   1558         if package not in allowedTools:
   1559             raise Exception("The tool '%s' isn't allowed. Allowed tools are %s" % (package, allowedTools))
   1560         if package not in self.visualization:
   1561             self.visualization.append(package)
   1562             if len(self.visualization) == 1:
   1563                 temp_script = '\n        <script type="text/javascript" src="https://www.google.com/jsapi"></script>\n                '
   1564                 self.WriteDirect('scripts', temp_script)
   1565 
   1566     def EnableResizable(self, ctrlID):
   1567         jscript = '\n    <SCRIPT type=text/javascript>\n        $(function() {\n            $("#%(ctrlID)s").resizable({\n                handles: "se" });\n        });\n        </SCRIPT>' % {'ctrlID': ctrlID}
   1568         self.WriteDirect('jscript', jscript)
   1569 
   1570     def EnableFormValidation(self, formID):
   1571         jscript = '\n    $(document).ready(function() {\n        $("#%(formID)s").validate(%(formrules)s);\n    });\n        ' % {'formID': formID,
   1572          'formrules': '' if self.current_form_rules is None else '{rules: %s}' % self.current_form_rules}
   1573         self.WriteDirect('jscript', jscript)
   1574 
   1575     def EnableFormRules(self, rules = {}):
   1576         self.current_form_rules = self.toJavascriptDict(rules)
   1577 
   1578     def EnableTextEditor(self, ctrlID):
   1579         jscript = '\n        <script type="text/javascript">\n        $(function() {\n            $("#%(ctrlID)s").htmlarea();\n        })\n        </script>\n        ' % {'ctrlID': ctrlID}
   1580         self.Write(jscript)
   1581 
   1582     def EnableAccordion(self, ctrlID):
   1583         jscript = '\n    $(function() {\n        $("#%(ctrlID)s").accordion();\n    });\n        ' % {'ctrlID': ctrlID}
   1584         self.WriteDirect('jscript', jscript)
   1585 
   1586     def EnableChar(self):
   1587         jscript = 'var api = new jGCharts.Api();\n'
   1588         self.WriteDirect('jscript', jscript)
   1589 
   1590     def __EnableTabs(self, tabsID):
   1591         jscript = '\n    <script type="text/javascript">\n    $(function() {\n        $("#%(tabsID)s").tabs({\n            select: function(event, ui) {\n                window.location = "#"+ui.index\n            },\n            spinner: \'Retrieving data\'\n        });\n    });\n    $(function() {\n        var loc = window.location.toString().split("#");\n        if (loc.length == 2) {\n            $("#%(tabsID)s").tabs("option", "selected", parseInt(loc[1]));\n        }\n    });\n    </script>\n        ' % {'tabsID': tabsID}
   1592         self.Write(jscript)
   1593 
   1594     def GetStyleButton(self, href, innerText, cssClass = 'buttonblack'):
   1595         res = UnicodeMemStream()
   1596         if cssClass is not None:
   1597             cssClass = ' %s' % cssClass
   1598         res.Write('<a href="%s" class="button %s">' % (href, cssClass))
   1599         res.Write(innerText)
   1600         res.Write('</a>')
   1601         res.Seek(0)
   1602         return res.Read()
   1603 
   1604     def GetImage(self, src, ctrlID = None, className = None, style = None, title = None, rel = None):
   1605         ctrlID = CheckValue('id', ctrlID)
   1606         className = CheckValue('class', className)
   1607         style = CheckValue('style', style)
   1608         title = CheckValue('title', title)
   1609         rel = CheckValue('rel', rel)
   1610         return '<img src="%s" %s%s%s%s%s/>' % (src,
   1611          ctrlID,
   1612          className,
   1613          style,
   1614          title,
   1615          rel)
   1616 
   1617     def GetMessage(self, heading, label, cssClass):
   1618         if type(label) == types.ListType:
   1619             label = '<br/>' + '<br/>'.join(label)
   1620         return '<div class="message %s"><p><strong>%s</strong>&nbsp;%s</p></div>' % (cssClass, heading, label)
   1621 
   1622     def GetSuccess(self, label):
   1623         return self.GetMessage('Success!', label, 'success')
   1624 
   1625     def GetError(self, label):
   1626         return self.GetMessage('Error!', label, 'error')
   1627 
   1628     def GetWarning(self, label):
   1629         return self.GetMessage('Warning!', label, 'warning')
   1630 
   1631     def GetTip(self, label):
   1632         return self.GetMessage('Tip!', label, 'tip')
   1633 
   1634     def GetInfo(self, label):
   1635         return self.GetMessage('Info!', label, 'tip')
   1636 
   1637     def GetInlineHeader(self, ctrlID, innerText, rev):
   1638         return '<h3 id="%(ctrlID)s" class="editable" rev="%(rev)s">%(innerText)s</h3>' % {'ctrlID': ctrlID,
   1639          'rev': rev,
   1640          'innerText': innerText}
   1641 
   1642     def GetInlineArea(self, ctrlID, innerText, rev):
   1643         return '<p id="%(ctrlID)s" class="editable-area" rev="%(rev)s">%(innerText)s</p>' % {'ctrlID': ctrlID,
   1644          'rev': rev,
   1645          'innerText': innerText}
   1646 
   1647     def GetInlineHelp(self, label, header, body):
   1648         return '<span class="inlinehelp" title="%(header)s|%(body)s">%(label)s</span>' % {'label': label,
   1649          'header': header,
   1650          'body': body}
   1651 
   1652     def GetInline(self, ctrlID, innerText, rev):
   1653         return '<span id="%(ctrlID)s" class="editable" rev="%(rev)s">%(innerText)s</span>' % {'ctrlID': ctrlID,
   1654          'rev': rev,
   1655          'innerText': innerText}
   1656 
   1657     def GetWaitingMessage(self, message):
   1658         return WaitingMessage(message)
   1659 
   1660     def GetForm(self, ctrlID, formMethod, fileName, formAction, elements, multiPart = False, waitingMessage = None):
   1661         self.EnableFormValidation(ctrlID)
   1662         encType = ' enctype="multipart/form-data"' if multiPart else ''
   1663         formAction = '%(fileName)s?action=%(formAction)s' % {'fileName': fileName,
   1664          'formAction': formAction}
   1665         res = []
   1666         formMethod = str(formMethod).upper()
   1667         res.append(_GetElementBegin('form', ' id="%s" action="%s" method="%s"%s' % (ctrlID,
   1668          formAction,
   1669          formMethod,
   1670          encType)))
   1671         if formMethod not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
   1672             res.append(self.CSRFFormField())
   1673         if waitingMessage is not None:
   1674             res.append(self.GetWaitingMessage(waitingMessage))
   1675         for n in elements:
   1676             res.append(n)
   1677 
   1678         res.append(_GetElementEnd('form'))
   1679         return ''.join(res)
   1680 
   1681     def GetLayoutTable(self, ctrlID = None, rows = []):
   1682         res = UnicodeMemStream()
   1683         ctrlID = CheckValue('id', ctrlID)
   1684         res.Write('<div%s class="csp-table">' % ctrlID)
   1685         for r in rows:
   1686             res.Write('<div class="csp-row">')
   1687             if isinstance(r, list):
   1688                 for c in r:
   1689                     res.Write('<div class="csp-cell">%s</div>' % c)
   1690 
   1691             else:
   1692                 res.Write('<div class="csp-cell">%s</div>' % r)
   1693             res.Write('</div>')
   1694 
   1695         res.Write('</div>')
   1696         res.Seek(0)
   1697         return res.Read()
   1698 
   1699     def MaxPageNumber(self, current_page, page_size, num_of_records):
   1700         num_of_pages = num_of_records / page_size + (1 if num_of_records % page_size > 0 else 0)
   1701         if current_page > num_of_pages:
   1702             current_page = 1
   1703         return (current_page, num_of_pages)
   1704 
   1705     def GetPagingNext(self, nextOptions = {}, pageCount = 0, nextEnabled = True):
   1706         if nextEnabled:
   1707             url = urllib.urlencode(nextOptions)
   1708             return self.GetA(innerText='Next %s' % str(pageCount) if pageCount > 0 else '', href='?%s' % url, className='button buttonblue')
   1709         else:
   1710             return self.GetSpan(['Next %s' % str(pageCount) if pageCount > 0 else ''], className='dark-gray')
   1711 
   1712     def GetPagingFirstNext(self, firstOptions = {}, nextOptions = {}, pageCount = 0, firstEnabled = True, nextEnabled = True):
   1713         action = []
   1714         if firstEnabled:
   1715             url = urllib.urlencode(firstOptions)
   1716             action.append(self.GetA(innerText='First', href='?%s' % url, className='button buttonblue'))
   1717         else:
   1718             action.append(self.GetSpan(['First'], className='dark-gray'))
   1719         if nextEnabled:
   1720             url = urllib.urlencode(nextOptions)
   1721             action.append(self.GetA(innerText='Next %s' % str(pageCount) if pageCount > 0 else '', href='?%s' % url, className='button buttonblue'))
   1722         else:
   1723             action.append(self.GetSpan(['Next %s' % str(pageCount) if pageCount > 0 else ''], className='dark-gray'))
   1724         return ' | '.join(action)
   1725 
   1726     def GetPaging(self, current_page, page_size, num_of_records, page_sizes):
   1727         res = UnicodeMemStream()
   1728         num_of_pages = num_of_records / page_size + (1 if num_of_records % page_size > 0 else 0)
   1729         if page_size not in [0] + page_sizes:
   1730             page_sizes.append(page_size)
   1731         options = []
   1732         for p in page_sizes:
   1733             options.append('<option value="%(num)i"%(sel)s>%(num)i</option>' % {'num': p,
   1734              'sel': ' selected="selected"' if page_size == p else ''})
   1735 
   1736         options.append('<option value="0"%(sel)s>All</option>' % ' selected' if page_size is 0 else '')
   1737         res.Write('\n\n        <div class="%(tableid)s">\n            <span class="csp-paging"><img src="/img/ds_first.png" alt="First"/></span>\n            <span class="csp-paging"><img src="/img/ds_previous.png" alt="Previous"/></span>\n            <span class="csp-paging">&nbsp<input id="%(tableid)s_row" type="text" value="%(current_page)i" style="width:15px;"/> <span class="inlinehelp" title="Count: %(pages)i">of %(numofpages)i</span></span>\n            <span class="csp-paging"><img src="/img/ds_next.png" alt="Next"/></span>\n            <span class="csp-paging"><img src="/img/ds_last.png" alt="Last"/></span>\n            <span class="csp-paging">\n                &nbsp;<select id="%(tableid)s_numofpages">\n                    %(opt)s\n                </select>\n            </span>\n            <span class="csp-paging">&nbsp;<a class="csp-paging-submit button buttongray">Go</a></span>\n\n           <script type="text/javascript">\n                $(".csp-paging-submit").click(function(e) {\n                    p = parseInt($("#%(tableid)s_row").val());\n                    s = parseInt($("#%(tableid)s_numofpages").val());\n                    $.paginglocation(location.href, {"page": p, "pagesize": s});\n                });\n                $(".%(tableid)s").find("img").click(function(e) {\n                    var src = $(this).attr("src");\n                    p = parseInt($("#%(tableid)s_row").val());\n                    if (src.indexOf("ds_first") > 0 && p > 1) {\n                        $.paginglocation(location.href, {"page": 1, "pagesize": %(pagesize)i});\n                    }\n                    else if (src.indexOf("ds_previous") > 0 && p > 1) {\n                        $.paginglocation(location.href, {"page": p-1, "pagesize": %(pagesize)i});\n                    }\n                    else if (src.indexOf("ds_next") > 0 && p < %(numofpages)i) {\n                        $.paginglocation(location.href, {"page": p+1, "pagesize": %(pagesize)i});\n                    }\n                    else if (src.indexOf("ds_last") > 0) {\n                        $.paginglocation(location.href, {"page": %(numofpages)i, "pagesize": %(pagesize)i});\n                    }\n                });\n           </script>\n        </div>\n        ' % {'current_page': current_page,
   1738          'pages': num_of_records,
   1739          'pagesize': page_size,
   1740          'opt': '\n'.join(options),
   1741          'numofpages': num_of_pages,
   1742          'tableid': 'csp-table-paging'})
   1743         res.Seek(0)
   1744         return res.Read()
   1745 
   1746     def GetTreeTable(self, header = [], lines = [], index = 0, index_parent = 1):
   1747         id = 'table_%s' % str(random.randint(1, 1000))
   1748         res = UnicodeMemStream()
   1749         res.Write('\n            <script type="text/javascript">\n            $(document).ready(function()  {\n              $("#%(tableId)s").treeTable();\n            });\n            </script>\n        ' % {'tableId': id})
   1750         res.Write('<table id="%s" class="treeTable"><thead><tr>' % id)
   1751         res.Write(''.join([ '<th>%s</th>' % x for x in header ]))
   1752         res.Write('</tr></thead><tbody>')
   1753         for row in lines:
   1754             res.Write('<tr id="node-%i"%s>' % (row[index], '' if row[index_parent] is None else ' class="child-of-node-%i"' % row[index_parent]))
   1755             for i in range(0, len(row)):
   1756                 if index_parent != i:
   1757                     res.Write('<td%s>' % (' class="first"' if i == 0 else ''))
   1758                     res.Write(row[i])
   1759                     res.Write('</td>')
   1760 
   1761             res.Write('</tr>')
   1762 
   1763         res.Write('</tbody></table>')
   1764         res.Seek(0)
   1765         return res.Read()
   1766 
   1767     def GetPropertyTable(self, header = None, lines = None, style = 'width: 250px'):
   1768         lines = [ ['<strong>%s</strong>' % x, y] for x, y in lines.items() ]
   1769         return self.GetDiv([self.GetTable(header, lines, showCount=False)], style=style)
   1770 
   1771     def GetTable(self, header = None, lines = None, className = 'tablesorter', enableEdit = False, editUrl = None, enableDelete = False, deleteUrl = None, useFilter = False, invertSelection = False, paging = None, customActions = None, serverOrder = False, baseUrl = None, keyFields = None, readOnlyFields = None, showCount = True, cssByRow = None):
   1772         return GetTable(header, lines, className, enableEdit, editUrl, enableDelete, deleteUrl, useFilter, invertSelection, paging, customActions, serverOrder, baseUrl, keyFields, readOnlyFields, showCount, cssByRow)
   1773 
   1774     def GetDashboard(self, layout, area1, area2 = None, area3 = None, area4 = None, area5 = None, area6 = None, area7 = None, area8 = None, skin = True):
   1775         if layout not in ('dashboard0', 'dashboard1', 'dashboard2', 'dashboard3', 'dashboard4', 'dashboard5', 'dashboard6'):
   1776             raise Exception('GetDashboard(layout... only accepts one of those elements [dashboard0, dashboard1, dashboard2, dashboard3, dashboard4, dashboard5, dashboard6]')
   1777         layouts = {'dashboard0': '<div class="csp-row"><div class="csp-cell-dash">%(area1)s</div></div><div class="csp-row"><div class="csp-cell-dash">%(area2)s</div></div>',
   1778          'dashboard1': '<div class="csp-row"><div class="csp-cell-dash">%(area1)s</div><div class="csp-cell-dash">%(area2)s</div></div>',
   1779          'dashboard2': '<div class="csp-row"><div class="csp-cell-dash">%(area1)s</div><div class="csp-cell-dash">%(area2)s</div></div>\n                <div class="csp-row"><div class="csp-cell-dash">%(area3)s</div><div class="csp-cell-dash">%(area4)s</div></div>',
   1780          'dashboard3': '<div class="csp-row"><div class="csp-cell-dash">%(area1)s</div><div class="csp-cell-dash">%(area2)s</div></div>\n                <div class="csp-row"><div class="csp-cell-dash">%(area3)s</div><div class="csp-cell-dash"><div class="csp-table">\n                <div class="csp-row"><div class="csp-cell-dash">%(area4)s</div><div class="csp-cell-dash">%(area5)s</div></div></div></div></div>',
   1781          'dashboard4': '\n            <div class="csp-row">\n                <div class="csp-cell-dash">%(area1)s</div>\n                <div class="csp-cell-dash">%(area2)s</div>\n                <div class="csp-cell-dash">%(area3)s</div>\n            </div>\n            <div class="csp-row">\n                <div class="csp-cell-dash">%(area4)s</div>\n                <div class="csp-cell-dash">%(area5)s</div>\n                <div class="csp-cell-dash">%(area6)s</div>\n            </div>',
   1782          'dashboard5': '<div class="csp-row"><div class="csp-cell-dash">%(area1)s</div><div class="csp-cell-dash">%(area2)s</div>\n                <div class="csp-cell-dash">%(area3)s</div><div class="csp-cell-dash">%(area4)s</div></div>\n                <div class="csp-row"><div class="csp-cell-dash">%(area5)s</div><div class="csp-cell-dash">%(area6)s</div>\n                <div class="csp-cell-dash">%(area7)s</div><div class="csp-cell-dash">%(area8)s</div></div>',
   1783          'dashboard6': '\n                <div class="csp-row">\n                    <div class="csp-cell-dash">%(area1)s</div>\n                </div>\n            </div>\n            <div class="csp-table-dash">\n                <div class="csp-row">\n                    <div class="csp-cell-dash">%(area2)s</div>\n                    <div class="csp-cell-dash">%(area3)s</div>\n                </div>'}
   1784         cssHPart = CheckValue('class', 'csp-part-header' if skin is True else 'csp-part-header-noskin')
   1785         cssPart = CheckValue('class', 'csp-part' if skin is True else 'csp-part-noskin')
   1786         checkTuple = lambda x, y: '<div%(cssheader)s>%(h)s</div><div%(cssbody)s>%(b)s</div>' % {'h': x,
   1787          'b': y,
   1788          'cssheader': cssHPart,
   1789          'cssbody': cssPart}
   1790         checkIndex = lambda area: (''.join([ checkTuple(x, y) for x, y in area ]) if area is not None else '')
   1791         res = UnicodeMemStream()
   1792         res.Write('<div class="csp-table-dash">')
   1793         res.Write(layouts[layout] % {'area1': checkIndex(area1),
   1794          'area2': checkIndex(area2),
   1795          'area3': checkIndex(area3),
   1796          'area4': checkIndex(area4),
   1797          'area5': checkIndex(area5),
   1798          'area6': checkIndex(area6),
   1799          'area7': checkIndex(area7),
   1800          'area8': checkIndex(area8)})
   1801         res.Write('</div>')
   1802         res.Seek(0)
   1803         return res.Read()
   1804 
   1805     def GetProcessCssClass(self, percent, process_scale):
   1806         className = 'bar-high'
   1807         if process_scale == 3:
   1808             if percent < 34:
   1809                 className = 'bar-low'
   1810             elif percent < 67:
   1811                 className = 'bar-mid'
   1812             elif percent < 100:
   1813                 className = 'bar-high-mid'
   1814             elif percent > 99:
   1815                 className = 'bar-high'
   1816         elif process_scale == 5:
   1817             if percent < 26:
   1818                 className = 'bar-low'
   1819             elif percent < 51:
   1820                 className = 'bar-mid-low'
   1821             elif percent < 76:
   1822                 className = 'bar-mid'
   1823             elif percent < 100:
   1824                 className = 'bar-high-mid'
   1825         return className
   1826 
   1827     def GetCellBullet(self, percent, process_scale = 3):
   1828         className = self.GetProcessCssClass(percent, process_scale)
   1829         className = CheckValue('class', className)
   1830         return '<div class="csp-bullet"><div %(className)sstyle="width: 100%%"></div></div>' % {'className': className}
   1831 
   1832     def GetCellProcess(self, percent, process_scale = 3):
   1833         className = self.GetProcessCssClass(percent, process_scale)
   1834         className = CheckValue('class', className)
   1835         return '<div class="csp-progress"><div title="%(percenttext)s%%" %(className)sstyle="width: %(percent)s%%"></div></div>' % {'percent': min(int(percent), 100),
   1836          'percenttext': int(percent),
   1837          'className': className}
   1838 
   1839     def GetFieldset(self, ctrlID = None, elements = [], label = None):
   1840         res = []
   1841         res.append(_GetElementBegin('fieldset', ''))
   1842         if label:
   1843             res.append('<legend>%s</legend>' % label)
   1844         for n in elements:
   1845             res.append(n)
   1846 
   1847         res.append(_GetElementEnd('fieldset'))
   1848         return '\n'.join(res)
   1849 
   1850     def GetSimpleForm(self, formMethod = 'post', fileName = '', formAction = '', elements = None, waitingMessage = None, closeForm = True):
   1851         ctrlID = 'form_%s' % str(random.randint(1, 1000))
   1852         self.EnableFormValidation(ctrlID)
   1853         formAction = '%(fileName)s?action=%(formAction)s' % {'fileName': fileName,
   1854          'formAction': formAction}
   1855         res = []
   1856         formMethod = str(formMethod).upper()
   1857         res.append(_GetElementBegin('form', ' id="%s" action="%s" method="%s"' % (ctrlID, formAction, str(formMethod).upper())))
   1858         if formMethod not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
   1859             res.append(self.CSRFFormField())
   1860         if waitingMessage is not None:
   1861             res.append(self.GetWaitingMessage(waitingMessage))
   1862         res.append(_GetElementBegin('div', ''))
   1863         for n in elements:
   1864             res.append(self.GetP([n]))
   1865 
   1866         res.append(_GetElementEnd('div'))
   1867         if closeForm:
   1868             res.append(_GetElementEnd('form'))
   1869         return '\n'.join(res)
   1870 
   1871     def GetSimpleDialogForm(self, formMethod = 'post', fileName = '', formAction = '', elements = None, height = 140, width = 300, title = '', autoOpen = True, ctrlID = None, submitButton = 'Submit'):
   1872         if ctrlID is None:
   1873             ctrlID = 'form_%i' % random.randint(1, 1000)
   1874         m_ctrlID = 'dialog-modal_%s' % ctrlID
   1875         self.Write('<script type="text/javascript">\n    $(function() {\n        $( "#%(ctrlID)s" ).dialog({\n            autoOpen: %(autoOpen)s,\n            height: %(height)s,\n            width: %(width)s,\n            modal: true,\n            buttons: {\n                "%(submitButton)s" : function() {\n                    $("#%(formID)s").submit();\n                },\n                Cancel: function() {\n                    $(this).dialog("close");\n                }\n            }\n        });\n    });\n    </script>' % {'ctrlID': m_ctrlID,
   1876          'height': height,
   1877          'width': width,
   1878          'autoOpen': str(autoOpen).lower(),
   1879          'submitButton': submitButton,
   1880          'formID': ctrlID})
   1881         form = self.GetForm(ctrlID=ctrlID, formMethod=formMethod, fileName=fileName, formAction=formAction, elements=elements)
   1882         return self.GetDiv([form], ctrlID=m_ctrlID, title=title)
   1883 
   1884     def GetHidden(self, name, value = ''):
   1885         return '<input type="hidden" name="%(name)s" id="%(name)s" value="%(value)s"/>' % {'name': name,
   1886          'value': value}
   1887 
   1888     def GetDiv(self, elements, ctrlID = None, className = None, style = None, title = None, dataBind = None):
   1889         return _GetElements(elements, 'div', ctrlID, className, style, title, dataBind)
   1890 
   1891     def GetSpan(self, elements, ctrlID = None, className = None, style = None, title = None, dataBind = None):
   1892         return GetSpan(elements, ctrlID, className, style, title, dataBind)
   1893 
   1894     def GetP(self, elements, ctrlID = None, className = None, style = None):
   1895         return _GetElements(elements, 'p', ctrlID, className, style)
   1896 
   1897     def GetUl(self, elements, ctrlID = None, className = None, style = None):
   1898         return _GetElements(elements, 'ul', ctrlID, className, style)
   1899 
   1900     def GetLi(self, elements, ctrlID = None, className = None, style = None):
   1901         return _GetElements(elements, 'li', ctrlID, className, style)
   1902 
   1903     def GetA(self, innerText, href, ctrlID = None, className = None, style = None, onClick = None, title = None, rel = None, rev = None, target = None):
   1904         return GetA(innerText, href, ctrlID, className, style, onClick, title, rel, rev, target)
   1905 
   1906     def H1(self, innerText, ctrlID = None, className = None, style = None):
   1907         return _GetElement(ctrlName='h1', ctrlValue=innerText, ctrlID=ctrlID, className=className, style=style)
   1908 
   1909     def H2(self, innerText, ctrlID = None, className = None, style = None):
   1910         return _GetElement(ctrlName='h2', ctrlValue=innerText, ctrlID=ctrlID, className=className, style=style)
   1911 
   1912     def H3(self, innerText, ctrlID = None, className = None, style = None):
   1913         return _GetElement(ctrlName='h3', ctrlValue=innerText, ctrlID=ctrlID, className=className, style=style)
   1914 
   1915     def H4(self, innerText, ctrlID = None, className = None, style = None):
   1916         return _GetElement(ctrlName='h4', ctrlValue=innerText, ctrlID=ctrlID, className=className, style=style)
   1917 
   1918     def GetFileInput(self, ctrlID, labelCaption, className = None, width = None, title = ''):
   1919         if width:
   1920             width = ' style="width:%spx"' % width
   1921         else:
   1922             width = ''
   1923         className = CheckValue('class', className)
   1924         title = CheckValue('title', title)
   1925         res = []
   1926         if labelCaption is not None:
   1927             res.append('<label class="form-label" for="%s">%s</label>' % (ctrlID, labelCaption))
   1928         res.append('<input id="%(ctrlID)s" name="%(ctrlID)s" type="file" %(className)s%(width)s%(title)s/>' % {'ctrlID': ctrlID,
   1929          'className': className,
   1930          'width': width,
   1931          'title': title})
   1932         return '\n'.join(res)
   1933 
   1934     def GetInput(self, ctrlID, labelCaption, className = None, minLength = None, width = None, value = None, title = ''):
   1935         return GetInput(ctrlID, labelCaption, className, minLength, width, value, title)
   1936 
   1937     def GetSelect(self, elements, controlID, labelCaption = None, elementClass = None, selectedValue = None, attributes = None):
   1938         return GetSelect(elements, controlID, labelCaption, elementClass, selectedValue, attributes)
   1939 
   1940     def GetToggle(self):
   1941         return self.GetCheck(ctrlID='', labelCaption='', className='toggle-table', checked=False)
   1942 
   1943     def WriteCheckAndColoredLabel(self, label, isChecked = False, isDisabled = True, isSmall = True, isSubContainer = False):
   1944         label = self.FontBoldGreen(label) if isChecked else self.FontBoldBrown(label)
   1945         self.WriteCheckAndLabel(label, isChecked, isDisabled, isSmall, isSubContainer)
   1946 
   1947     def WriteCheckAndLabel(self, label, isChecked = False, isDisabled = False, isSmall = True, isSubContainer = False):
   1948         checkClass = 'checkbox-small' if isSmall else 'checkbox-big'
   1949         check = self.GetCheck(ctrlID='', labelCaption='', className=checkClass, checked=isChecked, disabled=isDisabled)
   1950         containerClass = 'flex-subcontainer' if isSubContainer else 'flex-container'
   1951         self.WriteInContainer(html=check + label, className=containerClass)
   1952 
   1953     def GetCheck(self, ctrlID, labelCaption, className, checked, disabled = False):
   1954         className = CheckValue('class', className)
   1955         title = CheckValue('title', labelCaption)
   1956         controlID = CheckValue('id', ctrlID)
   1957         controlName = CheckValue('name', ctrlID)
   1958         res = []
   1959         if labelCaption is not None and len(labelCaption) > 0:
   1960             res.append('<label class="form-label" for="%s">%s</label>' % (ctrlID, labelCaption))
   1961         che = ' checked="checked"' if checked else ''
   1962         dis = ' disabled' if disabled else ''
   1963         res.append('<input type="checkbox"%s%s%s%s%s%s/>' % (controlID,
   1964          controlName,
   1965          className,
   1966          che,
   1967          title,
   1968          dis))
   1969         return '\n'.join(res)
   1970 
   1971     def GetTextArea(self, ctrlID, labelCaption, className, cols, rows, innerHtml = None):
   1972         return GetTextArea(ctrlID, labelCaption, className, cols, rows, innerHtml)
   1973 
   1974     def GetButton(self, className, elementValue):
   1975         return '<input class="%(className)s" type="submit" value="%(elementValue)s" />' % {'className': className,
   1976          'elementValue': elementValue}
   1977 
   1978     def GetTabs(self, tabs, content, ctrlID):
   1979         self.__EnableTabs(ctrlID)
   1980         tabs = ['<ul>'] + tabs + ['</ul>'] + content
   1981         return self.GetDiv(tabs, ctrlID)
   1982 
   1983     def GetTabsAjax(self, links):
   1984         ctrlID = 'tabs_%s' % str(random.randint(1, 1000))
   1985         self.__EnableTabs(ctrlID)
   1986         elements = []
   1987         elements.append('<ul>')
   1988         for l in links:
   1989             if len(l[0]):
   1990                 elements.append('<li><a href="%s"><span>%s</span></a></li>' % (l[0], l[1]))
   1991             else:
   1992                 elements.append('<li><span>%s</span></li>' % (l[1],))
   1993 
   1994         elements.append('</ul>')
   1995         return self.GetDiv(elements, ctrlID=ctrlID, className=None, style=None)
   1996 
   1997     def GetTab(self, ctrlID, label):
   1998         return self.GetLi([self.GetA(label, '#%s' % ctrlID)], None)
   1999 
   2000     def GetFloatMessage(self, messageText):
   2001         return '\n        <div class="messagebox information">\n            Information<br />\n            <strong>%s</strong>\n        </div>\n        <script type="text/javascript">\n            var options = {};\n            $(".messagebox").show("blind", options, 1500, showCallback);\n\n            function showCallback() {\n                $(".messagebox").hide("blind", options, 1500);\n            }\n        </script>\n        ' % messageText
   2002 
   2003     def GetTooltip(self, href, ajax, title, caption, ctrlID = None):
   2004         return self.GetA(innerText=caption, href=href, ctrlID=ctrlID, className='a-tip', style=None, onClick=None, title=title, rel=ajax)
   2005 
   2006     def GetTooltipActions(self, href, ajax, title, caption, actions = [], ctrlID = None):
   2007         html = '<div class="csp-dropmenu"><span>%(tooltip)s<em class="csp-opener"><img alt="dropdown" src="/img/down.png"></em>\n                    <ul class="csp-sublist">%(links)s</ul></span></div>' % {'tooltip': self.GetTooltip(href, ajax, title, caption + ' &nbsp;'),
   2008          'links': ''.join([ '<li>%s</li>' % x for x in actions ])}
   2009         return html
   2010 
   2011     def Write(self, html):
   2012         if html is not None:
   2013             if self.redirect not in self.inserts:
   2014                 ms = UnicodeMemStream()
   2015                 self.inserts[self.redirect] = ms
   2016             else:
   2017                 ms = self.inserts[self.redirect]
   2018             if isinstance(html, list):
   2019                 for htmlItem in html:
   2020                     ms.Write(htmlItem)
   2021 
   2022             elif isinstance(html, UnicodeMemStream):
   2023                 html.Seek(0)
   2024                 ms.Write(html.Read())
   2025             else:
   2026                 ms.Write(html)
   2027 
   2028     def WriteFloatMessage(self, messageText):
   2029         self.Write(self.GetFloatMessage(messageText))
   2030 
   2031     def WriteBeginForm(self, formName, formMethod, formAction):
   2032         self.Write('<form id="%s" method="%s" action="%s">' % (formName, formMethod, formAction))
   2033 
   2034     def WriteEndForm(self):
   2035         self.Write('</form>')
   2036 
   2037     def GetDatePicker(self, ctrlID, ctrlCaption, ctrlFormat = 'yy-mm-dd', ctrlValue = None, ctrlTitle = None):
   2038         now = datetime.now()
   2039         res = []
   2040         res.append('<SCRIPT type=text/javascript>\n            $(function() {\n                $("#%(ctrlID)s").datepicker({\n                    dateFormat: \'%(ctrlFormat)s\',\n                    changeMonth: true,\n                    changeYear: true,\n                    showWeek: true,\n                    showOn: \'button\',\n                    yearRange: \'%(fromYear)i:%(toYear)s\'\n                    });\n            });\n            </SCRIPT>\n' % {'ctrlID': ctrlID,
   2041          'ctrlFormat': ctrlFormat,
   2042          'fromYear': now.year - 85,
   2043          'toYear': now.year + 35})
   2044         ctrlValue = CheckValue('value', ctrlValue)
   2045         ctrlTitle = CheckValue('title', ctrlTitle)
   2046         if ctrlCaption:
   2047             res.append('<label class="form-label" for="%(ctrlID)s">%(ctrlCaption)s</label>' % {'ctrlID': ctrlID,
   2048              'ctrlCaption': ctrlCaption})
   2049         res.append('<input id="%(ctrlID)s" name="%(ctrlID)s" type="text" style="width: 80px;"%(ctrlValue)s%(ctrlTitle)s/>' % {'ctrlID': ctrlID,
   2050          'ctrlValue': ctrlValue,
   2051          'ctrlTitle': ctrlTitle})
   2052         return ''.join(res)
   2053 
   2054     def GetDateTimePicker(self, ctrlID, caption, value = None, title = None):
   2055         self.AddJsFile('/static/js/libs/jquery.datetimepicker.js')
   2056         self.AddCssFile('/static/css/libs/jquery.datetimepicker.css')
   2057         script = "<script> $('#{}').datetimepicker({{step:15, format:'Y.m.d H:i'}}); </script>".format(ctrlID)
   2058         field = self.GetInput(ctrlID=ctrlID, labelCaption=caption, className=None, minLength=None, width='150', value=value, title=title)
   2059         return field + script
   2060 
   2061     def GetAjaxTree(self, ctrlID, url, child_function, search_function = None, create_function = None, remove_function = None, rename_function = None, move_function = None, contextMenu = None):
   2062         mem = UnicodeMemStream()
   2063         contextMenu = ' "contextmenu": { "items": %s },  ' % contextMenu if contextMenu is not None else ''
   2064         mem.Write('\n<script type="text/javascript">\n$(function () {\n        if (!$.jstree) {\n            alert("Tree is disabled, please contact help desk!")\n        } else {\n        $("#%(ctrlID)s")\n        .jstree({\n            "plugins" : [ "themes", "json_data", "ui", "crrm", "dnd", "search", "types", "hotkeys", "contextmenu" ],\n            %(contextMenu)s\n            "json_data" : {\n                "ajax" : {\n                    "url" : "%(url)s",\n                    "data" : function (n) {\n                        return { "action" : "%(child_function)s", "id" : n.attr ? n.attr("id") : -1 };\n                    }}},' % {'ctrlID': ctrlID,
   2065          'url': url,
   2066          'child_function': child_function,
   2067          'contextMenu': contextMenu})
   2068         if search_function is not None:
   2069             mem.Write('\n                "search" : {\n                    "ajax" : {\n                        "url" : "%(url)s",\n                        "data" : function (str) {\n                            return { "action" : "%(search_function)s", "search" : str };\n                        }}},' % {'url': url,
   2070              'search_function': search_function})
   2071         mem.Write('\n            "types" : {\n                "max_depth" : -2, "max_children" : -2, "valid_children" : [ "root" ],\n                "types" : {\n                    "default" : { "valid_children" : "none", "icon" : { "image" : "/img/folder.png" } },\n                    "file" : { "valid_children" : [ ],\n                        "icon" : { "image" : "/img/file.png" }\n                    },\n                    "folder" : { "valid_children" : [ "default", "folder", "file" ],\n                        "icon" : { "image" : "/img/folder.png" }\n                    },\n                    "root" : {\n                        "valid_children" : [ "default", "folder", "file" ],\n                        "icon" : { "image" : "/img/root.png" },\n                        "start_drag" : false, "move_node" : false, "delete_node" : false, "remove" : false\n                    }\n                }\n            }\n        })')
   2072         if create_function is not None:
   2073             mem.Write('\n            .bind("create.jstree", function (e, data) {\n                $.post(\n                    "%(url)s?action=%(create_function)s",\n                    {\n                        "id" : data.rslt.parent.attr("id"),\n                        "position" : data.rslt.position,\n                        "title" : data.rslt.name,\n                        "type" : data.rslt.obj.attr("rel")\n                    },\n                    function (r) {\n                        if(r.status) {\n                            $(data.rslt.obj).attr("id", r.id);\n                            $(data.rslt.obj).attr("rel", r.type);\n                        } else { $.jstree.rollback(data.rlbk); }\n                    }\n                );\n            })' % {'url': url,
   2074              'create_function': create_function})
   2075         if remove_function is not None:
   2076             mem.Write('\n            .bind("remove.jstree", function (e, data) {\n                data.rslt.obj.each(function () {\n                    $.ajax({\n                        async : false,\n                        type: \'POST\',\n                        url: "%(url)s?action=%(remove_function)s",\n                        data : {\n                            "id" : this.id\n                        },\n                        success : function (r) {\n                            if(!r.status) { data.inst.refresh(); }\n                        }\n                    });\n                });\n            })' % {'url': url,
   2077              'remove_function': remove_function})
   2078         if rename_function is not None:
   2079             mem.Write('\n            .bind("rename.jstree", function (e, data) {\n                $.post(\n                    "%(url)s?action=%(rename_function)s",\n                    {\n                        "id" : data.rslt.obj.attr("id"),\n                        "title" : data.rslt.new_name\n                    },\n                    function (r) {\n                        if(!r.status) { $.jstree.rollback(data.rlbk); }\n                    }\n                );\n            })' % {'url': url,
   2080              'rename_function': rename_function})
   2081         if move_function is not None:
   2082             mem.Write('\n            .bind("move_node.jstree", function (e, data) {\n                data.rslt.o.each(function (i) {\n                    $.ajax({\n                        async : false,\n                        type: \'POST\',\n                        url: "%(url)s?action=%(move_function)s",\n                        data : {\n                            "id" : $(this).attr("id"),\n                            "ref" : data.rslt.np.attr("id"),\n                            "position" : data.rslt.cp + i,\n                            "title" : data.rslt.name,\n                            "copy" : data.rslt.cy ? 1 : 0\n                        },\n                        success : function (r) {\n                            if(!r.status) { $.jstree.rollback(data.rlbk); }\n                            else {\n                                $(data.rslt.oc).attr("id", r.id);\n                                if(data.rslt.cy && $(data.rslt.oc).children("UL").length) {\n                                    data.inst.refresh(data.inst._get_parent(data.rslt.oc));\n                                }\n                            }\n                        }\n                    });\n                });\n            });' % {'url': url,
   2083              'move_function': move_function})
   2084         mem.Write('\n}});\n</script>')
   2085         mem.Seek(0)
   2086         self.WriteDirect('jscript', mem.Read())
   2087         return '<div id="%s"><a href="base:%s" style="display:hidden;"></a></div>' % (ctrlID, url)
   2088 
   2089     def GetDatetimeWithDelta(self, dt):
   2090         if dt is None:
   2091             return
   2092         elif dt < datetime.now():
   2093             delta = datetime.now() - dt
   2094             return '<code>{0}</code> <font color=lightgrey>({1} ago)</font>'.format(dt, deltastr(delta))
   2095         else:
   2096             delta = dt - datetime.now()
   2097             return '<code>{0}</code> <font color=lightgrey>(In {1})</font>'.format(dt, deltastr(delta))
   2098 
   2099     def GetTree(self, ctrlID, data, expand = []):
   2100         caseCon = []
   2101         treeCon = []
   2102         treeCon.append('<input type="hidden" id="%s" value="" />' % ctrlID)
   2103         treeCon.append('<div id="%s_container">' % ctrlID)
   2104         if_then = lambda a, b: (b if a is None else '%s:%s' % (a, b))
   2105         recFunc = lambda idel, label, url: '<li id="li_%s"><a href="%s">%s</a>' % (idel, if_then(url, idel), label)
   2106         level = {}
   2107         last = -1
   2108         for idel, par, lab, url in data:
   2109             if idel not in level:
   2110                 if par not in level:
   2111                     level[par] = 0
   2112                     level[idel] = 0
   2113                 else:
   2114                     level[idel] = level[par] + 1
   2115             if last < level[idel]:
   2116                 treeCon.append('<ul>')
   2117             elif last == level[idel]:
   2118                 treeCon.append('</li>')
   2119             if url is not None and url.startswith('js:'):
   2120                 caseCon.append('case "%s": %s(%s); break;' % (idel, url.split(':')[1], idel))
   2121             if level[idel] < last:
   2122                 for x in range(0, last - level[idel]):
   2123                     treeCon.append('</ul></li>')
   2124 
   2125             treeCon.append(recFunc(idel, lab, url))
   2126             last = level[idel]
   2127 
   2128         treeCon.append('</div>')
   2129         js = '\n        <script type="text/javascript">\n        $("#%(ctrlID)s_container").bind("select_node.jstree", function (event, data) {\n                         var mar = data.args[0];\n                         try {\n                         if (mar.href != undefined) {\n                             var myargs = mar.href.split(\':\');\n                             if (myargs[0] == "url") {\n                                window.location.href = mar.href.split(\':\')[1]+":"+mar.href.split(\':\')[2];\n                                return;\n                             } else if (myargs[0] == "js") {\n                                switch(myargs[myargs.length-1])\n                                {\n                                    %(caselines)s\n                                }\n                             }\n                             var myargs = marhref.split(\'/\');\n                             a = myargs[myargs.length-1]\n                             $("#%(ctrlID)s").val(a);\n                         }\n                         } catch(err) {}\n                    })\n        $("#%(ctrlID)s_container")\n            .jstree({\n                "core" : { "initially_open"  : %(expand)s },\n                "plugins" : ["themes","html_data","ui","crrm","hotkeys"]\n        });\n        </script>\n        ' % {'ctrlID': ctrlID,
   2130          'expand': [ 'li_%s' % x for x in expand ],
   2131          'caselines': '\n'.join(caseCon)}
   2132         return ''.join([''.join(treeCon), js])
   2133 
   2134     def GetAutoComplete(self, ctrlID, ctrlLabel, callbackPy, minLength = 4, labelClass = 'form-label'):
   2135         jscript = '\n    <script type="text/javascript">\n    $(function() {\n        var cache_%(ctrlID)s = {};\n        $("#%(ctrlID)s_ac").autocomplete({\n            select: function(event,ui) {\n                $("#%(ctrlID)s").val(ui.item.id);\n                $("#%(ctrlID)s_sr").html(" <span class=\'inlinehelp\' title=\'"+ui.item.label+"\' style=\'display: none\'> Selected: "+ui.item.id+"</span>");\n            },\n            source: function(request, response) {\n\n                if (cache_%(ctrlID)s.term == request.term && cache_%(ctrlID)s.content) {\n                    response(cache_%(ctrlID)s.content);\n                    return;\n                }\n                if (new RegExp(cache_%(ctrlID)s.term).test(request.term) && cache_%(ctrlID)s.content && cache_%(ctrlID)s.content.length < 13) {\n                    response($.ui.autocomplete.filter(cache_%(ctrlID)s.content, request.term));\n                    return;\n                }\n                $.ajax({\n                    url: "%(callbackPy)s",\n                    dataType: "json",\n                    data: request,\n                    success: function(data) {\n                        if ( data.length == 0 ) {\n                            var no = parseInt(request.term);\n                            if (!isNaN(no)) {\n                                response($.parseJSON(\'[{"id":"\'+no+\'","label":"\'+no+\'"}]\'));\n                                $("#%(ctrlID)s").val(no);\n                                $("#%(ctrlID)s_sr").html(" <span class=\'inlinehelp\' title=\'"+no+"\'> Selected: "+no+"</span>");\n                                return;\n                            }\n                        }\n                        cache_%(ctrlID)s.term = request.term;\n                        cache_%(ctrlID)s.content = data;\n                        response(data);\n                    }\n                });\n            },\n            minLength: %(minLength)s\n        });\n    });\n    </script>' % {'ctrlID': ctrlID,
   2136          'callbackPy': callbackPy,
   2137          'minLength': str(minLength)}
   2138         labelClass = CheckValue('class', labelClass)
   2139         return ''.join([jscript,
   2140          '<input type="hidden" id="%(ctrlID)s" name="%(ctrlID)s" value="" />' % {'ctrlID': ctrlID},
   2141          '<label %(labelClass)s for="%(ctrlID)s_ac">%(ctrlLabel)s</label>' % {'labelClass': labelClass,
   2142           'ctrlID': ctrlID,
   2143           'ctrlLabel': ctrlLabel} if ctrlLabel else '',
   2144          '<input id="%(ctrlID)s_ac"/>' % {'ctrlID': ctrlID},
   2145          '<span id="%(ctrlID)s_sr" class=""></span>' % {'ctrlID': ctrlID}])
   2146 
   2147     def SetAutoCompleteDefault(self, ctrlID, defaultValue, defaultName = None):
   2148         self.Write("<script>$(function(){ $('#%s_ac').val('%s'); $('#%s').val(%s); });</script>" % (ctrlID,
   2149          defaultName or defaultValue,
   2150          ctrlID,
   2151          defaultValue))
   2152 
   2153     def GetGrowl(self, innerText, header, body):
   2154         return self.GetA(innerText=innerText, href='#', onClick="javascript:addNotice('<strong>%s</strong><p>%s<p>')" % (header, body))
   2155 
   2156     def RecordToDateTime(self, r):
   2157         year, month, wd, day, hour, mi, sec, ms = blue.os.GetTimeParts(r)
   2158         return datetime(year, month, day, hour, mi, sec, ms)
   2159 
   2160     def WriteTable(self, header = None, lines = None, className = 'tablesorter', enableEdit = False, editUrl = None, enableDelete = False, deleteUrl = None, useFilter = False, invertSelection = False, paging = None, customActions = None, serverOrder = False, baseUrl = None, keyFields = None, readOnlyFields = None, showCount = True, maxRows = None, title = None, ctrlID = None, cssByRow = None, paddingLeft = 0):
   2161         if maxRows:
   2162             if len(lines) == maxRows:
   2163                 if customActions is None:
   2164                     customActions = []
   2165                 customActions.append(self.FontRed('ONLY TOP %s RECORDS ARE LISTED' % maxRows))
   2166         t = self.GetTable(header, lines, showCount=showCount, customActions=customActions, useFilter=useFilter, invertSelection=invertSelection, paging=paging, enableEdit=enableEdit, editUrl=editUrl, enableDelete=enableDelete, deleteUrl=deleteUrl, serverOrder=serverOrder, baseUrl=baseUrl, keyFields=keyFields, readOnlyFields=readOnlyFields, className=className, cssByRow=cssByRow)
   2167         if title is None:
   2168             self.WriteWithLeftPadding(t, paddingLeft=paddingLeft)
   2169         else:
   2170             if ctrlID is None:
   2171                 if title is None or title == '':
   2172                     ctrlID = 'wp_1'
   2173                 else:
   2174                     ctrlID = 'wp_' + title.lower().replace(' ', '')
   2175             self.WriteWithLeftPadding(self.WebPart(title, t, ctrlID), paddingLeft=paddingLeft)
   2176 
   2177     def WriteForm(self, ctrlID, formMethod, fileName, formAction, elements, waitingMessage = None):
   2178         self.Write(self.GetForm(ctrlID, formMethod, fileName, formAction, elements, waitingMessage))
   2179 
   2180     def WriteSimpleForm(self, formMethod = 'post', fileName = '', formAction = '', elements = [], waitingMessage = None):
   2181         self.Write(self.GetSimpleForm(formMethod, fileName, formAction, elements, waitingMessage))
   2182 
   2183     def WriteDatePicker(self, ctrlID, ctrlCaption, ctrlFormat = 'dd-mm-yy', ctrlValue = None, ctrlTitle = None):
   2184         self.Write(self.GetDatePicker(ctrlID, ctrlCaption, ctrlFormat, ctrlValue, ctrlTitle))
   2185 
   2186     def WriteDateTimePicker(self, ctrlDateID, ctrlCaption, ctrlValue = None, ctrlTitle = None):
   2187         self.Write(self.GetDateTimePicker(ctrlID=ctrlDateID, caption=ctrlCaption, value=ctrlValue, title=ctrlTitle))
   2188 
   2189     def WriteAutoComplete(self, ctrlID, ctrlLabel, callbackPy):
   2190         self.Write(self.GetAutoComplete(ctrlID, ctrlLabel, callbackPy))
   2191 
   2192     def WriteAccordion(self, controlId, headers, bodies):
   2193         self.EnableAccordion(controlId)
   2194         self.Write('<div id="%s">' % controlId)
   2195         for i in range(0, len(headers)):
   2196             self.Write('<h3><a href="#">%s</a></h3>' % headers[i])
   2197             self.Write('<div><p>%s</p></div>' % bodies[i])
   2198 
   2199         self.Write('</div>')
   2200 
   2201     def WriteInContainer(self, html, className = 'flex-container'):
   2202         self.Write('<div class="%s">' % className)
   2203         self.Write(html)
   2204         self.Write('</div>')
   2205 
   2206     def WriteWithLeftPadding(self, html, paddingLeft):
   2207         self.Write('<div style="padding-left: %dpx">' % paddingLeft)
   2208         self.Write(html)
   2209         self.Write('</div>')
   2210 
   2211     def WriteLn(self, html, breaks = 1):
   2212         self.Write(html + '<br/>' * breaks)
   2213 
   2214     def WriteLi(self, html):
   2215         self.Write('   <li>%s</li>\n' % html)
   2216 
   2217     def WriteH1(self, html):
   2218         self.Write(self.H1(html))
   2219 
   2220     def WriteH2(self, html):
   2221         self.Write(self.H2(html))
   2222 
   2223     def WriteH3(self, html):
   2224         self.Write(self.H3(html))
   2225 
   2226     def WriteH4(self, html):
   2227         self.Write(self.H4(html))
   2228 
   2229     def WriteNotice(self, title, txt):
   2230         if type(txt) == types.ListType:
   2231             self.Write('<br/>' + self.GetMessage(title, txt, 'tip'))
   2232         else:
   2233             self.Write('<br/>' + self.GetMessage(title, '<br/>' + txt, 'tip'))
   2234 
   2235     def WriteError(self, txt):
   2236         self.Write('<br/>' + self.GetError(txt))
   2237 
   2238     def WriteWarning(self, txt):
   2239         self.Write('<br/>' + self.GetWarning(txt))
   2240 
   2241     def WriteSuccess(self, txt):
   2242         self.Write('<br/>' + self.GetSuccess(txt))
   2243 
   2244     def WriteTip(self, txt):
   2245         self.Write('<br/>' + self.GetTip(txt))
   2246 
   2247     def WriteInfo(self, txt):
   2248         self.Write('<br/>' + self.GetInfo(txt))
   2249 
   2250     def WriteDirect(self, redirect, html):
   2251         if html is not None:
   2252             if redirect not in self.inserts:
   2253                 ms = UnicodeMemStream()
   2254                 self.inserts[redirect] = ms
   2255             else:
   2256                 ms = self.inserts[redirect]
   2257             if isinstance(html, list):
   2258                 for htmlItem in html:
   2259                     ms.Write(htmlItem)
   2260 
   2261             elif isinstance(html, UnicodeMemStream):
   2262                 html.Seek(0)
   2263                 ms.Write(html.Read())
   2264             elif isinstance(ms, basestring):
   2265                 self.inserts[redirect] += html
   2266             else:
   2267                 ms.Write(html)
   2268 
   2269     def Break(self, num = 1):
   2270         return '<br />\n' * num
   2271 
   2272     def WriteBreak(self, num = 1):
   2273         self.Write(self.Break(num))
   2274 
   2275     def WriteScript(self, html):
   2276         self.WriteDirect('jscript', html)
   2277 
   2278     def Split(self, breaks = None):
   2279         html = ''
   2280         if breaks:
   2281             html += self.Break(breaks)
   2282         html += '<hr />'
   2283         if breaks:
   2284             html += self.Break(breaks)
   2285         return html
   2286 
   2287     def WriteSplit(self, breaks = None):
   2288         self.Write(self.Split(breaks))
   2289 
   2290     def WriteAnchor(self, anchor):
   2291         self.Write('<a name="%s"></a>\n' % anchor)
   2292 
   2293     def NoBreak(self, txt):
   2294         return '<nobr>%s</nobr>' % txt
   2295 
   2296     def Preformatted(self, txt):
   2297         return '<pre>%s</pre>' % txt
   2298 
   2299     def Paragraph(self, p, style = '', ctrlID = '', className = ''):
   2300         ms = UnicodeMemStream()
   2301         ms.Write('<p%s%s%s>' % (CheckValue('id', ctrlID), CheckValue('style', style), CheckValue('class', className)))
   2302         if isinstance(p, types.ListType):
   2303             for element in p:
   2304                 ms.Write(element)
   2305 
   2306         else:
   2307             ms.Write(p)
   2308         ms.Write('</p>')
   2309         ms.Seek(0)
   2310         return ms.Read()
   2311 
   2312     def WriteParagraph(self, p, style = '', ctrlID = '', className = ''):
   2313         self.Write(self.Paragraph(p, style, ctrlID, className))
   2314 
   2315     def WriteAligned(self, align, paragraph):
   2316         self.Write('<div align="%s">%s</div>' % (align, paragraph))
   2317 
   2318     def LinkAction(self, bold, linkText, linkAction, linkAttribute, linkID):
   2319         if bold:
   2320             linkText = '<strong>%s</strong>' % linkText
   2321         return self.Link('', linkText, {'action': linkAction,
   2322          linkAttribute: linkID})
   2323 
   2324     def WriteRightActions(self, actions):
   2325         self.Write('<ul class="csp-action-list right">')
   2326         self.Write([ '<li>%s</li>' % x for x in actions ])
   2327         self.Write('</ul><div class="clear-both"></div>')
   2328 
   2329     def AddNavbar(self, html):
   2330         self.inserts['navigation'] = self.inserts['navigation'] + ' ' + html + '<br/>\n'
   2331 
   2332     def UnorderedList(self, lines):
   2333         s = UnicodeMemStream()
   2334         s.Write('<ul>\n')
   2335         for line in lines:
   2336             s.Write('<li>')
   2337             s.Write(line)
   2338             s.Write('</li>\n')
   2339 
   2340         s.Write('</ul>\n')
   2341         s.Seek(0)
   2342         return s.Read()
   2343 
   2344     def OrderedList(self, lines):
   2345         s = UnicodeMemStream()
   2346         s.Write('<ol>\n')
   2347         for line in lines:
   2348             s.Write('<li>')
   2349             s.Write(line)
   2350             s.Write('</li>\n')
   2351 
   2352         s.Write('</ol>\n')
   2353         s.Seek(0)
   2354         return s.Read()
   2355 
   2356     def CheckAll(self, element):
   2357         return '<input type="checkbox" name="checkall" onclick="ch=false;if(this.checked){ch=true;};o=document.getElementsByName(\'%s\');for(i=0;i<o.length;i++){o[i].checked=ch;};">' % element
   2358 
   2359     def AppBottomLeft(self):
   2360         return ''
   2361 
   2362     def AppBottomRight(self):
   2363         return ''
   2364 
   2365     def Generate(self):
   2366         if hasattr(self, 'response') and getattr(self.response, 'done', False):
   2367             return ''
   2368         cacheKiller = '?nocache=%d' % boot.sync
   2369         if prefs.clusterMode == 'LOCAL':
   2370             cacheKiller += '%d' % time.time()
   2371         self.inserts['cacheKiller'] = cacheKiller
   2372         if self.redirect not in self.inserts:
   2373             self.Write('')
   2374         if self.visualization:
   2375             self.WriteDirect('scripts', '    <script type="text/javascript">\n')
   2376             self.WriteDirect('scripts', "        google.load('visualization', '1', {packages: [%s]});\n" % ','.join([ "'%s'" % x for x in self.visualization ]))
   2377             self.WriteDirect('scripts', '    </script>\n')
   2378         if self.cssFiles:
   2379             self.WriteDirect('scripts', ''.join([ '\n    <link rel="stylesheet" type="text/css" href="%s%s" />' % (src, cacheKiller) for src in self.cssFiles ]))
   2380         if self.jsFiles:
   2381             self.WriteDirect('scripts', ''.join([ '\n    <script type="text/javascript" src="%s%s"></script>' % (src, cacheKiller) for src in self.jsFiles ]))
   2382         if self.scripts:
   2383             self.WriteDirect('scripts', '\n    '.join(self.scripts))
   2384         if self.headjsScripts:
   2385             self.WriteDirect('headjs', '\n    '.join(self.headjsScripts))
   2386         if self.visualcallbacks:
   2387             self.inserts['visualscripts'].Seek(0)
   2388             vScript = self.inserts['visualscripts'].Read()
   2389             visualScripts = ['function doVisual() {\n', vScript, '\n}\n'] + self.visualcallbacks
   2390             self.inserts['visualscripts'] = ''.join(['<script type="text/javascript">\n'] + visualScripts + ['\ngoogle.setOnLoadCallback(doVisual);</script>'])
   2391         stop, hz = blue.os.GetCycles()
   2392         dt = (stop - self.start) / float(hz) * 1000.0
   2393         self.inserts['bottom_left'].Seek(0)
   2394         self.WriteDirect('bottom_left', '%s (%.1f ms) %s' % (FmtDate(blue.os.GetWallclockTime()), dt, self.AppBottomLeft()))
   2395         trackID = 1 if prefs.clusterMode == 'LOCAL' else prefs.GetValue('webTrackID', -1)
   2396         trackUrl = prefs.GetValue('trackUrl', 'http://piwik/')
   2397         if not trackUrl.endswith('/'):
   2398             trackUrl += '/'
   2399         if trackID > -1:
   2400             self.WriteDirect('trackpage', '\n    <!-- Piwik -->\n    <script type="text/javascript">\n    var pkBaseURL = (("https:" == document.location.protocol) ? "%(trackHttps)s" : "%(trackHttp)s");\n    document.write(unescape("%%3Cscript src=\'" + pkBaseURL + "piwik.js\' type=\'text/javascript\'%%3E%%3C/script%%3E"));\n    </script><script type="text/javascript">\n    try {\n    var user = { \'userID\' : %(userid)s };\n    var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", %(trackID)d);\n    piwikTracker.setCustomData(user);\n    piwikTracker.setDocumentTitle(\'%(documentTitle)s\');\n    piwikTracker.trackPageView();\n    piwikTracker.enableLinkTracking();\n    } catch( err ) {}\n    </script><noscript><p><img src="%(trackHttp)spiwik.php?idsite=%(trackID)d" style="border:0" alt="" /></p></noscript>\n    <!-- End Piwik Tracking Tag -->\n            ' % {'userid': session.userid,
   2401              'documentTitle': self.inserts['title'],
   2402              'trackID': trackID,
   2403              'trackHttp': trackUrl,
   2404              'trackHttps': trackUrl.replace('http:', 'https:')})
   2405         nodeInfo = ''
   2406         if machoNet.mode == 'server':
   2407             nodeID = sm.services['machoNet'].GetNodeID()
   2408             nodeInfo = '&nbsp;&middot;&nbsp; Node: %s' % self.Link('/admin/network.py', '<span id="currentNodeID" clsss="gray">%s</span>' % nodeID, {'action': 'Node',
   2409              'nodeID': nodeID})
   2410         self.WriteDirect('bottom_right', '<span class="gray">%s%s</span> %s' % (prefs.clusterName, nodeInfo, self.AppBottomRight()))
   2411         for ins in self.inserts.iterkeys():
   2412             m = self.inserts[ins]
   2413             if isinstance(m, UnicodeMemStream):
   2414                 m.Seek(0)
   2415                 self.inserts[ins] = m.Read()
   2416 
   2417         try:
   2418             template = self.baseTemplate
   2419             if template.startswith('script:') or template.startswith('wwwroot:'):
   2420                 template = template.split('/')[-1]
   2421             return scriber.scribe(template, **self.inserts)
   2422         except Exception as ex:
   2423             scriber.log.exception('Exception while Scribing')
   2424             self.LogError('Exception while Scribing: %r' % ex)
   2425             print('Exception while Scribing: %r' % ex)
   2426             raise ex
   2427 
   2428     def Canonicalize(self, *args, **kw):
   2429         return Canonicalize(*args, **kw)
   2430 
   2431     def HTMLEncode(self, *args, **kw):
   2432         return HTMLEncode(*args, **kw)
   2433 
   2434     def Link(self, *args, **kw):
   2435         return Link(*args, **kw)
   2436 
   2437     def Button(self, *args, **kw):
   2438         return Button(*args, **kw)
   2439 
   2440     def Image(self, *args, **kw):
   2441         return Image(*args, **kw)
   2442 
   2443     def Table(self, *args, **kw):
   2444         return Table(*args, **kw)
   2445 
   2446     def OutlineDictTable(self, *args, **kw):
   2447         return OutlineDictTable(*args, **kw)
   2448 
   2449     def OutlineSortedSegmentedTable(self, *args, **kw):
   2450         return OutlineSortedSegmentedTable(*args, **kw)
   2451 
   2452     def OutlineSegmentedTable(self, *args, **kw):
   2453         return OutlineSegmentedTable(*args, **kw)
   2454 
   2455     def OutlineTable(self, *args, **kw):
   2456         return OutlineTable(*args, **kw)
   2457 
   2458     def OutlinePropertyTable(self, *args, **kw):
   2459         return OutlinePropertyTable(*args, **kw)
   2460 
   2461     def OutlineKeyValTable(self, *args, **kw):
   2462         return OutlineKeyValTable(*args, **kw)
   2463 
   2464     def BarChart(self, *args, **kw):
   2465         return BarChart(*args, **kw)
   2466 
   2467     def BrowserCheck(self, *args, **kw):
   2468         return BrowserCheck(*args, **kw)
   2469 
   2470     def WriteEOFModalDialogue(self, buttonName = 'Done'):
   2471         element = self.request.QueryString('modalDialogueElement')
   2472         if buttonName is not None:
   2473             self.Write('\n                <input type="button" value="%s" onClick="all2Element(\'%s\')">\n            ' % (buttonName, element))
   2474         self.Write('\n                <script language="javascript">\n                    function all2Element(element){\n                        var pairs = new Array();\n                        var disp  = new Array();\n\n                        var allInput = document.getElementsByTagName(\'input\');\n                        for(i=0;i<allInput.length;i++){\n                            if((allInput[i].type==\'radio\')||(allInput[i].type==\'checkbox\')){\n                                if(allInput[i].checked){\n                                    pairs[pairs.length]=allInput[i].name+\'=\'+allInput[i].value;\n                                    disp[disp.length]=allInput[i].name;\n                                }\n                            }else if ((allInput[i].type!=\'button\') && (allInput[i].value.length>0)) {\n                                pairs[pairs.length]=allInput[i].name+\'=\'+allInput[i].value;\n                                disp[disp.length]=allInput[i].name+\'=\'+allInput[i].value;\n                            }\n                        }\n                        var allSelect = document.getElementsByTagName(\'select\')\n                        for(i=0;i<allSelect.length;i++){\n                            for (n=0;n<allSelect[i].options.length;n++){\n                                if (allSelect[i].options[n].selected && allSelect[i].options[n].value!=\'None\') {\n                                    pairs[pairs.length]= allSelect[i].name+\'=\'+allSelect[i].options[n].value;\n                                    disp[disp.length]  = allSelect[i].name+\'=\'+allSelect[i].options[n].text;\n                                }\n                            }\n                        }\n\n                        window.opener.document.all[element].value=pairs.join("&");\n                        window.opener.document.all[element+\'Disp\'].value=disp.join(", ");\n\n                        self.close();\n                    }\n\n                    function fetchFromElement(element){\n                        if(window.opener.document.all[element].value!=\'\'){\n                            var currentValues = window.opener.document.all[element].value.split("&");\n                            for(i=0;i<currentValues.length;i++){\n                                var pair = currentValues[i].split(\'=\');\n                                var modularElement = document.all[pair[0]]\n                                if(modularElement){\n                                    if(modularElement.tagName==\'SELECT\'){\n                                        for(si=0;si<modularElement.options.length;si++){\n                                            if(modularElement.options[si].value == pair[1]){\n                                                modularElement.options[si].selected = true;\n                                            }\n                                        }\n                                    }\n                                    if(modularElement.tagName==\'INPUT\'){\n                                        if(modularElement.type==\'text\'){\n                                            document.all[pair[0]].value = pair[1];\n                                        }\n                                        if(modularElement.type==\'checkbox\'){\n                                            if(pair[1]==\'on\'){document.all[pair[0]].checked=true;}\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n\n                    document.onload = fetchFromElement(\'%s\');\n                </script>\n            ' % element)
   2475 
   2476     def UnpackModalDialogueResult(self, *args):
   2477         return UnpackModalDialogueResult(*args)
   2478 
   2479     def PackModalDialogueResult(self, *args):
   2480         return PackModalDialogueResult(*args)
   2481 
   2482     def FormatHtmlKWArgs(self, htmlKwargs = {}, prohibitedKeys = []):
   2483         result = ''
   2484         htmlOpt = lambda each: " %s='%s'" % each
   2485         for each in htmlKwargs.iteritems():
   2486             if each[0].lower() not in prohibitedKeys:
   2487                 result += htmlOpt(each)
   2488 
   2489         return result
   2490 
   2491     def FormatFunctionArguments(self, functionArgs, trailingComma = False):
   2492         argStr = ''
   2493         if functionArgs is not None:
   2494             for each in functionArgs:
   2495                 if each is None:
   2496                     argStr += 'null,'
   2497                 elif type(each) == types.BooleanType:
   2498                     argStr += '%s,' % str(each).lower()
   2499                 elif type(each) in types.StringTypes:
   2500                     if each.startswith('this'):
   2501                         argStr += '%s,' % each
   2502                     else:
   2503                         argStr += "'%s'," % each
   2504                 else:
   2505                     argStr += '%s,' % each
   2506 
   2507             if not trailingComma:
   2508                 argStr = argStr[:-1]
   2509         return argStr
   2510 
   2511     def FormatSelectOptions(self, elements):
   2512         selectOpt = lambda row: '<option value="%s" %s >%s</option>\n' % (row[0], 'selected' if row[2] else '', row[1])
   2513         return '  '.join(map(selectOpt, elements))
   2514 
   2515     def FormatAjaxArgs(self, action, arguments, extras):
   2516         extraArgs = {}
   2517         ajaxArgsStr = 'action=%s' % action
   2518         if arguments is not None:
   2519             for each in arguments.iteritems():
   2520                 if each[1] is None:
   2521                     ajaxArgsStr += '&%s=None' % each[0]
   2522                 elif each[0] in extras:
   2523                     extraArgs[each[0]] = each[1]
   2524                 elif type(each[1]) in types.StringTypes:
   2525                     ajaxArgsStr += '&%s=%s' % each
   2526                 else:
   2527                     ajaxArgsStr += '&%s=%s' % each
   2528 
   2529         return (ajaxArgsStr, extraArgs)
   2530 
   2531     def SetAjaxDefaultUrl(self, url):
   2532         self.ajaxDefaultURL = url
   2533 
   2534     def EnableAjaxInput(self):
   2535         if 'EnableAjaxInput' in self.enabledJscript:
   2536             return
   2537         self.enabledJscript.add('EnableAjaxInput')
   2538         jscript = '\n                    function OnChangeAjax(dataID, attr, value, url, action, successFunc) {\n                        if (null == successFunc) successFunc = function(data){};\n                        $.ajax({\n                            url: url,\n                            cache: false,\n                            dataType: \'json\',\n                            data: "action="+ action + "&dataID=" + dataID + "&attr=" + attr + "&value=" +  value,\n                            success: successFunc\n                            }); //end Ajax\n                        };// end Function\n                    '
   2539         self.WriteDirect('jscript', jscript)
   2540 
   2541     def GetAjaxInput(self, labelCaption, dataID, attr, url, funcName, title = '', ctrlID = '', className = '', minLength = None, width = None, value = None):
   2542         self.EnableAjaxInput()
   2543         minLength = CheckValue('minlength', str(minLength))
   2544         if width:
   2545             width = ' style="width:%spx"' % width
   2546         className = CheckValue('class', className)
   2547         value = CheckValue('value', value)
   2548         res = []
   2549         if labelCaption is not None:
   2550             res.append('<label class="form-label" id="lbl_%s">%s</label>' % (ctrlID, labelCaption))
   2551         attrs = {'ctrlID': ctrlID,
   2552          'className': className,
   2553          'minLength': minLength,
   2554          'width': width,
   2555          'value': value,
   2556          'dataID': dataID,
   2557          'attr': attr,
   2558          'url': url,
   2559          'funcName': funcName,
   2560          'title': title,
   2561          'successFunc': "function(data){$('#%s').val(data)}" % ctrlID}
   2562         res.append('<input id="%(ctrlID)s"\n                       name="%(ctrlID)s"%(className)s%(minLength)s%(width)s%(value)s\n                       onchange="OnChangeAjax(%(dataID)s, \'%(attr)s\', this.value, \'%(url)s\', \'%(funcName)s\', %(successFunc)s )"\n                       title="%(title)s" />\n                   ' % attrs)
   2563         return '\n'.join(res)
   2564 
   2565     def EnableAjaxA(self):
   2566         if 'EnableAjaxA' not in self.enabledJscript:
   2567             self.enabledJscript.add('EnableAjaxA')
   2568             jscript = '\n                        function OnClickAjaxAnchor(url, action, successFunc) {\n                            if (null == successFunc) successFunc = function(data){}\n                            $.ajax({\n                                url: url,\n                                cache: false,\n                                dataType: \'json\',\n                                data: "action="+ action,\n                                success: successFunc\n                                }); //end Ajax\n                            };// end Function\n                        '
   2569             self.WriteDirect('jscript', jscript)
   2570 
   2571     def EnableAjaxExpandableTitle(self):
   2572         if 'EnableAjaxExpandableTitle' in self.enabledJscript:
   2573             return
   2574         self.enabledJscript.add('EnableAjaxExpandableTitle')
   2575         jscript = '\n                    function OnClickExpandableTitle(url, action, dataID, ctrlID) {\n                        var divKey="#div_" + ctrlID;\n                        var icnKey="#icn_" + ctrlID;\n                        if ($("div").find(divKey).css("display") == \'none\'){\n                            $.ajax({\n                                url: url,\n                                cache: false,\n                                data: "action="+ action + "&dataID=" + dataID,\n                                success: function(html){\n                                    if (html.length > 2){\n                                        $("div").find(divKey).empty();\n                                        $("div").find(divKey).append(html);\n                                        $(icnKey).attr("src", "/img/toggle-collapse-dark.png");\n                                        $("div").find(divKey).toggle( );\n                                        }\n                                    }\n                               }); //end Ajax\n                            } // end if\n                            else {\n                                $(icnKey).attr("src", "/img/toggle-expand-dark.png");\n                                $("div").find(divKey).toggle( );\n                            }\n                        };// end Function\n                    '
   2576         self.WriteDirect('jscript', jscript)
   2577 
   2578     def GetAjaxExpandableTitle(self, innerText, dataID, url, action, ctrlID, title = ''):
   2579         self.EnableAjaxExpandableTitle()
   2580         args = {'ctrlID': ctrlID,
   2581          'url': url,
   2582          'action': action,
   2583          'dataID': dataID,
   2584          'innerText': innerText,
   2585          'title': title}
   2586         args['icon'] = self.Image('/img/toggle-expand-dark.png', 'border=0 id="icn_%(ctrlID)s" onClick="OnClickExpandableTitle(\'%(url)s\', \'%(action)s\', %(dataID)s, %(ctrlID)s)" title="%(title)s" ' % args)
   2587         expand = 'toggle-collapse-dark.png'
   2588         return '%(icon)s %(innerText)s<div id="div_%(ctrlID)s"  style="display:none;" >Hi there this is the div</div>' % args
   2589 
   2590     def GetAjaxSelect(self, elements, dataID, url, action, attr, ctrlID, title = '', labelCaption = '', elementClass = ''):
   2591         self.EnableAjaxInput()
   2592         args = {'ctrlID': ctrlID,
   2593          'url': url or self.ajaxDefaultURL,
   2594          'action': action,
   2595          'attr': attr,
   2596          'dataID': dataID,
   2597          'title': title,
   2598          'elementClass': elementClass}
   2599         if ctrlID:
   2600             ctrlID = ' id="%s" name="%s"' % (ctrlID, ctrlID)
   2601         opt1 = lambda row: '<option value="%s" %s >%s</option>\n' % (row[0], ['', 'selected'][row[2]], row[1])
   2602         attrTxt = 'class="%(elementClass)s" onchange="OnChangeAjax(%(dataID)s, \'%(attr)s\', this.value, \'%(url)s\', \'%(action)s\', null)" title="%(title)s" ' % args
   2603         return ''.join(['<label class="form-label" for="%s">%s</label>' % (ctrlID, labelCaption),
   2604          _GetElementBegin('select', '%s %s' % (ctrlID, attrTxt)),
   2605          ''.join(map(opt1, elements)),
   2606          _GetElementEnd('select')])
   2607 
   2608     def GetAjaxCheckbox(self, ctrlID, labelCaption, className, checked, dataID, url, action, attr, title = '', disabled = False, successFunc = 'null'):
   2609         self.EnableAjaxInput()
   2610         className = CheckValue('class', className)
   2611         if checked:
   2612             checked = ' checked ="true"'
   2613         else:
   2614             checked = ''
   2615         res = []
   2616         if labelCaption:
   2617             res.append('<label class="form-label" for="%s">%s</label>' % (ctrlID, labelCaption))
   2618         args = {'ctrlID': ctrlID,
   2619          'className': className,
   2620          'checked': checked,
   2621          'dataID': dataID,
   2622          'attr': attr,
   2623          'url': url,
   2624          'action': action,
   2625          'title': title,
   2626          'disabled': ['', 'disabled'][disabled or False],
   2627          'successFunc': successFunc}
   2628         res.append('<input type="checkbox" id="%(ctrlID)s" name="%(ctrlID)s" %(className)s %(checked)s %(disabled)s onchange="OnChangeAjax(%(dataID)s, \'%(attr)s\', this.checked, \'%(url)s\', \'%(action)s\', %(successFunc)s )" title="%(title)s" />' % args)
   2629         return '\n'.join(res)
   2630 
   2631     def EnableAjaxBtn(self):
   2632         if 'EnableAjaxIconBtn' not in self.enabledJscript:
   2633             self.enabledJscript.add('EnableAjaxIconBtn')
   2634             jscript = '\n                        function OnClickAjaxBtn(url, ajaxArgs, successFunc, confirmTxt, dataType) {\n                            if (null == successFunc) successFunc = function(data){};\n                            if (confirmTxt.length == 0 || confirm(confirmTxt) ) {\n                                $.ajax({\n                                    url: url,\n                                    cache: false,\n                                    dataType: dataType,\n                                    data: ajaxArgs,\n                                    success: successFunc\n                                    }); //end Ajax\n                                };\n                            };// end Function\n                        '
   2635             self.WriteDirect('jscript', jscript)
   2636 
   2637     def GetAjaxBtn(self, btnText, action, actionKwargs = {}, htmlKWargs = {}, url = None):
   2638         htmlArgStr = self.FormatHtmlKWArgs(htmlKWargs, ['onclick', 'type'])
   2639         ajaxArgsStr, extraArgs = self.FormatAjaxArgs(action, actionKwargs, ['successFunc', 'confirm', 'dataType'])
   2640         args = {'btnText': btnText,
   2641          'url': url or self.ajaxDefaultURL,
   2642          'dataType': 'json',
   2643          'htmlArgs': htmlArgStr,
   2644          'ajaxArgs': ajaxArgsStr,
   2645          'confirm': ''}
   2646         args.update(extraArgs)
   2647         return '<button type="button" onclick="OnClickAjaxBtn(\'%(url)s\', \'%(ajaxArgs)s\', %(successFunc)s, \'%(confirm)s\', \'%(dataType)s\')" \'%(htmlArgs)s\' >%(btnText)s</button>' % args
   2648 
   2649     def GetAjaxIconBtn(self, imageURL, action, actionKwargs = {}, htmlKwargs = {}, url = None):
   2650         self.EnableAjaxBtn()
   2651         if (url or self.ajaxDefaultURL) is None:
   2652             raise RuntimeError('There is no URL for this call, please pass a URL or set the default RLL')
   2653         ajaxArgsStr, extraArgs = self.FormatAjaxArgs(action, actionKwargs, ['confirm', 'dataType', 'successFunc'])
   2654         htmlArgsStr = self.FormatHtmlKWArgs(htmlKwargs)
   2655         args = {'imageUrl': imageURL,
   2656          'htmlArgs': htmlArgsStr,
   2657          'url': url or self.ajaxDefaultURL,
   2658          'action': action,
   2659          'ajaxArgs': ajaxArgsStr,
   2660          'dataType': 'json',
   2661          'successFunc': 'null',
   2662          'confirm': ''}
   2663         args.update(extraArgs)
   2664         return '<img src="%(imageUrl)s" %(htmlArgs)s onclick="OnClickAjaxBtn(\'%(url)s\', \'%(ajaxArgs)s\', %(successFunc)s, \'%(confirm)s\', \'%(dataType)s\')" />' % args
   2665 
   2666     def GetAjaxA(self, innerText, action, actionKwargs = {}, htmlKwargs = {}, url = None):
   2667         self.EnableAjaxBtn()
   2668         if (url or self.ajaxDefaultURL) is None:
   2669             raise RuntimeError('There is no URL for this call, please pass a URL or set the default RLL')
   2670         ajaxArgsStr, extraArgs = self.FormatAjaxArgs(action, actionKwargs, ['confirm', 'dataType', 'successFunc'])
   2671         htmlArgsStr = self.FormatHtmlKWArgs(htmlKwargs)
   2672         args = {'innerText': innerText,
   2673          'htmlArgs': htmlArgsStr,
   2674          'url': url or self.ajaxDefaultURL,
   2675          'action': action,
   2676          'ajaxArgs': ajaxArgsStr,
   2677          'dataType': 'json',
   2678          'successFunc': 'null',
   2679          'confirm': ''}
   2680         args.update(extraArgs)
   2681         return '<a %(htmlArgs)s onclick="OnClickAjaxBtn(\'%(url)s\', \'%(ajaxArgs)s\', %(successFunc)s, \'\', \'%(dataType)s\')">%(innerText)s</a>' % args
   2682 
   2683     def GetJscriptIconBtn(self, func, imgSrc, funcArgs = None, htmlKwargs = {}):
   2684         argStr = self.FormatFunctionArguments(funcArgs)
   2685         htmlArgsStr = self.FormatHtmlKWArgs(htmlKwargs, ['onclick']) + ' onclick="%s(%s)" ' % (func, argStr)
   2686         return '<img src="%(imgSrc)s" %(htmlArgs)s />' % {'imgSrc': imgSrc,
   2687          'htmlArgs': htmlArgsStr}
   2688 
   2689     def GetJscriptSelect(self, elements, func, funcArgs = None, htmlKwargs = {}):
   2690         argStr = self.FormatFunctionArguments(funcArgs, True)
   2691         htmlArgsStr = self.FormatHtmlKWArgs(htmlKwargs, ['onchange']) + ' onchange="%s(%s this.value)" ' % (func, argStr)
   2692         selectStr = '<select %s >\n' % htmlArgsStr
   2693         selectStr += self.FormatSelectOptions(elements)
   2694         selectStr += '</select>\n'
   2695         return selectStr
   2696 
   2697     def GetJscriptA(self, innerText, func, funcArgs = None, htmlKwargs = {}):
   2698         argStr = self.FormatFunctionArguments(funcArgs)
   2699         htmlArgsStr = self.FormatHtmlKWArgs(htmlKwargs, ['onclick']) + ' onclick="%s(%s)" ' % (func, argStr)
   2700         return '<a %s ">%s</a>' % (htmlArgsStr, innerText)
   2701 
   2702     def GetExportedForAction(self, action):
   2703         exported = self.__exportedactions__.get(action)
   2704         if exported is None:
   2705             return
   2706         if callable(exported):
   2707             exported = exported()
   2708         return exported
   2709 
   2710     def GetActionArgs(self, request, action):
   2711         exported = self.GetExportedForAction(action)
   2712         if exported is None:
   2713             return
   2714         if len(exported) and isinstance(exported[0], numbers.Integral):
   2715             if exported[0] & session.role == 0:
   2716                 roles = self.session.ConnectToAnyService('cache').Rowset(const.cacheUserRoles)
   2717                 requiredRoles = ''
   2718                 for role in roles:
   2719                     if exported[0] & role.roleID == role.roleID:
   2720                         requiredRoles = requiredRoles + role.roleName + ', '
   2721 
   2722                 session.LogSessionError("Called %s::%s, which requires of the following roles: %s, which the user doesn't have" % (request.path, action, requiredRoles))
   2723                 raise RoleNotAssignedError(exported[0])
   2724             preargs = exported[1:]
   2725         else:
   2726             preargs = exported
   2727         if preargs == ['**']:
   2728             params = request.QueryStrings()
   2729             for k, v in request.FormItems().items():
   2730                 params[k] = v
   2731 
   2732         else:
   2733             params = {}
   2734             for each in preargs:
   2735                 if request.QueryString(each) is not None:
   2736                     params[each] = request.QueryString(each)
   2737                 elif request.Form(each) is not None:
   2738                     params[each] = request.Form(each)
   2739 
   2740         return ((), params)
   2741 
   2742     def IsPost(self):
   2743         try:
   2744             return str(self.request.method).upper() == 'POST'
   2745         except Exception as ex:
   2746             return len(self.request.form) > 0
   2747 
   2748 
   2749 class HtmlWriterEx(HtmlWriter):
   2750     useAutomagicTitles = 1
   2751     useItemProps = 0
   2752     useOwnerProps = 0
   2753     useSessionProps = 0
   2754     usePannelUpdate = 1
   2755 
   2756     def __init__(self, template = 'script:/wwwroot/lib/template/base.html', populateGlobals = 1):
   2757         HtmlWriter.__init__(self, template)
   2758         self.inserts['stylesheet'] = '/lib/ex.css'
   2759         self.inserts['topMenu'] = ''
   2760         self.inserts['topMenuSub'] = ''
   2761         self.inserts['l1Menu'] = ''
   2762         self.inserts['l1MenuSub'] = ''
   2763         self.inserts['lMenu'] = ''
   2764         self.inserts['lSelected'] = ''
   2765         self.inserts['rMenu'] = ''
   2766         self.inserts['props'] = ''
   2767         self.inserts['floaters'] = ''
   2768         self.inserts['watchlist'] = ''
   2769         self.inserts['body'] = ' onload="SetFocusToFirst();"'
   2770         self.inserts['onload'] = ''
   2771         self.inserts['rMenuWidth'] = 200
   2772         self.request = None
   2773         self.response = None
   2774 
   2775     def HandleAction(self, action, request, response):
   2776         self.request = request
   2777         self.response = response
   2778         if self.ajaxCSRF and str(request.method).upper() in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
   2779             response.cookie['csrftoken'] = GenerateCSRFToken(request)
   2780         if not (prefs.clusterMode in ('LOCAL', 'MASTER') and prefs.GetValue('csrf_override', '') == 'on'):
   2781             if str(request.method).upper() == 'POST' and not self.ValidateCSRF():
   2782                 if 'x-csrftoken' not in request.header or request.header['x-csrftoken'] != GenerateCSRFToken(request):
   2783                     raise CSRFException('CSRF Token not valid')
   2784         if action is None:
   2785             action = self.__defaultaction__
   2786         argstuple = self.GetActionArgs(request, action)
   2787         if argstuple:
   2788             args, kwargs = argstuple
   2789             try:
   2790                 getattr(self, action)(*args, **kwargs)
   2791             except TypeError as e:
   2792                 sm.services['http'].LogError('Bad params (', kwargs, ') sent to ', action, ', resulting in exception: ', e)
   2793                 raise
   2794 
   2795         else:
   2796             self.Write('Action not listed ' + str(action) + '<br/>')
   2797         if self.useAutomagicTitles == 1:
   2798             self.inserts['title'] = self.inserts['title'] + ' - ' + str(action)
   2799             self.inserts['heading'] = self.inserts['title']
   2800         elif self.useAutomagicTitles == 2:
   2801             self.inserts['heading'] = self.inserts['title']
   2802 
   2803     def AddTopMenu(self, id, disp, help, url, args = None):
   2804         if args:
   2805             url = url + '?'
   2806             for k, v in args.iteritems():
   2807                 url = url + str(k) + '=' + str(v) + '&'
   2808 
   2809         if id:
   2810             self.inserts['topMenu'] = self.inserts['topMenu'] + '        addMSMenu("%s", "%s", "%s", "%s");\n' % (id,
   2811              disp,
   2812              help,
   2813              url)
   2814         else:
   2815             self.inserts['topMenu'] = self.inserts['topMenu'] + '        addMSMenuDivider();\n'
   2816 
   2817     def AddTopButton(self, linkText, url, args = None):
   2818         if args:
   2819             url = url + '?'
   2820             for k, v in args.iteritems():
   2821                 url = url + str(k) + '=' + str(v) + '&'
   2822 
   2823         self.inserts['topMenu'] = self.inserts['topMenu'] + '        addMSButton("%s", "%s");\n' % (linkText, url)
   2824         self.inserts['topMenu'] = self.inserts['topMenu'] + '        addMSMenuDivider();\n'
   2825 
   2826     def AddTopMenuSub(self, parentid, disp, url, args = None, disabled = False):
   2827         if args:
   2828             url = url + '?'
   2829             for k, v in args.iteritems():
   2830                 url = url + str(k) + '=' + str(v) + '&'
   2831 
   2832         d = '%s' % disp
   2833         color = 'gray' if disabled else ''
   2834         self.inserts['topMenuSub'] = self.inserts['topMenuSub'] + '        addMSSubMenu("%s", "%s", "%s", "%s");\n' % (parentid,
   2835          d,
   2836          url,
   2837          color)
   2838 
   2839     def AddTopMenuSubLine(self, parentid):
   2840         self.inserts['topMenuSub'] = self.inserts['topMenuSub'] + '        addMSSubMenuLine("%s");\n' % parentid
   2841 
   2842     def AddMenu(self, id, disp, help, url, args = None):
   2843         if args:
   2844             url = url + '?'
   2845             for k, v in args.iteritems():
   2846                 url = url + str(k) + '=' + str(v) + '&'
   2847 
   2848         self.inserts['l1Menu'] = self.inserts['l1Menu'] + '        addICPMenu("%s", "%s", "%s", "%s");\n' % (id,
   2849          disp,
   2850          help,
   2851          url)
   2852 
   2853     def SelectMenu(self, id, color = '#FFB043'):
   2854         self.inserts['l1Menu'] = self.inserts['l1Menu'] + '        setICPSeleted("%s", "%s");\n' % (id, color)
   2855 
   2856     def AddMenuSub(self, parentid, disp, url, args = None):
   2857         if args:
   2858             url = url + '?'
   2859             for k, v in args.iteritems():
   2860                 url = url + str(k) + '=' + str(v) + '&'
   2861 
   2862         self.inserts['l1MenuSub'] = self.inserts['l1MenuSub'] + '  addICPSubMenu("%s", "%s", "%s");\n' % (parentid, disp, url)
   2863 
   2864     def AddHorMenu(self, id, disp, url, args = None):
   2865         if args:
   2866             url = url + '?'
   2867             for k, v in args.iteritems():
   2868                 url = url + str(k) + '=' + str(v) + '&'
   2869 
   2870         self.inserts['l1Menu'] = self.inserts['l1Menu'] + '        AddHorMenuItem("%s", "%s", "%s");\n' % (id, disp, url)
   2871 
   2872     def SelectHorMenu(self, id):
   2873         self.inserts['l1Menu'] = self.inserts['l1Menu'] + '        setHorSeleted("%s");\n' % id
   2874 
   2875     def AddLeftHeading(self, id, disp):
   2876         self.inserts['lMenu'] = self.inserts['lMenu'] + '        AddLeftMenu("%s", "%s");\n' % (id, disp)
   2877 
   2878     def AddLeftMenu(self, parentid, id, disp, url, args = None, cssClass = None):
   2879         if args:
   2880             url = url + '?'
   2881             for k, v in args.iteritems():
   2882                 url = url + str(k) + '=' + str(v) + '&'
   2883 
   2884         if cssClass is None:
   2885             cssClass = ''
   2886         self.inserts['lMenu'] = self.inserts['lMenu'] + '        AddLeftMenuItem("%s","%s","%s","%s","%s");\n' % (parentid,
   2887          id,
   2888          disp,
   2889          url,
   2890          cssClass)
   2891 
   2892     def CurrentHeadingID(self):
   2893         return 'm{}'.format(self.currentLeftHeadingIndex)
   2894 
   2895     def CurrentMenuID(self):
   2896         return 'm{}{}'.format(self.currentLeftHeadingIndex, chr(ord('a') + self.currentLeftMenuIndex))
   2897 
   2898     def AddAutonumberedLeftHeading(self, disp):
   2899         if not hasattr(self, 'currentLeftHeadingIndex'):
   2900             self.currentLeftHeadingIndex = 0
   2901         self.currentLeftHeadingIndex += 1
   2902         self.currentLeftMenuIndex = 0
   2903         self.AddLeftHeading(self.CurrentHeadingID(), disp)
   2904 
   2905     def AddAutonumberedLeftMenu(self, disp, url, args = None, cssClass = None):
   2906         self.currentLeftMenuIndex += 1
   2907         self.AddLeftMenu(self.CurrentHeadingID(), self.CurrentMenuID(), disp, url, args, cssClass)
   2908 
   2909     def ContainerDetails(self, heading, header, lines, opLink = ''):
   2910         s = UnicodeMemStream()
   2911         if heading and len(heading):
   2912             s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   2913             s.Write('<tr>\n')
   2914             s.Write('<td class="tableOutline" valign="top">\n')
   2915             s.Write('<table border="0" cellpadding="7" cellspacing="1" width="100%">\n')
   2916             s.Write('<tr>\n')
   2917             s.Write('    <td class="tableHeader"><small><b>%s</b></small></td>\n' % (heading,))
   2918             s.Write('</tr>\n')
   2919             s.Write('</table>\n')
   2920             s.Write('</td>\n')
   2921             s.Write('</tr>\n')
   2922             s.Write('</table>\n')
   2923         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   2924         s.Write('<tr>\n')
   2925         s.Write('<td class="tableOutline" valign="top">\n')
   2926         s.Write('<table border="0" cellpadding="7" cellspacing="1" width="100%">\n')
   2927         if header and len(header):
   2928             s.Write('<tr>\n')
   2929             for i in header:
   2930                 s.Write('    <td class="tableHeader"><b>%s</b></td>\n' % i)
   2931 
   2932             s.Write('</tr>\n')
   2933         c = 1
   2934         if len(lines):
   2935             for row in lines:
   2936                 klass = 'class="tC"'
   2937                 if c:
   2938                     c = 0
   2939                 else:
   2940                     klass = 'class="tCO"'
   2941                     c = 1
   2942                 s.Write('<tr>\n')
   2943                 for col in row:
   2944                     if col == row[0] and opLink != '':
   2945                         link = opLink % (col, col)
   2946                         s.Write('    <td %s valign=top><small>%s</small></td>\n' % (klass, link))
   2947                     else:
   2948                         s.Write('    <td %s valign=top><small>%s</small></td>\n' % (klass, unicode(col)))
   2949 
   2950                 s.Write('</tr>\n')
   2951 
   2952         s.Write('</table>\n')
   2953         s.Write('</td>\n')
   2954         s.Write('</tr>\n')
   2955         s.Write('</table>\n')
   2956         s.Seek(0)
   2957         return s.Read()
   2958 
   2959     def WebPart(self, *args, **kw):
   2960         return WebPart(*args, **kw)
   2961 
   2962     def Floater(self, title, content, ID = 'NotUnique', width = '50%'):
   2963         s = UnicodeMemStream()
   2964         s.Write('<div id="' + str(ID) + '" class="clsFloaterContainer" style="position:absolute;top:0;left:0;width:' + str(width) + ';visibility:hidden;">')
   2965         s.Write('<table id="goup" cellpadding="0" cellspacing="0" class="clsFloater" width="100%" border="0">\n')
   2966         s.Write('<tr>\n')
   2967         s.Write('    <td class="clsFloaterHead" valign="top" align="left" style="width:15" >')
   2968         s.Write('    <img class="clsFloaterHead" src="/lib/webparts/gripgray.gif" height="19" width="15">')
   2969         s.Write('    </td>\n')
   2970         s.Write('    <td class="clsFloaterHead" valign="middle" align="left" width="' + str(width - 45) + '" STYLE="BORDER-TOP:1px solid #999999;">\n')
   2971         s.Write('    <b class="clsFloaterHead">' + title + '</b>\n')
   2972         s.Write('    </td>\n')
   2973         s.Write('    <td class="clsFloaterRight" valign="top" align="right" height="19" width="25" >\n')
   2974         s.Write('    <img class="clsMinimize" src="/lib/webparts/downlevel.gif" height="19" width="25">\n')
   2975         s.Write('    </td>\n')
   2976         s.Write('</tr>\n')
   2977         s.Write('<tr>\n')
   2978         s.Write('    <td colspan="3">\n')
   2979         s.Write('    <table bgcolor="#ffffff" width="100%" cellpadding="0" cellspacing="0" border="0">\n')
   2980         s.Write('    <tr>\n')
   2981         s.Write('        <td bgcolor="#aaaaaa" colspan="1" width="1px" valign="top"><div style="margin:1px;padding:0px;"></div></td>\n')
   2982         s.Write('        <td  colspan="2" bgcolor="#f1f1f1" valign="top" style="padding:10px">\n')
   2983         s.Write(content)
   2984         s.Write('        </td>\n')
   2985         s.Write('        <td bgcolor="#aaaaaa" colspan="1" width="1px" valign="top"><div style="margin:1px;padding:0px;"></div>\n')
   2986         s.Write('        </td>\n')
   2987         s.Write('     </tr>\n')
   2988         s.Write('     <tr>\n')
   2989         s.Write('        <td bgcolor="#aaaaaa" colspan="3" height="1" valign="top"><div style="margin:1px;padding:0px;"></div>\n')
   2990         s.Write('        </td>\n')
   2991         s.Write('     </tr>\n')
   2992         s.Write('     </table>\n')
   2993         s.Write('     </td>\n')
   2994         s.Write('</tr>\n')
   2995         s.Write('</table>\n')
   2996         s.Write('</div>\n\n<SCRIPT>prepCentralize("' + str(ID) + '")</SCRIPT>')
   2997         s.Seek(0)
   2998         return s.Read()
   2999 
   3000     def MessageboardStyles(self, Config):
   3001         s = UnicodeMemStream()
   3002         s.Write('<style type="text/css"> \n')
   3003         s.Write('<!--  \n')
   3004         s.Write('  a:link    {color:%s;text-decoration:%s} \n' % (Config['LinkColor'], Config['LinkTextDecoration']))
   3005         s.Write('  a:visited {color:%s;text-decoration:%s} \n' % (Config['VisitedLinkColor'], Config['VisitedTextDecoration']))
   3006         s.Write('  a:hover   {color:%s;text-decoration:%s} \n' % (Config['HoverLinkColor'], Config['HoverTextDecoration']))
   3007         s.Write('  a:active  {color:%s;text-decoration:%s} \n' % (Config['ActiveLinkColor'], Config['ActiveTextDecoration']))
   3008         s.Write('.mbHead{ \n')
   3009         s.Write('   BACKGROUND-COLOR:%s;\n' % Config['HeadCellColor'])
   3010         s.Write('   COLOR:%s;\n' % Config['HeadFontColor'])
   3011         s.Write('   FONT-FAMILY:%s;\n' % Config['DefaultFontFace'])
   3012         s.Write('   FONT-SIZE:%s;\n' % Config['HeaderFontSize'])
   3013         s.Write(' } \n')
   3014         s.Write('.mbForum{ \n')
   3015         s.Write('   BACKGROUND-COLOR:%s;\n' % Config['ForumCellColor'])
   3016         s.Write('   COLOR:%s;\n' % Config['DefaultFontColor'])
   3017         s.Write('   FONT-FAMILY:%s;\n' % Config['DefaultFontFace'])
   3018         s.Write('   FONT-SIZE:%s;\n' % Config['DefaultFontSize'])
   3019         s.Write(' } \n')
   3020         s.Write('.mbForumFirst{ \n')
   3021         s.Write('   BACKGROUND-COLOR:%s;\n' % Config['ForumFirstCellColor'])
   3022         s.Write('   COLOR:%s;\n' % Config['DefaultFontColor'])
   3023         s.Write('   FONT-FAMILY:%s;\n' % Config['DefaultFontFace'])
   3024         s.Write('   FONT-SIZE:%s;\n' % Config['DefaultFontSize'])
   3025         s.Write(' } \n')
   3026         s.Write('.mbForumAlt{ \n')
   3027         s.Write('   BACKGROUND-COLOR:%s;\n' % Config['ForumAltCellColor'])
   3028         s.Write('   COLOR:%s;\n' % Config['DefaultFontColor'])
   3029         s.Write('   FONT-FAMILY:%s;\n' % Config['DefaultFontFace'])
   3030         s.Write('   FONT-SIZE:%s;\n' % Config['DefaultFontSize'])
   3031         s.Write(' } \n')
   3032         s.Write('--> \n')
   3033         s.Write('</style> \n')
   3034         s.Seek(0)
   3035         return s.Read()
   3036 
   3037     def MessageboardChannels(self, Data, Config):
   3038         s = UnicodeMemStream()
   3039         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3040         s.Write('<tr>\n')
   3041         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3042         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3043         if len(Data):
   3044             s.Write('<tr>\n')
   3045             s.Write('    <td class="mbHead" align=center valign=middle width="10">&nbsp</td>\n')
   3046             s.Write('    <td class="mbHead" align=center valign=middle><b>Forums</td>\n')
   3047             s.Write('    <td class="mbHead" align=center valign=middle width="10%%"><b>Posts</font></b></td>\n')
   3048             s.Write('    <td class="mbHead" align=center valign=middle width="10%%"><b>Last Post</b></td>\n')
   3049             s.Write('    <td class="mbHead" align=center valign=middle width="10">&nbsp</td>\n')
   3050             s.Write('</tr>\n')
   3051             for d in Data:
   3052                 s.Write('<tr>\n')
   3053                 s.Write('    <td class="mbForum" align=left valign=middle width="10">&nbsp</td>\n')
   3054                 s.Write('    <td class="mbForum" align=left valign=middle><A href="?action=WebTopics&channelID=%s">%s</A><br/>%s</td>\n' % (d['channelID'], d['channelName'], d['description']))
   3055                 s.Write('    <td class="mbForum" align=center valign=middle Width="10%%">%s</td>\n' % d['numPosts'])
   3056                 s.Write('    <td class="mbForum" align=center valign=middle Width="10%%"><nobr>%s</nobr><br/>by: <A href="javascript:pop(\'mb_pop.py?action=character&characterID=%s\',\'mb_popChar\',300,400);">%s</A></td>\n' % (FmtDateEng(d['lastPost'], 'ss'), d['lastAuthor'], d['lastAuthorName']))
   3057                 s.Write('    <td class="mbForum" align=left valign=middle width="10">&nbsp</td>\n')
   3058                 s.Write('</tr>\n')
   3059 
   3060         s.Write('</table>\n')
   3061         s.Write('</td>\n')
   3062         s.Write('</tr>\n')
   3063         s.Write('</table>\n')
   3064         s.Seek(0)
   3065         return self.MessageboardStyles(Config) + s.Read()
   3066 
   3067     def MessageboardThreads(self, Data, Config, Threads, Page):
   3068         if not Page:
   3069             Page = 1
   3070         s = UnicodeMemStream()
   3071         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3072         s.Write('<tr>\n')
   3073         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3074         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3075         if len(Data):
   3076             s.Write('<tr>\n')
   3077             s.Write('    <td class="mbHead" align=center valign=middle width="10">&nbsp</td>\n')
   3078             s.Write('    <td class="mbHead" align=center valign=middle><b>Topic</td>\n')
   3079             s.Write('    <td class="mbHead" align=center valign=middle><b>Author</td>\n')
   3080             s.Write('    <td class="mbHead" align=center valign=middle><b>Replies</td>\n')
   3081             s.Write('    <td class="mbHead" align=center valign=middle><b>Read</td>\n')
   3082             s.Write('    <td class="mbHead" align=center valign=middle><b>LastPost</td>\n')
   3083             s.Write('</tr>\n')
   3084             for d in Data:
   3085                 replies = d['sc_replies'] - 1
   3086                 if replies == -1:
   3087                     replies = 0
   3088                 s.Write('<tr>\n')
   3089                 s.Write('    <td class="mbForum" align=left valign=middle width="10">&nbsp</td>\n')
   3090                 s.Write('    <td class="mbForum" align=left valign=middle><A href="?action=WebTopic&threadID=%s">%s</A>' % (d['threadID'], d['sc_title']))
   3091                 if d['sc_replies'] > int(Config['PageSize']):
   3092                     s.Write('<br/>&nbsp;&nbsp;&nbsp;' + self.MessageBoardPages(d['sc_replies'], Config['PageSize'], 0, '?action=WebTopic&threadID=' + str(d['threadID']), Config['ActiveLinkColor']))
   3093                 s.Write('    </td>\n')
   3094                 s.Write('    <td class="mbForum" align=center valign=middle><A href="charpop.py?characterID=%s">%s</A></td>\n' % (d['sc_authorID'], d['authorName']))
   3095                 s.Write('    <td class="mbForum" align=center valign=middle>%s</td>\n' % replies)
   3096                 s.Write('    <td class="mbForum" align=center valign=middle>%s</td>\n' % d['sc_reads'])
   3097                 s.Write('    <td class="mbForum" align=center valign=middle Width="10%%"><nobr>%s</nobr><br/>by: <A href="javascript:pop(\'mb_pop.py?action=character&characterID=%s\',\'mb_popChar\',300,400);">%s</A></td>\n' % (FmtDateEng(d['sc_modified'], 'ss'), d['sc_lastauthorID'], d['lastAuthorName']))
   3098                 s.Write('</tr>\n')
   3099 
   3100         s.Write('</table>\n')
   3101         s.Write('</td>\n')
   3102         s.Write('</tr>\n')
   3103         s.Write('</table>\n')
   3104         if Threads > int(Config['PageSize']):
   3105             s.Write('<br/>Pages: %s\n' % self.MessageBoardPages(Threads, Config['PageSize'], Page, '?action=WebTopics&channelID=' + str(d['channelID']), Config['ActiveLinkColor']))
   3106         s.Seek(0)
   3107         return self.MessageboardStyles(Config) + s.Read()
   3108 
   3109     def MessageboardResponses(self, Data, Config, Replies, Page, User):
   3110         if not Page:
   3111             Page = 1
   3112         s = UnicodeMemStream()
   3113         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3114         s.Write('<tr>\n')
   3115         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3116         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3117         if len(Data):
   3118             s.Write('<tr>\n')
   3119             s.Write('    <td class="mbHead" align=center valign=middle width="%s"><b>Author</td>\n' % Config['TopicWidthLeft'])
   3120             s.Write('    <td class="mbHead" align=center valign=middle ><b>Topic</td>\n')
   3121             s.Write('</tr>\n')
   3122             i = 0
   3123             for d in Data:
   3124                 if i == 0 and d['line'] == 1:
   3125                     mbClass = 'mbForumFirst'
   3126                 elif i % 2 == 0:
   3127                     mbClass = 'mbForumAlt'
   3128                 else:
   3129                     mbClass = 'mbForum'
   3130                 Edit = ''
   3131                 if d['userID'] == User:
   3132                     Edit = ' - <a href="?action=WebEdit&threadID=%s&line=%s">Edit</A>' % (d['threadID'], d['line'])
   3133                 s.Write('<tr>\n')
   3134                 s.Write('    <td class="%s" align=left valign=top width="%s"><A href="javascript:pop(\'mb_pop.py?action=character&characterID=%s\',\'mb_popChar\',300,400);">%s</A></td>\n' % (mbClass,
   3135                  Config['TopicWidthLeft'],
   3136                  d['authorID'],
   3137                  d['authorName']))
   3138                 s.Write('    <td class="%s" align=left valign=top >Posted - %s %s<hr noshade size="1">%s</td>\n' % (mbClass,
   3139                  FmtDateEng(d['created'], 'ss'),
   3140                  Edit,
   3141                  self.MessageBoardFormat(d['text'])))
   3142                 s.Write('</tr>\n')
   3143                 i = i + 1
   3144 
   3145         s.Write('</table>\n')
   3146         s.Write('</td>\n')
   3147         s.Write('</tr>\n')
   3148         s.Write('</table>\n')
   3149         if len(Data):
   3150             if Replies > int(Config['PageSize']):
   3151                 s.Write('<br/>Pages: %s\n' % self.MessageBoardPages(Replies, Config['PageSize'], Page, '?action=WebTopic&threadID=' + str(d['threadID']), Config['ActiveLinkColor']))
   3152         s.Seek(0)
   3153         return self.MessageboardStyles(Config) + s.Read()
   3154 
   3155     def MessageboardPost(self, postType, channelID, threadID, Data, Config, ActiveCharacter, Characters):
   3156         Header = ''
   3157         text = ''
   3158         line = ''
   3159         if postType == 'Post':
   3160             Header = 'Post New Topic'
   3161             Button = 'Post New Topic'
   3162         if postType == 'Reply':
   3163             Header = 'Reply to Topic'
   3164             Button = 'Post Reply'
   3165         if postType == 'Edit':
   3166             Header = 'Edit'
   3167             Button = '   Update   '
   3168             line = threadID[1]
   3169             threadID = threadID[0]
   3170             text = Data.text
   3171         s = UnicodeMemStream()
   3172         s.Write('<form name="WebPost" method="post" action="?action=%s">\n' % postType)
   3173         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3174         s.Write('<tr>\n')
   3175         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3176         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3177         s.Write('<tr>\n')
   3178         s.Write('    <td class="mbHead" align=center valign=middle colspan=2><b>%s</td>\n' % Header)
   3179         s.Write('</tr>\n')
   3180         if Config['AllowForumCode'] == '1':
   3181             s.Write('<tr>\n')
   3182             s.Write('    <td class="mbForum" align=right valign=center >Format:</td>\n')
   3183             s.Write('    <td class="mbForum" align=left valign=top >Formating of Forum Code</td>\n')
   3184             s.Write('</tr>\n')
   3185         if postType == 'Post':
   3186             s.Write('<tr>\n')
   3187             s.Write('    <td class="mbForum" align=right valign=center >Subject:</td>\n')
   3188             s.Write('    <td class="mbForum" align=left valign=top ><input type="text" name="subject" size=63 /></td>\n')
   3189             s.Write('</tr>\n')
   3190         s.Write('<tr>\n')
   3191         s.Write('    <td class="mbForum" align=right valign=center >Message:</td>\n')
   3192         s.Write('    <td class="mbForum" align=left valign=top ><textarea name=text cols=48 rows=10>%s</textarea></td>\n' % text)
   3193         s.Write('</tr>\n')
   3194         if postType == 'Post' or postType == 'Reply':
   3195             s.Write('<tr>\n')
   3196             s.Write('    <td class="mbForum" align=right valign=center >Character:</td>\n')
   3197             s.Write('    <td class="mbForum" align=left valign=top>')
   3198             s.Write('    <select name=characterID>')
   3199             for char in Characters:
   3200                 s.Write('    <option ')
   3201                 if session.charid == char.characterID:
   3202                     s.Write('  SELECTED  ')
   3203                 s.Write('    value="%s">%s</option>' % (char.characterID, char.characterName))
   3204 
   3205             s.Write('    </select><input type=checkbox name=signature value=1>Check here to include your profile signature')
   3206             s.Write('    </td>\n')
   3207             s.Write('</tr>\n')
   3208         s.Write('<tr>\n')
   3209         s.Write('    <td class="mbForum" align=right valign=center >&nbsp;</td>\n')
   3210         s.Write('    <td class="mbForum" align=left valign=top >')
   3211         s.Write('    <input type=submit value="%s">\n' % Button)
   3212         s.Write('    <input type=button value="Cancel" onClick="JavaScript:history.go(-1);">\n')
   3213         s.Write('    <input type=button value="Preview">\n')
   3214         s.Write('    <input type=reset value="Reset Fields">\n')
   3215         s.Write('    <input type=hidden name="channelID" value="%s">\n' % channelID)
   3216         s.Write('    <input type=hidden name="threadID"  value="%s">\n' % threadID)
   3217         s.Write('    <input type=hidden name="line"      value="%s">\n' % line)
   3218         s.Write('    </td>\n')
   3219         s.Write('</tr>\n')
   3220         s.Write('</table>\n')
   3221         s.Write('</td>\n')
   3222         s.Write('</tr>\n')
   3223         s.Write('</table>\n')
   3224         s.Write('</form>\n')
   3225         s.Seek(0)
   3226         return self.MessageboardStyles(Config) + s.Read()
   3227 
   3228     def MessageBoardPages(self, replies, pagesize, current, url, color):
   3229         pages = ''
   3230         if int(replies) > int(pagesize):
   3231             for i in range(1, int(replies / int(pagesize) + 2)):
   3232                 style = ''
   3233                 if i == current:
   3234                     style = 'STYLE="FONT-WEIGHT:bolder;Color:%s;"' % color
   3235                 pages = pages + '<A href="%s&page=%s" %s>%s</A>&nbsp;' % (url,
   3236                  str(i),
   3237                  style,
   3238                  str(i))
   3239 
   3240         return pages
   3241 
   3242     def MessageBoardFormat(self, text):
   3243         text = text.replace(chr(13) + chr(10), '<br/>')
   3244         return text
   3245 
   3246     def MessageboardNoCharacter(self, Config):
   3247         s = UnicodeMemStream()
   3248         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3249         s.Write('<tr>\n')
   3250         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3251         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3252         s.Write('<tr>\n')
   3253         s.Write('    <td class="mbHead" align=center valign=middle><b>No Character</b></td>\n')
   3254         s.Write('</tr>\n')
   3255         s.Write('<tr>\n')
   3256         s.Write('    <td class="mbForum" align=center valign=middle>&nbsp;<br/>You must create at least one character before you can participate in the forums.<br/>&nbsp;<br/>\n')
   3257         s.Write('    <A href="JavaScript:history.go(-1);">Continue</A></td>\n')
   3258         s.Write('</tr>\n')
   3259         s.Write('</table>\n')
   3260         s.Write('</td>\n')
   3261         s.Write('</tr>\n')
   3262         s.Write('</table>\n')
   3263         s.Seek(0)
   3264         return self.MessageboardStyles(Config) + s.Read()
   3265 
   3266     def MessageboardNotYourCharacter(self, Config):
   3267         s = UnicodeMemStream()
   3268         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3269         s.Write('<tr>\n')
   3270         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3271         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3272         s.Write('<tr>\n')
   3273         s.Write('    <td class="mbHead" align=center valign=middle><b>Not Your Character</b></td>\n')
   3274         s.Write('</tr>\n')
   3275         s.Write('<tr>\n')
   3276         s.Write('    <td class="mbForum" align=center valign=middle>&nbsp;<br/>The character you used to post does for some reason not belong to you!<br/>CONCORD has been notified<br/>&nbsp;<br/>\n')
   3277         s.Write('    <A href="JavaScript:history.go(-1);">Continue</A></td>\n')
   3278         s.Write('</tr>\n')
   3279         s.Write('</table>\n')
   3280         s.Write('</td>\n')
   3281         s.Write('</tr>\n')
   3282         s.Write('</table>\n')
   3283         s.Seek(0)
   3284         return self.MessageboardStyles(Config) + s.Read()
   3285 
   3286     def MessageBoardNotYourPost(self, Config):
   3287         s = UnicodeMemStream()
   3288         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3289         s.Write('<tr>\n')
   3290         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3291         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3292         s.Write('<tr>\n')
   3293         s.Write('    <td class="mbHead" align=center valign=middle><b>Not Your Post</b></td>\n')
   3294         s.Write('</tr>\n')
   3295         s.Write('<tr>\n')
   3296         s.Write('    <td class="mbForum" align=center valign=middle>&nbsp;<br/>The Post/Reply you trying to edit does for some reason not belong to you!<br/>CONCORD has been notified<br/>&nbsp;<br/>\n')
   3297         s.Write('    <A href="JavaScript:history.go(-1);">Continue</A></td>\n')
   3298         s.Write('</tr>\n')
   3299         s.Write('</table>\n')
   3300         s.Write('</td>\n')
   3301         s.Write('</tr>\n')
   3302         s.Write('</table>\n')
   3303         s.Seek(0)
   3304         return self.MessageboardStyles(Config) + s.Read()
   3305 
   3306     def MessageBoardEOF(self, Config):
   3307         s = UnicodeMemStream()
   3308         s.Write('<table border="0" cellpadding="0" cellspacing="0" width="100%">\n')
   3309         s.Write('<tr>\n')
   3310         s.Write('<td valign="top" bgcolor="%s">\n' % Config['TableBorderColor'])
   3311         s.Write('<table border="0" cellpadding="4" cellspacing="1" width="100%">\n')
   3312         s.Write('<tr>\n')
   3313         s.Write('    <td class="mbHead" align=center valign=middle><b>Data could not be retrieved</b></td>\n')
   3314         s.Write('</tr>\n')
   3315         s.Write('<tr>\n')
   3316         s.Write('    <td class="mbForum" align=center valign=middle>&nbsp;<br/>For some reason the data you requested could not be retrieved. [EOF]<br/>&nbsp;<br/>\n')
   3317         s.Write('    <A href="JavaScript:history.go(-1);">Continue</A></td>\n')
   3318         s.Write('</tr>\n')
   3319         s.Write('</table>\n')
   3320         s.Write('</td>\n')
   3321         s.Write('</tr>\n')
   3322         s.Write('</table>\n')
   3323         s.Seek(0)
   3324         return self.MessageboardStyles(Config) + s.Read()
   3325 
   3326 
   3327 class Form():
   3328 
   3329     def __init__(self, url = '', action = '', method = 'GET', target = '', formName = 'NeedAName', encType = '', request = None, waitingMessage = None, **kwargs):
   3330         self.url = url
   3331         self.formName = formName
   3332         self.action = action
   3333         self.method = str(method).upper()
   3334         self.target = target
   3335         self.elements = []
   3336         self.encType = encType
   3337         self.request = request
   3338         self.waitingMessage = waitingMessage
   3339         self.extraAttributes = kwargs
   3340 
   3341     def AddFSDLocalizationFields(self, textHolderField, messageIDField, groupPath, messageID, previewWindowSize = 30, preview = True):
   3342         if messageID is None:
   3343             messageID = ''
   3344         html = '<div>{}</div>'.format(self.Input(messageIDField, messageID, 20, '', None, params=''))
   3345         self.elements.append([messageIDField, html, ''])
   3346         if preview:
   3347             text = localization.GetByMessageID(messageID)
   3348             html = '<div>{}</div>'.format(self.Input(textHolderField, text, previewWindowSize, '', None, params='disabled'))
   3349             self.elements.append([textHolderField, html, ''])
   3350 
   3351     def AddFSDLocalizationFieldsWithTextArea(self, textHolderField, messageIDField, groupPath, messageID, rows = 26, cols = 68):
   3352         html = '<div>{}</div>'.format(self.Input(messageIDField, messageID, 20, '', None, params=''))
   3353         self.elements.append([messageIDField, html, ''])
   3354         text = localization.GetByMessageID(messageID)
   3355         textAreaHtml = '    <textarea name="%s" cols=%d rows=%d %s>%s</textarea>' % (textHolderField,
   3356          cols,
   3357          rows,
   3358          'disabled',
   3359          text)
   3360         html = '<div>{}</div>'.format(textAreaHtml)
   3361         self.elements.append([textHolderField, html, ''])
   3362 
   3363     def AddDateTimePicker(self, label, dateName, timeName, value):
   3364         v = value.split(' ')
   3365         if len(v) == 1:
   3366             v = (value, '00:00')
   3367         writer = HtmlWriter()
   3368         self.elements.append([label, writer.GetDatePicker(ctrlID=dateName, ctrlCaption=None, ctrlFormat='yy-mm-dd', ctrlValue=v[0], ctrlTitle=''), writer.GetInput(ctrlID=timeName, labelCaption=None, className=None, minLength=None, width='60', value=v[1])])
   3369 
   3370     def AddDateTimeInput(self, label, name, value, text = ''):
   3371         writer = HtmlWriter()
   3372         self.elements.append([label, writer.GetDateTimePicker(ctrlID=name, caption=None, value=value, title=''), text])
   3373 
   3374     def AddDatePicker(self, label, name, value):
   3375         writer = HtmlWriter()
   3376         self.elements.append([label, writer.GetDatePicker(ctrlID=name, ctrlCaption=None, ctrlFormat='yy-mm-dd', ctrlValue=value, ctrlTitle=''), ''])
   3377 
   3378     def AddText(self, text):
   3379         self.elements.append(['', text, ''])
   3380 
   3381     def AddHTML(self, html):
   3382         self.elements.append(['', html, ''])
   3383 
   3384     def AddHTMLWithLabel(self, label, html):
   3385         self.elements.append([label, html, ''])
   3386 
   3387     def AddReadOnly(self, name, value = '', prettyName = None):
   3388         if prettyName is None:
   3389             prettyName = name
   3390         self.elements.append([prettyName, value, ''])
   3391 
   3392     def AddHidden(self, name, value = '', prettyName = None):
   3393         self.elements.append(['', '<input type="hidden" name="%s" id="%s" value="%s">' % (name, name, value), ''])
   3394 
   3395     def AddInput(self, name, value = '', size = 20, text = '', prettyName = None, params = ''):
   3396         if prettyName is None:
   3397             prettyName = name
   3398         self.elements.append([prettyName, self.Input(name, value, size, text, prettyName, params), text])
   3399 
   3400     def AddDeprecatedInput(self, name, value = '', size = 20, text = '', prettyName = None, params = ''):
   3401         if prettyName is None:
   3402             prettyName = name
   3403         self.elements.append([prettyName, '<input style="background-color:yellow" type="text" id="%s" name="%s" size="%d" value="%s" %s>' % (name,
   3404           name,
   3405           size,
   3406           value,
   3407           params), text])
   3408 
   3409     def Input(self, name, value = '', size = 20, text = '', prettyName = None, params = ''):
   3410         if prettyName is None:
   3411             prettyName = name
   3412         return '<input type="text" id="%s" name="%s" size="%d" value="%s" %s>' % (name,
   3413          name,
   3414          size,
   3415          value,
   3416          params)
   3417 
   3418     def AddNumberInput(self, name, value = 0.0, min = None, max = None, step = None, size = 20, text = '', prettyName = None, params = ''):
   3419         if prettyName is None:
   3420             prettyName = name
   3421         self.elements.append([prettyName, '<input type="number" id="{id}" name="{name}" size="{size}" value="{value}" min="{min}" max="{max}" step="{step}" {params}>'.format(id=name, name=name, size=size, value=value, min=min, max=max, step=step, params=params), text])
   3422 
   3423     def AddCheckedInput(self, chkName, edtName, edtValue = '', label = None, chkLabel = '', edtLabel = '', edtSize = 20, chkChecked = 0, chkDisabled = 0, edtParams = '', lblParams = ''):
   3424         if label is None:
   3425             label = chkName + edtName
   3426         self.elements.append([self.Checkbox(chkName, checked=chkChecked, disabled=chkDisabled, prettyName=chkLabel), '<label class="form-label" for="%s" %s>%s</label></td><td>' % (chkName, lblParams, label), self.Input(edtName, value=edtValue, size=edtSize, prettyName=edtLabel, params=edtParams)])
   3427 
   3428     def AddPassword(self, name, value = '', size = 20, prettyName = None, text = ''):
   3429         if prettyName is None:
   3430             prettyName = name
   3431         self.elements.append([prettyName, '<input type="password" name="%s" size="%d" value="%s">' % (name, size, value), text])
   3432 
   3433     def AddCheckbox(self, name, text = '', checked = 0, prettyName = None, disabled = 0, value = None):
   3434         if prettyName is None:
   3435             prettyName = name
   3436         self.elements.append(['<label class="form-padding-label" for="%s">%s</label>' % (name, prettyName), self.Checkbox(name, text, checked, prettyName, disabled, value), '<label class="form-label" for="%s">%s</label>' % (name, text)])
   3437 
   3438     def Checkbox(self, name, text = '', checked = 0, prettyName = None, disabled = 0, value = None):
   3439         if prettyName is None:
   3440             prettyName = name
   3441         value = '' if value is None else 'value="%s"' % str(value)
   3442         code = '    <input type="checkbox" id="%s" %s name="%s"%s%s>' % (name,
   3443          value,
   3444          name,
   3445          ['', ' checked'][not not checked],
   3446          ['', ' disabled'][not not disabled])
   3447         return code
   3448 
   3449     def AddRadioGroup(self, name, valname, selected = None, prettyName = None):
   3450         if prettyName is None:
   3451             prettyName = name
   3452         bms = UnicodeMemStream()
   3453         if selected is not None:
   3454             checked = ' '
   3455         else:
   3456             checked = 'checked'
   3457         if type(valname) == types.DictType:
   3458             valname = valname.items()
   3459         for k, v in valname:
   3460             if selected is not None and selected == k:
   3461                 bms.Write('<input type=radio name="%s" value="%s" checked>%s\n' % (name, k, v))
   3462             else:
   3463                 bms.Write('<input type=radio name="%s" value="%s" %s>%s\n' % (name,
   3464                  k,
   3465                  checked,
   3466                  v))
   3467                 checked = ' '
   3468 
   3469         bms.Seek(0)
   3470         self.elements.append([prettyName, bms.Read(), ''])
   3471 
   3472     def AddSelect(self, name, valname, prettyName = None, selected = None, multiple = 0, evenhandler = '', Text = '', style = None, sort = True, rel = None, cssByValue = None):
   3473         if prettyName is None:
   3474             prettyName = name
   3475         self.elements.append([prettyName, self.Select(name, valname, prettyName=prettyName, selected=selected, multiple=multiple, evenhandler=evenhandler, Text=Text, style=style, sort=sort, rel=rel, cssByValue=cssByValue), ''])
   3476 
   3477     def Select(self, name, valname, prettyName = None, selected = None, multiple = 0, evenhandler = '', Text = '', style = None, sort = True, rel = None, cssByValue = None):
   3478         if prettyName is None:
   3479             prettyName = name
   3480         style = CheckValue('style', style)
   3481         rel = CheckValue('rel', rel)
   3482         bms = UnicodeMemStream()
   3483         if multiple:
   3484             if multiple < 2 or type(multiple) != type(1):
   3485                 multiple = 4
   3486             bms.Write('    <select id="%s" name="%s" %s size=%s%s%s multiple>\n' % (name,
   3487              name,
   3488              evenhandler,
   3489              multiple,
   3490              style,
   3491              rel))
   3492         else:
   3493             bms.Write('    <select id="%s" name="%s" %s%s%s>\n' % (name,
   3494              name,
   3495              evenhandler,
   3496              style,
   3497              rel))
   3498         if type(valname) == type([]):
   3499             for i in valname:
   3500                 if selected is not None and (i[0] == selected or multiple and i[0] in selected):
   3501                     bms.Write('        <option value="%s" Selected>%s</option>\n' % (i[0], i[1]))
   3502                 else:
   3503                     bms.Write('        <option value="%s">%s</option>\n' % (i[0], i[1]))
   3504 
   3505         else:
   3506             sortedList = valname.items()
   3507             if sort:
   3508                 sortedList.sort(key=lambda key_value: key_value[1])
   3509             for key, value in sortedList:
   3510                 rowStyleString = ''
   3511                 if isinstance(cssByValue, dict) and key in cssByValue:
   3512                     rowStyleString = 'style="%s" ' % cssByValue[key]
   3513                 if selected is not None and selected == key:
   3514                     bms.Write('        <option %svalue="%s" selected>%s</option>\n' % (rowStyleString, key, value))
   3515                 else:
   3516                     bms.Write('        <option %svalue="%s">%s</option>\n' % (rowStyleString, key, value))
   3517 
   3518         bms.Write('    </select> %s\n' % Text)
   3519         bms.Seek(0)
   3520         return bms.Read()
   3521 
   3522     def AddSoundSelect(self, name, valname, prettyName = None, selected = None, eventHandler = ''):
   3523         if prettyName is None:
   3524             prettyName = name
   3525         self.elements.append([prettyName, self.SoundSelect(name, valname, prettyName, selected, eventHandler), ''])
   3526 
   3527     def SoundSelect(self, name, valname, prettyName = None, selected = None, eventHandler = ''):
   3528         if prettyName is None:
   3529             prettyName = name
   3530         bms = UnicodeMemStream()
   3531         bms.Write('    <select id="%s" name="%s" onchange="play_%s(this);">\n' % (name, name, name))
   3532         if type(valname) == type([]):
   3533             for i in valname:
   3534                 if selected is not None and (i[0] == selected or multiple and i[0] in selected):
   3535                     bms.Write('        <option value="%s" SELECTED>  %s  </option>\n' % (i[0], i[1]))
   3536                 else:
   3537                     bms.Write('        <option value="%s">  %s  </option>\n' % (i[0], i[1]))
   3538 
   3539         else:
   3540             l = valname.items()
   3541             l.sort(lambda x, y: cmp(x[1], y[1]))
   3542             for i in l:
   3543                 if selected is not None and selected == i[0]:
   3544                     bms.Write('        <option value="%s" SELECTED>  %s  </option>\n' % (i[0], i[1]))
   3545                 else:
   3546                     bms.Write('        <option value="%s">  %s  </option>\n' % (i[0], i[1]))
   3547 
   3548         bms.Write('    </select> \n')
   3549         bms.Seek(0)
   3550         return bms.Read()
   3551 
   3552     def AddTextArea(self, name, value = '', cols = 68, rows = 26, prettyName = None, props = ''):
   3553         if prettyName is None:
   3554             prettyName = name
   3555         self.elements.append([prettyName, '    <textarea name="%s" cols=%d rows=%d %s>%s</textarea>' % (name,
   3556           cols,
   3557           rows,
   3558           props,
   3559           value), ''])
   3560 
   3561     def AddReadOnlyTextArea(self, value, cols, rows):
   3562         self.AddHTML('\n            <textarea style="color: -internal-light-dark-color(black, gray);" disabled cols="{}", rows="{}">{}</textarea>\n            '.format(cols, rows, value))
   3563 
   3564     def AddHtmlArea(self, name, value = '', cols = 68, rows = 26, prettyName = None, props = ''):
   3565         if prettyName is None:
   3566             prettyName = name
   3567         self.elements.append([prettyName, '    <textarea name="%s" cols=%d rows=%d %s>%s</textarea>\n<script language=\'javascript1.2\'>editor_generate(\'%s\');</script>' % (name,
   3568           cols,
   3569           rows,
   3570           props,
   3571           value,
   3572           name), ''])
   3573 
   3574     def AddPicker(self, name, Action, Strokes = 3, prettyName = None, SubmitOnSelect = 0, default = None):
   3575         if prefs.languageID == 'ZH':
   3576             Strokes = 100
   3577         if prettyName is None:
   3578             prettyName = name
   3579         self.elements.append([prettyName, self.Picker(name, Action, Strokes, prettyName, SubmitOnSelect, default), ''])
   3580 
   3581     def Picker(self, name, Action, Strokes = 3, prettyName = None, SubmitOnSelect = 0, default = None):
   3582         if prettyName is None:
   3583             prettyName = name
   3584         self.s = UnicodeMemStream()
   3585         if self.formName == 'NeedAName':
   3586             if default is not None:
   3587                 OnSelect = ''
   3588                 if SubmitOnSelect:
   3589                     OnSelect = 'onChange="this.form.submit();"'
   3590                 self.s.Write('<!--BegMark--><SELECT Name="%s" ID="%s" onClick ="%sgoBack(this, event);"  onContextMenu="%sgoBack(this, event);" %s>\n' % (name,
   3591                  name,
   3592                  name,
   3593                  name,
   3594                  OnSelect))
   3595                 self.s.Write('<OPTION VALUE="%s" Selected>%s</OPTION>\\n\'\n' % (default[0], default[1]))
   3596                 self.s.Write('</SELECT>\n')
   3597                 self.s.Write('<!--EndMark-->\n')
   3598             else:
   3599                 self.s.Write('<!--BegMark--><input type="text" name="%s" ID="%s" onKeyUp="%sGetSelect(event)"><!--EndMark-->\n' % (name, name, name))
   3600             self.s.Write('<iframe id="ifrGetSelect%s" style="display: none; width:1;height:1;border:0px;"></iframe>\n' % name)
   3601             self.s.Write('<script type="text/javascript">\n')
   3602             self.s.Write('     function %sGetSelect(e){\n' % name)
   3603             self.s.Write('         var el = $("#%s"); \n' % name)
   3604             self.s.Write('         window.status = el.val(); \n')
   3605             self.s.Write('         PressEnter = false; \n')
   3606             self.s.Write('         if((e.keyCode == 13)&&(el.val().length > 0)){PressEnter = true;}; \n')
   3607             self.s.Write('         if(isNaN(el.val())){\n')
   3608             self.s.Write('             if((el.val().length >= %s)||(PressEnter == true)){\n' % Strokes)
   3609             self.s.Write("               document.getElementById('ifrGetSelect%s').src='/lib/pickpopulate.py?action=%s&ReplaceElement=%s&Search='+UESconvertText(el.val())+'&SubmitOnSelect=%s';\n" % (name,
   3610              Action,
   3611              name,
   3612              SubmitOnSelect))
   3613             self.s.Write("               document.getElementById('%s').disabled=true;\n" % name)
   3614             self.s.Write('             }\n')
   3615             self.s.Write('         }\n')
   3616             self.s.Write('     }\n')
   3617             self.s.Write('     function %sgoBack(oElement, e){\n' % name)
   3618             self.s.Write("         if((e.ctrlLeft)||(e.type == 'contextmenu')){\n")
   3619             self.s.Write('             repCode = \'<input type="text" name="%s" id="%s" onKeyUp="%sGetSelect(event)">\';\n' % (name, name, name))
   3620             self.s.Write("             inJect = oElement.parentNode.innerHTML.replace(/^.*(.|\\n)*?<\\!--EndMark-->/gi,'<\\!--BegMark-->'+repCode+'<\\!--EndMark-->');\n")
   3621             self.s.Write('             oElement.parentNode.innerHTML = inJect;\n')
   3622             self.s.Write('             e.cancelBubble = true;\n')
   3623             self.s.Write('         }\n')
   3624             self.s.Write('     }\n')
   3625             self.s.Write('</script>\n')
   3626         else:
   3627             if default is not None:
   3628                 OnSelect = ''
   3629                 if SubmitOnSelect:
   3630                     OnSelect = 'onChange="this.form.submit();"'
   3631                 self.s.Write('<!--BegMark--><select Name="%s" ID="%s" onClick ="%s%sgoBack(this, event);"  onContextMenu="%s%sgoBack(this, event);" %s>\n' % (name,
   3632                  name,
   3633                  self.formName,
   3634                  name,
   3635                  self.formName,
   3636                  name,
   3637                  OnSelect))
   3638                 self.s.Write('<option value="%s" Selected>%s</option>\\n\'\n' % (default[0], default[1]))
   3639                 self.s.Write('</select>\n')
   3640                 self.s.Write('<!--EndMark-->\n')
   3641             else:
   3642                 self.s.Write('<!--BegMark--><input type="text" name="%s" id="%s" onKeyUp="%s%sGetSelect(event)"><!--EndMark-->\n' % (name,
   3643                  name,
   3644                  self.formName,
   3645                  name))
   3646             self.s.Write('<iframe id="ifrGetSelect%s" style="display: none; width:1;height:1;border:0px;"></iframe>\n\n' % name)
   3647             self.s.Write('<script type="text/javascript">\n')
   3648             self.s.Write('     function %s%sGetSelect(e){\n' % (self.formName, name))
   3649             self.s.Write('         var el = document.%s.%s;\n' % (self.formName, name))
   3650             self.s.Write('         window.status = el.value; \n')
   3651             self.s.Write('         PressEnter = false \n')
   3652             self.s.Write('         if((e.keyCode == 13)&&(el.value.length > 0)){PressEnter = true;} \n')
   3653             self.s.Write('         if(isNaN(el.value)){\n')
   3654             self.s.Write('             if((el.value.length >= %s)||(PressEnter == true)){\n' % Strokes)
   3655             self.s.Write('               document.getElementById("ifrGetSelect%s").src=\'/lib/pickpopulate.py?action=%s&ReplaceElement=%s&Search=\'+UESconvertText(el.value)+\'&SubmitOnSelect=%s&FormName=%s\';\n' % (name,
   3656              Action,
   3657              name,
   3658              SubmitOnSelect,
   3659              self.formName))
   3660             self.s.Write('               el.disabled=true;\n')
   3661             self.s.Write('             }\n')
   3662             self.s.Write('         }\n')
   3663             self.s.Write('     }\n')
   3664             self.s.Write('     function %s%sgoBack(oElement, e){\n' % (self.formName, name))
   3665             self.s.Write("         if((e.ctrlLeft)||(e.type == 'contextmenu')){\n")
   3666             self.s.Write('             repCode = \'<input type="text" name="%s" ID="%s" onKeyUp="%s%sGetSelect(event)">\';\n' % (name,
   3667              name,
   3668              self.formName,
   3669              name))
   3670             self.s.Write("             inJect = oElement.parentNode.innerHTML.replace(/^.*(.|\\n)*?<\\!--EndMark-->/gi,'<\\!--BegMark-->'+repCode+'<\\!--EndMark-->');\n")
   3671             self.s.Write('             oElement.parentNode.innerHTML = inJect;\n')
   3672             self.s.Write('             e.cancelBubble = true;\n')
   3673             self.s.Write('         }\n')
   3674             self.s.Write('     }\n')
   3675             self.s.Write('</script>\n')
   3676         self.s.Seek(0)
   3677         return self.s.Read()
   3678 
   3679     def AddModal(self, name, url, parameters = {}, initValues = '', size = 20, prettyName = None, width = 350, height = 350, scroll = 0):
   3680         if prettyName is None:
   3681             prettyName = name
   3682         p = '&modalDialoguePopUp=1'
   3683         for k, v in parameters.iteritems():
   3684             p += '&%s=%s' % (k, v)
   3685 
   3686         self.elements.append(['<a href="javascript:Wopen%s();">%s</a>\n\n<script>\nfunction Wopen%s(){\nmodal=window.open(\'%s?modalDialogueElement=%s%s\',\'modalForm\',\'toolbar=0,location=0,directories=no,status=no,menubar=no,scrollbars=%s,resizable=no,width=%s,height=%s\');\n}\n</script>\n' % (name,
   3687           prettyName,
   3688           name,
   3689           url,
   3690           name,
   3691           p,
   3692           scroll,
   3693           width,
   3694           height), '<input type="text" name="%sDisp" size="%s" id="%sDisp"><input type="hidden" value="%s" name="%s" id="%s">' % (name,
   3695           size,
   3696           name,
   3697           initValues,
   3698           name,
   3699           name), ''])
   3700 
   3701     def AddButton(self, onClick = '', nm = 'Create', error = '', bolConfirm = 0):
   3702         if bolConfirm:
   3703             onClick = 'onClick="if(confirm(\'Are you sure?\')){%s}"' % onClick
   3704         else:
   3705             onClick = 'onClick="%s"' % onClick
   3706         self.elements.append(['', '<input type="button" name="%s" value="%s"  %s>' % (nm, nm, onClick), error])
   3707 
   3708     def AddFile(self, name = 'file1', prettyName = None):
   3709         if prettyName is None:
   3710             prettyName = name
   3711         self.elements.append([prettyName, '<input type="file" name="%s">' % name, ''])
   3712 
   3713     def AddSubmit(self, action = '', nm = 'Create', error = '', bolConfirm = 0, onClick = 0, style = 1, name = 'Post', confirmMessage = None):
   3714         inLineJavaScript = ''
   3715         if bolConfirm:
   3716             confirmMessage = confirmMessage or 'Are you sure?'
   3717             inLineJavaScript = 'onClick="return confirm(\'%s\')"' % confirmMessage
   3718         if onClick != 0:
   3719             inLineJavaScript = 'onClick="%s"' % onClick
   3720         value = '<input type="submit" id="%s" name="%s" value="%s"  %s>' % (name,
   3721          name,
   3722          nm,
   3723          inLineJavaScript)
   3724         if action:
   3725             value += '\n<input type="hidden" name="action" value="%s">' % (action,)
   3726         self.elements.append(['', value, error])
   3727         return self.Generate(style)
   3728 
   3729     def SetColumnCount(self, columnCount = 0):
   3730         self.elements.append(['/*SETCOLUMNS*/', columnCount])
   3731 
   3732     def SetColumnWidth(self, n, w = None):
   3733         self.elements.append(['/*SETCOLUMNWIDTH*/', n, w])
   3734 
   3735     def Generate(self, style = 1):
   3736         self.s = UnicodeMemStream()
   3737         target = ''
   3738         if self.target != '':
   3739             target = 'target="%s"' % self.target
   3740         encType = ''
   3741         if self.encType != '':
   3742             encType = 'enctype="%s"' % self.encType
   3743         extra = []
   3744         if self.extraAttributes and isinstance(self.extraAttributes, dict):
   3745             for k, v in self.extraAttributes.iteritems():
   3746                 extra.append('%s="%s"' % (k, v))
   3747 
   3748         if extra:
   3749             extra = ' %s' % ' '.join(extra)
   3750         else:
   3751             extra = ''
   3752         if not (self.url or self.action):
   3753             self.s.Write('<form name="%s" method="%s" %s%s>\n' % (self.formName,
   3754              self.method,
   3755              target,
   3756              extra))
   3757         elif not self.action:
   3758             self.s.Write('<form name="%s" id="%s" action="%s" method="%s" %s %s%s>\n' % (self.formName,
   3759              self.formName,
   3760              self.url,
   3761              self.method,
   3762              target,
   3763              encType,
   3764              extra))
   3765         else:
   3766             stuff = self.url.split('#')
   3767             if len(stuff) > 1:
   3768                 self.s.Write('<form name="%s" id="%s" action="%s?action=%s" method="%s" %s %s%s>\n' % (self.formName,
   3769                  self.formName,
   3770                  stuff[0],
   3771                  self.action + '#' + stuff[1],
   3772                  self.method,
   3773                  target,
   3774                  encType,
   3775                  extra))
   3776             else:
   3777                 self.s.Write('<form name="%s" id="%s" action="%s?action=%s" method="%s" %s %s%s>\n' % (self.formName,
   3778                  self.formName,
   3779                  self.url,
   3780                  self.action,
   3781                  self.method,
   3782                  target,
   3783                  encType,
   3784                  extra))
   3785         if self.waitingMessage is not None:
   3786             self.s.Write(WaitingMessage(self.waitingMessage))
   3787         if style != 2:
   3788             self.s.Write('<table Border=0>\n')
   3789         if style == 1:
   3790             columns = 1
   3791             prevcolumns = []
   3792             width = {}
   3793             i = 0
   3794             while i < len(self.elements):
   3795                 self.s.Write('<tr>\n')
   3796                 istart = i
   3797                 for n in range(columns):
   3798                     try:
   3799                         if istart + n < len(self.elements):
   3800                             element = self.elements[istart + n]
   3801                             if element[0] == '/*SETCOLUMNS*/':
   3802                                 if element[1]:
   3803                                     prevcolumns.append(columns)
   3804                                     columns = element[1]
   3805                                 else:
   3806                                     columns = prevcolumns.pop(-1)
   3807                                 self.s.Write('</tr>\n')
   3808                                 self.s.Write('</table>\n')
   3809                                 self.s.Write('<table Border=0>\n')
   3810                                 self.s.Write('<tr>\n')
   3811                                 break
   3812                             if element[0] == '/*SETCOLUMNWIDTH*/':
   3813                                 width[element[1]] = element[2]
   3814                             else:
   3815                                 w = width.get(0, None)
   3816                                 if w:
   3817                                     w = 'width=%s' % w
   3818                                 else:
   3819                                     w = ''
   3820                                 self.s.Write('    <td valign=top %s><span>%s</span></td>\n' % (w, element[0]))
   3821                                 w = width.get(n + 1, None)
   3822                                 if w:
   3823                                     w = 'width=%s' % w
   3824                                 else:
   3825                                     w = ''
   3826                                 self.s.Write('    <td %s>\n' % w)
   3827                                 self.s.Write('    %s %s\n' % (element[1], element[2]))
   3828                                 self.s.Write('    </td>\n')
   3829                     finally:
   3830                         i += 1
   3831 
   3832                 self.s.Write('</tr>\n')
   3833 
   3834         elif style == 0:
   3835             self.s.Write('<tr>')
   3836             for element in self.elements:
   3837                 self.s.Write('    <td valign=top>%s</td>\n' % element[0])
   3838 
   3839             self.s.Write('</tr>')
   3840             self.s.Write('<tr>')
   3841             for element in self.elements:
   3842                 self.s.Write('    <td valign=top>%s</font></td>\n' % element[1])
   3843 
   3844             self.s.Write('</tr>')
   3845         elif style == 2:
   3846             for element in self.elements:
   3847                 if not element[0].startswith('/*'):
   3848                     self.s.Write(element[0])
   3849                     self.s.Write(element[1])
   3850                     self.s.Write(element[2])
   3851 
   3852         if style != 2:
   3853             self.s.Write('</table>')
   3854         if self.request is not None and self.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
   3855             self.s.Write(CSRFFormField(self.request))
   3856         self.s.Write('</form>')
   3857         self.s.Seek(0)
   3858         return self.s.Read()
   3859 
   3860 
   3861 MultipleReplacer = multiple_replacer
   3862 try:
   3863     sm.services['http'].codeCache.clear()
   3864 except:
   3865     sys.exc_clear()