[Python-checkins] r56578 - in tracker/instances/meta: detectors detectors/config.ini detectors/mailonmsgchanges.py detectors/messagesummary.py detectors/nosyreaction.py detectors/statusauditor.py detectors/userauditor.py extensions extensions/README.txt html html/_generic.calendar.html html/_generic.collision.html html/_generic.help-empty.html html/_generic.help-list.html html/_generic.help-search.html html/_generic.help-submit.html html/_generic.help.html html/_generic.index.html html/_generic.item.html html/file.index.html html/file.item.html html/help.html html/help_controls.js html/home.classlist.html html/home.html html/issue.index.html html/issue.item.html html/issue.search.html html/keyword.item.html html/msg.index.html html/msg.item.html html/page.html html/query.edit.html html/query.item.html html/style.css html/user.forgotten.html html/user.help-search.html html/user.help.html html/user.index.html html/user.item.html html/user.register.html html/user.rego_progress.html html/user_utils.js initial_data.py schema.py
erik.forsberg
python-checkins at python.org
Fri Jul 27 16:30:02 CEST 2007
Author: erik.forsberg
Date: Fri Jul 27 16:30:01 2007
New Revision: 56578
Added:
tracker/instances/meta/detectors/
tracker/instances/meta/detectors/config.ini
tracker/instances/meta/detectors/mailonmsgchanges.py
tracker/instances/meta/detectors/messagesummary.py
tracker/instances/meta/detectors/nosyreaction.py
tracker/instances/meta/detectors/statusauditor.py
tracker/instances/meta/detectors/userauditor.py
tracker/instances/meta/extensions/
tracker/instances/meta/extensions/README.txt
tracker/instances/meta/html/
tracker/instances/meta/html/_generic.calendar.html
tracker/instances/meta/html/_generic.collision.html
tracker/instances/meta/html/_generic.help-empty.html
tracker/instances/meta/html/_generic.help-list.html
tracker/instances/meta/html/_generic.help-search.html
tracker/instances/meta/html/_generic.help-submit.html
tracker/instances/meta/html/_generic.help.html
tracker/instances/meta/html/_generic.index.html
tracker/instances/meta/html/_generic.item.html
tracker/instances/meta/html/file.index.html
tracker/instances/meta/html/file.item.html
tracker/instances/meta/html/help.html
tracker/instances/meta/html/help_controls.js
tracker/instances/meta/html/home.classlist.html
tracker/instances/meta/html/home.html
tracker/instances/meta/html/issue.index.html
tracker/instances/meta/html/issue.item.html
tracker/instances/meta/html/issue.search.html
tracker/instances/meta/html/keyword.item.html
tracker/instances/meta/html/msg.index.html
tracker/instances/meta/html/msg.item.html
tracker/instances/meta/html/page.html
tracker/instances/meta/html/query.edit.html
tracker/instances/meta/html/query.item.html
tracker/instances/meta/html/style.css
tracker/instances/meta/html/user.forgotten.html
tracker/instances/meta/html/user.help-search.html
tracker/instances/meta/html/user.help.html
tracker/instances/meta/html/user.index.html
tracker/instances/meta/html/user.item.html
tracker/instances/meta/html/user.register.html
tracker/instances/meta/html/user.rego_progress.html
tracker/instances/meta/html/user_utils.js
tracker/instances/meta/initial_data.py
tracker/instances/meta/schema.py
Modified:
tracker/instances/meta/ (props changed)
Log:
Adding meta tracker
Added: tracker/instances/meta/detectors/config.ini
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/config.ini Fri Jul 27 16:30:01 2007
@@ -0,0 +1,4 @@
+[main]
+
+msgchange_email = tracker-discuss at python.org
+
Added: tracker/instances/meta/detectors/mailonmsgchanges.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/mailonmsgchanges.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+# Modified from nosyreaction by P. Dubois to send mail to a busybody list
+# that wants to know about EVERY change.
+
+import sets
+
+from roundup import roundupdb, hyperdb
+
+def mailonmsgchange(db, cl, nodeid, oldvalues):
+ ''' busybody mail
+ '''
+ try:
+ sendto = db.config.detectors['MSGCHANGE_EMAIL'].split()
+ except KeyError:
+ return
+
+ msgIDS = determineNewMessages(cl, nodeid, oldvalues)
+ if oldvalues is None: # a create
+ note = cl.generateCreateNote(nodeid)
+ else:
+ note = cl.generateChangeNote(nodeid, oldvalues)
+
+ for msgid in msgIDS:
+ try:
+ cl.send_message(nodeid, msgid, note, sendto)
+ 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 init(db):
+ db.issue.react('create', mailonmsgchange)
+ db.issue.react('set', mailonmsgchange)
+
+# vim: set filetype=python ts=4 sw=4 et si
Added: tracker/instances/meta/detectors/messagesummary.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/messagesummary.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,20 @@
+#$Id$
+
+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
+#SHA: 38d7638272923ba22aa28342f267b611f3be392d
Added: tracker/instances/meta/detectors/nosyreaction.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/nosyreaction.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,144 @@
+#
+# 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$
+
+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
+#SHA: 6bce01e5c67b0b2250122770f4ffe2d224768ab7
Added: tracker/instances/meta/detectors/statusauditor.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/statusauditor.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,86 @@
+# 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$
+
+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
+#SHA: 2c27850eaa007e0021a9427b4c538d812849d218
Added: tracker/instances/meta/detectors/userauditor.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/detectors/userauditor.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,45 @@
+# Copyright (c) 2003 Richard Jones (richard at 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$
+
+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') and newvalues['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
+#SHA: 5663145877b5c4999449e4b1f28e88c2f721872a
Added: tracker/instances/meta/extensions/README.txt
==============================================================================
--- (empty file)
+++ tracker/instances/meta/extensions/README.txt Fri Jul 27 16:30:01 2007
@@ -0,0 +1,6 @@
+This directory is for tracker extensions:
+
+- CGI Actions
+- Templating functions
+
+See the customisation doc for more information.
Added: tracker/instances/meta/html/_generic.calendar.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.calendar.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,19 @@
+<!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>
+<!-- SHA: 3c1535fe01902cf3fa7115c982c45f7b0674b590 -->
Added: tracker/instances/meta/html/_generic.collision.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.collision.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,17 @@
+<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>
+<!-- SHA: db15fb6c88215d4baf223910a6c9cd81c63dc994 -->
Added: tracker/instances/meta/html/_generic.help-empty.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.help-empty.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Empty page (no search performed yet)</title>
+ </head>
+ <body>
+ <p i18n:translate="">Please specify your search parameters!</p>
+ </body>
+</html>
+<!-- SHA: 9a118377b03172347d95097ff75fca26a6dd3738 -->
Added: tracker/instances/meta/html/_generic.help-list.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.help-list.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,84 @@
+<!-- $Id$ vim: sw=2 ts=8 et
+--><html tal:define="vok context/is_view_ok">
+ <head>
+ <title>Search result for user helper</title>
+ <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+ <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 = parent.opener.document.${request/form/form/value};
+ field = '${request/form/property/value}';
+ //-->"></script>
+ <script src="@@file/help_controls.js" type="text/javascript"></script>
+<script type="text/javascript"><!--
+var text_field = parent.submit.document.frm_help.text_preview;
+//--></script>
+ </head>
+ <body>
+ <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+
+ <p tal:condition="not:vok" i18n:translate="">You are not
+ allowed to view this page.</p>
+
+ <tal:if condition="context/is_view_ok">
+ <tal:def define="batch request/batch;">
+ <form name=dummyform>
+ <table width="100%"
+ tal:define="template string:help-list"
+ metal:use-macro="templates/help/macros/batch_navi"
+ >
+ <tr class="navigation">
+ <th>
+ <a href="#"><< previous</a>
+ </th>
+ <th i18n:translate="">1..25 out of 50
+ </th>
+ <th>
+ <a href="#">next >></a>
+ </th>
+ </tr>
+ </table>
+
+ <form name=dummyform>
+ <table class="classhelp"
+ tal:define="
+ props python:request.form['properties'].value.split(',');
+ legend templates/help/macros/legend;
+ "><thead>
+ <tr metal:use-macro="legend">
+ <th> <b>x</b></th>
+ <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+ </tr>
+ </thead>
+ <tfoot tal:condition=true>
+ <tr metal:use-macro="legend" />
+ </tfoot>
+ <tbody>
+ <tr tal:repeat="item batch">
+ <tal:block tal:define="attr python:item[props[0]]" >
+ <td>
+ <input name="check"
+ onclick="switch_val(text_field, this);" type="checkbox"
+ tal:attributes="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>
+ </tbody>
+ </table>
+ </form>
+ </tal:def>
+ </tal:if>
+
+ <pre tal:content=request tal:condition=false />
+ <script type="text/javascript"><!--
+ parent.submit.document.frm_help.cb_listpresent.checked=true;
+ reviseList_framed(document.dummyform, text_field)
+ //--></script>
+ </body>
+</html>
+<!-- SHA: aa9e4672e16fed4877738b69079d6cb0f9ffa523 -->
Added: tracker/instances/meta/html/_generic.help-search.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.help-search.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <title>Frame for search input fields</title>
+ </head>
+ <body>
+ <p i18n:translate="">Generic template
+ <span tal:replace="request/template" i18n:name="template">help-search</span>
+ or version for class
+ <span tal:replace="request/classname" i18n:name="classname">user</span>
+ is not yet implemented</p>
+ </body>
+</html>
+
+<!-- SHA: b95a7bda7189c0747d2f4112d1d3d02808fd1753 -->
Added: tracker/instances/meta/html/_generic.help-submit.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.help-submit.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,74 @@
+<html>
+ <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>Generic submit page for framed helper windows</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 = parent.opener.document.${request/form/form/value};
+callingform=form
+field = '${request/form/property/value}';
+var listform = null
+function listPresent() {
+ return document.frm_help.cb_listpresent.checked
+}
+function getListForm() {
+ if (listPresent()) {
+ return parent.list.document.forms.dummyform
+ } else {
+ return null
+ }
+}
+
+
+function checkListForm() {
+ // global listform
+ if (listform != null)
+ if (parent.list.document.dummyform) {
+ listform = parent.list.document.dummyform
+ alert(listform)
+ }
+
+ var bol= listform != null
+ alert('checkListForm: bol='+bol)
+ return bol
+}
+//-->">
+ </script>
+ <script src="@@file/help_controls.js" type="text/javascript"></script>
+ </tal:block>
+ </head>
+ <body class="body" onload="parent.focus();" id="submit">
+ <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+ <form name="frm_help"
+ tal:define="batch request/batch;
+ props python:request.form['properties'].value.split(',')"
+ class="help-submit"
+ id="classhelp-controls">
+ <div style="width:100%;text-align:left;margin-bottom:0.2em">
+ <input type="text" name="text_preview" size="24" class="preview"
+ onchange="f=getListForm();if(f){ reviseList_framed(f, this)};"
+ />
+ </div>
+ <input type=checkbox name="cb_listpresent" readonly="readonly" style="display:none">
+ <input type="button" id="btn_cancel"
+ value=" Cancel " onclick="parent.close();return false;"
+ i18n:attributes="value" />
+ <input type="reset" id="btn_reset"
+ onclick="text_field.value=original_field;f=getListForm();if (f) {reviseList_framed(f, this)};return false"
+ />
+ <input type="submit" id="btn_apply" class="apply"
+ value=" Apply " onclick="callingform[field].value=text_field.value; parent.close();"
+ i18n:attributes="value" />
+ </form>
+ <script type="text/javascript"><!--
+var text_field = document.frm_help.text_preview;
+original_field=form[field].value;
+text_field.value=original_field;
+//--></script>
+ </body>
+</html>
+<!-- SHA: 1de39ac0d15dc59c64187b6c691d58ba20931372 -->
Added: tracker/instances/meta/html/_generic.help.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.help.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,99 @@
+<!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="" ><< previous</a>
+
+ </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 >></a>
+
+ </th>
+ </tr>
+ </table>
+
+ <table class="classhelp">
+ <tr>
+ <th> <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> <b>x</b></th>
+ <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+ </tr>
+ </table>
+
+ </form>
+ </body>
+</html>
+<!-- SHA: 10ab79422912c5dab17ede05ca8293133eb76951 -->
Added: tracker/instances/meta/html/_generic.index.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.index.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,71 @@
+<!-- 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()
+ or request.user.hasRole('Anonymous'))"
+ tal:omit-tag="python:1" i18n:translate=""
+>You are not allowed to view this page.</span>
+
+<span tal:condition="python:not (context.is_view_ok() or context.is_edit_ok())
+ and request.user.hasRole('Anonymous')"
+ tal:omit-tag="python:1" i18n:translate=""
+>Please login with your username and password.</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"> </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"
+ > </td>
+ </tr>
+ </tal:block>
+</table>
+
+</td>
+
+</tal:block>
+<!-- SHA: 822d4da93deeec71c56224d26ad970a2b141cdd1 -->
Added: tracker/instances/meta/html/_generic.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/_generic.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,54 @@
+<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="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</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> </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>
+<!-- SHA: 62cac36f8fe4e3705913e32fc0e91f803e8b0167 -->
Added: tracker/instances/meta/html/file.index.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/file.index.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,32 @@
+<!-- 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>
+<!-- SHA: f3a343f0682801cb8c47bd793d2d436fc7258d73 -->
Added: tracker/instances/meta/html/file.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/file.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,54 @@
+<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="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</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>
+
+ <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>
+<!-- SHA: 9b0955c553e1df1d791dfc24530aeb945896cf46 -->
Added: tracker/instances/meta/html/help.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/help.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,39 @@
+<!--
+Macros for framed help windows
+-->
+
+<!-- legend for helper search results -->
+<thead>
+<tr metal:define-macro="legend">
+ <th><b>x</b></th>
+ <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+</tr>
+</thead>
+
+<table width="100%"
+ metal:define-macro="batch_navi"
+ tal:define="prev batch/previous;
+ next batch/next;
+ "
+ tal:condition="python:prev or next">
+ <tr class="navigation">
+ <th width="30%">
+ <a tal:condition="prev"
+ tal:attributes="href python:request.indexargs_url(request.classname, {'@template':'help-list', 'property': request.form['property'].value, 'properties': request.form['properties'].value, 'form': request.form['form'].value, '@startwith':prev.first, '@pagesize':prev.size})"
+ i18n:translate="" ><< previous</a>
+
+ </th>
+ <th i18n:translate="" width="40%"><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 width="30%">
+ <a tal:condition="next"
+ tal:attributes="href python:request.indexargs_url(request.classname, {'@template':'help-list', 'property': request.form['property'].value, 'properties': request.form['properties'].value, 'form': request.form['form'].value, '@startwith':next.first, '@pagesize':next.size})"
+ i18n:translate="" >next >></a>
+
+ </th>
+ </tr>
+ </table>
+<!-- SHA: 5bb5e9db92d4dea06f6bd0224f34dce86020d4c2 -->
Added: tracker/instances/meta/html/help_controls.js
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/help_controls.js Fri Jul 27 16:30:01 2007
@@ -0,0 +1,324 @@
+// 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;
+}
+
+/**
+ * update the field in the opening window;
+ * the text_field variable must be set in the calling page
+ */
+function updateOpener() {
+ // write back to opener window
+ if (document.frm_help.check==undefined) { return; }
+ form[field].value = text_field.value;
+}
+
+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_framed(form, textfield) {
+ // update the checkboxes based on the preview field
+ // alert('reviseList_framed')
+ // alert(form)
+ if (form.check==undefined)
+ return;
+ // alert(textfield)
+ var to_check;
+ var list = textfield.value.split(",");
+ if (form.check.length==undefined) {
+ check = form.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 < form.check.length; box++) {
+ check = form.check[box];
+ to_check = false;
+ for (val in list) {
+ if (check.value==trim(list[val])) {
+ to_check = true;
+ break;
+ }
+ }
+ check.checked = to_check;
+ }
+ }
+}
+
+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();}
+ }
+}
+
+function checkRequiredFields(fields)
+{
+ var bonk='';
+ var res='';
+ var argv = checkRequiredFields.arguments;
+ var argc = argv.length;
+ var input = '';
+ var val='';
+
+ for (var i=0; i < argc; i++) {
+ fi = argv[i];
+ input = document.getElementById(fi);
+ if (input) {
+ val = input.value
+ if (val == '' || val == '-1' || val == -1) {
+ if (res == '') {
+ res = fi;
+ bonk = input;
+ } else {
+ res += ', '+fi;
+ }
+ }
+ } else {
+ alert('Field with id='+fi+' not found!')
+ }
+ }
+ if (res == '') {
+ return submit_once();
+ } else {
+ alert('Missing value here ('+res+')!');
+ if (window.event && window.event.returnvalue) {
+ event.returnValue = 0; // work-around for IE
+ }
+ bonk.focus();
+ return false;
+ }
+}
+
+/**
+ * seeks the given value (2nd argument)
+ * in the value of the given input element (1st argument),
+ * which is considered a list of values, separated by commas
+ */
+function has_value(input, val)
+{
+ var actval = input.value
+ var arr = feld.value.split(',');
+ var max = arr.length;
+ for (i=0;i<max;i++) {
+ if (trim(arr[i]) == val) {
+ return true
+ }
+ }
+ return false
+}
+
+/**
+ * Switch Value:
+ * change the value of the given input field (might be of type text or hidden),
+ * adding or removing the value of the given checkbox field (might be a radio
+ * button as well)
+ *
+ * This function doesn't care whether or not the checkboxes of all values of
+ * interest are present; but of course it doesn't have total control of the
+ * text field.
+ */
+function switch_val(text, check)
+{
+ var switched_val = check.value
+ var arr = text.value.split(',')
+ var max = arr.length
+ if (check.checked) {
+ for (i=0; i<max; i++) {
+ if (trim(arr[i]) == switched_val) {
+ return
+ }
+ }
+ if (text.value)
+ text.value = text.value+','+switched_val
+ else
+ text.value = switched_val
+ } else {
+ var neu = ''
+ var changed = false
+ for (i=0; i<max; i++) {
+ if (trim(arr[i]) == switched_val) {
+ changed=true
+ } else {
+ neu = neu+','+trim(arr[i])
+ }
+ }
+ if (changed) {
+ text.value = neu.substr(1)
+ }
+ }
+}
+
+/**
+ * append the given value (2nd argument) to an input field
+ * (1st argument) which contains comma-separated values;
+ * see --> remove_val()
+ *
+ * This will work nicely even for batched lists
+ */
+function append_val(name, val)
+{
+ var feld = document.itemSynopsis[name];
+ var actval = feld.value;
+ if (actval == '') {
+ feld.value = val
+ } else {
+ var arr = feld.value.split(',');
+ var max = arr.length;
+ for (i=0;i<max;i++) {
+ if (trim(arr[i]) == val) {
+ return
+ }
+ }
+ feld.value = actval+','+val
+ }
+}
+
+/**
+ * remove the given value (2nd argument) from the comma-separated values
+ * of the given input element (1st argument); see --> append_val()
+ */
+function remove_val(name, val)
+{
+ var feld = document.itemSynopsis[name];
+ var actval = feld.value;
+ var changed=false;
+ if (actval == '') {
+ return
+ } else {
+ var arr = feld.value.split(',');
+ var max = arr.length;
+ var neu = ''
+ for (i=0;i<max;i++) {
+ if (trim(arr[i]) == val) {
+ changed=true
+ } else {
+ neu = neu+','+trim(arr[i])
+ }
+ }
+ if (changed) {
+ feld.value = neu.substr(1)
+ }
+ }
+}
+
+/**
+ * give the focus to the element given by id
+ */
+function focus2id(name)
+{
+ document.getElementById(name).focus();
+}
Added: tracker/instances/meta/html/home.classlist.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/home.classlist.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,26 @@
+<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>
+<!-- SHA: e82456270ae1048cefdead99afda95578fff7b74 -->
Added: tracker/instances/meta/html/home.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/home.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,11 @@
+<!--
+ 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']})" />
+<!-- SHA: c87a4e18d59a527331f1d367c0c6cc67ee123e63 -->
Added: tracker/instances/meta/html/issue.index.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/issue.index.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,166 @@
+<!-- $Id$ -->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" >
+ <span tal:omit-tag="true" i18n:translate="" >List of issues</span>
+ <span tal:condition="request/dispname"
+ tal:replace="python:' - %s '%request.dispname"
+ /> - <span tal:replace="config/TRACKER_NAME" />
+</title>
+<span metal:fill-slot="body_title" tal:omit-tag="true">
+ <span tal:omit-tag="true" i18n:translate="" >List of issues</span>
+ <span tal:condition="request/dispname"
+ tal:replace="python:' - %s' % request.dispname" />
+</span>
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</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 To</th>
+ </tr>
+ <tal:block tal:repeat="i batch" condition=true>
+ <tr tal:define="group python:[r[1] for r in request.group]"
+ tal:condition="python:group and batch.propchanged(*group)">
+ <th tal:attributes="colspan python:len(request.columns)" class="group">
+ <tal:block tal:repeat="g group">
+ <tal:block tal:content="python:str(i[g]) or '(no %s set)'%g"/>
+ </tal:block>
+ </th>
+ </tr>
+
+ <tr>
+ <td tal:condition="request/show/priority"
+ tal:content="python:i.priority.plain() or default"> </td>
+ <td tal:condition="request/show/id" tal:content="i/id"> </td>
+ <td class="date" tal:condition="request/show/creation"
+ tal:content="i/creation/reldate"> </td>
+ <td class="date" tal:condition="request/show/activity"
+ tal:content="i/activity/reldate"> </td>
+ <td class="date" tal:condition="request/show/actor"
+ tal:content="python:i.actor.plain() or default"> </td>
+ <td tal:condition="request/show/topic"
+ tal:content="python:i.topic.plain() or default"> </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"> </td>
+ <td tal:condition="request/show/creator"
+ tal:content="python:i.creator.plain() or default"> </td>
+ <td tal:condition="request/show/assignedto"
+ tal:content="python:i.assignedto.plain() or default"> </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=""><< previous</a>
+
+ </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 >></a>
+
+ </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" tal:define="n_sort python:2">
+ <tal:block tal:repeat="n python:range(n_sort)" tal:condition="batch">
+ <tr tal:define="key python:len(request.sort)>n and request.sort[n]">
+ <th>
+ <tal:block tal:condition="not:n" i18n:translate="">Sort on:</tal:block>
+ </th>
+ <td>
+ <select tal:attributes="name python:'@sort%d'%n">
+ <option value="" i18n:translate="">- nothing -</option>
+ <option tal:repeat="col context/properties"
+ tal:attributes="value col/_name;
+ selected python:key and col._name == key[1]"
+ tal:content="col/_name"
+ i18n:translate="">column</option>
+ </select>
+ </td>
+ <th i18n:translate="">Descending:</th>
+ <td><input type="checkbox" tal:attributes="name python:'@sortdir%d'%n;
+ checked python:key and key[0] == '-'">
+ </td>
+ </tr>
+ </tal:block>
+ <tal:block tal:repeat="n python:range(n_sort)" tal:condition="batch">
+ <tr tal:define="key python:len(request.group)>n and request.group[n]">
+ <th>
+ <tal:block tal:condition="not:n" i18n:translate="">Group on:</tal:block>
+ </th>
+ <td>
+ <select tal:attributes="name python:'@group%d'%n">
+ <option value="" i18n:translate="">- nothing -</option>
+ <option tal:repeat="col context/properties"
+ tal:attributes="value col/_name;
+ selected python:key and col._name == key[1]"
+ tal:content="col/_name"
+ i18n:translate="">column</option>
+ </select>
+ </td>
+ <th i18n:translate="">Descending:</th>
+ <td><input type="checkbox" tal:attributes="name python:'@groupdir%d'%n;
+ checked python:key and key[0] == '-'">
+ </td>
+ </tr>
+ </tal:block>
+ <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><tal:comment condition=false> vim: sw=1 ts=8 et si
+</tal:comment>
+<!-- SHA: 365c00d84cea66ca020ffac4ba7ce67ebc299ed4 -->
Added: tracker/instances/meta/html/issue.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/issue.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,198 @@
+<!-- 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="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</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">
+ <br><span i18n:translate="">View:</span>
+ <a tal:repeat="sup context/superseder"
+ tal:content="python:sup['id'] + ', '*(not repeat['sup'].end)"
+ tal:attributes="href string:issue${sup/id}"></a>
+ </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>
+
+ <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: </td>
+ <th class="required">highlighted</th>
+ <td> 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 at 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 at 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>
+<!-- SHA: f965b25e85afd7e6a6c51a960977d9254cfb5963 -->
Added: tracker/instances/meta/html/issue.search.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/issue.search.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,232 @@
+<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 and request.sort[0] or nothing;
+ sort_desc python:sort_on and sort_on[0] == '-';
+ sort_on python:(sort_on and sort_on[1]) or 'activity';
+ group_on python:request.group and request.group[0] or nothing;
+ group_desc python:group_on and group_on[0] == '-';
+ group_on python:(group_on and group_on[1]) or 'priority';
+
+ search_input templates/page/macros/search_input;
+ search_date templates/page/macros/search_date;
+ 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_select_translated templates/page/macros/search_select_translated;
+ search_multiselect templates/page/macros/search_multiselect;">
+
+<tr>
+ <th class="header"> </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> </td>
+ <td> </td>
+ <td> </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> </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> </td>
+</tr>
+
+<tr tal:define="name string:creation">
+ <th i18n:translate="">Creation Date:</th>
+ <td metal:use-macro="search_date"></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_date"></td>
+ <td metal:use-macro="column_input"></td>
+ <td metal:use-macro="sort_input"></td>
+ <td> </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> </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_translated">
+ <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_translated">
+ <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> </td>
+ <td> </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 sort_desc">
+</td>
+</tr>
+
+<tr>
+<th i18n:translate="">Group Descending:</th>
+<td><input type="checkbox" name="@groupdir"
+ tal:attributes="checked group_desc">
+</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>
+
+ <input type="hidden" name="@action" value="search">
+ </td>
+ <td><input type="submit" value="Search" i18n:attributes="value"></td>
+</tr>
+
+<tr><td> </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>
+<!-- SHA: 2890cebb4a90343489c03f28914840c6048c43bb -->
Added: tracker/instances/meta/html/keyword.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/keyword.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,56 @@
+<!-- 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>
+
+ <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>
+<!-- SHA: 69377db97f924d320fdd672dd8363256ef6f2fbb -->
Added: tracker/instances/meta/html/msg.index.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/msg.index.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,26 @@
+<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>
+<!-- SHA: 474d801a8005811ea3f2600ff719af95c6b798ce -->
Added: tracker/instances/meta/html/msg.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/msg.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,84 @@
+<!-- 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="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</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>
+<!-- SHA: e4a1c71477429a3750a6013098faa17f75d38f5d -->
Added: tracker/instances/meta/html/page.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/page.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,346 @@
+<!-- vim:sw=2 sts=2
+--><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>
+<metal:x define-slot="more-javascript" />
+
+</head>
+<body class="body">
+
+<table class="body"
+ tal:define="
+kw_edit python:request.user.hasPermission('Edit', 'keyword');
+kw_create python:request.user.hasPermission('Create', 'keyword');
+kw_edit_link python:kw_edit and db.keyword.list();
+columns string:id,activity,title,creator,status;
+columns_showall string:id,activity,title,creator,assignedto,status;
+status_notresolved string:-1,1,2,3,4,5,6,7;
+"
+>
+
+<tr>
+ <td class="page-header-left"> </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"
+ tal:attributes="value columns_showall"
+ 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" tal:attributes="value request/search_text" />
+ </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 href="#" 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="#"
+ tal:attributes="href python:request.indexargs_url('issue', {
+ '@sort': '-activity',
+ '@group': 'priority',
+ '@filter': 'status,assignedto',
+ '@columns': columns,
+ '@search_text': '',
+ 'status': status_notresolved,
+ 'assignedto': '-1',
+ '@dispname': i18n.gettext('Show Unassigned'),
+ })"
+ i18n:translate="">Show Unassigned</a><br>
+ <a href="#"
+ tal:attributes="href python:request.indexargs_url('issue', {
+ '@sort': '-activity',
+ '@group': 'priority',
+ '@filter': 'status',
+ '@columns': columns_showall,
+ '@search_text': '',
+ 'status': status_notresolved,
+ '@dispname': i18n.gettext('Show All'),
+ })"
+ 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:kw_edit or kw_create">
+ <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="kw_edit_link">
+ <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 your 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 href="#"
+ tal:attributes="href python:request.indexargs_url('issue', {
+ '@sort': '-activity',
+ '@group': 'priority',
+ '@filter': 'status,assignedto',
+ '@columns': 'id,activity,title,creator,status',
+ '@search_text': '',
+ 'status': status_notresolved,
+ 'assignedto': request.user.id,
+ '@dispname': i18n.gettext('Your Issues'),
+ })"
+ i18n:translate="">Your Issues</a><br>
+ <a href="#" tal:attributes="href string:user${request/user/id}"
+ i18n:translate="">Your Details</a><br>
+ <a href="#" 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.
+-->
+
+<!-- creates a th and a label: -->
+<th metal:define-macro="th_label"
+ tal:define="required required | python:[]"
+ tal:attributes="class python:(name in required) and 'required' or nothing">
+ <label tal:attributes="for name" tal:content="label" i18n:translate="">text</label>
+ <metal:x define-slot="behind_the_label" />
+</th>
+
+<td metal:define-macro="search_input">
+ <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+ name name;
+ id name">
+</td>
+
+<td metal:define-macro="search_date">
+ <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+ name name;
+ id name">
+ <a class="classhelp"
+ tal:attributes="href python:'''javascript:help_window('issue?@template=calendar&property=%s&form=itemSynopsis', 300, 200)'''%name">(cal)</a>
+</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;
+ id name">
+ <span tal:replace="structure python:db.issue.classhelp(columns,
+ property=name)" />
+</td>
+
+<td metal:define-macro="search_select">
+ <select tal:attributes="name name; id name"
+ tal:define="value python:request.form.getvalue(name)">
+ <option value="" i18n:translate="">don't care</option>
+ <metal:slot define-slot="extra_options" />
+ <option value="" i18n:translate="" disabled="disabled">------------</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>
+
+<!-- like search_select, but translates the further values.
+Could extend it (METAL 1.1 attribute "extend-macro")
+-->
+<td metal:define-macro="search_select_translated">
+ <select tal:attributes="name name; id name"
+ tal:define="value python:request.form.getvalue(name)">
+ <option value="" i18n:translate="">don't care</option>
+ <metal:slot define-slot="extra_options" />
+ <option value="" i18n:translate="" disabled="disabled">------------</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]"
+ i18n:translate=""></option>
+ </select>
+</td>
+
+<!-- currently, there is no convenient API to get a list of all roles -->
+<td metal:define-macro="search_select_roles"
+ tal:define="onchange onchange | nothing">
+ <select name=roles id=roles tal:attributes="onchange onchange">
+ <option value="" i18n:translate="">don't care</option>
+ <option value="" i18n:translate="" disabled="disabled">------------</option>
+ <option value="User">User</option>
+ <option value="Admin">Admin</option>
+ <option value="Anonymous">Anonymous</option>
+ </select>
+</td>
+
+<td metal:define-macro="search_multiselect">
+ <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+ name name;
+ id 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>
+
+<!--
+The following macros are intended for user editing.
+
+The invoking context must define a "name" variable which names the
+property being searched; the "edit_ok" variable tells whether the
+current user is allowed to edit.
+
+See user.item.html in the classic template for examples.
+-->
+<script metal:define-macro="user_utils" type="text/javascript" src="@@file/user_utils.js"></script>
+
+<!-- src: value will be re-used for other input fields -->
+<input metal:define-macro="user_src_input"
+ type="text" tal:attributes="onblur python:edit_ok and 'split_name(this)';
+ id name; name name; value value; readonly not:edit_ok"
+ value="heinz.kunz">
+<!-- normal: no re-using -->
+<input metal:define-macro="user_normal_input" type="text"
+ tal:attributes="id name; name name; value value; readonly not:edit_ok"
+ value="heinz">
+<!-- password: type; no initial value -->
+ <input metal:define-macro="user_pw_input" type="password"
+ tal:attributes="id name; name name; readonly not:edit_ok" value="">
+ <input metal:define-macro="user_confirm_input" type="password"
+ tal:attributes="id name; name string:@confirm@$name; readonly not:edit_ok" value="">
+
+<!-- SHA: 9096ab1b7036f66d0cb803952f017ac5f68aa1e2 -->
Added: tracker/instances/meta/html/query.edit.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/query.edit.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,110 @@
+<!-- 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> </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 at 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 at 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>
+<!-- SHA: 25fd54495eba6eae4c158db72f56fbe839d4c87d -->
Added: tracker/instances/meta/html/query.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/query.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,4 @@
+<!-- query.item -->
+<span tal:replace="structure context/renderQueryForm" />
+
+<!-- SHA: 027820442d9341987bbb8b732cd6233aa1e56bed -->
Added: tracker/instances/meta/html/style.css
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/style.css Fri Jul 27 16:30:01 2007
@@ -0,0 +1,444 @@
+/* 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 */
+ at 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: 0;
+ 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;
+ vertical-align: top;
+ padding: 2px 5px 2px 5px;
+ max-width: 50em;
+}
+
+table.messages td pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+
+table.messages td:first-child {
+ border-left: 1px solid #afafaf;
+ border-right: 1px solid #afafaf;
+}
+
+/* style for file displays */
+table.files {
+ border-spacing: 0;
+ border-collapse: separate;
+ width: 100%;
+}
+
+table.files th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
+ font-weight: bold;
+ background-color: white;
+ color: #707040;
+}
+
+table.files th {
+ border-bottom: 1px solid #afafaf;
+ font-weight: bold;
+ text-align: left;
+}
+
+table.files td {
+ font-family: monospace;
+ empty-cells: show;
+}
+
+/* style for history displays */
+table.history {
+ border-spacing: 0;
+ border-collapse: separate;
+ width: 100%;
+}
+
+table.history th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
+ font-weight: bold;
+ background-color: white;
+ color: #707040;
+ font-size: 100%;
+}
+
+table.history th {
+ border-bottom: 1px solid #afafaf;
+ font-weight: bold;
+ text-align: left;
+ font-size: 90%;
+}
+
+table.history td {
+ font-size: 90%;
+ vertical-align: top;
+ empty-cells: show;
+}
+
+
+/* style for class list */
+table.classlist {
+ border-spacing: 0;
+ border-collapse: separate;
+ width: 100%;
+}
+
+table.classlist th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
+ font-weight: bold;
+ background-color: white;
+ color: #707040;
+}
+
+table.classlist th {
+ font-weight: bold;
+ text-align: left;
+}
+
+
+/* style for class help display */
+table.classhelp { /* the table-layout: fixed; */
+ table-layout: fixed; /* compromises quality for speed */
+ overflow: hidden;
+ font-size: .9em;
+ padding-bottom: 3em;
+}
+
+table.classhelp th {
+ font-weight: normal;
+ text-align: left;
+ color: #444;
+ background-color: #efefef;
+ border-bottom: 1px solid #afafaf;
+ border-top: 1px solid #afafaf;
+ text-transform: uppercase;
+ vertical-align: middle;
+ line-height:1.5em;
+}
+
+table.classhelp td {
+ vertical-align: middle;
+ padding-right: .2em;
+ border-bottom: 1px solid #efefef;
+ text-align: left;
+ empty-cells: show;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+table.classhelp tr:hover {
+ background-color: #eee;
+}
+
+label.classhelp-label {
+ cursor: pointer;
+}
+
+#classhelp-controls {
+ position: fixed;
+ display: block;
+ top: auto;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: .5em;
+ border-top: 2px solid #444;
+ background-color: #eee;
+}
+
+#classhelp-controls input.apply {
+ width: 7em;
+ font-weight: bold;
+ margin-right: 2em;
+ margin-left: 2em;
+}
+
+#classhelp-controls input.preview {
+ margin-right: 3em;
+ margin-left: 1em;
+}
+
+/* style for "other" displays */
+table.otherinfo {
+ border-spacing: 0;
+ border-collapse: separate;
+ width: 100%;
+}
+
+table.otherinfo th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
+ font-weight: bold;
+ background-color: white;
+ color: #707040;
+}
+
+table.otherinfo th {
+ border-bottom: 1px solid #afafaf;
+ font-weight: bold;
+ text-align: left;
+}
+input[type="text"]:focus,
+input[type="checkbox"]:focus,
+input[type="radio"]:focus,
+input[type="password"]:focus,
+textarea:focus, select:focus {
+ background-color: #ffffc0;
+}
+
+/* vim: sts=2 sw=2 et
+*/
+/* SHA: 7243da9b4e481a0b95a5367b45baaaa45fab8998 */
Added: tracker/instances/meta/html/user.forgotten.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.forgotten.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,44 @@
+<!-- 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="">Password reset request - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Password reset request</span>
+<td class="content" metal:fill-slot="content">
+
+<p i18n:translate="">You have two options if you have forgotten your password.
+If you know the email address you registered with, enter it below.</p>
+
+<form method="POST" onSubmit="return submit_once()"
+ tal:attributes="action context/designator">
+ <table class="form">
+ <tr>
+ <th i18n:translate="">Email Address:</th>
+ <td><input name="address"></td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>
+ <input type="hidden" name="@action" value="passrst">
+ <input type="hidden" name="@template" value="forgotten">
+ <input type="submit" value="Request password reset"
+ i18n:attributes="value">
+ </td>
+ </tr>
+</table>
+
+<p i18n:translate="">Or, if you know your username, then enter it below.</p>
+
+<table class="form">
+ <tr><th i18n:translate="">Username:</th> <td><input name="username"></td> </tr>
+ <tr><td></td><td><input type="submit" value="Request password reset"
+ i18n:attributes="value"></td></tr>
+</table>
+</form>
+
+<p i18n:translate="">A confirmation email will be sent to you -
+please follow the instructions within it to complete the reset process.</p>
+</td>
+
+</tal:block>
+<!-- SHA: 6fdb58c55fd854904ae98906d5935549a221fabf -->
Added: tracker/instances/meta/html/user.help-search.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.help-search.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,86 @@
+<html
+ tal:define="form request/form/form/value;
+ field request/form/property/value"
+ >
+ <head>
+ <title>Search input for user helper</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 = parent.opener.document.${form};
+ field = '${field}';
+ //-->">
+ </script>
+ <script type="text/javascript" src="@@file/help_controls.js"></script>
+ <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+ </head>
+ <body onload="parent.submit.url='...'"
+ tal:define="
+qs request/env/QUERY_STRING;
+qs python:'&'.join([a for a in qs.split('&') if not a.startswith('@template=')])"
+>
+ <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+ <form method="GET" name="itemSynopsis"
+ target="list"
+ tal:attributes="action request/classname"
+ tal:define="
+ property request/form/property/value;
+ cols python:request.columns or 'id username address realname roles'.split();
+ sort_on request/sort | nothing;
+ sort_desc python:sort_on and request.sort[0][0] == '-';
+ sort_on python:sort_on and request.sort[0][1] or 'lastname';
+
+ search_input templates/page/macros/search_input;
+ search_select templates/page/macros/search_select;
+ search_select_roles templates/page/macros/search_select_roles;
+ required python:[];
+ th_label templates/page/macros/th_label;
+ ">
+ <input type="hidden" name="@template" value="help-list">
+ <input type="hidden" name="property" value="" tal:attributes="value property">
+ <input type="hidden" name="form" value="" tal:attributes="value request/form/form/value">
+ <table>
+<tr tal:define="name string:username; label string:Username:">
+ <th metal:use-macro="th_label">Name</th>
+ <td metal:use-macro="search_input"><input type=text></td>
+</tr>
+
+<tr tal:define="name string:phone; label string:Phone number">
+ <th metal:use-macro="th_label">Phone</th>
+ <td metal:use-macro="search_input"><input type=text></td>
+</tr>
+
+<tr tal:define="name string:roles;
+ onchange string:this.form.submit();
+ label string:Rollen:"
+ >
+ <th metal:use-macro="th_label">role</th>
+ <td metal:use-macro="search_select_roles">
+ <select>
+ <option value="">jokester</option>
+ </select>
+ </td>
+</tr>
+
+<tr>
+ <td> </td>
+ <td>
+ <input type="hidden" name="@action" value="search">
+ <input type="submit" value="Search" i18n:attributes="value">
+ <input type="reset">
+ <input type="hidden" value="username,realname,phone,organisation,roles" name="properties">
+ <input type="text" name="@pagesize" id="sp-pagesize" value="25" size="2">
+ <label for="sp-pagesize" i18n:translate="">Pagesize</label>
+ </td>
+</tr>
+
+ </table>
+
+</form>
+<pre tal:content="request" tal:condition=false />
+<script type="text/javascript"><!--
+ focus2id('username');
+//--></script>
+ </body>
+</html>
+<!-- SHA: f2ee75bdf31f6188ea11dc8d4d7d3e3c7683bc44 -->
Added: tracker/instances/meta/html/user.help.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.help.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,50 @@
+<!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;
+qs request/env/QUERY_STRING;
+qs python:'&'.join([a for a in qs.split('&') if not a.startswith('@template=')]);
+form request/form/form/value;
+field 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><tal:x i18n:translate=""><tal:x i18n:name="property"
+ tal:content="property" i18n:translate="" /> help - <span i18n:name="tracker"
+ tal:replace="config/TRACKER_NAME" /></tal:x></title>
+ <script language="Javascript" type="text/javascript"
+ tal:condition=false
+ tal:content="structure string:<!--
+ // this is the name of the field in the original form that we're working on
+ form = window.opener.document.${form};
+ field = '${field}';
+ //-->">
+ </script>
+ <script src="@@file/help_controls.js"
+ tal:condition=false type="text/javascript"><!--
+ //--></script>
+ </tal:block>
+ </head>
+<frameset rows="123,*,62">
+ <frame src="#" tal:attributes="src string:?@template=help-search&${qs}" name="search">
+ <!-- for search results: help-list -->
+ <frame
+ tal:attributes="src string:?@template=help-empty&${qs}"
+ name="list">
+ <frame
+ tal:attributes="src string:?@template=help-submit&${qs}"
+ name="submit">
+ <!-- -->
+</frameset>
+<noframes>
+ <body>
+<p i18n:translate="">
+Your browser is not capable of using frames; you should be redirected immediately,
+or visit <a href="#" tal:attributes="href string:?${qs}&template=help-noframes"
+i18n:name="link">this link</a>.
+</p>
+</body>
+</noframes>
+
+</html>
+<!-- SHA: d59dff4cb24ac0eee209b10c121299a203170227 -->
Added: tracker/instances/meta/html/user.index.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.index.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,46 @@
+<!-- dollarId: user.index,v 1.3 2002/07/09 05:29:51 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title" i18n:translate="">User listing - <span
+ i18n:name="tracker" tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">User listing</span>
+<td class="content" metal:fill-slot="content">
+
+<span tal:condition="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))"
+ i18n:translate="">You are not allowed to view this page.</span>
+
+<span tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')"
+ i18n:translate="">Please login with your username and password.</span>
+
+<table width="100%" tal:condition="context/is_view_ok" class="list">
+<tr>
+ <th i18n:translate="">Username</th>
+ <th i18n:translate="">Real name</th>
+ <th i18n:translate="">Organisation</th>
+ <th i18n:translate="">Email address</th>
+ <th i18n:translate="">Phone number</th>
+ <th tal:condition="context/is_edit_ok" i18n:translate="">Retire</th>
+</tr>
+<tal:block repeat="user context/list">
+<tr tal:attributes="class python:['normal', 'alt'][repeat['user'].index%6/3]">
+ <td>
+ <a tal:attributes="href string:user${user/id}"
+ tal:content="user/username">username</a>
+ </td>
+ <td tal:content="python:user.realname.plain() or default"> </td>
+ <td tal:content="python:user.organisation.plain() or default"> </td>
+ <td tal:content="python:user.address.email() or default"> </td>
+ <td tal:content="python:user.phone.plain() or default"> </td>
+ <td tal:condition="context/is_edit_ok">
+ <a tal:attributes="href string:user${user/id}?@action=retire&@template=index"
+ i18n:translate="">retire</a>
+ </td>
+</tr>
+</tal:block>
+</table>
+</td>
+
+</tal:block>
+<!-- SHA: 1485cb25086920d22caccce366e25c6f4e061b25 -->
Added: tracker/instances/meta/html/user.item.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.item.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,169 @@
+<!-- dollarId: user.item,v 1.7 2002/08/16 04:29:04 richard Exp dollar-->
+<tal:doc metal:use-macro="templates/page/macros/icing"
+define="edit_ok context/is_edit_ok"
+>
+<title metal:fill-slot="head_title">
+<tal:if condition="context/id" i18n:translate=""
+ >User <span tal:replace="context/id" i18n:name="id"
+ />: <span tal:replace="context/username" i18n:name="title"
+ /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:if>
+<tal:if condition="not:context/id" i18n:translate=""
+ >New User - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
+/></tal:if>
+</title>
+<metal:slot fill-slot="more-javascript">
+<script metal:use-macro="templates/page/macros/user_utils"></script>
+<script type="text/javascript" src="@@file/help_controls.js"></script>
+</metal:slot>
+<tal:block metal:fill-slot="body_title"
+ define="edit_ok context/is_edit_ok">
+ <span tal:condition="python: not (context.id or edit_ok)"
+ tal:omit-tag="python:1" i18n:translate="">New User</span>
+ <span tal:condition="python: not context.id and edit_ok"
+ tal:omit-tag="python:1" i18n:translate="">New User Editing</span>
+ <span tal:condition="python: context.id and not edit_ok"
+ tal:omit-tag="python:1" i18n:translate="">User<tal:x
+ replace="context/id" i18n:name="id" /></span>
+ <span tal:condition="python: context.id and edit_ok"
+ tal:omit-tag="python:1" i18n:translate="">User<tal:x
+ replace="context/id" i18n:name="id" /> Editing</span>
+</tal:block>
+
+<td class="content" metal:fill-slot="content">
+
+<p tal:condition="python:not (context.is_view_ok()
+ or request.user.hasRole('Anonymous'))" i18n:translate="">
+ You are not allowed to view this page.</p>
+
+<p tal:condition="python:not context.is_view_ok()
+ and request.user.hasRole('Anonymous')" i18n:translate="">
+ Please login with your username and password.</p>
+
+<div tal:condition="context/is_view_ok">
+
+<form method="POST"
+ tal:define="required python:'username address'.split()"
+ enctype="multipart/form-data"
+ tal:attributes="action context/designator;
+ onSubmit python:'return checkRequiredFields(\'%s\')'%'\', \''.join(required);
+ ">
+<table class="form" tal:define="
+ th_label templates/page/macros/th_label;
+ src_input templates/page/macros/user_src_input;
+ normal_input templates/page/macros/user_normal_input;
+ pw_input templates/page/macros/user_pw_input;
+ confirm_input templates/page/macros/user_confirm_input;
+ edit_ok context/is_edit_ok;
+ ">
+ <tr tal:define="name string:realname; label string:Name; value context/realname; edit_ok edit_ok">
+ <th metal:use-macro="th_label">Name</th>
+ <td><input name="realname" metal:use-macro="src_input"></td>
+ </tr>
+ <tr tal:define="name string:username; label string:Login Name; value context/username">
+ <th metal:use-macro="th_label">Login Name</th>
+ <td><input metal:use-macro="src_input"></td>
+ </tr>
+ <tal:if condition="edit_ok">
+ <tr tal:define="name string:password; label string:Login Password">
+ <th metal:use-macro="th_label">Login Password</th>
+ <td><input metal:use-macro="pw_input" type="password"></td>
+ </tr>
+ <tr tal:define="name string:password; label string:Confirm Password">
+ <th metal:use-macro="th_label">Confirm Password</th>
+ <td><input metal:use-macro="confirm_input" type="password"></td>
+ </tr>
+ </tal:if>
+ <tal:if condition="python:request.user.hasPermission('Web Roles')">
+ <tr tal:define="name string:roles; label string:Roles;">
+ <th><label for="roles" i18n:translate="">Roles</label></th>
+ <td tal:define="gips context/id">
+ <tal:subif condition=gips define="value context/roles">
+ <input metal:use-macro="normal_input">
+ </tal:subif>
+ <tal:subif condition="not:gips" define="value db/config/NEW_WEB_USER_ROLES">
+ <input metal:use-macro="normal_input">
+ </tal:subif>
+ <tal:block i18n:translate="">(to give the user more than one role,
+ enter a comma,separated,list)</tal:block>
+ </td>
+ </tr>
+ </tal:if>
+
+ <tr tal:define="name string:phone; label string:Phone; value context/phone">
+ <th metal:use-macro="th_label">Phone</th>
+ <td><input name="phone" metal:use-macro="normal_input"></td>
+ </tr>
+
+ <tr tal:define="name string:organisation; label string:Organisation; value context/organisation">
+ <th metal:use-macro="th_label">Organisation</th>
+ <td><input name="organisation" metal:use-macro="normal_input"></td>
+ </tr>
+
+ <tr tal:condition="python:edit_ok or context.timezone"
+ tal:define="name string:timezone; label string:Timezone; value context/timezone">
+ <th metal:use-macro="th_label">Timezone</th>
+ <td><input name="timezone" metal:use-macro="normal_input">
+ <tal:block tal:condition="edit_ok" i18n:translate="">(this is a numeric hour offset, the default is
+ <span tal:replace="db/config/DEFAULT_TIMEZONE" i18n:name="zone"
+ />)</tal:block>
+ </td>
+ </tr>
+
+ <tr tal:define="name string:address; label string:E-mail address; value context/address">
+ <th metal:use-macro="th_label">E-mail address</th>
+ <td tal:define="mailto python:context.address.field(id='address');
+ mklink python:mailto and not edit_ok">
+ <a href="mailto:calvin at the-z.org"
+ tal:attributes="href string:mailto:$value"
+ tal:content="value"
+ tal:condition="python:mklink">calvin at the-z.org</a>
+ <tal:if condition=edit_ok>
+ <input metal:use-macro="src_input" value="calvin at the-z.org">
+ </tal:if>
+
+ </td>
+ </tr>
+
+ <tr>
+ <th><label for="alternate_addresses" i18n:translate="">Alternate E-mail addresses<br>One address per line</label></th>
+ <td>
+ <textarea rows=5 cols=40 tal:replace="structure context/alternate_addresses/multiline">nobody at nowhere.org
+anybody at everywhere.net
+(alternate_addresses)
+ </textarea>
+ </td>
+ </tr>
+
+ <tr tal:condition="edit_ok">
+ <td>
+
+ <input type="hidden" name="@template" value="item">
+ <input type="hidden" name="@required" value="username,address"
+ tal:attributes="value python:','.join(required)">
+ </td>
+ <td><input type="submit" value="save" tal:replace="structure context/submit"><!--submit button here-->
+ <input type=reset>
+ </td>
+ </tr>
+</table>
+</form>
+
+<tal:block tal:condition="not:context/id" i18n:translate="">
+<table class="form">
+<tr>
+ <td>Note: </td>
+ <th class="required">highlighted</th>
+ <td> fields are required.</td>
+</tr>
+</table>
+</tal:block>
+
+<tal:block tal:condition="context/id" tal:replace="structure context/history" />
+
+</div>
+
+</td>
+
+</tal:doc>
+<!-- SHA: 1d69807a423e10d56fcb4b50619c85239daae892 -->
Added: tracker/instances/meta/html/user.register.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.register.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,82 @@
+<!-- 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="">Registering with <span i18n:name="tracker"
+ tal:replace="db/config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Registering with <span i18n:name="tracker"
+ tal:replace="db/config/TRACKER_NAME" /></span>
+<td class="content" metal:fill-slot="content">
+
+<form method="POST" onSubmit="return submit_once()"
+ enctype="multipart/form-data"
+ tal:attributes="action context/designator">
+
+<table class="form">
+ <tr>
+ <th i18n:translate="">Name</th>
+ <td tal:content="structure context/realname/field">realname</td>
+ </tr>
+ <tr>
+ <th class="required" i18n:translate="">Login Name</th>
+ <td tal:content="structure context/username/field">username</td>
+ </tr>
+ <tr>
+ <th class="required" i18n:translate="">Login Password</th>
+ <td tal:content="structure context/password/field">password</td>
+ </tr>
+ <tr>
+ <th class="required" i18n:translate="">Confirm Password</th>
+ <td tal:content="structure context/password/confirm">password</td>
+ </tr>
+ <tr tal:condition="python:request.user.hasPermission('Web Roles')">
+ <th i18n:translate="">Roles</th>
+ <td tal:condition="exists:item"
+ tal:content="structure context/roles/field">roles</td>
+ <td tal:condition="not:exists:item">
+ <input name="roles" tal:attributes="value db/config/NEW_WEB_USER_ROLES">
+ </td>
+ </tr>
+ <tr>
+ <th i18n:translate="">Phone</th>
+ <td tal:content="structure context/phone/field">phone</td>
+ </tr>
+ <tr>
+ <th i18n:translate="">Organisation</th>
+ <td tal:content="structure context/organisation/field">organisation</td>
+ </tr>
+ <tr>
+ <th class="required" i18n:translate="">E-mail address</th>
+ <td tal:content="structure context/address/field">address</td>
+ </tr>
+ <tr>
+ <th i18n:translate="">Alternate E-mail addresses<br>One address per line</th>
+ <td tal:content="structure context/alternate_addresses/multiline">alternate_addresses</td>
+ </tr>
+
+ <tr>
+ <td> </td>
+ <td>
+ <input type="hidden" name="@template" value="register">
+ <input type="hidden" name="@required" value="username,password,address">
+ <input type="hidden" name="@action" value="register">
+ <input type="submit" name="submit" value="Register" i18n:attributes="value">
+ </td>
+ </tr>
+</table>
+</form>
+
+<tal:block tal:condition="not:context/id" i18n:translate="">
+<table class="form">
+<tr>
+ <td>Note: </td>
+ <th class="required">highlighted</th>
+ <td> fields are required.</td>
+</tr>
+</table>
+</tal:block>
+
+</td>
+
+</tal:block>
+<!-- SHA: ed7a2465033194eb71fa79431b028150c23be650 -->
Added: tracker/instances/meta/html/user.rego_progress.html
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user.rego_progress.html Fri Jul 27 16:30:01 2007
@@ -0,0 +1,17 @@
+<!-- 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="">Registration in progress - <span i18n:name="tracker"
+ tal:replace="config/TRACKER_NAME" /></title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1"
+ i18n:translate="">Registration in progress...</span>
+<td class="content" metal:fill-slot="content">
+
+<p i18n:translate="">You will shortly receive an email
+to confirm your registration. To complete the registration process,
+visit the link indicated in the email.
+</p>
+
+</td>
+</tal:block>
+<!-- SHA: c2f389db861a4e0d20b41e5ff88120270862f609 -->
Added: tracker/instances/meta/html/user_utils.js
==============================================================================
--- (empty file)
+++ tracker/instances/meta/html/user_utils.js Fri Jul 27 16:30:01 2007
@@ -0,0 +1,111 @@
+// User Editing Utilities
+
+/**
+ * for new users:
+ * Depending on the input field which calls it, takes the value
+ * and dispatches it to certain other input fields:
+ *
+ * address
+ * +-> username
+ * | `-> realname
+ * `-> organisation
+ */
+function split_name(that) {
+ var raw = that.value
+ var val = trim(raw)
+ if (val == '') {
+ return
+ }
+ var username=''
+ var realname=''
+ var address=''
+ switch (that.name) {
+ case 'address':
+ address=val
+ break
+ case 'username':
+ username=val
+ break
+ case 'realname':
+ realname=val
+ break
+ default:
+ alert('Ooops - unknown name field '+that.name+'!')
+ return
+ }
+ var the_form = that.form;
+
+ function field_empty(name) {
+ return the_form[name].value == ''
+ }
+
+ // no break statements - on purpose!
+ switch (that.name) {
+ case 'address':
+ var split1 = address.split('@')
+ if (field_empty('username')) {
+ username = split1[0]
+ the_form.username.value = username
+ }
+ if (field_empty('organisation')) {
+ the_form.organisation.value = default_organisation(split1[1])
+ }
+ case 'username':
+ if (field_empty('realname')) {
+ realname = Cap(username.split('.').join(' '))
+ the_form.realname.value = realname
+ }
+ case 'realname':
+ if (field_empty('username')) {
+ username = Cap(realname.replace(' ', '.'))
+ the_form.username.value = username
+ }
+ if (the_form.firstname && the_form.lastname) {
+ var split2 = realname.split(' ')
+ var firstname='', lastname=''
+ firstname = split2[0]
+ lastname = split2.slice(1).join(' ')
+ if (field_empty('firstname')) {
+ the_form.firstname.value = firstname
+ }
+ if (field_empty('lastname')) {
+ the_form.lastname.value = lastname
+ }
+ }
+ }
+}
+
+function SubCap(str) {
+ switch (str) {
+ case 'de': case 'do': case 'da':
+ case 'du': case 'von':
+ return str;
+ }
+ if (str.toLowerCase().slice(0,2) == 'mc') {
+ return 'Mc'+str.slice(2,3).toUpperCase()+str.slice(3).toLowerCase()
+ }
+ return str.slice(0,1).toUpperCase()+str.slice(1).toLowerCase()
+}
+
+function Cap(str) {
+ var liz = str.split(' ')
+ for (var i=0; i<liz.length; i++) {
+ liz[i] = SubCap(liz[i])
+ }
+ return liz.join(' ')
+}
+
+/**
+ * Takes a domain name (behind the @ part of an email address)
+ * Customise this to handle the mail domains you're interested in
+ */
+function default_organisation(orga) {
+ switch (orga.toLowerCase()) {
+ case 'gmx':
+ case 'yahoo':
+ return ''
+ default:
+ return orga
+ }
+}
+
Added: tracker/instances/meta/initial_data.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/initial_data.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,32 @@
+#
+# TRACKER INITIAL PRIORITY AND STATUS VALUES
+#
+pri = db.getclass('priority')
+pri.create(name="critical", order="1")
+pri.create(name="urgent", order="2")
+pri.create(name="bug", order="3")
+pri.create(name="feature", order="4")
+pri.create(name="wish", order="5")
+
+stat = db.getclass('status')
+stat.create(name="unread", order="1")
+stat.create(name="deferred", order="2")
+stat.create(name="chatting", order="3")
+stat.create(name="need-eg", order="4")
+stat.create(name="in-progress", order="5")
+stat.create(name="testing", order="6")
+stat.create(name="done-cbb", order="7")
+stat.create(name="resolved", order="8")
+
+# create the two default users
+user = db.getclass('user')
+user.create(username="admin", password=adminpw,
+ address=admin_email, roles='Admin')
+user.create(username="anonymous", roles='Anonymous')
+
+# add any additional database creation steps here - but only if you
+# haven't initialised the database with the admin "initialise" command
+
+
+# vim: set filetype=python sts=4 sw=4 et si
+#SHA: e50b465a504eb8c8b2a0413f90bcca6dba8b9222
Added: tracker/instances/meta/schema.py
==============================================================================
--- (empty file)
+++ tracker/instances/meta/schema.py Fri Jul 27 16:30:01 2007
@@ -0,0 +1,170 @@
+
+#
+# TRACKER SCHEMA
+#
+
+# Class automatically gets these properties:
+# creation = Date()
+# activity = Date()
+# creator = Link('user')
+# actor = Link('user')
+
+# Priorities
+pri = Class(db, "priority",
+ name=String(),
+ order=Number())
+pri.setkey("name")
+
+# Statuses
+stat = Class(db, "status",
+ name=String(),
+ order=Number())
+stat.setkey("name")
+
+# Keywords
+keyword = Class(db, "keyword",
+ name=String())
+keyword.setkey("name")
+
+# User-defined saved searches
+query = Class(db, "query",
+ klass=String(),
+ name=String(),
+ url=String(),
+ private_for=Link('user'))
+
+# add any additional database schema configuration here
+
+user = Class(db, "user",
+ username=String(),
+ password=Password(),
+ address=String(),
+ realname=String(),
+ phone=String(),
+ organisation=String(),
+ alternate_addresses=String(),
+ queries=Multilink('query'),
+ roles=String(), # comma-separated string of Role names
+ timezone=String())
+user.setkey("username")
+
+# FileClass automatically gets this property in addition to the Class ones:
+# content = String() [saved to disk in <tracker home>/db/files/]
+# type = String() [MIME type of the content, default 'text/plain']
+msg = FileClass(db, "msg",
+ author=Link("user", do_journal='no'),
+ recipients=Multilink("user", do_journal='no'),
+ date=Date(),
+ summary=String(),
+ files=Multilink("file"),
+ messageid=String(),
+ inreplyto=String())
+
+file = FileClass(db, "file",
+ name=String())
+
+# IssueClass automatically gets these properties in addition to the Class ones:
+# title = String()
+# messages = Multilink("msg")
+# files = Multilink("file")
+# nosy = Multilink("user")
+# superseder = Multilink("issue")
+issue = IssueClass(db, "issue",
+ assignedto=Link("user"),
+ topic=Multilink("keyword"),
+ priority=Link("priority"),
+ status=Link("status"))
+
+#
+# TRACKER SECURITY SETTINGS
+#
+# See the configuration and customisation document for information
+# about security setup.
+
+#
+# REGULAR USERS
+#
+# Give the regular users access to the web and email interface
+db.security.addPermissionToRole('User', 'Web Access')
+db.security.addPermissionToRole('User', 'Email Access')
+
+# Assign the access and edit Permissions for issue, file and message
+# to regular users now
+for cl in 'issue', 'file', 'msg', 'keyword':
+ db.security.addPermissionToRole('User', 'View', cl)
+ db.security.addPermissionToRole('User', 'Edit', cl)
+ db.security.addPermissionToRole('User', 'Create', cl)
+for cl in 'priority', 'status':
+ db.security.addPermissionToRole('User', 'View', cl)
+
+# May users view other user information? Comment these lines out
+# if you don't want them to
+db.security.addPermissionToRole('User', 'View', 'user')
+
+# Users should be able to edit their own details -- this permission is
+# limited to only the situation where the Viewed or Edited item is their own.
+def own_record(db, userid, itemid):
+ '''Determine whether the userid matches the item being accessed.'''
+ return userid == itemid
+p = db.security.addPermission(name='View', klass='user', check=own_record,
+ description="User is allowed to view their own user details")
+db.security.addPermissionToRole('User', p)
+p = db.security.addPermission(name='Edit', klass='user', check=own_record,
+ description="User is allowed to edit their own user details")
+db.security.addPermissionToRole('User', p)
+
+# Users should be able to edit and view their own queries. They should also
+# be able to view any marked as not private. They should not be able to
+# edit others' queries, even if they're not private
+def view_query(db, userid, itemid):
+ private_for = db.query.get(itemid, 'private_for')
+ if not private_for: return True
+ return userid == private_for
+def edit_query(db, userid, itemid):
+ return userid == db.query.get(itemid, 'creator')
+p = db.security.addPermission(name='View', klass='query', check=view_query,
+ description="User is allowed to view their own and public queries")
+db.security.addPermissionToRole('User', p)
+p = db.security.addPermission(name='Edit', klass='query', check=edit_query,
+ description="User is allowed to edit their queries")
+db.security.addPermissionToRole('User', p)
+p = db.security.addPermission(name='Create', klass='query',
+ description="User is allowed to create queries")
+db.security.addPermissionToRole('User', p)
+
+
+#
+# ANONYMOUS USER PERMISSIONS
+#
+# Let anonymous users access the web interface. Note that almost all
+# trackers will need this Permission. The only situation where it's not
+# required is in a tracker that uses an HTTP Basic Authenticated front-end.
+db.security.addPermissionToRole('Anonymous', 'Web Access')
+
+# Let anonymous users access the email interface (note that this implies
+# that they will be registered automatically, hence they will need the
+# "Create" user Permission below)
+# This is disabled by default to stop spam from auto-registering users on
+# public trackers.
+#db.security.addPermissionToRole('Anonymous', 'Email Access')
+
+# Assign the appropriate permissions to the anonymous user's Anonymous
+# Role. Choices here are:
+# - Allow anonymous users to register
+db.security.addPermissionToRole('Anonymous', 'Create', 'user')
+
+# Allow anonymous users access to view issues (and the related, linked
+# information)
+for cl in 'issue', 'file', 'msg', 'keyword', 'priority', 'status':
+ db.security.addPermissionToRole('Anonymous', 'View', cl)
+
+# [OPTIONAL]
+# Allow anonymous users access to create or edit "issue" items (and the
+# related file and message items)
+#for cl in 'issue', 'file', 'msg':
+# db.security.addPermissionToRole('Anonymous', 'Create', cl)
+# db.security.addPermissionToRole('Anonymous', 'Edit', cl)
+
+
+# vim: set filetype=python sts=4 sw=4 et si :
+#SHA: e3fa5650097bb3baf7e65ecbfb138b38d2d70cae
More information about the Python-checkins
mailing list