# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Library for program-based construction of an HTML documents.
Encapsulate HTML formatting directives in classes that act as containers
for python and, recursively, for nested HTML formatting objects.
"""
from Mailman import Defaults
from Mailman import Utils
from Mailman import Version
from Mailman.configuration import config
from Mailman.i18n import _
SPACE = ' '
EMPTYSTRING = ''
NL = '\n'
# Format an arbitrary object.
def HTMLFormatObject(item, indent):
"Return a presentation of an object, invoking their Format method if any."
if hasattr(item, 'Format'):
return item.Format(indent)
if isinstance(item, basestring):
return item
return str(item)
def CaseInsensitiveKeyedDict(d):
result = {}
for (k,v) in d.items():
result[k.lower()] = v
return result
# Given references to two dictionaries, copy the second dictionary into the
# first one.
def DictMerge(destination, fresh_dict):
for (key, value) in fresh_dict.items():
destination[key] = value
class Table:
def __init__(self, **table_opts):
self.cells = []
self.cell_info = {}
self.row_info = {}
self.opts = table_opts
def AddOptions(self, opts):
DictMerge(self.opts, opts)
# Sets all of the cells. It writes over whatever cells you had there
# previously.
def SetAllCells(self, cells):
self.cells = cells
# Add a new blank row at the end
def NewRow(self):
self.cells.append([])
# Add a new blank cell at the end
def NewCell(self):
self.cells[-1].append('')
def AddRow(self, row):
self.cells.append(row)
def AddCell(self, cell):
self.cells[-1].append(cell)
def AddCellInfo(self, row, col, **kws):
kws = CaseInsensitiveKeyedDict(kws)
if not self.cell_info.has_key(row):
self.cell_info[row] = { col : kws }
elif self.cell_info[row].has_key(col):
DictMerge(self.cell_info[row], kws)
else:
self.cell_info[row][col] = kws
def AddRowInfo(self, row, **kws):
kws = CaseInsensitiveKeyedDict(kws)
if not self.row_info.has_key(row):
self.row_info[row] = kws
else:
DictMerge(self.row_info[row], kws)
# What's the index for the row we just put in?
def GetCurrentRowIndex(self):
return len(self.cells)-1
# What's the index for the col we just put in?
def GetCurrentCellIndex(self):
return len(self.cells[-1])-1
def ExtractCellInfo(self, info):
valid_mods = ['align', 'valign', 'nowrap', 'rowspan', 'colspan',
'bgcolor']
output = ''
for (key, val) in info.items():
if not key in valid_mods:
continue
if key == 'nowrap':
output = output + ' NOWRAP'
continue
else:
output = output + ' %s="%s"' % (key.upper(), val)
return output
def ExtractRowInfo(self, info):
valid_mods = ['align', 'valign', 'bgcolor']
output = ''
for (key, val) in info.items():
if not key in valid_mods:
continue
output = output + ' %s="%s"' % (key.upper(), val)
return output
def ExtractTableInfo(self, info):
valid_mods = ['align', 'width', 'border', 'cellspacing', 'cellpadding',
'bgcolor']
output = ''
for (key, val) in info.items():
if not key in valid_mods:
continue
if key == 'border' and val == None:
output = output + ' BORDER'
continue
else:
output = output + ' %s="%s"' % (key.upper(), val)
return output
def FormatCell(self, row, col, indent):
try:
my_info = self.cell_info[row][col]
except:
my_info = None
output = '\n' + ' '*indent + '
'
for i in range(len(self.cells[row])):
output = output + self.FormatCell(row, i, indent + 2)
output = output + '\n' + ' '*indent + ''
return output
def Format(self, indent=0):
output = '\n' + ' '*indent + '
'
for i in range(len(self.cells)):
output = output + self.FormatRow(i, indent + 2)
output = output + '\n' + ' '*indent + '
\n'
return output
class Link:
def __init__(self, href, text, target=None):
self.href = href
self.text = text
self.target = target
def Format(self, indent=0):
texpr = ""
if self.target != None:
texpr = ' target="%s"' % self.target
return '%s' % (HTMLFormatObject(self.href, indent),
texpr,
HTMLFormatObject(self.text, indent))
class FontSize:
"""FontSize is being deprecated - use FontAttr(..., size="...") instead."""
def __init__(self, size, *items):
self.items = list(items)
self.size = size
def Format(self, indent=0):
output = '' % self.size
for item in self.items:
output = output + HTMLFormatObject(item, indent)
output = output + ''
return output
class FontAttr:
"""Present arbitrary font attributes."""
def __init__(self, *items, **kw):
self.items = list(items)
self.attrs = kw
def Format(self, indent=0):
seq = []
for k, v in self.attrs.items():
seq.append('%s="%s"' % (k, v))
output = '' % SPACE.join(seq)
for item in self.items:
output = output + HTMLFormatObject(item, indent)
output = output + ''
return output
class Container:
def __init__(self, *items):
if not items:
self.items = []
else:
self.items = items
def AddItem(self, obj):
self.items.append(obj)
def Format(self, indent=0):
output = []
for item in self.items:
output.append(HTMLFormatObject(item, indent))
return EMPTYSTRING.join(output)
class Label(Container):
align = 'right'
def __init__(self, *items):
Container.__init__(self, *items)
def Format(self, indent=0):
return ('
\n' % spaces
return output
# Logo constants
#
# These are the URLs which the image logos link to. The Mailman home page now
# points at the gnu.org site instead of the www.list.org mirror.
PYTHON_URL = 'http://www.python.org/'
GNU_URL = 'http://www.gnu.org/'
# The names of the image logo files. These are concatentated onto
# config.IMAGE_LOGOS (not urljoined).
DELIVERED_BY = 'mailman.jpg'
PYTHON_POWERED = 'PythonPowered.png'
GNU_HEAD = 'gnu-head-tiny.jpg'
def MailmanLogo():
t = Table(border=0, width='100%')
if config.IMAGE_LOGOS:
def logo(file):
return config.IMAGE_LOGOS + file
mmlink = '' \
' version %s' % (logo(DELIVERED_BY), Version.VERSION)
pylink = '' % \
logo(PYTHON_POWERED)
gnulink = '' % \
logo(GNU_HEAD)
t.AddRow([mmlink, pylink, gnulink])
else:
# use only textual links
version = Version.VERSION
mmlink = Link(config.MAILMAN_URL,
_('Delivered by Mailman version %(version)s'))
pylink = Link(PYTHON_URL, _('Python Powered'))
gnulink = Link(GNU_URL, _("Gnu's Not Unix"))
t.AddRow([mmlink, pylink, gnulink])
return t
class SelectOptions:
def __init__(self, varname, values, legend,
selected=0, size=1, multiple=None):
self.varname = varname
self.values = values
self.legend = legend
self.size = size
self.multiple = multiple
# we convert any type to tuple, commas are needed
if not multiple:
if isinstance(selected, int):
self.selected = (selected,)
elif isinstance(selected, tuple):
self.selected = (selected[0],)
elif isinstance(selected, list):
self.selected = (selected[0],)
else:
self.selected = (0,)
def Format(self, indent=0):
spaces = " " * indent
items = min( len(self.values), len(self.legend) )
# jcrey: If there is no argument, we return nothing to avoid errors
if items == 0:
return ""
text = "\n" + spaces + "'