Code

more doc
[roundup.git] / doc / customizing.txt
1 ===================
2 Customising Roundup
3 ===================
5 :Version: $Revision: 1.24 $
7 .. This document borrows from the ZopeBook section on ZPT. The original is at:
8    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
10 .. contents::
13 What You Can Do
14 ---------------
16 Customisation of Roundup can take one of five forms:
18 1. `tracker configuration`_ file changes
19 2. database, or `tracker schema`_ changes
20 3. "definition" class `database content`_ changes
21 4. behavioural changes, through detectors_
22 5. `access controls`_
24 The third case is special because it takes two distinctly different forms
25 depending upon whether the tracker has been initialised or not. The other two
26 may be done at any time, before or after tracker initialisation. Yes, this
27 includes adding or removing properties from classes.
30 Trackers in a Nutshell
31 ----------------------
33 Trackers have the following structure:
35 +-------------------+--------------------------------------------------------+
36 |config.py          |Holds the basic `tracker configuration`_                |
37 +-------------------+--------------------------------------------------------+
38 |dbinit.py          |Holds the `tracker schema`_                             |
39 +-------------------+--------------------------------------------------------+
40 |interfaces.py      |Defines the Web and E-Mail interfaces for the tracker   |
41 +-------------------+--------------------------------------------------------+
42 |select_db.py       |Selects the database back-end for the tracker           |
43 +-------------------+--------------------------------------------------------+
44 |db/                |Holds the tracker's database                            |
45 +-------------------+--------------------------------------------------------+
46 |db/files/          |Holds the tracker's upload files and messages           |
47 +-------------------+--------------------------------------------------------+
48 |detectors/         |Auditors and reactors for this tracker                  |
49 +-------------------+--------------------------------------------------------+
50 |html/              |Web interface templates, images and style sheets        |
51 +-------------------+--------------------------------------------------------+
53 Tracker Configuration
54 ---------------------
56 The config.py located in your tracker home contains the basic
57 configuration for the web and e-mail components of roundup's interfaces. This
58 file is a Python module. The configuration variables available are:
60 **INSTANCE_HOME** - ``os.path.split(__file__)[0]``
61  The tracker home directory. The above default code will automatically
62  determine the tracker home for you.
64 **MAILHOST** - ``'localhost'``
65  The SMTP mail host that roundup will use to send e-mail.
67 **MAIL_DOMAIN** - ``'your.tracker.email.domain.example'``
68  The domain name used for email addresses.
70 **DATABASE** - ``os.path.join(INSTANCE_HOME, 'db')``
71  This is the directory that the database is going to be stored in. By default
72  it is in the tracker home.
74 **TEMPLATES** - ``os.path.join(INSTANCE_HOME, 'html')``
75  This is the directory that the HTML templates reside in. By default they are
76  in the tracker home.
78 **INSTANCE_NAME** - ``'Roundup issue tracker'``
79  A descriptive name for your roundup tracker. This is sent out in e-mails and
80  appears in the heading of CGI pages.
82 **ISSUE_TRACKER_EMAIL** - ``'issue_tracker@%s'%MAIL_DOMAIN``
83  The email address that e-mail sent to roundup should go to. Think of it as the
84  tracker's personal e-mail address.
86 **ISSUE_TRACKER_WEB** - ``'http://your.tracker.url.example/'``
87  The web address that the tracker is viewable at. This will be included in
88  information sent to users of the tracker.
90 **ADMIN_EMAIL** - ``'roundup-admin@%s'%MAIL_DOMAIN``
91  The email address that roundup will complain to if it runs into trouble.
93 **MESSAGES_TO_AUTHOR** - ``'yes'`` or``'no'``
94  Send nosy messages to the author of the message.
96 **ADD_AUTHOR_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
97  Does the author of a message get placed on the nosy list automatically?
98  If ``'new'`` is used, then the author will only be added when a message
99  creates a new issue. If ``'yes'``, then the author will be added on followups
100  too. If ``'no'``, they're never added to the nosy.
102 **ADD_RECIPIENTS_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
103  Do the recipients (To:, Cc:) of a message get placed on the nosy list?
104  If ``'new'`` is used, then the recipients will only be added when a message
105  creates a new issue. If ``'yes'``, then the recipients will be added on
106  followups too. If ``'no'``, they're never added to the nosy.
108 **EMAIL_SIGNATURE_POSITION** - ``'top'``, ``'bottom'`` or ``'none'``
109  Where to place the email signature in messages that Roundup generates.
111 **EMAIL_KEEP_QUOTED_TEXT** - ``'yes'`` or ``'no'``
112  Keep email citations. Citations are the part of e-mail which the sender has
113  quoted in their reply to previous e-mail.
115 **EMAIL_LEAVE_BODY_UNCHANGED** - ``'no'``
116  Preserve the email body as is. Enabiling this will cause the entire message
117  body to be stored, including all citations and signatures. It should be
118  either ``'yes'`` or ``'no'``.
120 **MAIL_DEFAULT_CLASS** - ``'issue'`` or ``''``
121  Default class to use in the mailgw if one isn't supplied in email
122  subjects. To disable, comment out the variable below or leave it blank.
124 The default config.py is given below - as you
125 can see, the MAIL_DOMAIN must be edited before any interaction with the
126 tracker is attempted.::
128     # roundup home is this package's directory
129     INSTANCE_HOME=os.path.split(__file__)[0]
131     # The SMTP mail host that roundup will use to send mail
132     MAILHOST = 'localhost'
134     # The domain name used for email addresses.
135     MAIL_DOMAIN = 'your.tracker.email.domain.example'
137     # This is the directory that the database is going to be stored in
138     DATABASE = os.path.join(INSTANCE_HOME, 'db')
140     # This is the directory that the HTML templates reside in
141     TEMPLATES = os.path.join(INSTANCE_HOME, 'html')
143     # A descriptive name for your roundup tracker
144     INSTANCE_NAME = 'Roundup issue tracker'
146     # The email address that mail to roundup should go to
147     ISSUE_TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
149     # The web address that the tracker is viewable at
150     ISSUE_TRACKER_WEB = 'http://your.tracker.url.example/'
152     # The email address that roundup will complain to if it runs into trouble
153     ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
155     # Somewhere for roundup to log stuff internally sent to stdout or stderr
156     LOG = os.path.join(INSTANCE_HOME, 'roundup.log')
158     # Send nosy messages to the author of the message
159     MESSAGES_TO_AUTHOR = 'no'           # either 'yes' or 'no'
161     # Does the author of a message get placed on the nosy list automatically?
162     # If 'new' is used, then the author will only be added when a message
163     # creates a new issue. If 'yes', then the author will be added on followups
164     # too. If 'no', they're never added to the nosy.
165     ADD_AUTHOR_TO_NOSY = 'new'          # one of 'yes', 'no', 'new'
167     # Do the recipients (To:, Cc:) of a message get placed on the nosy list?
168     # If 'new' is used, then the recipients will only be added when a message
169     # creates a new issue. If 'yes', then the recipients will be added on followups
170     # too. If 'no', they're never added to the nosy.
171     ADD_RECIPIENTS_TO_NOSY = 'new'      # either 'yes', 'no', 'new'
173     # Where to place the email signature
174     EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
176     # Keep email citations
177     EMAIL_KEEP_QUOTED_TEXT = 'no'       # either 'yes' or 'no'
179     # Preserve the email body as is
180     EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
182     # Default class to use in the mailgw if one isn't supplied in email
183     # subjects. To disable, comment out the variable below or leave it blank.
184     # Examples:
185     MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
186     #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
188 Tracker Schema
189 --------------
191 Note: if you modify the schema, you'll most likely need to edit the
192       `web interface`_ HTML template files and `detectors`_ to reflect
193       your changes.
195 A tracker schema defines what data is stored in the tracker's database.
196 The
197 schemas shipped with Roundup turn it into a typical software bug tracker or
198 help desk.
200 XXX make sure we ship the help desk
202 Schemas are defined using Python code in the ``dbinit.py`` module of your
203 tracker. The "classic" schema looks like this::
205     pri = Class(db, "priority", name=String(), order=String())
206     pri.setkey("name")
207     pri.create(name="critical", order="1")
208     pri.create(name="urgent", order="2")
209     pri.create(name="bug", order="3")
210     pri.create(name="feature", order="4")
211     pri.create(name="wish", order="5")
213     stat = Class(db, "status", name=String(), order=String())
214     stat.setkey("name")
215     stat.create(name="unread", order="1")
216     stat.create(name="deferred", order="2")
217     stat.create(name="chatting", order="3")
218     stat.create(name="need-eg", order="4")
219     stat.create(name="in-progress", order="5")
220     stat.create(name="testing", order="6")
221     stat.create(name="done-cbb", order="7")
222     stat.create(name="resolved", order="8")
224     keyword = Class(db, "keyword", name=String())
225     keyword.setkey("name")
227     user = Class(db, "user", username=String(), password=String(),
228         address=String(), realname=String(), phone=String(),
229         organisation=String())
230     user.setkey("username")
231     user.create(username="admin", password=adminpw,
232         address=config.ADMIN_EMAIL)
234     msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
235         ("user"), date=Date(), summary=String(), files=Multilink("file"))
237     file = FileClass(db, "file", name=String(), type=String())
239     issue = IssueClass(db, "issue", assignedto=Link("user"),
240         topic=Multilink("keyword"), priority=Link("priority"), status=Link
241         ("status"))
242     issue.setkey('title')
244 XXX security definitions
246 Classes and Properties - creating a new information store
247 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
249 In the tracker above, we've defined 7 classes of information:
251   priority
252       Defines the possible levels of urgency for issues.
254   status
255       Defines the possible states of processing the issue may be in.
257   keyword
258       Initially empty, will hold keywords useful for searching issues.
260   user
261       Initially holding the "admin" user, will eventually have an entry for all
262       users using roundup.
264   msg
265       Initially empty, will all e-mail messages sent to or generated by
266       roundup.
268   file
269       Initially empty, will all files attached to issues.
271   issue
272       Initially empty, this is where the issue information is stored.
274 We define the "priority" and "status" classes to allow two things: reduction in
275 the amount of information stored on the issue and more powerful, accurate
276 searching of issues by priority and status. By only requiring a link on the
277 issue (which is stored as a single number) we reduce the chance that someone
278 mis-types a priority or status - or simply makes a new one up.
280 Class and Items
281 :::::::::::::::
283 A Class defines a particular class (or type) of data that will be stored in the
284 database. A class comprises one or more properties, which given the information
285 about the class items.
286 The actual data entered into the database, using class.create() are called
287 items. They have a special immutable property called id. We sometimes refer to
288 this as the itemid.
290 Properties
291 ::::::::::
293 A Class is comprised of one or more properties of the following types:
295 * String properties are for storing arbitrary-length strings.
296 * Password properties are for storing encoded arbitrary-length strings. The
297   default encoding is defined on the roundup.password.Password class.
298 * Date properties store date-and-time stamps. Their values are Timestamp
299   objects.
300 * Number properties store numeric values.
301 * Boolean properties store on/off, yes/no, true/false values.
302 * A Link property refers to a single other item selected from a specified
303   class. The class is part of the property; the value is an integer, the id
304   of the chosen item.
305 * A Multilink property refers to possibly many items in a specified class.
306   The value is a list of integers.
308 FileClass
309 :::::::::
311 FileClasses save their "content" attribute off in a separate file from the rest
312 of the database. This reduces the number of large entries in the database,
313 which generally makes databases more efficient, and also allows us to use
314 command-line tools to operate on the files. They are stored in the files sub-
315 directory of the db directory in your tracker.
317 IssueClass
318 ::::::::::
320 IssueClasses automatically include the "messages", "files", "nosy", and
321 "superseder" properties.
322 The messages and files properties list the links to the messages and files
323 related to the issue. The nosy property is a list of links to users who wish to
324 be informed of changes to the issue - they get "CC'ed" e-mails when messages
325 are sent to or generated by the issue. The nosy reactor (in the detectors
326 directory) handles this action. The superceder link indicates an issue which
327 has superceded this one.
328 They also have the dynamically generated "creation", "activity" and "creator"
329 properties.
330 The value of the "creation" property is the date when an item was created, and
331 the value of the "activity" property is the date when any property on the item
332 was last edited (equivalently, these are the dates on the first and last
333 records in the item's journal). The "creator" property holds a link to the user
334 that created the issue.
336 setkey(property)
337 ::::::::::::::::
339 Select a String property of the class to be the key property. The key property
340 muse be unique, and allows references to the items in the class by the content
341 of the key property. That is, we can refer to users by their username, e.g.
342 let's say that there's an issue in roundup, issue 23. There's also a user,
343 richard who happens to be user 2. To assign an issue to him, we could do either
344 of::
346      roundup-admin set issue assignedto=2
348 or::
350      roundup-admin set issue assignedto=richard
352 Note, the same thing can be done in the web and e-mail interfaces.
354 create(information)
355 :::::::::::::::::::
357 Create an item in the database. This is generally used to create items in the
358 "definitional" classes like "priority" and "status".
361 Examples of adding to your schema
362 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
364 TODO
367 Detectors - adding behaviour to your tracker
368 --------------------------------------------
369 .. _detectors:
371 The detectors in your tracker fire before (*auditors*) and after (*reactors*)
372 changes to the contents of your database. They are Python modules that sit in
373 your tracker's ``detectors`` directory. You will have some installed by
374 default - have a look. You can write new detectors or modify the existing
375 ones. The existing detectors installed for you are:
377 **nosyreaction.py**
378   This provides the automatic nosy list maintenance and email sending. The nosy
379   reactor (``nosyreaction``) fires when new messages are added to issues.
380   The nosy auditor (``updatenosy``) fires when issues are changed and figures
381   what changes need to be made to the nosy list (like adding new authors etc)
382 **statusauditor.py**
383   This provides the ``chatty`` auditor which changes the issue status from
384   ``unread`` or ``closed`` to ``chatting`` if new messages appear. It also
385   provides the ``presetunread`` auditor which pre-sets the status to
386   ``unread`` on new items if the status isn't explicitly defined.
388 See the detectors section in the `design document`__ for details of the
389 interface for detectors.
391 __ design.html
393 Sample additional detectors that have been found useful will appear in the
394 ``detectors`` directory of the Roundup distribution:
396 **newissuecopy.py**
397   This detector sends an email to a team address whenever a new issue is
398   created. The address is hard-coded into the detector, so edit it before you
399   use it (look for the text 'team@team.host') or you'll get email errors!
401 XXX give the example here.
404 Database Content
405 ----------------
407 Note: if you modify the content of definitional classes, you'll most likely
408        need to edit the tracker `detectors`_ to reflect your changes.
410 Customisation of the special "definitional" classes (eg. status, priority,
411 resolution, ...) may be done either before or after the tracker is
412 initialised. The actual method of doing so is completely different in each
413 case though, so be careful to use the right one.
415 **Changing content before tracker initialisation**
416     Edit the dbinit module in your tracker to alter the items created in using
417     the create() methods.
420 **Changing content after tracker initialisation**
421     Use the roundup-admin interface's create, set and retire methods to add,
422     alter or remove items from the classes in question.
424 XXX example
427 Web Interface
428 -------------
430 The web is provided by the roundup.cgi.client module and is used by
431 roundup.cgi, roundup-server and ZRoundup.
432 In all cases, we determine which tracker is being accessed
433 (the first part of the URL path inside the scope of the CGI handler) and pass
434 control on to the tracker interfaces.Client class - which uses the Client class
435 from roundup.cgi.client - which handles the rest of
436 the access through its main() method. This means that you can do pretty much
437 anything you want as a web interface to your tracker.
439 Repurcussions of changing the tracker schema
440 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
442 If you choose to change the `tracker schema`_ you will need to ensure the web
443 interface knows about it:
445 1. Index, item and search pages for the relevant classes may need to have
446    properties added or removed,
447 2. The "page" template may require links to be changed, as might the "home"
448    page's content arguments.
450 How requests are processed
451 ~~~~~~~~~~~~~~~~~~~~~~~~~~
453 The basic processing of a web request proceeds as follows:
455 1. figure out who we are, defaulting to the "anonymous" user
456 2. figure out what the request is for - we call this the "context"
457 3. handle any requested action (item edit, search, ...)
458 4. render a template, resulting in HTML output
460 In some situations, exceptions occur:
462 - HTTP Redirect  (generally raised by an action)
463 - SendFile       (generally raised by determine_context)
464   here we serve up a FileClass "content" property
465 - SendStaticFile (generally raised by determine_context)
466   here we serve up a file from the tracker "html" directory
467 - Unauthorised   (generally raised by an action)
468   here the action is cancelled, the request is rendered and an error
469   message is displayed indicating that permission was not
470   granted for the action to take place
471 - NotFound       (raised wherever it needs to be)
472   this exception percolates up to the CGI interface that called the client
474 Determining web context
475 ~~~~~~~~~~~~~~~~~~~~~~~
477 To determine the "context" of a request, we look at the URL and the special
478 request variable ``:template``. The URL path after the tracker identifier
479 is examined. Typical URL paths look like:
481 1.  ``/tracker/issue``
482 2.  ``/tracker/issue1``
483 3.  ``/tracker/_file/style.css``
484 4.  ``/cgi-bin/roundup.cgi/tracker/file1``
485 5.  ``/cgi-bin/roundup.cgi/tracker/file1/kitten.png``
487 where the "tracker identifier" is "tracker" in the above cases. That means
488 we're looking at "issue", "issue1", "_file/style.css", "file1" and
489 "file1/kitten.png" in the cases above. The path is generally only one
490 entry long - longer paths are handled differently.
492 a. if there is no path, then we are in the "home" context.
493 b. if the path starts with "_file" (as in example 3,
494    "/tracker/_file/style.css"), then the additional path entry,
495    "style.css" specifies the filename of a static file we're to serve up
496    from the tracker "html" directory. Raises a SendStaticFile
497    exception.
498 c. if there is something in the path (as in example 1, "issue"), it identifies
499    the tracker class we're to display.
500 d. if the path is an item designator (as in examples 2 and 4, "issue1" and
501    "file1"), then we're to display a specific item.
502 e. if the path starts with an item designator and is longer than
503    one entry (as in example 5, "file1/kitten.png"), then we're assumed
504    to be handling an item of a
505    FileClass, and the extra path information gives the filename
506    that the client is going to label the download with (ie
507    "file1/kitten.png" is nicer to download than "file1"). This
508    raises a SendFile exception.
510 Both b. and e. stop before we bother to
511 determine the template we're going to use. That's because they
512 don't actually use templates.
514 The template used is specified by the ``:template`` CGI variable,
515 which defaults to:
517 - only classname suplied:          "index"
518 - full item designator supplied:   "item"
521 Performing actions in web requests
522 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
524 When a user requests a web page, they may optionally also request for an
525 action to take place. As described in `how requests are processed`_, the
526 action is performed before the requested page is generated. Actions are
527 triggered by using a ``:action`` CGI variable, where the value is one of:
529 login
530  Attempt to log a user in.
531 logout
532  Log the user out - make them "anonymous".
533 register
534  Attempt to create a new user based on the contents of the form and then log
535  them in.
536 edit
537  Perform an edit of an item in the database. There are some special form
538  elements you may use:
540  :link=designator:property and :multilink=designator:property
541   The value specifies an item designator and the property on that
542   item to add _this_ item to as a link or multilink.
543  :note
544   Create a message and attach it to the current item's
545   "messages" property.
546  :file
547   Create a file and attach it to the current item's
548   "files" property. Attach the file to the message created from
549   the :note if it's supplied.
550  :required=property,property,...
551   The named properties are required to be filled in the form.
553 new
554  Add a new item to the database. You may use the same special form elements
555  as in the "edit" action.
557 editCSV
558  Performs an edit of all of a class' items in one go. See also the
559  *class*.csv templating method which generates the CSV data to be edited, and
560  the "_generic.index" template which uses both of these features.
562 search
563  Mangle some of the form variables.
565  Set the form ":filter" variable based on the values of the
566  filter variables - if they're set to anything other than
567  "dontcare" then add them to :filter.
569  Also handle the ":queryname" variable and save off the query to
570  the user's query list.
572 Each of the actions is implemented by a corresponding *actionAction* (where
573 "action" is the name of the action) method on
574 the roundup.cgi.Client class, which also happens to be in your tracker as
575 interfaces.Client. So if you need to define new actions, you may add them
576 there (see `definining new web actions`_).
578 Each action also has a corresponding *actionPermission* (where
579 "action" is the name of the action) method which determines
580 whether the action is permissible given the current user. The base permission
581 checks are:
583 login
584  Determine whether the user has permission to log in.
585  Base behaviour is to check the user has "Web Access".
586 logout
587  No permission checks are made.
588 register
589  Determine whether the user has permission to register
590  Base behaviour is to check the user has "Web Registration".
591 edit
592  Determine whether the user has permission to edit this item.
593  Base behaviour is to check the user can edit this class. If we're
594  editing the "user" class, users are allowed to edit their own
595  details. Unless it's the "roles" property, which requires the
596  special Permission "Web Roles".
597 new
598  Determine whether the user has permission to create (edit) this item.
599  Base behaviour is to check the user can edit this class. No
600  additional property checks are made. Additionally, new user items
601  may be created if the user has the "Web Registration" Permission.
602 editCSV
603  Determine whether the user has permission to edit this class.
604  Base behaviour is to check the user can edit this class.
605 search
606  Determine whether the user has permission to search this class.
607  Base behaviour is to check the user can view this class.
610 Default templates
611 ~~~~~~~~~~~~~~~~~
613 Most customisation of the web view can be done by modifying the templates in
614 the tracker **html** directory. There are several types of files in there:
616 page
617   defines the overall look of your tracker. When you
618   view an issue, it appears inside this template. When you view an index, it
619   also appears inside this template.
620 home
621   the default page displayed when no other page is indicated by the user
622 home.classlist
623   a special version of the default page that lists the classes in the tracker
624 *classname*.item
625   displays an item of the *classname* class
626 *classname*.index
627   displays a list of *classname* items
628 *classname*.search
629   displays a search page for *classname* items
630 _generic.index
631   used to display a list of items where there is no *classname*.index available
632 _generic.help
633   used to display a "class help" page where there is no *classname*.help
634 user.register
635   a special page just for the user class that renders the registration page
636 style.css
637   a static file that is served up as-is
639 Overall Look - "page" template
640 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
642 XXX
644 How the templates work
645 ~~~~~~~~~~~~~~~~~~~~~~
647 Roundup's templates consist of special attributes on your template tags. These
648 attributes form the Template Attribute Language, or TAL. The commands are:
651 tal:define="variable expression; variable expression; ..."
652    Define a new variable that is local to this tag and its contents. For
653    example::
655       <html tal:define="title request/description">
656        <head><title tal:content="title"></title></head>
657       </html>
659    In the example, the variable "title" is defined as being the result of the
660    expression "request/description". The tal:content command inside the <html>
661    tag may then use the "title" variable.
663 tal:condition="expression"
664    Only keep this tag and its contents if the expression is true. For example::
666      <p tal:condition="python:request.user.hasPermission('View', 'issue')">
667       Display some issue information.
668      </p>
670    In the example, the <p> tag and its contents are only displayed if the
671    user has the View permission for issues. We consider the number zero, a
672    blank string, an empty list, and the built-in variable nothing to be false
673    values. Nearly every other value is true, including non-zero numbers, and
674    strings with anything in them (even spaces!).
676 tal:repeat="variable expression"
677    Repeat this tag and its contents for each element of the sequence that the
678    expression returns, defining a new local variable and a special "repeat"
679    variable for each element. For example::
681      <tr tal:repeat="u user/list">
682       <td tal:content="u/id"></td>
683       <td tal:content="u/username"></td>
684       <td tal:content="u/realname"></td>
685      </tr>
687    The example would iterate over the sequence of users returned by
688    "user/list" and define the local variable "u" for each entry.
690 tal:replace="expression"
691    Replace this tag with the result of the expression. For example::
693     <span tal:replace="request/user/realname"></span>
695    The example would replace the <span> tag and its contents with the user's
696    realname. If the user's realname was "Bruce" then the resultant output
697    would be "Bruce".
699 tal:content="expression"
700    Replace the contents of this tag with the result of the expression. For
701    example::
703     <span tal:content="request/user/realname">user's name appears here</span>
705    The example would replace the contents of the <span> tag with the user's
706    realname. If the user's realname was "Bruce" then the resultant output
707    would be "<span>Bruce</span>".
709 tal:attributes="attribute expression; attribute expression; ..."
710    Set attributes on this tag to the results of expressions. For example::
712      <a tal:attributes="href string:user${request/user/id}">My Details</a>
714    In the example, the "href" attribute of the <a> tag is set to the value of
715    the "string:user${request/user/id}" expression, which will be something
716    like "user123".
718 tal:omit-tag="expression"
719    Remove this tag (but not its contents) if the expression is true. For
720    example::
722       <span tal:omit-tag="python:1">Hello, world!</span>
724    would result in output of::
726       Hello, world!
728 Note that the commands on a given tag are evaulated in the order above, so
729 *define* comes before *condition*, and so on.
731 Additionally, a tag is defined, tal:block, which is removed from output. Its
732 content is not, but the tag itself is (so don't go using any tal:attributes
733 commands on it). This is useful for making arbitrary blocks of HTML
734 conditional or repeatable (very handy for repeating multiple table rows,
735 which would othewise require an illegal tag placement to effect the repeat).
737 The expressions you may use in the attibute values may be one of the following
738 three forms:
740 Path Expressions - eg. ``item/status/checklist``
741    These are object attribute / item accesses. Roughly speaking, the path
742    ``item/status/checklist`` is broken into parts ``item``, ``status``
743    and ``checklist``. The ``item`` part is the root of the expression.
744    We then look for a ``status`` attribute on ``item``, or failing that, a
745    ``status`` item (as in ``item['status']``). If that
746    fails, the path expression fails. When we get to the end, the object we're
747    left with is evaluated to get a string - methods are called, objects are
748    stringified. Path expressions may have an optional ``path:`` prefix, though
749    they are the default expression type, so it's not necessary.
751    XXX | components of expressions
753    XXX "nothing" and "default"
755 String Expressions - eg. ``string:hello ${user/name}``
756    These expressions are simple string interpolations (though they can be just
757    plain strings with no interpolation if you want. The expression in the
758    ``${ ... }`` is just a path expression as above.
760 Python Expressions - eg. ``python: 1+1``
761    These expressions give the full power of Python. All the "root level"
762    variables are available, so ``python:item.status.checklist()`` would be
763    equivalent to ``item/status/checklist``, assuming that ``checklist`` is
764    a method.
766 Information available to templates
767 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
769 The following variables are available to templates.
771 .. taken from roundup.cgi.templating.RoundupPageTemplate docstring
773 *context*
774   The current context. This is either None, a wrapper around a
775   hyperdb class (an HTMLClass) or a wrapper around a hyperdb item (an
776   HTMLItem).
777 *request*
778   Includes information about the current request, including:
779    - the url
780    - the current index information (``filterspec``, ``filter`` args,
781      ``properties``, etc) parsed out of the form. 
782    - methods for easy filterspec link generation
783    - *user*, the current user item as an HTMLItem instance
784    - *form*
785      The current CGI form information as a mapping of form argument
786      name to value
787 *tracker*
788   The current tracker
789 *db*
790   The current database, through which db.config may be reached.
791 *nothing*
792   XXX a special variable
793 *default*
794   XXX a special variable
796 The context variable
797 ::::::::::::::::::::
799 The *context* variable is one of three things based on the current context
800 (see `determining web context`_ for how we figure this out):
802 1. if we're looking at a "home" page, then it's None
803 2. if we're looking at a specific hyperdb class, it's an HTMLClass instance
804 3. if we're looking at a specific hyperdb item, it's an HTMLItem instance
806 If the context is not None, we can access the properties of the class or item.
807 The only real difference between cases 2 and 3 above are:
809 1. the properties may have a real value behind them, and this will appear if
810    the property is displayed through ``context/property`` or
811    ``context/property/field``.
812 2. the context's "id" property will be a false value in the second case, but
813    a real, or true value in the third. Thus we can determine whether we're
814    looking at a real item from the hyperdb by testing "context/id".
817 The request variable
818 ::::::::::::::::::::
820 The request variable is packed with information about the current request.
822 .. taken from roundup.cgi.templating.HTMLRequest docstring
824 =========== ================================================================
825 Variable    Holds
826 =========== ================================================================
827 form        the CGI form as a cgi.FieldStorage
828 env         the CGI environment variables
829 url         the current URL path for this request
830 base        the base URL for this tracker
831 user        a HTMLUser instance for this user
832 classname   the current classname (possibly None)
833 template    the current template (suffix, also possibly None)
834 form        the current CGI form variables in a FieldStorage
835 =========== ================================================================
837 **Index page specific variables (indexing arguments)**
839 =========== ================================================================
840 Variable    Holds
841 =========== ================================================================
842 columns     dictionary of the columns to display in an index page
843 show        a convenience access to columns - request/show/colname will
844               be true if the columns should be displayed, false otherwise
845 sort        index sort column (direction, column name)
846 group       index grouping property (direction, column name)
847 filter      properties to filter the index on
848 filterspec  values to filter the index on
849 search_text text to perform a full-text search on for an index
850 =========== ================================================================
853 Displaying Properties
854 ~~~~~~~~~~~~~~~~~~~~~
856 Properties appear in the user interface in three contexts: in indices, in
857 editors, and as search arguments.
858 For each type of property, there are several display possibilities.
859 For example, in an index view, a string property may just be
860 printed as a plain string, but in an editor view, that property may be
861 displayed in an editable field.
864 Index Views
865 ~~~~~~~~~~~
867 This is one of the class context views. It is also the default view for
868 classes. The template used is "*classname*.index".
870 Index View Specifiers
871 :::::::::::::::::::::
873 An index view specifier (URL fragment) looks like this (whitespace has been
874 added for clarity)::
876      /issue?status=unread,in-progress,resolved&
877             topic=security,ui&
878             :group=+priority&
879             :sort=-activity&
880             :filters=status,topic&
881             :columns=title,status,fixer
883 The index view is determined by two parts of the specifier: the layout part and
884 the filter part. The layout part consists of the query parameters that begin
885 with colons, and it determines the way that the properties of selected items
886 are displayed. The filter part consists of all the other query parameters, and
887 it determines the criteria by which items are selected for display.
888 The filter part is interactively manipulated with the form widgets displayed in
889 the filter section. The layout part is interactively manipulated by clicking on
890 the column headings in the table.
892 The filter part selects the union of the sets of items with values matching any
893 specified Link properties and the intersection of the sets of items with values
894 matching any specified Multilink properties.
896 The example specifies an index of "issue" items. Only items with a "status" of
897 either "unread" or "in-progres" or "resolved" are displayed, and only items
898 with "topic" values including both "security" and "ui" are displayed. The items
899 are grouped by priority, arranged in ascending order; and within groups, sorted
900 by activity, arranged in descending order. The filter section shows filters for
901 the "status" and "topic" properties, and the table includes columns for the
902 "title", "status", and "fixer" properties.
904 Filtering of indexes
905 ::::::::::::::::::::
907 TODO
909 Searching Views
910 ~~~~~~~~~~~~~~~
912 This is one of the class context views. The template used is typically
913 "*classname*.search".
915 TODO
917 Item Views
918 ~~~~~~~~~~
920 The basic view of a hyperdb item is provided by the "*classname*.item"
921 template. It generally has three sections; an "editor", a "spool" and a
922 "history" section.
926 Editor Section
927 ::::::::::::::
929 The editor section is used to manipulate the item - it may be a
930 static display if the user doesn't have permission to edit the item.
932 Here's an example of a basic editor template (this is the default "classic"
933 template issue item edit form - from the "issue.item" template)::
935  <table class="form">
936  <tr>
937   <th nowrap>Title</th>
938   <td colspan=3 tal:content="structure python:context.title.field(size=60)">title</td>
939  </tr>
940  
941  <tr>
942   <th nowrap>Priority</th>
943   <td tal:content="structure context/priority/menu">priority</td>
944   <th nowrap>Status</th>
945   <td tal:content="structure context/status/menu">status</td>
946  </tr>
947  
948  <tr>
949   <th nowrap>Superseder</th>
950   <td>
951    <span tal:replace="structure python:context.superseder.field(showid=1, size=20)" />
952    <span tal:replace="structure python:db.issue.classhelp('id,title', label='list', width=500)" />
953    <span tal:condition="context/superseder">
954     <br>View: <span tal:replace="structure python:context.superseder.link(showid=1)" />
955    </span>
956   </td>
957   <th nowrap>Nosy List</th>
958   <td>
959    <span tal:replace="structure context/nosy/field" />
960    <span tal:replace="structure python:db.user.classhelp('username,realname,address,phone', label='list', width=500)" />
961   </td>
962  </tr>
963  
964  <tr>
965   <th nowrap>Assigned To</th>
966   <td tal:content="structure context/assignedto/menu">
967    assignedto menu
968   </td>
969   <td>&nbsp;</td>
970   <td>&nbsp;</td>
971  </tr>
972  
973  <tr>
974   <th nowrap>Change Note</th>
975   <td colspan=3>
976    <textarea name=":note" wrap="hard" rows="5" cols="60"></textarea>
977   </td>
978  </tr>
979  
980  <tr>
981   <th nowrap>File</th>
982   <td colspan=3><input type="file" name=":file" size="40"></td>
983  </tr>
984  
985  <tr>
986   <td>&nbsp;</td>
987   <td colspan=3 tal:content="structure context/submit">
988    submit button will go here
989   </td>
990  </tr>
991  </table>
994 When a change is submitted, the system automatically generates a message
995 describing the changed properties. As shown in the example, the editor
996 template can use the ":note" and ":file" fields, which are added to the
997 standard change note message generated by Roundup.
999 Spool Section
1000 :::::::::::::
1002 The spool section lists related information like the messages and files of
1003 an issue.
1005 TODO
1008 History Section
1009 :::::::::::::::
1011 The final section displayed is the history of the item - its database journal.
1012 This is generally generated with the template::
1014  <tal:block tal:replace="structure context/history" />
1016 *To be done:*
1018 *The actual history entries of the item may be accessed for manual templating
1019 through the "journal" method of the item*::
1021  <tal:block tal:repeat="entry context/journal">
1022   a journal entry
1023  </tal:block>
1025 *where each journal entry is an HTMLJournalEntry.*
1028 Access Controls
1029 ---------------
1031 A set of Permissions are built in to the security module by default:
1033 - Edit (everything)
1034 - View (everything)
1036 The default interfaces define:
1038 - Web Registration
1039 - Web Access
1040 - Web Roles
1041 - Email Registration
1042 - Email Access
1044 These are hooked into the default Roles:
1046 - Admin (Edit everything, View everything, Web Roles)
1047 - User (Web Access, Email Access)
1048 - Anonymous (Web Registration, Email Registration)
1050 And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
1051 gets the "Anonymous" assigned when the database is initialised on installation.
1052 The two default schemas then define:
1054 - Edit issue, View issue (both)
1055 - Edit file, View file (both)
1056 - Edit msg, View msg (both)
1057 - Edit support, View support (extended only)
1059 and assign those Permissions to the "User" Role. New users are assigned the
1060 Roles defined in the config file as:
1062 - NEW_WEB_USER_ROLES
1063 - NEW_EMAIL_USER_ROLES
1065 You may alter the configuration variables to change the Role that new web or
1066 email users get, for example to not give them access to the web interface if
1067 they register through email.
1069 You may use the ``roundup-admin`` "``security``" command to display the
1070 current Role and Permission configuration in your tracker.
1072 Adding a new Permission
1073 ~~~~~~~~~~~~~~~~~~~~~~~
1075 When adding a new Permission, you will need to:
1077 1. add it to your tracker's dbinit so it is created
1078 2. enable it for the Roles that should have it (verify with
1079    "``roundup-admin security``")
1080 3. add it to the relevant HTML interface templates
1081 4. add it to the appropriate xxxPermission methods on in your tracker
1082    interfaces module
1086 -----------------
1088 Back to `Table of Contents`_
1090 .. _`Table of Contents`: index.html