diff --git a/doc/customizing.txt b/doc/customizing.txt
index 068e9076244f7e1096625e299bebd443f584b10c..eb6b8adceeed1c70a49388a0f576c9aea12d2d35 100644 (file)
--- a/doc/customizing.txt
+++ b/doc/customizing.txt
Customising Roundup
===================
-:Version: $Revision: 1.64 $
+:Version: $Revision: 1.72 $
.. This document borrows from the ZopeBook section on ZPT. The original is at:
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
The email address that e-mail sent to roundup should go to. Think of it as the
tracker's personal e-mail address.
-**TRACKER_WEB** - ``'http://your.tracker.url.example/'``
+**TRACKER_WEB** - ``'http://tracker.example/cgi-bin/roundup.cgi/bugs/'``
The web address that the tracker is viewable at. This will be included in
- information sent to users of the tracker.
+ information sent to users of the tracker. The URL **must** include the
+ cgi-bin part or anything else that is required to get to the home page of
+ the tracker. You **must** include a trailing '/' in the URL.
**ADMIN_EMAIL** - ``'roundup-admin@%s'%MAIL_DOMAIN``
The email address that roundup will complain to if it runs into trouble.
+**EMAIL_FROM_TAG** - ``''``
+ Additional text to include in the "name" part of the ``From:`` address used
+ in nosy messages. If the sending user is "Foo Bar", the ``From:`` line is
+ usually::
+ "Foo Bar" <issue_tracker@tracker.example>
+
+ the EMAIL_FROM_TAG goes inside the "Foo Bar" quotes like so::
+
+ "Foo Bar EMAIL_FROM_TAG" <issue_tracker@tracker.example>
+
**MESSAGES_TO_AUTHOR** - ``'yes'`` or``'no'``
Send nosy messages to the author of the message.
# The email address that mail to roundup should go to
TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
- # The web address that the tracker is viewable at
- TRACKER_WEB = 'http://your.tracker.url.example/'
+ # The web address that the tracker is viewable at. This will be included in
+ # information sent to users of the tracker. The URL MUST include the cgi-bin
+ # part or anything else that is required to get to the home page of the
+ # tracker. You MUST include a trailing '/' in the URL.
+ TRACKER_WEB = 'http://tracker.example/cgi-bin/roundup.cgi/bugs/'
# The email address that roundup will complain to if it runs into trouble
ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
+ # Additional text to include in the "name" part of the From: address used
+ # in nosy messages. If the sending user is "Foo Bar", the From: line is
+ # usually: "Foo Bar" <issue_tracker@tracker.example>
+ # the EMAIL_FROM_TAG goes inside the "Foo Bar" quotes like so:
+ # "Foo Bar EMAIL_FROM_TAG" <issue_tracker@tracker.example>
+ EMAIL_FROM_TAG = ""
+
# Send nosy messages to the author of the message
MESSAGES_TO_AUTHOR = 'no' # either 'yes' or 'no'
MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default
#MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out)
+ #
+ # SECURITY DEFINITIONS
+ #
+ # define the Roles that a user gets when they register with the tracker
+ # these are a comma-separated string of role names (e.g. 'Admin,User')
+ NEW_WEB_USER_ROLES = 'User'
+ NEW_EMAIL_USER_ROLES = 'User'
+
Tracker Schema
==============
the :note if it's supplied.
:required=property,property,...
The named properties are required to be filled in the form.
+ :remove:<propname>=id(s)
+ The ids will be removed from the multilink property. You may have multiple
+ :remove:<propname> form elements for a single <propname>.
+ :add:<propname>=id(s)
+ The ids will be added to the multilink property. You may have multiple
+ :add:<propname> form elements for a single <propname>.
**new**
Add a new item to the database. You may use the same special form elements
in place if the "foo" form variable doesn't exist.
**String Expressions** - eg. ``string:hello ${user/name}``
- These expressions are simple string interpolations (though they can be just
+ These expressions are simple string interpolations - though they can be just
plain strings with no interpolation if you want. The expression in the
``${ ... }`` is just a path expression as above.
There are several methods available on these wrapper objects:
-=========== =================================================================
-Method Description
-=========== =================================================================
-plain render a "plain" representation of the property
-field render a form edit field for the property
-stext only on String properties - render the value of the
- property as StructuredText (requires the StructureText module
- to be installed separately)
-multiline only on String properties - render a multiline form edit
- field for the property
-email only on String properties - render the value of the
- property as an obscured email address
-confirm only on Password properties - render a second form edit field for
- the property, used for confirmation that the user typed the
- password correctly. Generates a field with name "name:confirm".
-reldate only on Date properties - render the interval between the
- date and now
-pretty only on Interval properties - render the interval in a
- pretty format (eg. "yesterday")
-menu only on Link and Multilink properties - render a form select
- list for this property
-reverse only on Multilink properties - produce a list of the linked
- items in reverse order
-=========== =================================================================
+========= =====================================================================
+Method Description
+========= =====================================================================
+plain render a "plain" representation of the property. This method may
+ take two arguments:
+
+ escape
+ If true, escape the text so it is HTML safe (default: no). The
+ reason this defaults to off is that text is usually escaped
+ at a later stage by the TAL commands, unless the "structure"
+ option is used in the template. The following are all equivalent::
+
+ <p tal:content="structure python:msg.content.plain(escape=1)" />
+ <p tal:content="python:msg.content.plain()" />
+ <p tal:content="msg/content/plain" />
+ <p tal:content="msg/content" />
+
+ Usually you'll only want to use the escape option in a complex
+ expression.
+
+ hyperlink
+ If true, turn URLs, email addresses and hyperdb item designators
+ in the text into hyperlinks (default: no). Note that you'll need
+ to use the "structure" TAL option if you want to use this::
+
+ <p tal:content="structure python:msg.content.plain(hyperlink=1)" />
+
+ Note also that the text is automatically HTML-escape before the
+ hyperlinking transformation.
+
+field render an appropriate form edit field for the property - for most
+ types this is a text entry box, but for Booleans it's a tri-state
+ yes/no/neither selection.
+stext only on String properties - render the value of the
+ property as StructuredText (requires the StructureText module
+ to be installed separately)
+multiline only on String properties - render a multiline form edit
+ field for the property
+email only on String properties - render the value of the
+ property as an obscured email address
+confirm only on Password properties - render a second form edit field for
+ the property, used for confirmation that the user typed the
+ password correctly. Generates a field with name "name:confirm".
+reldate only on Date properties - render the interval between the
+ date and now
+pretty only on Interval properties - render the interval in a
+ pretty format (eg. "yesterday")
+menu only on Link and Multilink properties - render a form select
+ list for this property
+reverse only on Multilink properties - produce a list of the linked
+ items in reverse order
+========= =====================================================================
The request variable
~~~~~~~~~~~~~~~~~~~~
---------------
Note: if you add a new column to the ``:columns`` form variable potentials
- then you will need to add the column to the `index view`_
+ then you will need to add the column to the appropriate `index views`_
template so it is actually displayed.
This is one of the class context views. The template used is typically
The code to do this is::
- actions = client.Client.actions + (
- ('edit_with_timelog', 'timelogEditAction'),
- )
+ class Client(client.Client):
+ ''' derives basic CGI implementation from the standard module,
+ with any specific extensions
+ '''
+ actions = client.Client.actions + (
+ ('edit_with_timelog', 'timelogEditAction'),
+ )
+
+ def timelogEditAction(self):
+ ''' Handle the creation of a new time log entry if necessary.
+
+ If we create a new entry, fake up a CGI form value for the
+ altered "times" property of the issue being edited.
+
+ Punt to the regular edit action when we're done.
+ '''
+ # if there's a timelog value specified, create an entry
+ if self.form.has_key(':timelog') and \
+ self.form[':timelog'].value.strip():
+ period = Interval(self.form[':timelog'].value)
+ # create it
+ newid = self.db.timelog.create(period=period)
+
+ # if we're editing an existing item, get the old timelog value
+ if self.nodeid:
+ l = self.db.issue.get(self.nodeid, 'times')
+ l.append(newid)
+ else:
+ l = [newid]
- def timelogEditAction(self):
- ''' Handle the creation of a new time log entry if necessary.
+ # now make the fake CGI form values
+ for entry in l:
+ self.form.list.append(MiniFieldStorage('times', entry))
- If we create a new entry, fake up a CGI form value for the altered
- "times" property of the issue being edited.
+ # punt to the normal edit action
+ return self.editItemAction()
+
+ you add this code to your Client class in your tracker's ``interfaces.py``
+ file. Locate the section that looks like::
- Punt to the regular edit action when we're done.
- '''
- # if there's a timelog value specified, create an entry
- if self.form.has_key(':timelog') and \
- self.form[':timelog'].value.strip():
- period = Interval(self.form[':timelog'].value)
- # create it
- newid = self.db.timelog.create(period=period)
-
- # if we're editing an existing item, get the old timelog value
- if self.nodeid:
- l = self.db.issue.get(self.nodeid, 'times')
- l.append(newid)
- else:
- l = [newid]
-
- # now make the fake CGI form values
- for entry in l:
- self.form.list.append(MiniFieldStorage('times', entry))
-
- # punt to the normal edit action
- return self.editItemAction()
+ class Client:
+ ''' derives basic CGI implementation from the standard module,
+ with any specific extensions
+ '''
+ pass
- you add this code to your Client class in your tracker's ``interfaces.py``
- file.
+ and insert this code in place of the ``pass`` statement.
5. You'll also need to modify your ``issue.item`` form submit action so it
- calls the time logging action we just created::
+ calls the time logging action we just created. The current template will
+ look like this::
+
+ <tr>
+ <td> </td>
+ <td colspan=3 tal:content="structure context/submit">
+ submit button will go here
+ </td>
+ </tr>
+
+ replace it with this::
<tr>
<td> </td>
</td>
</tr>
- Note that the "context/submit" command usually handles all that for you -
- isn't it handy? The important change is setting the action to
- "edit_with_timelog" for edit operations (where the item exists)
+ The important change is setting the action to "edit_with_timelog" for
+ edit operations (where the item exists)
6. We want to display a total of the time log times that have been accumulated
for an issue. To do this, we'll need to actually write some Python code,
We do this by adding a method to the TemplatingUtils class in our tracker
``interfaces.py`` module::
-
class TemplatingUtils:
''' Methods implemented on this class will be available to HTML
templates through the 'utils' variable.
total += time.period._value
return total
+ Replace the ``pass`` line as we did in step 4 above with the Client class.
As indicated in the docstrings, we will be able to access the
- ``totalTimeSpent`` method via the ``utils`` variable in our templates. See
+ ``totalTimeSpent`` method via the ``utils`` variable in our templates.
7. Display the time log for an issue::