Anonymous avatar Anonymous committed ae1df84

Copying classic template as base for the python-dev tracker instance

Comments (0)

Files changed (35)

+*.pyc
+*.pyo
+htmlbase.py
+*.cover

TEMPLATE-INFO.txt

+Name: classic
+Description: This is a generic issue tracker that may be used to track bugs,
+             feature requests, project issues or any number of other types
+             of issues. Most users of Roundup will find that this template
+             suits them, with perhaps a few customisations.
+Intended-For: All first-time Roundup users
+

detectors/.cvsignore

+*.pyc
+*.pyo
+*.cover

detectors/messagesummary.py

+#$Id: messagesummary.py,v 1.1 2003/04/17 03:26:38 richard Exp $
+
+from roundup.mailgw import parseContent
+
+def summarygenerator(db, cl, nodeid, newvalues):
+    ''' If the message doesn't have a summary, make one for it.
+    '''
+    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+        return
+
+    summary, content = parseContent(newvalues['content'], 1, 1)
+    newvalues['summary'] = summary
+
+
+def init(db):
+    # fire before changes are made
+    db.msg.audit('create', summarygenerator)
+
+# vim: set filetype=python ts=4 sw=4 et si

detectors/nosyreaction.py

+#
+# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
+# This module is free software, and you may redistribute it and/or modify
+# under the same terms as Python, so long as this copyright message and
+# disclaimer are retained in their original form.
+#
+# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
+# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
+# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# 
+#$Id: nosyreaction.py,v 1.4 2005/04/04 08:47:14 richard Exp $
+
+import sets
+
+from roundup import roundupdb, hyperdb
+
+def nosyreaction(db, cl, nodeid, oldvalues):
+    ''' A standard detector is provided that watches for additions to the
+        "messages" property.
+        
+        When a new message is added, the detector sends it to all the users on
+        the "nosy" list for the issue that are not already on the "recipients"
+        list of the message.
+        
+        Those users are then appended to the "recipients" property on the
+        message, so multiple copies of a message are never sent to the same
+        user.
+        
+        The journal recorded by the hyperdatabase on the "recipients" property
+        then provides a log of when the message was sent to whom. 
+    '''
+    # send a copy of all new messages to the nosy list
+    for msgid in determineNewMessages(cl, nodeid, oldvalues):
+        try:
+            cl.nosymessage(nodeid, msgid, oldvalues)
+        except roundupdb.MessageSendError, message:
+            raise roundupdb.DetectorError, message
+
+def determineNewMessages(cl, nodeid, oldvalues):
+    ''' Figure a list of the messages that are being added to the given
+        node in this transaction.
+    '''
+    messages = []
+    if oldvalues is None:
+        # the action was a create, so use all the messages in the create
+        messages = cl.get(nodeid, 'messages')
+    elif oldvalues.has_key('messages'):
+        # the action was a set (so adding new messages to an existing issue)
+        m = {}
+        for msgid in oldvalues['messages']:
+            m[msgid] = 1
+        messages = []
+        # figure which of the messages now on the issue weren't there before
+        for msgid in cl.get(nodeid, 'messages'):
+            if not m.has_key(msgid):
+                messages.append(msgid)
+    return messages
+
+def updatenosy(db, cl, nodeid, newvalues):
+    '''Update the nosy list for changes to the assignedto
+    '''
+    # nodeid will be None if this is a new node
+    current_nosy = sets.Set()
+    if nodeid is None:
+        ok = ('new', 'yes')
+    else:
+        ok = ('yes',)
+        # old node, get the current values from the node if they haven't
+        # changed
+        if not newvalues.has_key('nosy'):
+            nosy = cl.get(nodeid, 'nosy')
+            for value in nosy:
+                current_nosy.add(value)
+
+    # if the nosy list changed in this transaction, init from the new value
+    if newvalues.has_key('nosy'):
+        nosy = newvalues.get('nosy', [])
+        for value in nosy:
+            if not db.hasnode('user', value):
+                continue
+            current_nosy.add(value)
+
+    new_nosy = sets.Set(current_nosy)
+
+    # add assignedto(s) to the nosy list
+    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+        propdef = cl.getprops()
+        if isinstance(propdef['assignedto'], hyperdb.Link):
+            assignedto_ids = [newvalues['assignedto']]
+        elif isinstance(propdef['assignedto'], hyperdb.Multilink):
+            assignedto_ids = newvalues['assignedto']
+        for assignedto_id in assignedto_ids:
+            new_nosy.add(assignedto_id)
+
+    # see if there's any new messages - if so, possibly add the author and
+    # recipient to the nosy
+    if newvalues.has_key('messages'):
+        if nodeid is None:
+            ok = ('new', 'yes')
+            messages = newvalues['messages']
+        else:
+            ok = ('yes',)
+            # figure which of the messages now on the issue weren't
+            oldmessages = cl.get(nodeid, 'messages')
+            messages = []
+            for msgid in newvalues['messages']:
+                if msgid not in oldmessages:
+                    messages.append(msgid)
+
+        # configs for nosy modifications
+        add_author = getattr(db.config, 'ADD_AUTHOR_TO_NOSY', 'new')
+        add_recips = getattr(db.config, 'ADD_RECIPIENTS_TO_NOSY', 'new')
+
+        # now for each new message:
+        msg = db.msg
+        for msgid in messages:
+            if add_author in ok:
+                authid = msg.get(msgid, 'author')
+                new_nosy.add(authid)
+
+            # add on the recipients of the message
+            if add_recips in ok:
+                for recipient in msg.get(msgid, 'recipients'):
+                    new_nosy.add(recipient)
+
+    if current_nosy != new_nosy:
+        # that's it, save off the new nosy list
+        newvalues['nosy'] = list(new_nosy)
+
+def init(db):
+    db.issue.react('create', nosyreaction)
+    db.issue.react('set', nosyreaction)
+    db.issue.audit('create', updatenosy)
+    db.issue.audit('set', updatenosy)
+
+# vim: set filetype=python ts=4 sw=4 et si

detectors/statusauditor.py

+# Copyright (c) 2002 ekit.com Inc (http://www.ekit-inc.com/)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included in
+#   all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: statusauditor.py,v 1.5 2004/03/27 00:01:48 richard Exp $
+
+def chatty(db, cl, nodeid, newvalues):
+    ''' If the issue is currently 'unread', 'resolved', 'done-cbb' or None,
+        then set it to 'chatting'
+    '''
+    # don't fire if there's no new message (ie. chat)
+    if not newvalues.has_key('messages'):
+        return
+    if newvalues['messages'] == cl.get(nodeid, 'messages'):
+        return
+
+    # get the chatting state ID
+    try:
+        chatting_id = db.status.lookup('chatting')
+    except KeyError:
+        # no chatting state, ignore all this stuff
+        return
+
+    # get the current value
+    current_status = cl.get(nodeid, 'status')
+
+    # see if there's an explicit change in this transaction
+    if newvalues.has_key('status'):
+        # yep, skip
+        return
+
+    # determine the id of 'unread', 'resolved' and 'chatting'
+    fromstates = []
+    for state in 'unread resolved done-cbb'.split():
+        try:
+            fromstates.append(db.status.lookup(state))
+        except KeyError:
+            pass
+
+    # ok, there's no explicit change, so check if we are in a state that
+    # should be changed
+    if current_status in fromstates + [None]:
+        # yep, we're now chatting
+        newvalues['status'] = chatting_id
+
+
+def presetunread(db, cl, nodeid, newvalues):
+    ''' Make sure the status is set on new issues
+    '''
+    if newvalues.has_key('status') and newvalues['status']:
+        return
+
+    # get the unread state ID
+    try:
+        unread_id = db.status.lookup('unread')
+    except KeyError:
+        # no unread state, ignore all this stuff
+        return
+
+    # ok, do it
+    newvalues['status'] = unread_id
+
+
+def init(db):
+    # fire before changes are made
+    db.issue.audit('set', chatty)
+    db.issue.audit('create', presetunread)
+
+# vim: set filetype=python ts=4 sw=4 et si

detectors/userauditor.py

+# Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included in
+#   all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: userauditor.py,v 1.2 2003/11/11 22:25:37 richard Exp $
+
+def audit_user_fields(db, cl, nodeid, newvalues):
+    ''' Make sure user properties are valid.
+
+        - email address has no spaces in it
+        - roles specified exist
+    '''
+    if newvalues.has_key('address') and ' ' in newvalues['address']:
+        raise ValueError, 'Email address must not contain spaces'
+
+    if newvalues.has_key('roles'):
+        roles = [x.lower().strip() for x in newvalues['roles'].split(',')]
+        for rolename in roles:
+            if not db.security.role.has_key(rolename):
+                raise ValueError, 'Role "%s" does not exist'%rolename
+
+
+def init(db):
+    # fire before changes are made
+    db.user.audit('set', audit_user_fields)
+    db.user.audit('create', audit_user_fields)
+
+# vim: set filetype=python ts=4 sw=4 et si

extensions/README.txt

+This directory is for tracker extensions:
+
+- CGI Actions
+- Templating functions
+
+See the customisation doc for more information.

html/_generic.calendar.html

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <head>
+  <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8;" />
+  <title tal:content="string:Roundup Calendar"></title>
+  <script language="Javascript"
+          type="text/javascript"
+          tal:content="structure string:
+          // this is the name of the field in the original form that we're working on
+          form  = window.opener.document.${request/form/form/value};
+          field = '${request/form/property/value}';" >
+  </script>
+ </head>
+ <body class="body"
+       tal:content="structure python:utils.html_calendar(request)">
+ </body>
+</html>

html/_generic.collision.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> Edit Collision - <span i18n:name="tracker"
+ tal:replace="config/TRACKER_NAME" /></title>
+<tal:block metal:fill-slot="body_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> Edit Collision</tal:block>
+
+<td class="content" metal:fill-slot="content" i18n:translate="
+  There has been a collision. Another user updated this node
+  while you were editing. Please <a href='${context}'>reload</a>
+  the node and review your edits.
+"><span tal:replace="context/designator" i18n:name="context" />
+</td>
+</tal:block>

html/_generic.help.html

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html tal:define="property request/form/property/value" >
+  <head>
+      <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+      <meta http-equiv="Content-Type"
+       tal:attributes="content string:text/html;; charset=${request/client/charset}" />
+      <tal:block tal:condition="python:request.form.has_key('property')">
+      <title i18n:translate=""><tal:x i18n:name="property"
+       tal:content="property" i18n:translate="" /> help - <span i18n:name="tracker"
+       tal:replace="config/TRACKER_NAME" /></title>
+      <script language="Javascript" type="text/javascript"
+          tal:content="structure string:
+          // this is the name of the field in the original form that we're working on
+          form  = window.opener.document.${request/form/form/value};
+          field  = '${request/form/property/value}';">
+      </script>
+      <script src="@@file/help_controls.js" type="text/javascript"><!--
+      //--></script>
+      </tal:block>
+  </head>
+ <body class="body" onload="resetList();">
+ <form name="frm_help" tal:attributes="action request/base"
+       tal:define="batch request/batch;
+                   props python:request.form['properties'].value.split(',')">
+
+     <div id="classhelp-controls">
+       <!--input type="button" name="btn_clear"
+              value="Clear" onClick="clearList()"/ -->
+       <input type="text" name="text_preview" size="24" class="preview"
+              onchange="reviseList(this.value);"/>
+       <input type="button" name="btn_reset"
+              value=" Cancel " onclick="resetList(); window.close();"
+              i18n:attributes="value" />
+       <input type="button" name="btn_apply" class="apply"
+              value=" Apply " onclick="updateList(); window.close();"
+              i18n:attributes="value" />
+     </div>
+     <table width="100%">
+      <tr class="navigation">
+       <th>
+        <a tal:define="prev batch/previous" tal:condition="prev"
+           tal:attributes="href python:request.indexargs_url(request.classname,
+           {'@template':'help', 'property': request.form['property'].value,
+            'properties': request.form['properties'].value,
+            'form': request.form['form'].value,
+            'type': request.form['type'].value,
+            '@startwith':prev.first, '@pagesize':prev.size})"
+           i18n:translate="" >&lt;&lt; previous</a>
+        &nbsp;
+       </th>
+       <th i18n:translate=""><span tal:replace="batch/start" i18n:name="start"
+        />..<span tal:replace="python: batch.start + batch.length -1" i18n:name="end"
+        /> out of <span tal:replace="batch/sequence_length" i18n:name="total"
+        />
+       </th>
+       <th>
+        <a tal:define="next batch/next" tal:condition="next"
+           tal:attributes="href python:request.indexargs_url(request.classname,
+           {'@template':'help', 'property': request.form['property'].value,
+            'properties': request.form['properties'].value,
+            'form': request.form['form'].value,
+            'type': request.form['type'].value,
+            '@startwith':next.first, '@pagesize':next.size})"
+           i18n:translate="" >next &gt;&gt;</a>
+        &nbsp;
+       </th>
+      </tr>
+     </table>
+
+     <table class="classhelp">
+       <tr>
+           <th>&nbsp;<b>x</b></th>
+           <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+       </tr>
+       <tr tal:repeat="item batch">
+         <tal:block tal:define="attr python:item[props[0]]" >
+           <td>
+             <input name="check"
+                 onclick="updatePreview();"
+                 tal:attributes="type python:request.form['type'].value;
+                                 value attr; id string:id_$attr" />
+             </td>
+             <td tal:repeat="prop props">
+                 <label class="classhelp-label"
+                        tal:attributes="for string:id_$attr"
+                        tal:content="structure python:item[prop]"></label>
+             </td>
+           </tal:block>
+       </tr>
+       <tr>
+           <th>&nbsp;<b>x</b></th>
+           <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+       </tr>
+     </table>
+
+ </form>
+ </body>
+</html>

html/_generic.index.html

+<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
+
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> editing - <span i18n:name="tracker"
+ tal:replace="config/TRACKER_NAME" /></title>
+<tal:block metal:fill-slot="body_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> editing</tal:block>
+
+<td class="content" metal:fill-slot="content">
+
+<span tal:condition="python:not (context.is_view_ok() or context.is_edit_ok())"
+ tal:omit-tag="python:1" i18n:translate=""
+>You are not allowed to view this page.</span>
+
+<tal:block tal:condition="context/is_edit_ok">
+<tal:block i18n:translate="">
+<p class="form-help">
+ You may edit the contents of the
+ <span tal:replace="request/classname" i18n:name="classname"/>
+ class using this form. Commas, newlines and double quotes (") must be
+ handled delicately. You may include commas and newlines by enclosing the
+ values in double-quotes ("). Double quotes themselves must be quoted by
+ doubling ("").
+</p>
+
+<p class="form-help">
+ Multilink properties have their multiple values colon (":") separated
+ (... ,"one:two:three", ...)
+</p>
+
+<p class="form-help">
+ Remove entries by deleting their line. Add new entries by appending
+ them to the table - put an X in the id column.
+</p>
+</tal:block>
+<form onSubmit="return submit_once()" method="POST"
+      tal:attributes="action context/designator">
+<textarea rows="15" style="width:90%" name="rows" tal:content="context/csv"></textarea>
+<br>
+<input type="hidden" name="@action" value="editCSV">
+<input type="submit" value="Edit Items" i18n:attributes="value">
+</form>
+</tal:block>
+
+<table tal:condition="context/is_only_view_ok" width="100%" class="list">
+ <tr>
+  <th tal:repeat="property context/propnames" tal:content="property">&nbsp;</th>
+ </tr>
+ <tal:block repeat="item context/list">
+ <tr tal:condition="item/is_view_ok"
+     tal:attributes="class python:['normal', 'alt'][repeat['item'].index%6/3]">
+  <td tal:repeat="property context/propnames"
+   tal:content="python: item[property] or default"
+  >&nbsp;</td>
+ </tr>
+ </tal:block>
+</table>
+
+</td>
+
+</tal:block>

html/_generic.item.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> editing - <span i18n:name="tracker"
+ tal:replace="config/TRACKER_NAME" /></title>
+<tal:block metal:fill-slot="body_title" i18n:translate=""
+ ><span tal:replace="python:context._classname.capitalize()"
+ i18n:name="class" /> editing</tal:block>
+
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
+    allowed to view this page.</p>
+
+<div tal:condition="context/is_view_ok">
+
+<form method="POST" onSubmit="return submit_once()"
+      enctype="multipart/form-data" tal:condition="context/is_view_ok"
+      tal:attributes="action context/designator">
+
+<input type="hidden" name="@template" value="item">
+
+<table class="form">
+
+<tr tal:repeat="prop python:db[context._classname].properties()">
+ <tal:block tal:condition="python:prop._name not in ('id',
+   'creator', 'creation', 'actor', 'activity')">
+  <th tal:content="prop/_name"></th>
+  <td tal:content="structure python:context[prop._name].field()"></td>
+ </tal:block>
+</tr>
+<tr>
+ <td>&nbsp;</td>
+ <td colspan=3 tal:content="structure context/submit">
+  submit button will go here
+ </td>
+</tr>
+</table>
+
+</form>
+
+<tal:block tal:condition="context/id" tal:replace="structure context/history" />
+
+</div>
+
+</td>
+
+</tal:block>

html/file.index.html

+<!-- dollarId: file.index,v 1.4 2002/01/23 05:10:27 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ >List of files - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+  i18n:translate="">List of files</span>
+<td class="content" metal:fill-slot="content">
+
+<table class="otherinfo" tal:define="batch request/batch">
+ <tr><th style="padding-right: 10" i18n:translate="">Download</th>
+     <th style="padding-right: 10" i18n:translate="">Content Type</th>
+     <th style="padding-right: 10" i18n:translate="">Uploaded By</th>
+     <th style="padding-right: 10" i18n:translate="">Date</th>
+ </tr>
+ <tr tal:repeat="file batch" tal:attributes="class python:['normal', 'alt'][repeat['file'].index%6/3]">
+  <td>
+   <a tal:attributes="href string:file${file/id}/${file/name}"
+      tal:content="file/name">dld link</a>
+  </td>
+  <td tal:content="file/type">content type</td>
+  <td tal:content="file/creator">creator's name</td>
+  <td tal:content="file/creation">creation date</td>
+ </tr>
+
+ <metal:block use-macro="templates/issue.index/macros/batch-footer" />
+
+</table>
+
+</td>
+
+</tal:block>

html/file.item.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="">File display - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">File display</span>
+
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
+    allowed to view this page.</p>
+
+<form method="POST" onSubmit="return submit_once()"
+      enctype="multipart/form-data" tal:condition="context/is_view_ok"
+      tal:attributes="action context/designator">
+
+<table class="form">
+ <tr>
+  <th i18n:translate="">Name</th>
+  <td tal:content="structure context/name/field"></td>
+ </tr>
+ <tr>
+  <th i18n:translate="">Content Type</th>
+  <td tal:content="structure context/type/field"></td>
+ </tr>
+
+ <tr>
+  <td>
+   &nbsp;
+   <input type="hidden" name="@template" value="item">
+   <input type="hidden" name="@required" value="name,type">
+   <input type="hidden" name="@multilink"
+          tal:condition="python:request.form.has_key('@multilink')"
+          tal:attributes="value request/form/@multilink/value">
+  </td>
+  <td tal:content="structure context/submit">submit button here</td>
+ </tr>
+</table>
+</form>
+
+<a tal:condition="python:context.id and context.is_view_ok()"
+ tal:attributes="href string:file${context/id}/${context/name}"
+ i18n:translate="">download</a>
+
+<tal:block tal:condition="context/id" tal:replace="structure context/history" />
+
+</td>
+
+</tal:block>

html/help_controls.js

+// initial values for either Nosy, Superseder, Topic and Waiting On,
+// depending on which has called
+original_field = form[field].value;
+
+// Some browsers (ok, IE) don't define the "undefined" variable.
+undefined = document.geez_IE_is_really_friggin_annoying;
+
+function trim(value) {
+  var temp = value;
+  var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
+  if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
+  var obj = /  /g;
+  while (temp.match(obj)) { temp = temp.replace(obj, " "); }
+  return temp;
+}
+
+function determineList() {
+     // generate a comma-separated list of the checked items
+     var list = new String('');
+ 
+     // either a checkbox object or an array of checkboxes
+     var check = document.frm_help.check;
+ 
+     if ((check.length == undefined) && (check.checked != undefined)) {
+         // only one checkbox on page
+         if (check.checked) {
+             list = check.value;
+         }
+     } else {
+         // array of checkboxes
+         for (box=0; box < check.length; box++) {
+             if (check[box].checked) {
+                 if (list.length == 0) {
+                     separator = '';
+                 }
+                 else {
+                     separator = ',';
+                 }
+                 // we used to use an Array and push / join, but IE5.0 sux
+                 list = list + separator + check[box].value;
+             }
+         }
+     }
+     return list;  
+}
+
+function updateList() {
+  // write back to opener window
+  if (document.frm_help.check==undefined) { return; }
+  form[field].value = determineList();
+}
+
+function updatePreview() {
+  // update the preview box
+  if (document.frm_help.check==undefined) { return; }
+  writePreview(determineList());
+}
+
+function clearList() {
+  // uncheck all checkboxes
+  if (document.frm_help.check==undefined) { return; }
+  for (box=0; box < document.frm_help.check.length; box++) {
+      document.frm_help.check[box].checked = false;
+  }
+}
+
+function reviseList(vals) {
+  // update the checkboxes based on the preview field
+  if (document.frm_help.check==undefined) { return; }
+  var to_check;
+  var list = vals.split(",");
+  if (document.frm_help.check.length==undefined) {
+      check = document.frm_help.check;
+      to_check = false;
+      for (val in list) {
+          if (check.value==trim(list[val])) {
+              to_check = true;
+              break;
+          }
+      }
+      check.checked = to_check;
+  } else {
+    for (box=0; box < document.frm_help.check.length; box++) {
+      check = document.frm_help.check[box];
+      to_check = false;
+      for (val in list) {
+          if (check.value==trim(list[val])) {
+              to_check = true;
+              break;
+          }
+      }
+      check.checked = to_check;
+    }
+  }
+}
+
+function resetList() {
+  // reset preview and check boxes to initial values
+  if (document.frm_help.check==undefined) { return; }
+  writePreview(original_field);
+  reviseList(original_field);
+}
+
+function writePreview(val) {
+   // writes a value to the text_preview
+   document.frm_help.text_preview.value = val;
+}
+
+function focusField(name) {
+    for(i=0; i < document.forms.length; ++i) {
+      var obj = document.forms[i].elements[name];
+      if (obj && obj.focus) {obj.focus();}
+    }
+}
+
+function selectField(name) {
+    for(i=0; i < document.forms.length; ++i) {
+      var obj = document.forms[i].elements[name];
+      if (obj && obj.focus){obj.focus();} 
+      if (obj && obj.select){obj.select();}
+    }
+}
+

html/home.classlist.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="">List of classes - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">List of classes</span>
+<td class="content" metal:fill-slot="content">
+<table class="classlist">
+
+<tal:block tal:repeat="cl db/classes">
+ <tr>
+  <th class="header" colspan="2" align="left">
+   <a tal:attributes="href string:${cl/classname}"
+      tal:content="python:cl.classname.capitalize()">classname</a>
+  </th>
+ </tr>
+ <tr tal:repeat="prop cl/properties">
+  <th tal:content="prop/_name">name</th>
+  <td tal:content="prop/_prop">type</td>
+ </tr>
+</tal:block>
+
+</table>
+</td>
+
+</tal:block>
+<!--
+ This is the default body that is displayed when people visit the
+ tracker. The tag below lists the currently open issues. You may
+ replace it with a greeting message, or a different list of issues or
+ whatever. It's a good idea to have the issues on the front page though
+-->
+<span tal:replace="structure python:db.issue.renderWith('index',
+    sort=('-', 'activity'), group=('+', 'priority'), filter=['status'],
+    columns=['id','activity','title','creator','assignedto', 'status'],
+    filterspec={'status':['-1','1','2','3','4','5','6','7']})" />

html/issue.index.html

+<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="" >
+	List of issues - 
+	<span tal:condition="request/dispname"
+          tal:replace="python:' %s - '%request.dispname" />
+	<span tal:replace="config/TRACKER_NAME" i18n:name="tracker" />
+	</title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+	i18n:translate="">List of issues 
+	<span tal:condition="request/dispname"
+          tal:replace="python:' - %s' % request.dispname" />
+</span>
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
+  allowed to view this page.</p>
+
+<tal:block tal:define="batch request/batch" tal:condition="context/is_view_ok">
+ <table class="list">
+  <tr>
+   <th tal:condition="request/show/priority" i18n:translate="">Priority</th>
+   <th tal:condition="request/show/id" i18n:translate="">ID</th>
+   <th tal:condition="request/show/creation" i18n:translate="">Creation</th>
+   <th tal:condition="request/show/activity" i18n:translate="">Activity</th>
+   <th tal:condition="request/show/actor" i18n:translate="">Actor</th>
+   <th tal:condition="request/show/topic" i18n:translate="">Topic</th>
+   <th tal:condition="request/show/title" i18n:translate="">Title</th>
+   <th tal:condition="request/show/status" i18n:translate="">Status</th>
+   <th tal:condition="request/show/creator" i18n:translate="">Creator</th>
+   <th tal:condition="request/show/assignedto" i18n:translate="">Assigned&nbsp;To</th>
+  </tr>
+ <tal:block tal:repeat="i batch">
+  <tr tal:define="group python:request.group[1]"
+      tal:condition="python:group and batch.propchanged(group)">
+   <th tal:attributes="colspan python:len(request.columns)"
+       tal:content="python:str(i[group]) or '(no %s set)'%group" class="group">
+   </th>
+  </tr>
+
+  <tr>
+   <td tal:condition="request/show/priority"
+       tal:content="python:i.priority.plain() or default">&nbsp;</td>
+   <td tal:condition="request/show/id" tal:content="i/id">&nbsp;</td>
+   <td class="date" tal:condition="request/show/creation"
+       tal:content="i/creation/reldate">&nbsp;</td>
+   <td class="date" tal:condition="request/show/activity"
+       tal:content="i/activity/reldate">&nbsp;</td>
+   <td class="date" tal:condition="request/show/actor"
+       tal:content="python:i.actor.plain() or default">&nbsp;</td>
+   <td tal:condition="request/show/topic"
+       tal:content="python:i.topic.plain() or default">&nbsp;</td>
+   <td tal:condition="request/show/title">
+    <a tal:attributes="href string:issue${i/id}"
+		tal:content="python:str(i.title.plain(hyperlink=0)) or '[no title]'">title</a>
+   </td>
+   <td tal:condition="request/show/status"
+       tal:content="python:i.status.plain() or default">&nbsp;</td>
+   <td tal:condition="request/show/creator"
+       tal:content="python:i.creator.plain() or default">&nbsp;</td>
+   <td tal:condition="request/show/assignedto"
+       tal:content="python:i.assignedto.plain() or default">&nbsp;</td>
+  </tr>
+
+ </tal:block>
+
+ <metal:index define-macro="batch-footer">
+ <tr tal:condition="batch">
+  <th tal:attributes="colspan python:len(request.columns)">
+   <table width="100%">
+    <tr class="navigation">
+     <th>
+      <a tal:define="prev batch/previous" tal:condition="prev"
+         tal:attributes="href python:request.indexargs_url(request.classname,
+         {'@startwith':prev.first, '@pagesize':prev.size})"
+         i18n:translate="">&lt;&lt; previous</a>
+      &nbsp;
+     </th>
+     <th i18n:translate=""><span tal:replace="batch/start" i18n:name="start"
+     />..<span tal:replace="python: batch.start + batch.length -1" i18n:name="end"
+     /> out of <span tal:replace="batch/sequence_length" i18n:name="total"
+     /></th>
+     <th>
+      <a tal:define="next batch/next" tal:condition="next"
+         tal:attributes="href python:request.indexargs_url(request.classname,
+         {'@startwith':next.first, '@pagesize':next.size})"
+         i18n:translate="">next &gt;&gt;</a>
+      &nbsp;
+     </th>
+    </tr>
+   </table>
+  </th>
+ </tr>
+ </metal:index>
+</table>
+
+<a tal:attributes="href python:request.indexargs_url('issue',
+            {'@action':'export_csv'})" i18n:translate="">Download as CSV</a>
+
+<form method="GET" class="index-controls"
+    tal:attributes="action request/classname">
+
+ <table class="form">
+  <tr tal:condition="batch">
+   <th i18n:translate="">Sort on:</th>
+   <td>
+    <select name="@sort">
+     <option value="" i18n:translate="">- nothing -</option>
+     <option tal:repeat="col context/properties"
+             tal:attributes="value col/_name;
+                             selected python:col._name == request.sort[1]"
+             tal:content="col/_name"
+             i18n:translate="">column</option>
+    </select>
+   </td>
+   <th i18n:translate="">Descending:</th>
+   <td><input type="checkbox" name="@sortdir"
+              tal:attributes="checked python:request.sort[0] == '-'">
+   </td>
+  </tr>
+  <tr>
+   <th i18n:translate="">Group on:</th>
+   <td>
+    <select name="@group">
+     <option value="" i18n:translate="">- nothing -</option>
+     <option tal:repeat="col context/properties"
+             tal:attributes="value col/_name;
+                             selected python:col._name == request.group[1]"
+             tal:content="col/_name"
+             i18n:translate="">column</option>
+    </select>
+   </td>
+   <th i18n:translate="">Descending:</th>
+   <td><input type="checkbox" name="@groupdir"
+              tal:attributes="checked python:request.group[0] == '-'">
+   </td>
+  </tr>
+  <tr><td colspan="4">
+              <input type="submit" value="Redisplay" i18n:attributes="value">
+              <tal:block tal:replace="structure
+                python:request.indexargs_form(sort=0, group=0)" />
+  </td></tr>
+ </table>
+</form>
+
+</tal:block>
+
+</td>
+</tal:block>

html/issue.item.html

+<!-- dollarId: issue.item,v 1.4 2001/08/03 01:19:43 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title">
+<tal:block condition="context/id" i18n:translate=""
+ >Issue <span tal:replace="context/id" i18n:name="id"
+ />: <span tal:replace="context/title" i18n:name="title"
+ /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:block>
+<tal:block condition="not:context/id" i18n:translate=""
+ >New Issue - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:block>
+</title>
+<tal:block metal:fill-slot="body_title">
+ <span tal:condition="python: not (context.id or context.is_edit_ok())"
+  tal:omit-tag="python:1" i18n:translate="">New Issue</span>
+ <span tal:condition="python: not context.id and context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">New Issue Editing</span>
+ <span tal:condition="python: context.id and not context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">Issue<tal:x
+  replace="context/id" i18n:name="id" /></span>
+ <span tal:condition="python: context.id and context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">Issue<tal:x
+  replace="context/id" i18n:name="id" /> Editing</span>
+</tal:block>
+
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
+    allowed to view this page.</p>
+
+<div tal:condition="context/is_view_ok">
+
+<form method="POST" name="itemSynopsis"
+      onSubmit="return submit_once()" enctype="multipart/form-data"
+      tal:attributes="action context/designator">
+
+<table class="form">
+<tr>
+ <th class="required" i18n:translate="">Title</th>
+ <td colspan=3 tal:content="structure python:context.title.field(size=60)">title</td>
+</tr>
+
+<tr>
+ <th class="required" i18n:translate="">Priority</th>
+ <td tal:content="structure context/priority/menu">priority</td>
+ <th i18n:translate="">Status</th>
+ <td tal:content="structure context/status/menu">status</td>
+</tr>
+
+<tr>
+ <th i18n:translate="">Superseder</th>
+ <td>
+  <span tal:replace="structure python:context.superseder.field(showid=1, size=20)" />
+  <span tal:condition="context/is_edit_ok" tal:replace="structure python:db.issue.classhelp('id,title', property='superseder')" />
+  <span tal:condition="context/superseder" tal:repeat="sup context/superseder">
+   <br><span i18n:translate="">View: <a i18n:name="link" tal:content="sup/id"
+     tal:attributes="href string:issue${sup/id}"></a></span>
+  </span>
+ </td>
+ <th i18n:translate="">Nosy List</th>
+ <td>
+  <span tal:replace="structure context/nosy/field" />
+  <span tal:condition="context/is_edit_ok" tal:replace="structure
+python:db.user.classhelp('username,realname,address', property='nosy', width='600')" /><br>
+ </td>
+</tr>
+
+<tr>
+ <th i18n:translate="">Assigned To</th>
+ <td tal:content="structure context/assignedto/menu">assignedto menu</td>
+ <th i18n:translate="">Topics</th>
+ <td>
+  <span tal:replace="structure context/topic/field" />
+  <span tal:condition="context/is_edit_ok" tal:replace="structure python:db.keyword.classhelp(property='topic')" />
+ </td>
+</tr>
+
+<tr tal:condition="context/is_edit_ok">
+ <th i18n:translate="">Change Note</th>
+ <td colspan=3>
+  <textarea tal:content="request/form/@note/value | default"
+            name="@note" wrap="hard" rows="5" cols="80"></textarea>
+ </td>
+</tr>
+
+<tr tal:condition="context/is_edit_ok">
+ <th i18n:translate="">File</th>
+ <td colspan=3><input type="file" name="@file" size="40"></td>
+</tr>
+
+<tr tal:condition="context/is_edit_ok">
+ <td>
+  &nbsp;
+  <input type="hidden" name="@template" value="item">
+  <input type="hidden" name="@required" value="title,priority">
+ </td>
+ <td colspan=3>
+  <span tal:replace="structure context/submit">submit button</span>
+  <a tal:condition="context/id" tal:attributes="href context/copy_url"
+   i18n:translate="">Make a copy</a>
+ </td>
+</tr>
+
+</table>
+</form>
+
+<tal:block tal:condition="not:context/id" i18n:translate="">
+<table class="form">
+<tr>
+ <td>Note:&nbsp;</td>
+ <th class="required">highlighted</th>
+ <td>&nbsp;fields are required.</td>
+</tr>
+</table>
+</tal:block>
+
+<p tal:condition="context/id" i18n:translate="">
+ Created on <b><tal:x replace="context/creation" i18n:name="creation" /></b>
+ by <b><tal:x replace="context/creator" i18n:name="creator" /></b>,
+ last changed <b><tal:x replace="context/activity" i18n:name="activity" /></b>
+ by <b><tal:x replace="context/actor" i18n:name="actor" /></b>.
+</p>
+
+<table class="files" tal:condition="context/files">
+ <tr><th colspan="5" class="header" i18n:translate="">Files</th></tr>
+ <tr>
+  <th i18n:translate="">File name</th>
+  <th i18n:translate="">Uploaded</th>
+  <th i18n:translate="">Type</th>
+  <th i18n:translate="">Edit</th>
+  <th i18n:translate="">Remove</th>
+ </tr>
+ <tr tal:repeat="file context/files">
+  <td>
+   <a tal:attributes="href file/download_url"
+      tal:content="file/name">dld link</a>
+  </td>
+  <td>
+   <span tal:content="file/creator">creator's name</span>,
+   <span tal:content="file/creation">creation date</span>
+  </td>
+  <td tal:content="file/type" />
+  <td><a tal:condition="file/is_edit_ok"
+          tal:attributes="href string:file${file/id}">edit</a>
+  </td>
+  <td>
+   <form style="padding:0" tal:condition="context/is_edit_ok"
+         tal:attributes="action string:issue${context/id}">
+    <input type="hidden" name="@remove@files" tal:attributes="value file/id">
+    <input type="hidden" name="@action" value="edit">
+    <input type="submit" value="remove" i18n:attributes="value">
+   </form>
+  </td>
+ </tr>
+</table>
+
+<table class="messages" tal:condition="context/messages">
+ <tr><th colspan="4" class="header" i18n:translate="">Messages</th></tr>
+ <tal:block tal:repeat="msg context/messages/reverse">
+  <tr>
+   <th><a tal:attributes="href string:msg${msg/id}"
+    i18n:translate="">msg<tal:x replace="msg/id" i18n:name="id" /> (view)</a></th>
+   <th i18n:translate="">Author: <tal:x replace="msg/author"
+       i18n:name="author" /></th>
+   <th i18n:translate="">Date: <tal:x replace="msg/date"
+       i18n:name="date" /></th>
+   <th>
+    <form style="padding:0" tal:condition="context/is_edit_ok"
+          tal:attributes="action string:issue${context/id}">
+     <input type="hidden" name="@remove@messages" tal:attributes="value msg/id">
+     <input type="hidden" name="@action" value="edit">
+     <input type="submit" value="remove" i18n:attributes="value">
+    </form>
+   </th>
+  </tr>
+  <tr>
+   <td colspan="4" class="content">
+    <pre tal:content="structure msg/content/hyperlinked">content</pre>
+   </td>
+  </tr>
+ </tal:block>
+</table>
+
+<tal:block tal:condition="context/id" tal:replace="structure context/history" />
+
+</div>
+
+</td>
+
+</tal:block>

html/issue.search.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="">Issue searching - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Issue searching</span>
+<td class="content" metal:fill-slot="content">
+
+<form method="GET" name="itemSynopsis"
+      tal:attributes="action request/classname">
+
+<table class="form" tal:define="
+   cols python:request.columns or 'id activity title status assignedto'.split();
+   sort_on python:request.sort[1] or 'activity';
+   group_on python:request.group[1] or 'priority';
+
+   search_input templates/page/macros/search_input;
+   column_input templates/page/macros/column_input;
+   sort_input templates/page/macros/sort_input;
+   group_input templates/page/macros/group_input;
+   search_select templates/page/macros/search_select;
+   search_multiselect templates/page/macros/search_multiselect;">
+
+<tr>
+ <th class="header">&nbsp;</th>
+ <th class="header" i18n:translate="">Filter on</th>
+ <th class="header" i18n:translate="">Display</th>
+ <th class="header" i18n:translate="">Sort on</th>
+ <th class="header" i18n:translate="">Group on</th>
+</tr>
+
+<tr tal:define="name string:@search_text">
+  <th i18n:translate="">All text*:</th>
+  <td metal:use-macro="search_input"></td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:title">
+  <th i18n:translate="">Title:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:topic;
+                db_klass string:keyword;
+                db_content string:name;">
+  <th i18n:translate="">Topic:</th>
+  <td metal:use-macro="search_select"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:id">
+  <th i18n:translate="">ID:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:creation">
+  <th i18n:translate="">Creation Date:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:creator;
+                db_klass string:user;
+                db_content string:username;"
+    tal:condition="db/user/is_view_ok">
+  <th i18n:translate="">Creator:</th>
+  <td metal:use-macro="search_select">
+    <option metal:fill-slot="extra_options" i18n:translate=""
+            tal:attributes="value request/user/id">created by me</option>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:activity">
+  <th i18n:translate="">Activity:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:actor;
+                db_klass string:user;
+                db_content string:username;"
+    tal:condition="db/user/is_view_ok">
+  <th i18n:translate="">Actor:</th>
+  <td metal:use-macro="search_select">
+    <option metal:fill-slot="extra_options" i18n:translate=""
+            tal:attributes="value request/user/id">done by me</option>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:priority;
+                db_klass string:priority;
+                db_content string:name;">
+  <th i18n:translate="">Priority:</th>
+  <td metal:use-macro="search_select">
+    <option metal:fill-slot="extra_options" value="-1" i18n:translate=""
+            tal:attributes="selected python:value == '-1'">not selected</option>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:status;
+                db_klass string:status;
+                db_content string:name;">
+  <th i18n:translate="">Status:</th>
+  <td metal:use-macro="search_select">
+    <tal:block metal:fill-slot="extra_options">
+      <option value="-1,1,2,3,4,5,6,7" i18n:translate=""
+              tal:attributes="selected python:value == '-1,1,2,3,4,5,6,7'">not resolved</option>
+      <option value="-1" i18n:translate=""
+              tal:attributes="selected python:value == '-1'">not selected</option>
+    </tal:block>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:assignedto;
+                db_klass string:user;
+                db_content string:username;"
+    tal:condition="db/user/is_view_ok">
+  <th i18n:translate="">Assigned to:</th>
+  <td metal:use-macro="search_select">
+    <tal:block metal:fill-slot="extra_options">
+      <option tal:attributes="value request/user/id"
+       i18n:translate="">assigned to me</option>
+      <option value="-1" tal:attributes="selected python:value == '-1'"
+       i18n:translate="">unassigned</option>
+    </tal:block>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr>
+ <th i18n:translate="">No Sort or group:</th>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td><input type="radio" name="@sort" value=""></td>
+ <td><input type="radio" name="@group" value=""></td>
+</tr>
+
+<tr>
+<th i18n:translate="">Pagesize:</th>
+<td><input name="@pagesize" size="3" value="50"
+           tal:attributes="value request/form/@pagesize/value | default"></td>
+</tr>
+
+<tr>
+<th i18n:translate="">Start With:</th>
+<td><input name="@startwith" size="3" value="0"
+           tal:attributes="value request/form/@startwith/value | default"></td>
+</tr>
+
+<tr>
+<th i18n:translate="">Sort Descending:</th>
+<td><input type="checkbox" name="@sortdir"
+           tal:attributes="checked python:request.sort[0] == '-' or request.sort[0] is None">
+</td>
+</tr>
+
+<tr>
+<th i18n:translate="">Group Descending:</th>
+<td><input type="checkbox" name="@groupdir"
+           tal:attributes="checked python:request.group[0] == '-'">
+</td>
+</tr>
+
+<tr tal:condition="python:request.user.hasPermission('Edit', 'query')">
+ <th i18n:translate="">Query name**:</th>
+ <td tal:define="value request/form/@queryname/value | nothing">
+  <input name="@queryname" tal:attributes="value value">
+  <input type="hidden" name="@old-queryname" tal:attributes="value value">
+ </td>
+</tr>
+
+<tr>
+  <td>
+   &nbsp;
+   <input type="hidden" name="@action" value="search">
+  </td>
+  <td><input type="submit" value="Search" i18n:attributes="value"></td>
+</tr>
+
+<tr><td>&nbsp;</td>
+ <td colspan="4" class="help">
+  <span i18n:translate="" tal:omit-tag="true">
+   *: The "all text" field will look in message bodies and issue titles
+  </span><br>
+  <span tal:condition="python:request.user.hasPermission('Edit', 'query')"
+   i18n:translate="" tal:omit-tag="true"
+  >
+   **: If you supply a name, the query will be saved off and available as a
+       link in the sidebar
+  </span>
+ </td>
+</tr>
+</table>
+
+</form>
+</td>
+
+</tal:block>

html/keyword.item.html

+<!-- dollarId: keyword.item,v 1.3 2002/05/22 00:32:34 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="">Keyword editing - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Keyword editing</span>
+<td class="content" metal:fill-slot="content">
+
+<table class="otherinfo" tal:define="keywords db/keyword/list"
+       tal:condition="keywords">
+ <tr><th colspan="4" class="header" i18n:translate="">Existing Keywords</th></tr>
+ <tr tal:repeat="start python:range(0, len(keywords), 4)">
+  <td width="25%" tal:define="batch python:utils.Batch(keywords, 4, start)"
+      tal:repeat="keyword batch">
+    <a tal:attributes="href string:keyword${keyword/id}"
+       tal:content="keyword/name">keyword here</a>
+  </td>
+ </tr>
+ <tr>
+  <td colspan="4" style="border-top: 1px solid gray" i18n:translate="">
+   To edit an existing keyword (for spelling or typing errors),
+   click on its entry above.
+  </td>
+ </tr>
+</table>
+
+<p class="help" tal:condition="not:context/id" i18n:translate="">
+ To create a new keyword, enter it below and click "Submit New Entry".
+</p>
+
+<form method="POST" onSubmit="return submit_once()"
+      enctype="multipart/form-data"
+      tal:attributes="action context/designator">
+
+ <table class="form">
+  <tr>
+   <th i18n:translate="">Keyword</th>
+   <td tal:content="structure context/name/field">name</td>
+  </tr>
+
+  <tr>
+   <td>
+    &nbsp;
+    <input type="hidden" name="@required" value="name">
+    <input type="hidden" name="@template" value="item">
+   </td>
+   <td colspan=3 tal:content="structure context/submit">
+    submit button will go here
+   </td>
+  </tr>
+ </table>
+</form>
+</td>
+
+</tal:block>

html/msg.index.html

+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ >List of messages - <span tal:replace="config/TRACKER_NAME"
+ i18n:name="tracker"/></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Message listing</span>
+<td class="content" metal:fill-slot="content">
+<table tal:define="batch request/batch" class="messages">
+ <tr><th colspan=2 class="header" i18n:translate="">Messages</th></tr>
+ <tal:block tal:repeat="msg batch">
+  <tr>
+   <th tal:content="string:Author: ${msg/author}">author</th>
+   <th tal:content="string:Date: ${msg/date}">date</th>
+  </tr>
+  <tr>
+   <td colspan="2"><pre tal:content="msg/content">content</pre></td>
+  </tr>
+ </tal:block>
+
+ <metal:block use-macro="templates/issue.index/macros/batch-footer" />
+
+</table>
+</td>
+
+</tal:block>

html/msg.item.html

+<!-- dollarId: msg.item,v 1.3 2002/05/22 00:32:34 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title">
+<tal:block condition="context/id" i18n:translate=""
+ >Message <span tal:replace="context/id" i18n:name="id"
+ /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:block>
+<tal:block condition="not:context/id" i18n:translate=""
+ >New Message - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:block>
+</title>
+<tal:block metal:fill-slot="body_title">
+ <span tal:condition="python: not (context.id or context.is_edit_ok())"
+  tal:omit-tag="python:1" i18n:translate="">New Message</span>
+ <span tal:condition="python: not context.id and context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">New Message Editing</span>
+ <span tal:condition="python: context.id and not context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">Message<tal:x
+  replace="context/id" i18n:name="id" /></span>
+ <span tal:condition="python: context.id and context.is_edit_ok()"
+  tal:omit-tag="python:1" i18n:translate="">Message<tal:x
+  replace="context/id" i18n:name="id" /> Editing</span>
+</tal:block>
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
+    allowed to view this page.</p>
+
+<div tal:condition="context/is_view_ok">
+<table class="form">
+
+<tr>
+ <th i18n:translate="">Author</th>
+ <td tal:content="context/author"></td>
+</tr>
+
+<tr>
+ <th i18n:translate="">Recipients</th>
+ <td tal:content="context/recipients"></td>
+</tr>
+
+<tr>
+ <th i18n:translate="">Date</th>
+ <td tal:content="context/date"></td>
+</tr>
+</table>
+
+<table class="messages">
+ <tr><th colspan=2 class="header" i18n:translate="">Content</th></tr>
+ <tr>
+  <td class="content" colspan=2><pre tal:content="structure context/content/hyperlinked"></pre></td>
+ </tr>
+</table>
+
+<table class="files" tal:condition="context/files">
+ <tr><th colspan="2" class="header" i18n:translate="">Files</th></tr>
+ <tr>
+  <th i18n:translate="">File name</th>
+  <th i18n:translate="">Uploaded</th>
+ </tr>
+ <tr tal:repeat="file context/files">
+  <td>
+   <a tal:attributes="href string:file${file/id}/${file/name}"
+      tal:content="file/name">dld link</a>
+  </td>
+  <td>
+   <span tal:content="file/creator">creator's name</span>,
+   <span tal:content="file/creation">creation date</span>
+  </td>
+ </tr>
+</table>
+
+<tal:block tal:replace="structure context/history" />
+
+</div>
+</td>
+
+</tal:block>
+<tal:block metal:define-macro="icing">
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+                               "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title metal:define-slot="head_title">title goes here</title>
+<link rel="stylesheet" type="text/css" href="@@file/style.css">
+<meta http-equiv="Content-Type"
+ tal:attributes="content string:text/html;; charset=${request/client/charset}" />
+<script tal:replace="structure request/base_javascript">
+</script>
+
+</head>
+<body class="body">
+
+<table class="body">
+
+<tr>
+ <td class="page-header-left">&nbsp;</td>
+ <td class="page-header-top">
+   <div id="body-title">
+     <h2><span metal:define-slot="body_title">body title</span></h2>
+   </div>
+   <div id="searchbox">
+     <form method="GET" action="issue">
+       <input type="hidden" name="@columns"
+              value="id,activity,title,creator,assignedto,status"/>
+       <input type="hidden" name="@sort" value="activity"/>
+       <input type="hidden" name="@group" value="priority"/>
+       <input id="search-text" name="@search_text" size="10"/>
+       <input type="submit" id="submit" name="submit" value="Search" i18n:attributes="value"/>
+     </form>
+  </div>
+ </td>
+</tr>
+
+<tr>
+ <td rowspan="2" valign="top" class="sidebar">
+  <p class="classblock"
+     tal:condition="python:request.user.hasPermission('View', 'query')">
+   <span i18n:translate=""
+    ><b>Your Queries</b> (<a href="query?@template=edit">edit</a>)</span><br>
+   <tal:block tal:repeat="qs request/user/queries">
+    <a tal:attributes="href string:${qs/klass}?${qs/url}&@dispname=${qs/name}"
+       tal:content="qs/name">link</a><br>
+   </tal:block>
+  </p>
+
+  <form method="POST" tal:attributes="action request/base">
+   <p class="classblock"
+       tal:condition="python:request.user.hasPermission('View', 'issue')">
+    <b i18n:translate="">Issues</b><br>
+    <span tal:condition="python:request.user.hasPermission('Create', 'issue')">
+      <a href="issue?@template=item" i18n:translate="">Create New</a><br>
+    </span>
+    <a href="issue?@sort=-activity&@group=priority&@filter=status,assignedto&@columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=-1&@dispname=Show%20Unassigned"
+     i18n:translate="">Show Unassigned</a><br>
+    <a href="issue?@sort=-activity&@group=priority&@filter=status&@columns=id,activity,title,creator,assignedto,status&status=-1,1,2,3,4,5,6,7&@dispname=Show%20All"
+     i18n:translate="">Show All</a><br>
+    <a href="issue?@template=search" i18n:translate="">Search</a><br>
+    <input type="submit" class="form-small" value="Show issue:"
+     i18n:attributes="value"><input class="form-small" size="4"
+     type="text" name="@number">
+    <input type="hidden" name="@type" value="issue">
+    <input type="hidden" name="@action" value="show">
+   </p>
+  </form>
+
+  <p class="classblock"
+     tal:condition="python:request.user.hasPermission('Edit', 'keyword')
+        or request.user.hasPermission('Create', 'keyword')">
+   <b i18n:translate="">Keywords</b><br>
+   <span tal:condition="python:request.user.hasPermission('Create', 'keyword')">
+    <a href="keyword?@template=item" i18n:translate="">Create New</a><br>
+   </span>
+   <span tal:condition="python:db.keyword.list() and
+        request.user.hasPermission('Edit', 'keyword')">
+    <a href="keyword?@template=item" i18n:translate="">Edit Existing</a><br>
+   </span>
+  </p>
+
+  <p class="classblock"
+       tal:condition="python:request.user.hasPermission('View', 'user')">
+   <b i18n:translate="">Administration</b><br>
+   <span tal:condition="python:request.user.hasPermission('Edit', None)">
+    <a href="home?@template=classlist" i18n:translate="">Class List</a><br>
+   </span>
+   <span tal:condition="python:request.user.hasPermission('View', 'user')
+                            or request.user.hasPermission('Edit', 'user')">
+    <a href="user"  i18n:translate="">User List</a><br>
+   </span>
+   <a tal:condition="python:request.user.hasPermission('Create', 'user')"
+      href="user?@template=item" i18n:translate="">Add User</a>
+  </p>
+
+  <form method="POST" tal:condition="python:request.user.username=='anonymous'"
+        tal:attributes="action request/base">
+   <p class="userblock">
+    <b i18n:translate="">Login</b><br>
+    <input size="10" name="__login_name"><br>
+    <input size="10" type="password" name="__login_password"><br>
+    <input type="hidden" name="@action" value="Login">
+    <input type="checkbox" name="remember" id="remember">
+    <label for="remember" i18n:translate="">Remember me?</label><br>
+    <input type="submit" value="Login" i18n:attributes="value"><br>
+    <input type="hidden" name="__came_from" tal:attributes="value string:${request/base}${request/env/PATH_INFO}">
+    <span tal:replace="structure request/indexargs_form" />
+    <a href="user?@template=register"
+       tal:condition="python:request.user.hasPermission('Create', 'user')"
+     i18n:translate="">Register</a><br>
+    <a href="user?@template=forgotten" i18n:translate="">Lost&nbsp;your&nbsp;login?</a><br>
+   </p>
+  </form>
+
+  <p class="userblock" tal:condition="python:request.user.username != 'anonymous'">
+   <b i18n:translate="">Hello, <span i18n:name="user"
+    tal:replace="request/user/username">username</span></b><br>
+   <a tal:attributes="href string:issue?@sort=-activity&@group=priority&@filter=status,assignedto&@columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=${request/user/id}" i18n:translate="">Your Issues</a><br>
+   <a tal:attributes="href string:user${request/user/id}"
+    i18n:translate="">Your Details</a><br>
+   <a tal:attributes="href python:request.indexargs_url('',
+       {'@action':'logout'})" i18n:translate="">Logout</a>
+  </p>
+  <p class="userblock">
+   <b i18n:translate="">Help</b><br>
+   <a href="http://roundup.sourceforge.net/doc-1.0/"
+    i18n:translate="">Roundup docs</a>
+  </p>
+ </td>
+ <td>
+  <p tal:condition="options/error_message | nothing" class="error-message"
+     tal:repeat="m options/error_message" tal:content="structure m" />
+  <p tal:condition="options/ok_message | nothing" class="ok-message">
+    <span tal:repeat="m options/ok_message"
+       tal:content="structure string:$m <br/ > " />
+     <a class="form-small" tal:attributes="href request/current_url"
+        i18n:translate="">clear this message</a>
+  </p>
+ </td>
+</tr>
+<tr>
+ <td class="content" metal:define-slot="content">Page content goes here</td>
+</tr>
+
+</table>
+
+<pre tal:condition="request/form/debug | nothing" tal:content="request">
+</pre>
+
+</body>
+</html>
+</tal:block>
+
+<!--
+The following macros are intended to be used in search pages.
+
+The invoking context must define a "name" variable which names the
+property being searched.
+
+See issue.search.html in the classic template for examples.
+-->
+<td metal:define-macro="search_input">
+  <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+                         name name">
+</td>
+
+<td metal:define-macro="search_popup">
+  <!--
+    context needs to specify the popup "columns" as a comma-separated
+    string (eg. "id,title" or "id,name,description") as well as name
+  -->
+  <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+                         name name">
+  <span tal:replace="structure python:db.issue.classhelp(columns,
+                                      property=name)" />
+</td>
+
+<td metal:define-macro="search_select">
+  <select tal:attributes="name name"
+          tal:define="value python:request.form.getvalue(name)">
+    <option value="" i18n:translate="">don't care</option>
+    <tal:block metal:define-slot="extra_options"></tal:block>
+    <option value="" i18n:translate="">------------</option>
+    <option tal:repeat="s python:db[db_klass].list()"
+            tal:attributes="value s/id; selected python:value == s.id"
+            tal:content="python:s[db_content]"></option>
+  </select>
+</td>
+
+<td metal:define-macro="search_multiselect">
+  <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+                         name name">
+  <span tal:replace="structure python:db[db_klass].classhelp(db_content,
+                                        property=name, width='600')" />
+</td>
+
+<td metal:define-macro="search_checkboxes">
+ <ul class="search-checkboxes"
+     tal:define="value python:request.form.getvalue(name);
+                 values python:value and value.split(',') or []">
+ <li tal:repeat="s python:db[db_klass].list()">
+  <input type="checkbox" tal:attributes="name name; id string:$name-${s/id};
+    value s/id; checked python:s.id in values" />
+  <label tal:attributes="for string:$name-${s/id}"
+         tal:content="python:s[db_content]" />
+ </li>
+ <li metal:define-slot="no_value_item">
+  <input type="checkbox" value="-1" tal:attributes="name name;
+     id string:$name--1; checked python:value == '-1'" />
+  <label tal:attributes="for string:$name--1" i18n:translate="">no value</label>
+ </li>
+ </ul>
+</td>
+
+<td metal:define-macro="column_input">
+  <input type="checkbox" name="@columns"
+         tal:attributes="value name;
+                         checked python:name in cols">
+</td>
+
+<td metal:define-macro="sort_input">
+  <input type="radio" name="@sort"
+         tal:attributes="value name;
+                         checked python:name == sort_on">
+</td>
+
+<td metal:define-macro="group_input">
+  <input type="radio" name="@group"
+         tal:attributes="value name;
+                         checked python:name == group_on">
+</td>
+

html/query.edit.html

+<!-- dollarId: user.item,v 1.7 2002/08/16 04:29:04 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate=""
+ >"Your Queries" Editing - <span tal:replace="config/TRACKER_NAME"
+ i18n:name="tracker" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">"Your Queries" Editing</span>
+
+<td class="content" metal:fill-slot="content">
+
+<span tal:condition="not:context/is_edit_ok"
+ i18n:translate="">You are not allowed to edit queries.</span>
+
+<script language="javascript">
+// This exists solely because I can't figure how to get the & into an
+// attributes TALES expression, and so it keeps getting quoted.
+function retire(qid) {
+    window.location = 'query'+qid+'?@action=retire&@template=edit';
+}
+</script>
+
+<form method="POST" onSubmit="return submit_once()" action="query"
+      enctype="multipart/form-data" tal:condition="context/is_edit_ok">
+
+<table class="list" width="100%"
+       tal:define="uid request/user/id; mine request/user/queries">
+
+<tr><th i18n:translate="">Query</th>
+    <th i18n:translate="">Include in "Your Queries"</th>
+    <th i18n:translate="">Edit</th>
+    <th i18n:translate="">Private to you?</th>
+    <th>&nbsp;</th>
+</tr>
+
+<tr tal:repeat="query mine">
+ <tal:block condition="query/is_retired">
+
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+        tal:content="query/name">query</a></td>
+
+ <td metal:define-macro="include">
+  <select tal:condition="python:query.id not in mine"
+          tal:attributes="name string:user${uid}@add@queries">
+    <option value="" i18n:translate="">leave out</option>
+    <option tal:attributes="value query/id" i18n:translate="">include</option>
+  </select>
+  <select tal:condition="python:query.id in mine"
+          tal:attributes="name string:user${uid}@remove@queries">
+    <option value="" i18n:translate="">leave in</option>
+    <option tal:attributes="value query/id" i18n:translate="">remove</option>
+  </select>
+ </td>
+
+ <td colspan="3" i18n:translate="">[query is retired]</td>
+
+ <!-- <td> maybe offer "restore" some day </td> -->
+ </tal:block>
+</tr>
+
+<tr tal:define="queries python:db.query.filter(filterspec={'private_for':uid})"
+     tal:repeat="query queries">
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+        tal:content="query/name">query</a></td>
+
+ <td metal:use-macro="template/macros/include" />
+
+ <td><a tal:attributes="href string:query${query/id}" i18n:translate="">edit</a></td>
+
+ <td>
+  <select tal:attributes="name string:query${query/id}@private_for">
+   <option tal:attributes="selected python:query.private_for == uid;
+           value uid" i18n:translate="">yes</option>
+   <option tal:attributes="selected python:query.private_for == None"
+           value="-1" i18n:translate="">no</option>
+  </select>
+ </td>
+
+ <td>
+  <input type="button" value="Delete" i18n:attributes="value"
+  tal:attributes="onClick python:'''retire('%s')'''%query.id">
+  </td>
+</tr>
+
+<tr tal:define="queries python:db.query.filter(filterspec={'private_for':None})"
+     tal:repeat="query queries">
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+        tal:content="query/name">query</a></td>
+
+ <td metal:use-macro="template/macros/include" />
+
+ <td colspan="3" tal:condition="query/is_edit_ok">
+  <a tal:attributes="href string:query${query/id}" i18n:translate="">edit</a>
+ </td>
+ <td tal:condition="not:query/is_edit_ok" colspan="3"
+    i18n:translate="">[not yours to edit]</td>
+
+</tr>
+
+<tr><td colspan="5">
+   <input type="hidden" name="@action" value="edit">
+   <input type="hidden" name="@template" value="edit">
+   <input type="submit" value="Save Selection" i18n:attributes="value">
+</td></tr>
+
+</table>
+
+</form>
+</td>
+</tal:block>

html/query.item.html

+<!-- query.item -->
+<span tal:replace="structure context/renderQueryForm" />
+
+/* main page styles */
+body.body {
+  font-family: sans-serif, Arial, Helvetica;
+  background-color: white;
+  color: #333;
+  margin: 0;
+}
+a[href]:hover {
+  color:blue;
+  text-decoration: underline;
+}
+a[href], a[href]:link {
+  color:blue;
+  text-decoration: none;
+}
+
+table.body {
+  border: 0;
+  padding: 0;
+  border-spacing: 0;
+  border-collapse: separate;
+}
+
+td.page-header-left {
+  padding: 5px;
+  border-bottom: 1px solid #444;
+}
+td.sidebar {
+  padding: 1px 0 0 1px;
+  white-space: nowrap;
+}
+
+/* don't display the sidebar when printing */
+@media print {
+    td.page-header-left {
+        display: none;
+    }
+    td.sidebar {
+        display: none;
+    }
+    .index-controls {
+        display: none;
+    }
+    #searchbox {
+        display: none;
+    }
+}
+
+td.page-header-top {
+  padding: 5px;
+  border-bottom: 1px solid #444;
+}
+#searchbox {
+    float: right;
+}
+
+div#body-title {
+  float: left;
+}
+
+
+div#searchbox {
+  float: right;
+  padding-top: 1em;
+}
+
+div#searchbox input#search-text {
+  width: 10em;
+}
+
+form {
+  margin: 0;
+}
+
+textarea {
+    font-family: monospace;
+}
+
+td.sidebar p.classblock {
+  padding: 2px 5px 2px 5px;
+  margin: 1px;
+  border: 1px solid #444;
+  background-color: #eee;
+}
+
+td.sidebar p.userblock {
+  padding: 2px 5px 2px 5px;
+  margin: 1px 1px 1px 1px;
+  border: 1px solid #444;
+  background-color: #eef;
+}
+
+.form-small {
+  padding: 0;
+  font-size: 75%;
+}
+
+
+td.content {
+  padding: 1px 5px 1px 5px;
+  vertical-align: top;
+  width: 100%;
+}
+
+td.date, th.date { 
+  white-space: nowrap;
+}
+
+p.ok-message {
+  background-color: #22bb22;
+  padding: 5px;
+  color: white;
+  font-weight: bold;
+}
+p.error-message {
+  background-color: #bb2222;
+  padding: 5px;
+  color: white;
+  font-weight: bold;
+}
+p.error-message a[href] {
+  color: white;
+  text-decoration: underline;
+}
+
+
+/* style for search forms */
+ul.search-checkboxes {
+    display: inline;
+    padding: none;
+    list-style: none;
+}
+ul.search-checkboxes > li {
+    display: inline;
+    padding-right: .5em;
+}
+
+
+/* style for forms */
+table.form {
+  padding: 2px;
+  border-spacing: 0;
+  border-collapse: separate;
+}
+
+table.form th {
+  color: #338;
+  text-align: right;
+  vertical-align: top;
+  font-weight: normal;
+  white-space: nowrap;
+}
+
+table.form th.header {
+  font-weight: bold;
+  background-color: #eef;
+  text-align: left;
+}
+
+table.form th.required {
+  font-weight: bold;
+}
+
+table.form td {
+  color: #333;
+  empty-cells: show;
+  vertical-align: top;
+}
+
+table.form td.optional {
+  font-weight: bold;
+  font-style: italic;
+}
+
+table.form td.html {
+  color: #777;
+}
+
+/* style for lists */
+table.list {
+  border-spacing: 0;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.list th {
+  padding: 0 4px 0 4px;
+  color: #404070;
+  background-color: #eef;
+  border: 1px solid white;
+  vertical-align: top;
+  empty-cells: show;
+}
+table.list th a[href]:hover { color: #404070 }
+table.list th a[href]:link { color: #404070 }
+table.list th a[href] { color: #404070 }
+table.list th.group {
+  background-color: #f4f4ff;
+  text-align: center;
+}
+
+table.list td {
+  padding: 0 4px 0 4px;
+  border: 1px solid white;
+  color: #404070;
+  background-color: #efefef;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+table.list tr.navigation th {
+  width: 33%;
+  border-style: hidden;
+  text-align: center;
+}
+table.list tr.navigation td {
+    border: none
+}
+table.list tr.navigation th:first-child {
+  text-align: left;
+}
+table.list tr.navigation th:last-child {
+  text-align: right;
+}
+
+
+/* style for message displays */
+table.messages {
+  border-spacing: 0;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.messages th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.messages th {
+  font-weight: bold;
+  color: black;
+  text-align: left;
+  border-bottom: 1px solid #afafaf;
+}
+
+table.messages td {
+  font-family: monospace;
+  background-color: #efefef;
+  border-bottom: 1px solid #afafaf;
+  color: black;
+  empty-cells: show;
+  border-right: 1px solid #afafaf;