4 This module provides a simple framework for generating CSS-based dynamic web
5 pages with a typical layout consisting of a header, navigation bar, content area
6 and a footer. It is compatible with different web servers using e.g. CGI, mod_python,
9 __author__ =
'Juerg Beringer'
10 __version__ =
'WebPage.py atlas/athena'
14 from cgi
import escape
21 %(contentType)s<?xml version="1.0" encoding="UTF-8"?>
22 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
23 <html xmlns="http://www.w3.org/1999/xhtml">
25 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
26 <title>%(pageTitle)s</title>
27 <link href="%(cssName)s" rel="stylesheet" type="text/css" />
28 %(pageHeaderSnippets)s\
43 """Add separator to string s unless s is empty."""
44 return ' '+s
if s
else s
46 def htmlDiv(id, contents='', attr='', keepEmptyDiv=True):
47 """Make a named div element containing contents. If
48 contents is empty, an empty string is returned,
49 unless keepEmtpyDiv is set True."""
50 if contents
or keepEmptyDiv:
52 return '<div id="%s"%s>\n%s</div>\n' % (id,
sep(attr),contents)
54 return '<div%s>\n%s</div>\n' % (
sep(attr),contents)
59 """Make a text consisting of an unnamed div. Special HTML characters
60 in the text are properly replaced (using escape from cgi) if
61 escapeText is set True."""
62 return '<div%s>%s</div>\n' % (
sep(attr),escape(text)
if escapeText
else text)
64 def htmlPre(text, attr='', escapeText=False):
65 """Make a preformatted text section. Special HTML characters
66 in the text are properly replaced (using escape from cgi) if
67 escapeText is set True."""
68 return '<pre%s>\n%s\n</pre>\n' % (
sep(attr),escape(text)
if escapeText
else text)
70 def htmlPara(text='', attr='', escapeText=False):
71 """Make a paragraph."""
73 return '<p%s>%s</p>\n' % (
sep(attr), escape(text)
if escapeText
else text)
77 def htmlLink(text, link, attr='', escapeText=False):
78 return '<a href="%s"%s>%s</a>' % (link,
sep(attr),escape(text)
if escapeText
else text)
80 def htmlList(contents, attr='', listType='ul'):
81 """Enclose list contents (a string with one or more list items) with
82 the proper list tag. The type of the list is given by listType."""
83 return '<%s%s>\n%s</%s>\n' % (listType,
sep(attr),contents,listType)
85 def htmlLI(text, attr='', escapeText=False):
86 """Make a list item. Special HTML characters
87 in the text are properly replaced (using escape from cgi) if
88 escapeText is set True."""
89 return '<li%s>%s</li>\n' % (
sep(attr), escape(text)
if escapeText
else text)
92 """Make a table. Table row data is accumulated internally in a list.
93 The table can be made sortable (using jQuery's plugin Tablesorter)
94 by setting useTableSorter to True. Special HTML characters in cell
95 data are properly replaced (using escape from cgi) if escapeText
98 def __init__(self, tableAttr='', defaultCellAttr = [], useTableSorter=False, escapeText=False):
109 """Append a row to the table. cellData is a list of the contents of
110 the cells in the row. The data for each cell is either a string
111 with the contents of the cell, or a tuple where the first element
112 is the contents of the cell and the second element contains any
113 HTML tags. Special HTML characters are properly replaced (using
114 escape from cgi) if escapeText was set True when creating the
115 table. If the table uses Tablesorter, <th> is used instead of
116 <td> in the first row."""
117 r =
'<tr%s>\n' %
sep(rowAttr)
120 cellFormat =
'<th%s>%s</th>\n'
122 cellFormat =
'<td%s>%s</td>\n'
126 if isinstance(c,tuple):
135 """Return the HTML code for the table."""
142 h =
'<table class="tablesorter">\n'
145 h +=
'\n</thead><tbody>\n'
147 h +=
'\n</tbody></table>\n'
155 headerClass='section-header',
156 contentClass='section-content'):
157 """Generate the html for a folding section using the toggleSection JavaScript utility
158 from WebPageUtils.js and CSS classes section-closed, section-open, and hidden."""
160 s =
'<div class="section-closed" onclick="toggleSection(this);">'
162 s =
'<div class="section-open" onclick="toggleSection(this);">'
163 s +=
'<span class="%s">%s</span></div>\n' % (headerClass,header)
164 h =
' hidden' if isClosed
else ''
165 s +=
'<div class="%s%s">\n%s</div>\n' % (contentClass,h,content)
168 def htmlForm(contents, action='', method='post', attr=''):
169 snippet =
'<form action="%s" method="%s"%s>\n' % (action,method,
sep(attr))
170 snippet +=
'<fieldset>\n'
172 snippet +=
'</fieldset>\n</form>\n'
176 """Make a label for parName. If labelText is None,
177 an empty string is returned."""
179 return '<label for="%s"%s>%s</label>' % (parName,
sep(attr),labelText)
183 def htmlSelect(labelText, parName, args, choiceList, hint=None, descriptionSeparator='::
',
184 labelAttr='', attr=
''):
185 """Make a select statement (including label with text)."""
186 snippet =
htmlLabel(labelText,parName,labelAttr)
187 default = args[parName]
if parName
in args
else ''
188 if not isinstance(default,list):
190 snippet +=
'<select name="%s"%s>\n' % (parName,
sep(attr))
192 snippet +=
'<option value="">%s</option>\n' % hint
194 p = c.split(descriptionSeparator)
200 snippet +=
'<option selected="yes" value="%s">%s</option>\n' % (val,desc)
202 snippet +=
'<option value="%s">%s</option>\n' % (val,desc)
203 snippet +=
'</select>\n'
207 """Make a checkbox (including label with text)."""
208 snippet =
htmlLabel(labelText,parName,labelAttr)
209 checked =
'checked="checked"' if parName
in args
else ''
210 snippet +=
'<input type="checkbox" name="%s"%s%s/>\n' % (parName,
sep(checked),
sep(attr))
213 def htmlTextInput(labelText, parName, args, size=None, maxLength = None, labelAttr='', attr=''):
214 """Make a text input area (including label with text). Special HTML
215 characters in any default text are properly replaced."""
216 snippet =
htmlLabel(labelText,parName,labelAttr)
217 snippet +=
'<input type="text" name="%s"' % parName
219 snippet +=
' value="%s"' % escape(args[parName],
True)
221 snippet +=
' size="%s"' % size
223 snippet +=
' maxlength="%s"' % maxLength
224 snippet +=
'%s/>\n' %
sep(attr)
228 """Make a submit button. If onlyOnce is true, the button can only
229 be clicked once in order to prevent multiple clicking of the
233 s =
'<input type="button" name="%s" value="%s"' % (parName,text)
234 s +=
' onclick="this.form.submit()"'
235 s +=
'%s />\n' %
sep(attr)
238 return '<input type="submit" name="%s" value="%s"%s />\n' % (parName,text,
sep(attr))
246 Exception.__init__(self,args)
250 """Class to store global configuration data for a tree of web pages. Data members
251 are used to store information used by the framework, while the dict is
252 intended to be used for application-specific configuration data."""
268 """Base class for creating CSS-based dynamic web pages."""
271 """Constructor. You may override any of the default values in self.pageConfig by
272 passing the corresponding value in a named variable."""
276 'pageHeaderSnippets':
'',
277 'cssName':
'default.css',
278 'css_currentLink':
'acurrentlink',
283 'keepEmptyHeader':
False,
284 'keepEmptyNavigation':
False,
285 'keepEmptyContent':
False,
286 'keepEmptyFooter':
False
295 """Short cut to retrieve the name (the last element in the URL)
296 of the current page. This works only if the page has been
297 added into the page tree using addPage."""
301 """Add a new web page to the page tree. All pages added via addPage
302 share the same GlobalConfiguration object."""
304 page.pageConfig[
'pageName'] = name
307 setattr(self,name,page)
310 """Add a new link to the page tree. Links share page objects with
311 other pages through aliasing. This allows using different links
312 with different queries to the same page as if they were separate
313 pages. For links, highlighting of the current page is disabled
314 (if enabled, all links leading to the same page would be
321 """Add a snippet of code to the page header. Use this e.g. to include JavaScript libraries."""
322 self.
pageConfig[
'pageHeaderSnippets'] += snippet
325 """Return the complete page."""
330 self.
pageConfig[
'timeStamp'] = time.strftime(
'%a %b %d %X %Z %Y')
331 contents = self.
content(**args)
335 keepEmptyDiv=self.
pageConfig[
'keepEmptyHeader'])
337 keepEmptyDiv=self.
pageConfig[
'keepEmptyNavigation'])
338 s = s +
htmlDiv(
'content', contents,
339 keepEmptyDiv=self.
pageConfig[
'keepEmptyContent'])
341 keepEmptyDiv=self.
pageConfig[
'keepEmptyFooter'])
348 """Final configuration of web application after all data is initialized."""
354 """Based on configuration data, for each web page create two link entries in pageConfig:
355 The first entry has the form url_PAGENAME and contains the complete URL to link to
356 a page. The second entry is named href_PAGENAME and contains style information and a
357 href= prefix with proper quotation marks in addition to the URL. These link entries
358 should be used to generate links in HTML pages with snippets like <a %(href_PAGENAME)s>
359 or <a href="%(url_PAGENAME)>. The link entries can only be generated once all
360 configuration data is available, ie configureLinks must be called from configure
361 and not from __init__."""
376 """Override provides a hook where code to generate or redirect to an alternative
377 page can be placed by derived classes. If not override of the normal page is
378 desired, override should return None. Otherwise it should either raise an
379 appropriate exception or return a string containing the complete alternate page
384 """Generate the page header. Default value comes from self.pageConfig['header']."""
388 """Generate the navigation bar. Default value comes from self.pageConfig['navigation']."""
392 """Generate the page content. Default value comes from self.pageConfig['content']."""
396 """Generate the footer. Default value comes from self.pageConfig['footer']."""
421 if __name__ ==
'__main__':
425 return "Hello, world!"
437 cherrypy.quickstart(
HelloWorld(pageTitle=
'HelloWorld Test',contentType=
''))