This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/common/XhtmlGenerator.py

812 lines
22 KiB
Python
Executable File

# -*- coding: iso-8859-15 -*-
# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
# Romain Chantereau <rchantereau@entrouvert.com>
# Nicolas Clapiès <nclapies@easter-eggs.org>
# Pierre-Antoine Dejace <padejace@entrouvert.be>
# Thierry Dulieu <tdulieu@easter-eggs.com>
# Florent Monnier <monnier@codelutin.com>
# Cédric Musso <cmusso@easter-eggs.org>
# Frédéric Péters <fpeters@entrouvert.be>
# Benjamin Poussin <poussin@codelutin.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Régnier <regnier@codelutin.com>
# Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
# Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
# Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__doc__ = """XHTML Generator (not Glasnost specific)"""
__version__ = '$Revision$'[11:-2]
import types
import urllib
import context
import tools_new as commonTools
class asIs:
children = None
inline = 1
def __init__(self, *children):
if children:
self.children = list(children)
def __iadd__(self, child):
self.append(child)
return self
def __len__(self):
return len(str(self))
def __nonzero__(self):
if self.children:
return 1
else:
return 0
def __str__(self):
return self.getAsIs()
def append(self, child):
if self.children is None:
self.children = []
self.children.append(child)
def getAsIs(self, **keywords):
if not self.children:
return ''
else:
return ''.join([getAsIs(child, **keywords)
for child in self.children])
def getAsXml(self, parent = None, indent = 0, **keywords):
return self.getAsIs(**keywords)
def isInline(self):
return self.inline
class Character:
name = None
def __len__(self):
return len(str(self))
def __nonzero__(self):
return 1
def __str__(self):
return self.getAsXml()
def getAsIs(self, **keywords):
return self.getAsXml(**keywords)
def getAsXml(self, parent = None, indent = 0, **keywords):
if parent is not None and parent.preserveSpacing:
return '&%s;' % self.name
else:
return """\
%(spacing)s&%(name)s;
""" % {
'name': self.name,
'spacing': ' ' * indent,
}
def isInline(self):
return 1
class array:
children = None
def __init__(self, *children):
if children:
self.children = list(children)
def __iadd__(self, child):
self.append(child)
return self
def __len__(self):
return len(str(self))
def __nonzero__(self):
if self.children:
return 1
else:
return 0
def __str__(self):
return self.getAsXml()
def append(self, child):
if self.children is None:
self.children = []
self.children.append(child)
def getAsIs(self, **keywords):
if not self.children:
return ''
else:
return ''.join([ getAsIs(child, **keywords)
for child in self.children])
def getAsXml(self, parent = None, indent = 0, **keywords):
if not self.children:
return ''
else:
return ''.join([
getAsXml(child, parent = parent, indent = indent, **keywords)
for child in self.children])
def isInline(self):
if self.children:
for child in self.children:
if not isInline(child):
return 0
return 1
class Tag:
children = None
emptyElement = 0 # Can the tag use empty tag shorthand?
inline = 1 # Should the tag be enclosed in span (when inline is true) or
# div?
name = None
preserveSpacing = 0
typoEnhancements = 0
def __init__(self, *children, **attributes):
if children:
assert not attributes
self.children = list(children)
for name, value in attributes.items():
if name[0] == '_':
name = name[1:]
setattr(self, 'attribute_' + name, value)
def __call__(self, *children):
assert self.children is None
self.children = list(children)
return self
def __iadd__(self, child):
self.append(child)
return self
def __len__(self):
return len(str(self))
def __nonzero__(self):
return 1
def __str__(self):
return self.getAsXml()
def append(self, child):
if self.children is None:
self.children = []
self.children.append(child)
def getAsIs(self, **keywords):
return self.getAsXml(**keywords)
def getAsXml(self, parent = None, indent = 0, **keywords):
attributes = ' '.join([
'%s="%s"' % (name, getAsXmlAttributeValue(value, tag = self,
**keywords))
for name, value in self.getAttributes()])
if attributes:
attributes = ' ' + attributes
if not self.children:
children = ''
else:
children = ''.join([
self.getChildAsXml(child, parent = self, indent = indent + 1,
**keywords)
for child in self.children])
if not children and self.emptyElement:
if parent is not None and parent.preserveSpacing:
return '<%(tag)s%(attributes)s />' % {
'attributes': attributes,
'tag': self.name,
}
else:
return """\
%(spacing)s<%(tag)s%(attributes)s />
""" % {
'attributes': attributes,
'spacing': ' ' * indent,
'tag': self.name,
}
else:
if parent is not None and parent.preserveSpacing:
if self.preserveSpacing:
return '<%(tag)s%(attributes)s>%(children)s</%(tag)s>' % {
'attributes': attributes,
'children': children,
'tag': self.name,
}
else:
return """\
<%(tag)s%(attributes)s>
%(children)s\
%(spacing)s</%(tag)s>\
""" % {
'attributes': attributes,
'children': children,
'spacing': ' ' * indent,
'tag': self.name,
}
elif self.preserveSpacing:
return """\
%(spacing)s<%(tag)s%(attributes)s>%(children)s</%(tag)s>
""" % {
'attributes': attributes,
'children': children,
'spacing': ' ' * indent,
'tag': self.name,
}
else:
return """\
%(spacing)s<%(tag)s%(attributes)s>
%(children)s\
%(spacing)s</%(tag)s>
""" % {
'attributes': attributes,
'children': children,
'spacing': ' ' * indent,
'tag': self.name,
}
def getAttribute(self, name):
if name[0] == '_':
name = name[1:]
try:
return getattr(self, 'attribute_' + name)
except KeyError:
return ''
def getAttributeNames(self):
baseClasses = self.getC3ClassLinearization()
names = [ name
for name in self.__dict__.keys()
if name.startswith('attribute_')]
for baseClass in baseClasses:
for name in baseClass.__dict__.keys():
if name.startswith('attribute_') and name not in names:
names.append(name)
return [ name[10:]
for name in names
if getattr(self, name) is not None]
def getAttributes(self):
names = self.getAttributeNames()
names.sort()
return [ (name, getattr(self, 'attribute_' + name))
for name in names]
def getC3ClassLinearization(self):
return commonTools.getC3ClassLinearization(self.__class__)
def getChildAsXml(self, child, **keywords):
return getAsXml(child, **keywords)
def getPython22ClassLinearization(self):
baseClasses = []
commonTools.buildPython22ClassLinearization(
self.__class__, baseClasses)
return baseClasses
def isInline(self):
if not self.inline:
return 0
if self.children:
for child in self.children:
if not isInline(child):
return 0
return 1
def setAttribute(self, name, value):
if name[0] == '_':
name = name[1:]
setattr(self, 'attribute_' + name, value)
if value is None:
delattr(self, 'attribute_' + name)
class urlCommon:
hostName = None
port = None # Must be a string, not a integer.
protocol = None
def __len__(self):
return len(str(self))
def __nonzero__(self):
return 1
def __str__(self):
return self.getAsUrl()
def getAsIs(self, **keywords):
return self.getAsUrl(**keywords)
def getAsXmlAttributeValue(self, tag = None, **keywords):
return convertStringToXmlAttributeValue(self.getAsUrl(**keywords))
class httpUrl(urlCommon):
fragment = None
parameters = None
path = None
# protocol = http or https
query = None
def __init__(self, protocol = None, hostNameAndPort = None,
hostName = None, port = None, path = None, parameters = None,
query = None, fragment = None):
if protocol is not None:
self.protocol = protocol
else:
httpProtocol = context.getVar('httpProtocol')
if httpProtocol is not None:
self.protocol = httpProtocol
if hostNameAndPort is not None:
infos = hostNameAndPort.split(':', 1)
hostName = infos[0]
if len(infos) < 2:
port = None
else:
port = infos[1]
if hostName is not None:
self.hostName = hostName
else:
httpHostName = context.getVar('httpHostName')
if httpHostName is not None:
self.hostName = httpHostName
if port is not None:
self.port = port
else:
httpPort = context.getVar('httpPort')
if httpPort is not None:
self.port = httpPort
if path is not None:
self.path = path
assert self.path
if self.path[0] != '/':
self.path = '/' + self.path
else:
httpPath = context.getVar('httpPath')
if httpPath is not None:
self.path = httpPath
assert self.path
if self.path[0] != '/':
self.path = '/' + self.path
if parameters is not None:
self.parameters = parameters
if query is not None:
self.query = query
if fragment is not None:
self.fragment = fragment
def add(self, name, value):
setattr(self, 'argument_' + name, value)
return self
def getArgumentNames(self):
baseClasses = self.getC3ClassLinearization()
names = [ name
for name in self.__dict__.keys()
if name.startswith('argument_')]
for baseClass in baseClasses:
for name in baseClass.__dict__.keys():
if name.startswith('argument_') and name not in names:
names.append(name)
return [ name[9:]
for name in names
if getattr(self, name) is not None]
def getArguments(self):
names = self.getArgumentNames()
names.sort()
return [ (name, getattr(self, 'argument_' + name))
for name in names]
def getAsAbsoluteUrl(self, **keywords):
if self.protocol is not None:
protocol = self.protocol
else:
httpProtocol = context.getVar('httpProtocol')
if httpProtocol is not None:
protocol = httpProtocol
else:
protocol = 'http'
if self.hostName is not None:
hostName = self.hostName
else:
httpHostName = context.getVar('httpHostName')
if httpHostName is not None:
hostName = httpHostName
else:
hostName = 'localhost'
if self.port is not None:
port = self.port
else:
httpPort = context.getVar('httpPort')
if httpPort is not None:
port = httpPort
else:
port = None # Default http or https port.
pathAndQuery = self.getAsRelativeUrl()
if port:
return '%s://%s:%s%s' % (protocol, hostName, port, pathAndQuery)
else:
return '%s://%s%s' % (protocol, hostName, pathAndQuery)
def getAsRelativeUrl(self, **keywords):
relativeUrl = urllib.quote(self.getPath(**keywords))
if self.parameters:
relativeUrl = '%s;%s' % (relativeUrl, self.parameters)
query = '&'.join([ '%s=%s' % (name, urllib.quote(str(value)))
for name, value in self.getArguments()])
if self.query and query:
query = '%s&%s' % (self.query, query)
elif self.query:
query = self.query
if query:
relativeUrl = '%s?%s' % (relativeUrl, query)
if self.fragment:
relativeUrl = '%s#%s' % (relativeUrl, self.fragment)
return relativeUrl
def getAsIs(self, **keywords):
return self.getAsUrl(**keywords)
def getAsUrl(self, **keywords):
httpProtocol = context.getVar('httpProtocol')
httpHostName = context.getVar('httpHostName')
httpPort = context.getVar('httpPort')
if (self.protocol is None or self.protocol == httpProtocol) \
and (self.hostName is None or self.hostName == httpHostName) \
and (self.port is None or self.port == httpPort):
return self.getAsRelativeUrl(**keywords)
else:
return self.getAsAbsoluteUrl(**keywords)
def getC3ClassLinearization(self):
return commonTools.getC3ClassLinearization(self.__class__)
def getPath(self, **keywords):
httpPath = context.getVar('httpPath')
if self.path is not None:
path = self.path
elif httpPath is not None:
path = httpPath
else:
path = '/'
return path
def getPython22ClassLinearization(self):
baseClasses = []
commonTools.buildPython22ClassLinearization(
self.__class__, baseClasses)
return baseClasses
# Character entity references.
class Nbsp(Character):
name = 'nbsp'
nbsp = Nbsp()
# Tags.
class a(Tag):
name = 'a'
preserveSpacing = 1
class body(Tag):
name = 'body'
class br(Tag):
emptyElement = 1
name = 'br'
class button(Tag):
name = 'button'
preserveSpacing = 1
class code(Tag):
name = 'code'
preserveSpacing = 1
class col(Tag):
emptyElement = 1
name = 'col'
class colgroup(Tag):
name = 'colgroup'
class div(Tag):
inline = 0
name = 'div'
preserveSpacing = 0
class em(Tag):
name = 'em'
preserveSpacing = 1
class fieldset(Tag):
inline = 0
name = 'fieldset'
class form(Tag):
name = 'form'
class h1(Tag):
name = 'h1'
preserveSpacing = 1
class h2(Tag):
name = 'h2'
preserveSpacing = 1
class h3(Tag):
name = 'h3'
preserveSpacing = 1
class h4(Tag):
name = 'h4'
preserveSpacing = 1
class head(Tag):
name = 'head'
class html(Tag):
name = 'html'
class hr(Tag):
emptyElement = 1
name = 'hr'
class img(Tag):
emptyElement = 1
name = 'img'
class input(Tag):
emptyElement = 1
name = 'input'
class label(Tag):
name = 'label'
preserveSpacing = 1
class li(Tag):
name = 'li'
class optgroup(Tag):
name = 'optgroup'
preserveSpacing = 1
class option(Tag):
name = 'option'
preserveSpacing = 1
typoEnhancements = 1
class p(Tag):
inline = 0
name = 'p'
preserveSpacing = 0
typoEnhancements = 1
class pre(Tag):
inline = 0
name = 'pre'
preserveSpacing = 1
class select(Tag):
name = 'select'
class span(Tag):
name = 'span'
preserveSpacing = 1
class strong(Tag):
name = 'strong'
preserveSpacing = 1
class caption(Tag):
name = 'caption'
preserveSpacing = 1
class ol(Tag):
inline = 0
name = 'ol'
class table(Tag):
inline = 0
name = 'table'
class td(Tag):
name = 'td'
preserveSpacing = 1
class th(Tag):
name = 'th'
preserveSpacing = 1
class thead(Tag):
name = 'thead'
preserveSpacing = 1
class tbody(Tag):
name = 'tbody'
preserveSpacing = 1
class tfoot(Tag):
name = 'tfoot'
preserveSpacing = 1
class textarea(Tag):
name = 'textarea'
preserveSpacing = 1
class title(Tag):
name = 'title'
preserveSpacing = 1
class tr(Tag):
name = 'tr'
class tt(Tag):
name = 'tt'
preserveSpacing = 1
class ul(Tag):
inline = 0
name = 'ul'
class gridBagLayout(table):
# FIXME: Perhaps modify this component, if we can do better html code
# for this component feature.
_class = 'gridBagLayout'
def convertStringToXml(s):
s = s.replace('&', '&amp;')
s = s.replace('<', '&lt;')
return s
def convertStringToXmlAttributeValue(s):
### FIXME: Which characters should be transcoded?
s = s.replace('&', '&amp;') # ?
s = s.replace('"', '&quot;') # '"' must be converted.
s = s.replace('<', '&lt;') # ?
s = s.replace('>', '&gt;') # ?
return s
def enclose(object, **enclosingAttributes):
if not object:
return None
if isInline(object):
return span(**enclosingAttributes)(object)
else:
return div(**enclosingAttributes)(object)
def isInline(object):
if not object:
return 1
elif type(object) == types.InstanceType and hasattr(object, 'isInline'):
return object.isInline()
elif type(object) in [types.ListType, types.TupleType]:
for child in object:
if not isInline(object):
return 0
return 1
else:
return 1
def enhanceTypo(s):
# typographic junkie
# idea from: http://daringfireball.net/projects/smartypants/
s = s.replace('...', '&#8230;') # ellipsis (...)
s = s.replace(' -- ', ' &#8212; ') # em-dash
s = s.replace('(c)', '&copy; ') # copyright symbol
return s
def getAsIs(object, **keywords):
if object is None:
return ''
elif type(object) in [types.ListType, types.TupleType]:
return ''.join([
getAsIs(item, **keywords)
for item in object])
else:
return str(object)
def getAsXml(object, parent = None, indent = 0, **keywords):
if object is None:
return ''
elif type(object) == types.InstanceType and hasattr(object, 'getAsXml'):
return object.getAsXml(parent = parent, indent = indent, **keywords)
elif type(object) in [types.ListType, types.TupleType]:
return ''.join([
getAsXml(item, parent = parent, indent = indent, **keywords)
for item in object])
else:
strObject = convertStringToXml(str(object))
if parent is not None and parent.typoEnhancements:
strObject = enhanceTypo(strObject)
if parent is not None and parent.preserveSpacing:
return strObject
else:
return """\
%(spacing)s%(object)s
""" % {
'object': strObject,
'spacing': ' ' * indent,
}
def getAsXmlAttributeValue(object, tag = None, **keywords):
if object is None:
return ''
elif isinstance(object, urlCommon):
return object.getAsXmlAttributeValue(tag = tag, **keywords)
else:
return convertStringToXmlAttributeValue(str(object))
def main():
print html(
head(title('Test page')),
body(a(href = 'http://www.entrouvert.org')('entr\'ouvert'),
a(href = httpUrl(path = '/index.html'))('Index'),
a(href = httpUrl(port = '8080', path = '/index.html'))('Index'),
)
)
if __name__ == "__main__":
main()