Code

256583b82ad62b5d254f96dcb09199fd503c5c71
[roundup.git] / doc / customizing.txt
1 ===================
2 Customising Roundup
3 ===================
5 :Version: $Revision: 1.17 $
7 .. contents::
10 What You Can Do
11 ---------------
13 Customisation of Roundup can take one of five forms:
15 1. `instance configuration`_ file changes
16 2. database, or `instance schema`_ changes
17 3. "definition" class `database content`_ changes
18 4. behavioural changes, through detectors_
19 5. `access controls`_
21 The third case is special because it takes two distinctly different forms
22 depending upon whether the instance has been initialised or not. The other two
23 may be done at any time, before or after instance initialisation. Yes, this
24 includes adding or removing properties from classes.
27 Instances in a Nutshell
28 -----------------------
30 Instances have the following structure:
32 +-------------------+--------------------------------------------------------+
33 |instance_config.py |Holds the basic instance_configuration                  |
34 +-------------------+--------------------------------------------------------+
35 |dbinit.py          |Holds the instance_schema                               |
36 +-------------------+--------------------------------------------------------+
37 |interfaces.py      |Defines the Web and E-Mail interfaces for the instance  |
38 +-------------------+--------------------------------------------------------+
39 |select_db.py       |Selects the database back-end for the instance          |
40 +-------------------+--------------------------------------------------------+
41 |db/                |Holds the instance's database                           |
42 +-------------------+--------------------------------------------------------+
43 |db/files/          |Holds the instance's upload files and messages          |
44 +-------------------+--------------------------------------------------------+
45 |detectors/         |Auditors and reactors for this instance                 |
46 +-------------------+--------------------------------------------------------+
47 |html/              |Web interface templates, images and style sheets        |
48 +-------------------+--------------------------------------------------------+
50 Instance Configuration
51 ----------------------
53 The instance_config.py located in your instance home contains the basic
54 configuration for the web and e-mail components of roundup's interfaces. This
55 file is a Python module. The configuration variables available are:
57 **INSTANCE_HOME** - ``os.path.split(__file__)[0]``
58  The instance home directory. The above default code will automatically
59  determine the instance home for you.
61 **MAILHOST** - ``'localhost'``
62  The SMTP mail host that roundup will use to send e-mail.
64 **MAIL_DOMAIN** - ``'your.tracker.email.domain.example'``
65  The domain name used for email addresses.
67 **DATABASE** - ``os.path.join(INSTANCE_HOME, 'db')``
68  This is the directory that the database is going to be stored in. By default
69  it is in the instance home.
71 **TEMPLATES** - ``os.path.join(INSTANCE_HOME, 'html')``
72  This is the directory that the HTML templates reside in. By default they are
73  in the instance home.
75 **INSTANCE_NAME** - ``'Roundup issue tracker'``
76  A descriptive name for your roundup instance. This is sent out in e-mails and
77  appears in the heading of CGI pages.
79 **ISSUE_TRACKER_EMAIL** - ``'issue_tracker@%s'%MAIL_DOMAIN``
80  The email address that e-mail sent to roundup should go to. Think of it as the
81  instance's personal e-mail address.
83 **ISSUE_TRACKER_WEB** - ``'http://your.tracker.url.example/'``
84  The web address that the instance is viewable at. This will be included in
85  information sent to users of the tracker.
87 **ADMIN_EMAIL** - ``'roundup-admin@%s'%MAIL_DOMAIN``
88  The email address that roundup will complain to if it runs into trouble.
90 **FILTER_POSITION** - ``'top'``, ``'bottom'`` or ``'top and bottom'``
91  Where to place the web filtering HTML on the index page.
93 **ANONYMOUS_ACCESS** - ``'deny'`` or ``'allow'``
94  Deny or allow anonymous access to the web interface.
96 **ANONYMOUS_REGISTER** - ``'deny'`` or ``'allow'``
97  Deny or allow anonymous users to register through the web interface.
99 **ANONYMOUS_REGISTER_MAIL** - ``'deny'`` or ``'allow'``
100  Deny or allow anonymous users to register through the mail interface.
102 **MESSAGES_TO_AUTHOR** - ``'yes'`` or``'no'``
103  Send nosy messages to the author of the message.
105 **ADD_AUTHOR_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
106  Does the author of a message get placed on the nosy list automatically?
107  If ``'new'`` is used, then the author will only be added when a message
108  creates a new issue. If ``'yes'``, then the author will be added on followups
109  too. If ``'no'``, they're never added to the nosy.
111 **ADD_RECIPIENTS_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
112  Do the recipients (To:, Cc:) of a message get placed on the nosy list?
113  If ``'new'`` is used, then the recipients will only be added when a message
114  creates a new issue. If ``'yes'``, then the recipients will be added on
115  followups too. If ``'no'``, they're never added to the nosy.
117 **EMAIL_SIGNATURE_POSITION** - ``'top'``, ``'bottom'`` or ``'none'``
118  Where to place the email signature in messages that Roundup generates.
120 **EMAIL_KEEP_QUOTED_TEXT** - ``'yes'`` or ``'no'``
121  Keep email citations. Citations are the part of e-mail which the sender has
122  quoted in their reply to previous e-mail.
124 **EMAIL_LEAVE_BODY_UNCHANGED** - ``'no'``
125  Preserve the email body as is. Enabiling this will cause the entire message
126  body to be stored, including all citations and signatures. It should be
127  either ``'yes'`` or ``'no'``.
129 **MAIL_DEFAULT_CLASS** - ``'issue'`` or ``''``
130  Default class to use in the mailgw if one isn't supplied in email
131  subjects. To disable, comment out the variable below or leave it blank.
133 **HEADER_INDEX_LINKS** - ``['DEFAULT', 'UNASSIGNED', 'USER']``
134  Define what index links are available in the header, and what their
135  labels are. Each key is used to look up one of the index specifications
136  below - so ``'DEFAULT'`` will use ``'DEFAULT_INDEX'``.
138  Example ``DEFAULT_INDEX``::
140   {
141    'LABEL': 'All Issues',
142    'CLASS': 'issue',
143    'SORT': ['-activity'],
144    'GROUP': ['priority'],
145    'FILTER': ['status'],
146    'COLUMNS': ['id','activity','title','creator','assignedto'],
147    'FILTERSPEC': {
148      'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
149    },
150   }
152  This defines one of the index links that appears in the
153  ``HEADER_INDEX_LINKS`` list.
155  **LABEL** - ``'All Issues'``
156   The text that appears as the link label.
157  **CLASS** - ``'issue'``
158   The class to display the index for.
159  **SORT** - ``['-activity']``
160   Sort by prop name, optionally preceeded with '-' to give descending or
161   nothing for ascending sorting.
162  **GROUP** - ``['priority']``
163   Group by prop name, optionally preceeded with '-' or to sort in descending
164   or nothing for ascending order.
165  **FILTER** - ``['status']``
166   Selects which props should be displayed in the filter section.
167   Default is all. 
168  **COLUMNS** - ``['id','activity','title','creator','assignedto']``
169   Selects the columns that should be displayed. Default is all.
170  **FILTERSPEC** - *a dictionary giving the filter specification*
171   The ``FILTERSPEC`` gives the filtering arguments. This selects the values
172   the node properties given by propname must have.
174   Where the ``FILTERSPEC`` value is ``'CURRENT USER'``, it will be replaced
175   by the id of the logged-in user. For example::
177    'FILTERSPEC': {
178      'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
179      'assignedto': 'CURRENT USER',
180    },
182 **HEADER_ADD_LINKS** - ``['issue']``
183  List the classes that users are able to add nodes to.
185 **HEADER_SEARCH_LINKS** - ``['issue']``
186  List the classes that users can search.
188 **SEARCH_FILTERS** - ``['ISSUE_FILTER', 'SUPPORT_FILTER']``
189  List search filters per class. Like the INDEX entries above, each key is
190  used to look up one of the filter specifications below - so ``'ISSUE'``
191  will use ``'ISSUE_FILTER'``.
193  Example ``ISSUE_FILTER``::
195   ISSUE_FILTER = {
196     'CLASS': 'issue',
197     'FILTER': ['status', 'priority', 'assignedto', 'creator']
198   }
200   **CLASS** - ``'issue'``
201    The class that the search page is for.
202   **FILTER** - ``['status', 'priority', 'assignedto', 'creator']``
203    Selects which props should be displayed on the filter page. Default is
204    all.
206 The default instance_config.py is given below - as you
207 can see, the MAIL_DOMAIN must be edited before any interaction with the
208 instance is attempted.::
210     # roundup home is this package's directory
211     INSTANCE_HOME=os.path.split(__file__)[0]
213     # The SMTP mail host that roundup will use to send mail
214     MAILHOST = 'localhost'
216     # The domain name used for email addresses.
217     MAIL_DOMAIN = 'your.tracker.email.domain.example'
219     # the next two are only used for the standalone HTTP server.
220     HTTP_HOST = ''
221     HTTP_PORT = 9080
223     # This is the directory that the database is going to be stored in
224     DATABASE = os.path.join(INSTANCE_HOME, 'db')
226     # This is the directory that the HTML templates reside in
227     TEMPLATES = os.path.join(INSTANCE_HOME, 'html')
229     # A descriptive name for your roundup instance
230     INSTANCE_NAME = 'Roundup issue tracker'
232     # The email address that mail to roundup should go to
233     ISSUE_TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
235     # The web address that the instance is viewable at
236     ISSUE_TRACKER_WEB = 'http://your.tracker.url.example/'
238     # The email address that roundup will complain to if it runs into trouble
239     ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
241     # Somewhere for roundup to log stuff internally sent to stdout or stderr
242     LOG = os.path.join(INSTANCE_HOME, 'roundup.log')
244     # Where to place the web filtering HTML on the index page
245     FILTER_POSITION = 'bottom'          # one of 'top', 'bottom', 'top and bottom'
247     # Deny or allow anonymous access to the web interface
248     ANONYMOUS_ACCESS = 'deny'           # either 'deny' or 'allow'
250     # Deny or allow anonymous users to register through the web interface
251     ANONYMOUS_REGISTER = 'deny'         # either 'deny' or 'allow'
253     # Deny or allow anonymous users to register through the mail interface
254     ANONYMOUS_REGISTER_MAIL = 'deny'    # either 'deny' or 'allow'
256     # Send nosy messages to the author of the message
257     MESSAGES_TO_AUTHOR = 'no'           # either 'yes' or 'no'
259     # Does the author of a message get placed on the nosy list automatically?
260     # If 'new' is used, then the author will only be added when a message
261     # creates a new issue. If 'yes', then the author will be added on followups
262     # too. If 'no', they're never added to the nosy.
263     ADD_AUTHOR_TO_NOSY = 'new'          # one of 'yes', 'no', 'new'
265     # Do the recipients (To:, Cc:) of a message get placed on the nosy list?
266     # If 'new' is used, then the recipients will only be added when a message
267     # creates a new issue. If 'yes', then the recipients will be added on followups
268     # too. If 'no', they're never added to the nosy.
269     ADD_RECIPIENTS_TO_NOSY = 'new'      # either 'yes', 'no', 'new'
271     # Where to place the email signature
272     EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
274     # Keep email citations
275     EMAIL_KEEP_QUOTED_TEXT = 'no'       # either 'yes' or 'no'
277     # Preserve the email body as is
278     EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
280     # Default class to use in the mailgw if one isn't supplied in email
281     # subjects. To disable, comment out the variable below or leave it blank.
282     # Examples:
283     MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
284     #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
286     # Define what index links are available in the header, and what their
287     # labels are. Each key is used to look up one of the index specifications
288     # below - so 'DEFAULT' will use 'DEFAULT_INDEX'.
289     # Where the FILTERSPEC has 'assignedto' with a value of None, it will be
290     # replaced by the id of the logged-in user.
291     HEADER_INDEX_LINKS = ['DEFAULT', 'UNASSIGNED', 'USER']
293     # list the classes that users are able to add nodes to
294     HEADER_ADD_LINKS = ['issue']
296     # list the classes that users can search
297     HEADER_SEARCH_LINKS = ['issue']
299     # list search filters per class
300     SEARCH_FILTERS = ['ISSUE_FILTER', 'SUPPORT_FILTER']
302     # Now the DEFAULT display specification. TODO: describe format
303     DEFAULT_INDEX = {
304       'LABEL': 'All Issues',
305       'CLASS': 'issue',
306       'SORT': ['-activity'],
307       'GROUP': ['priority'],
308       'FILTER': ['status'],
309       'COLUMNS': ['id','activity','title','creator','assignedto'],
310       'FILTERSPEC': {
311         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
312       },
313     }
315     # The "unsassigned issues" index
316     UNASSIGNED_INDEX = {
317       'LABEL': 'Unassigned Issues',
318       'CLASS': 'issue',
319       'SORT': ['-activity'],
320       'GROUP': ['priority'],
321       'FILTER': ['status', 'assignedto'],
322       'COLUMNS': ['id','activity','title','creator','status'],
323       'FILTERSPEC': {
324         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
325         'assignedto': ['-1'],
326       },
327     }
329     # The "my issues" index -- note that the user's id will replace the
330     # 'CURRENT USER' value of the "assignedto" filterspec
331     USER_INDEX = {
332       'LABEL': 'My Issues',
333       'CLASS': 'issue',
334       'SORT': ['-activity'],
335       'GROUP': ['priority'],
336       'FILTER': ['status', 'assignedto'],
337       'COLUMNS': ['id','activity','title','creator','status'],
338       'FILTERSPEC': {
339         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
340         'assignedto': 'CURRENT USER',
341       },
342     }
344     ISSUE_FILTER = {
345       'CLASS': 'issue',
346       'FILTER': ['status', 'priority', 'assignedto', 'creator']
347     }
349     SUPPORT_FILTER = {
350       'CLASS': 'issue',
351       'FILTER': ['status', 'priority', 'assignedto', 'creator']
352     }
355 Instance Schema
356 ---------------
358 Note: if you modify the schema, you'll most likely need to edit the
359       `web interface`_ HTML template files and `detectors`_ to reflect
360       your changes.
362 An instance schema defines what data is stored in the instance's database. The
363 two schemas shipped with Roundup turn it into a typical software bug tracker
364 (the extended schema allowing for support issues as well as bugs). Schemas are
365 defined using Python code. The "classic" schema looks like this::
367     pri = Class(db, "priority", name=String(), order=String())
368     pri.setkey("name")
369     pri.create(name="critical", order="1")
370     pri.create(name="urgent", order="2")
371     pri.create(name="bug", order="3")
372     pri.create(name="feature", order="4")
373     pri.create(name="wish", order="5")
375     stat = Class(db, "status", name=String(), order=String())
376     stat.setkey("name")
377     stat.create(name="unread", order="1")
378     stat.create(name="deferred", order="2")
379     stat.create(name="chatting", order="3")
380     stat.create(name="need-eg", order="4")
381     stat.create(name="in-progress", order="5")
382     stat.create(name="testing", order="6")
383     stat.create(name="done-cbb", order="7")
384     stat.create(name="resolved", order="8")
386     keyword = Class(db, "keyword", name=String())
387     keyword.setkey("name")
389     user = Class(db, "user", username=String(), password=String(),
390         address=String(), realname=String(), phone=String(),
391         organisation=String())
392     user.setkey("username")
393     user.create(username="admin", password=adminpw,
394         address=instance_config.ADMIN_EMAIL)
396     msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
397         ("user"), date=Date(), summary=String(), files=Multilink("file"))
399     file = FileClass(db, "file", name=String(), type=String())
401     issue = IssueClass(db, "issue", assignedto=Link("user"),
402         topic=Multilink("keyword"), priority=Link("priority"), status=Link
403         ("status"))
404     issue.setkey('title')
406 Classes and Properties - creating a new information store
407 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
409 In the instance above, we've defined 7 classes of information:
411   priority
412       Defines the possible levels of urgency for issues.
414   status
415       Defines the possible states of processing the issue may be in.
417   keyword
418       Initially empty, will hold keywords useful for searching issues.
420   user
421       Initially holding the "admin" user, will eventually have an entry for all
422       users using roundup.
424   msg
425       Initially empty, will all e-mail messages sent to or generated by
426       roundup.
428   file
429       Initially empty, will all files attached to issues.
431   issue
432       Initially emtyp, this is where the issue information is stored.
434 We define the "priority" and "status" classes to allow two things: reduction in
435 the amount of information stored on the issue and more powerful, accurate
436 searching of issues by priority and status. By only requiring a link on the
437 issue (which is stored as a single number) we reduce the chance that someone
438 mis-types a priority or status - or simply makes a new one up.
440 Class and Nodes
441 :::::::::::::::
443 A Class defines a particular class (or type) of data that will be stored in the
444 database. A class comprises one or more properties, which given the information
445 about the class nodes.
446 The actual data entered into the database, using class.create() are called
447 nodes. They have a special immutable property called id. We sometimes refer to
448 this as the nodeid.
450 Properties
451 ::::::::::
453 A Class is comprised of one or more properties of the following types:
454     * String properties are for storing arbitrary-length strings.
455     * Password properties are for storing encoded arbitrary-length strings. The
456       default encoding is defined on the roundup.password.Password class.
457     * Date properties store date-and-time stamps. Their values are Timestamp
458       objects.
459     * A Link property refers to a single other node selected from a specified
460       class. The class is part of the property; the value is an integer, the id
461       of the chosen node.
462     * A Multilink property refers to possibly many nodes in a specified class.
463       The value is a list of integers.
465 FileClass
466 :::::::::
468 FileClasses save their "content" attribute off in a separate file from the rest
469 of the database. This reduces the number of large entries in the database,
470 which generally makes databases more efficient, and also allows us to use
471 command-line tools to operate on the files. They are stored in the files sub-
472 directory of the db directory in your instance.
474 IssueClass
475 ::::::::::
477 IssueClasses automatically include the "messages", "files", "nosy", and
478 "superseder" properties.
479 The messages and files properties list the links to the messages and files
480 related to the issue. The nosy property is a list of links to users who wish to
481 be informed of changes to the issue - they get "CC'ed" e-mails when messages
482 are sent to or generated by the issue. The nosy reactor (in the detectors
483 directory) handles this action. The superceder link indicates an issue which
484 has superceded this one.
485 They also have the dynamically generated "creation", "activity" and "creator"
486 properties.
487 The value of the "creation" property is the date when a node was created, and
488 the value of the "activity" property is the date when any property on the node
489 was last edited (equivalently, these are the dates on the first and last
490 records in the node's journal). The "creator" property holds a link to the user
491 that created the issue.
493 setkey(property)
494 ::::::::::::::::
496 Select a String property of the class to be the key property. The key property
497 muse be unique, and allows references to the nodes in the class by the content
498 of the key property. That is, we can refer to users by their username, e.g.
499 let's say that there's an issue in roundup, issue 23. There's also a user,
500 richard who happens to be user 2. To assign an issue to him, we could do either
501 of::
503      roundup-admin set issue assignedto=2
505 or::
507      roundup-admin set issue assignedto=richard
509 Note, the same thing can be done in the web and e-mail interfaces.
511 create(information)
512 :::::::::::::::::::
514 Create a node in the database. This is generally used to create nodes in the
515 "definitional" classes like "priority" and "status".
518 Examples of adding to your schema
519 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
521 TODO
524 Detectors - adding behaviour to your tracker
525 --------------------------------------------
526 .. _detectors:
528 The detectors in your instance fire before (*auditors*) and after (*reactors*)
529 changes to the contents of your database. They are Python modules that sit in
530 your instance's ``detectors`` directory. You will have some installed by
531 default - have a look. You can write new detectors or modify the existing
532 ones. The existing detectors installed for you are:
534 **nosyreaction.py**
535   This provides the automatic nosy list maintenance and email sending. The nosy
536   reactor (``nosyreaction``) fires when new messages are added to issues.
537   The nosy auditor (``updatenosy``) fires when issues are changed and figures
538   what changes need to be made to the nosy list (like adding new authors etc)
539 **statusauditor.py**
540   This provides the ``chatty`` auditor which changes the issue status from
541   ``unread`` or ``closed`` to ``chatting`` if new messages appear. It also
542   provides the ``presetunread`` auditor which pre-sets the status to
543   ``unread`` on new nodes if the status isn't explicitly defined.
545 See the detectors section in the `design document`__ for details of the
546 interface for detectors.
548 __ design.html
550 Sample additional detectors that have been found useful will appear in the
551 ``detectors`` directory of the Roundup distribution:
553 **newissuecopy.py**
554   This detector sends an email to a team address whenever a new issue is
555   created. The address is hard-coded into the detector, so edit it before you
556   use it (look for the text 'team@team.host') or you'll get email errors!
559 Database Content
560 ----------------
562 Note: if you modify the content of definitional classes, you'll most likely
563        need to edit the instance `detectors`_ to reflect your changes.
565 Customisation of the special "definitional" classes (eg. status, priority,
566 resolution, ...) may be done either before or after the instance is
567 initialised. The actual method of doing so is completely different in each
568 case though, so be careful to use the right one.
570 **Changing content before instance initialisation**
571     Edit the dbinit module in your instance to alter the nodes created in using
572     the create() methods.
575 **Changing content after instance initialisation**
576     Use the roundup-admin interface's create, set and retire methods to add,
577     alter or remove nodes from the classes in question.
581 Web Interface
582 -------------
584 The web interface works behind the cgi-bin/roundup.cgi or roundup-server
585 scripts. In both cases, the scripts determine which instance is being accessed
586 (the first part of the URL path inside the scope of the CGI handler) and pass
587 control on to the instance interfaces.Client class which handles the rest of
588 the access through its main() method. This means that you can do pretty much
589 anything you want as a web interface to your instance.
591 Most customisation of the web view can be done by modifying the templates in
592 the instance **html** directory. There are several types of files in there:
594 page
595   defines the overall look of your tracker. When you
596   view an issue, it appears inside this template. When you view an index, it
597   also appears inside this template.
598 home
599   the default page displayed when no other page is indicated by the user
600 home.classlist
601   a special version of the default page that lists the classes in the tracker
602 *classname*.item
603   displays an item of the *classname* class
604 *classname*.index
605   displays a list of *classname* items
606 *classname*.search
607   displays a search page for *classname* items
608 _generic.index
609   used to display a list of items where there is no *classname*.index available
610 user.register
611   a special page just for the user class that renders the registration page
612 style.css
613   a static file that is served up as-is
615 The basic processing of a web request proceeds as follows:
617 1. figure out who we are, defaulting to the "anonymous" user
618 2. figure out what the request is for - we call this the "context"
619 3. handle any requested action (item edit, search, ...)
620 4. render a template, resulting in HTML output
622 In some situations, exceptions occur:
623 - HTTP Redirect  (generally raised by an action)
624 - SendFile       (generally raised by determine_context)
625   here we serve up a FileClass "content" property
626 - SendStaticFile (generally raised by determine_context)
627   here we serve up a file from the tracker "html" directory
628 - Unauthorised   (generally raised by an action)
629   here the action is cancelled, the request is rendered and an error
630   message is displayed indicating that permission was not
631   granted for the action to take place
632 - NotFound       (raised wherever it needs to be)
633   this exception percolates up to the CGI interface that called the client
635 To determine the "context" of a request, we look at the URL and the special
636 request variable ``:template``. The URL path after the instance identifier
637 is examined. Typical URL paths look like:
639 1.  ``/tracker/issue``
640 2.  ``/tracker/issue1``
641 3.  ``/tracker/_file/style.css``
642 4.  ``/cgi-bin/roundup.cgi/tracker/file1``
643 5.  ``/cgi-bin/roundup.cgi/tracker/file1/kitten.png``
645 where the "instance identifier" is "tracker" in the above cases. That means
646 we're looking at "issue", "issue1", "_file/style.css", "file1" and
647 "file1/kitten.png" in the cases above. The path is generally only one
648 entry long - longer paths are handled differently.
650 a. if there is no path, then we are in the "home" context.
651 b. if the path starts with "_file" (as in example 3,
652    "/tracker/_file/style.css"), then the additional path entry,
653    "style.css" specifies the filename of a static file we're to serve up
654    from the instance "html" directory. Raises a SendStaticFile
655    exception.
656 c. if there is something in the path (as in example 1, "issue"), it identifies
657    the tracker class we're to display.
658 d. if the path is an item designator (as in examples 2 and 4, "issue1" and
659    "file1"), then we're to display a specific item.
660 e. if the path starts with an item designator and is longer than
661    one entry (as in example 5, "file1/kitten.png"), then we're assumed
662    to be handling an item of a
663    FileClass, and the extra path information gives the filename
664    that the client is going to label the download with (ie
665    "file1/kitten.png" is nicer to download than "file1"). This
666    raises a SendFile exception.
668 Both b. and e. stop before we bother to
669 determine the template we're going to use. That's because they
670 don't actually use templates.
672 The template used is specified by the ``:template`` CGI variable,
673 which defaults to:
675 - only classname suplied:          "index"
676 - full item designator supplied:   "item"
679 Repurcussions of changing the instance schema
680 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
682 If you choose to change the `instance schema`_ you will need to ensure the web
683 interface knows about it:
685 1. Index, item and search pages for the relevant classes may need to have
686    properties added or removed,
687 2. The "page" template may require links to be changed, as might the "home"
688    page's content arguments.
690 Overall Look - "page" template
691 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
693 The "page" template in your instances
694 roundup.cgi_client.Class. This class is mixed-in to your instance through the
695 instance's interfaces module. This means you can override the header and
696 footer with your own code. This allows you to use a sidebar navigation scheme,
697 for example.
700 PageTemplates in a Nutshell
701 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
703 PageTemplates consist of two core technologies:
705 TAL - Template Attribute Language
706   This is the syntax which is woven into the HTML using the ``tal:`` tag
707   attributes. A TAL parser pulls out the TAL commands from the attributes
708   runs them using some expression engine. TAL gives:
710   tal:define
711   tal:replace
712   tal:content
713   tal:repeat
714   tal:attributes
716   Additionally, a tag is defined, tal:block, which is removed from output. Its
717   content is not, but the tag itself is (so don't go using any tal:attributes
718   commands on it). This is useful for making arbitrary blocks of HTML
719   conditional or repeatable (very handy for repeating multiple table rows,
720   which would othewise require an illegal tag placement to effect the repeat).
722 TALES - TAL Expression Syntax
723   The expression engine used in this case is TALES, which runs the expressions
724   that form the tag attribute values. TALES expressions come in three
725   flavours:
727   Path Expressions - eg. ``item/status/checklist``
728    These are object attribute / item accesses. Roughly speaking, the path
729    ``item/status/checklist`` is broken into parts ``item``, ``status``
730    and ``checklist``. The ``item`` part is the root of the expression.
731    We then look for a ``status`` attribute on ``item``, or failing that, a
732    ``status`` item (as in ``item['status']``). If that
733    fails, the path expression fails. When we get to the end, the object we're
734    left with is evaluated to get a string - methods are called, objects are
735    stringified. Path expressions may have an optional ``path:`` prefix, though
736    they are the default expression type, so it's not necessary.
738   String Expressions - eg. ``string:hello ${user/name}``
739    These expressions are simple string interpolations (though they can be just
740    plain strings with no interpolation if you want. The expression in the
741    ``${ ... }`` is just a path expression as above.
743   Python Expressions - eg. ``python: 1+1``
744    These expressions give the full power of Python. All the "root level"
745    variables are available, so ``python:item.status.checklist()`` would be
746    equivalent to ``item/status/checklist``, assuming that ``checklist`` is
747    a method.
749 Displaying Properties
750 ~~~~~~~~~~~~~~~~~~~~~
752 Properties appear in the user interface in three contexts: in indices, in
753 editors, and as search arguments.
754 For each type of property, there are several display possibilities.
755 For example, in an index view, a string property may just be
756 printed as a plain string, but in an editor view, that property may be
757 displayed in an editable field.
760 Index Views
761 ~~~~~~~~~~~
763 Index View Specifiers
764 :::::::::::::::::::::
766 An index view specifier (URL fragment) looks like this (whitespace has been
767 added for clarity)::
769      /issue?status=unread,in-progress,resolved&
770              topic=security,ui&
771              :group=+priority&
772              :sort=-activity&
773              :filters=status,topic&
774              :columns=title,status,fixer
776 The index view is determined by two parts of the specifier: the layout part and
777 the filter part. The layout part consists of the query parameters that begin
778 with colons, and it determines the way that the properties of selected nodes
779 are displayed. The filter part consists of all the other query parameters, and
780 it determines the criteria by which nodes are selected for display.
781 The filter part is interactively manipulated with the form widgets displayed in
782 the filter section. The layout part is interactively manipulated by clicking on
783 the column headings in the table.
785 The filter part selects the union of the sets of items with values matching any
786 specified Link properties and the intersection of the sets of items with values
787 matching any specified Multilink properties.
789 The example specifies an index of "issue" nodes. Only items with a "status" of
790 either "unread" or "in-progres" or "resolved" are displayed, and only items
791 with "topic" values including both "security" and "ui" are displayed. The items
792 are grouped by priority, arranged in ascending order; and within groups, sorted
793 by activity, arranged in descending order. The filter section shows filters for
794 the "status" and "topic" properties, and the table includes columns for the
795 "title", "status", and "fixer" properties.
797 Associated with each item class is a default layout specifier. The layout
798 specifier in the above example is the default layout to be provided with the
799 default bug-tracker schema described above in section 4.4.
801 Filtering of indexes
802 ::::::::::::::::::::
804 TODO
806 Searching Views
807 ~~~~~~~~~~~~~~~
809 TODO
811 Item Views
812 ~~~~~~~~~~
814 An item view contains an editor section and a spool section. At the top of an
815 item view, links to superseding and superseded items are always displayed.
817 Editor Section
818 ::::::::::::::
820 The editor section is generated from a template containing <display> tags to
821 insert the appropriate widgets for editing properties.
823 Here's an example of a basic editor template.::
825      <table>
826      <tr>
827          <td colspan=2>
828              <display call="field('title', size=60)">
829          </td>
830      </tr>
831      <tr>
832          <td>
833              <display call="field('fixer', size=30)">
834          </td>
835          <td>
836              <display call="menu('status')>
837          </td>
838      </tr>
839      <tr>
840          <td>
841              <display call="field('nosy', size=30)">
842          </td>
843          <td>
844              <display call="menu('priority')>
845          </td>
846      </tr>
847      <tr>
848          <td colspan=2>
849              <display call="note()">
850          </td>
851      </tr>
852      </table>
854 As shown in the example, the editor template can also request the display of a
855 "note" field, which is a text area for entering a note to go along with a
856 change.
858 The <property> tag used in the index may also be used here - it checks to see
859 if the nominated Multilink property has any entries. This can be used to
860 eliminate sections of the editor section if the property has no entries::
862   <td class="form-text">
863     <display call="field('superseder', size=40, showid=1)">
864     <display call="classhelp('issue', 'id,title', label='list', width=500)">
865     <property name="superseder">
866       <br>View: <display call="link('superseder', showid=1)">
867     </property>
868   </td>
870 The "View: " part with the links will only display if the superseder property
871 has values.
873 When a change is submitted, the system automatically generates a message
874 describing the changed properties.
876 If a note is given in the "note" field, the note is appended to the
877 description. The message is then added to the item's message spool (thus
878 triggering the standard detector to react by sending out this message to the
879 nosy list).
881 The message also displays all of the property values on the item and indicates
882 which ones have changed. An example of such a message might be this::
884      Polly's taken a turn for the worse - this is now really important!
885      -----
886      title: Polly Parrot is dead
887      priority: critical
888      status: unread -> in-progress
889      fixer: terry
890      keywords: parrot,plumage,perch,nailed,dead
892 Spool Section
893 :::::::::::::
895 The spool section lists messages in the item's "messages" property. The index
896 of messages displays the "date", "author", and "summary" properties on the
897 message nodes, and selecting a message takes you to its content.
899 The <property> tag used in the index may also be used here - it checks to see
900 if the nominated Multilink property has any entries. This can be used to
901 eliminate sections of the spool section if the property has no entries::
903      <property name="files">
904       <tr class="strong-header">
905        <td><b>Files</b></td>
906       </tr>
908       <tr>
909        <td><display call="list('files')"></td>
910       </tr>
911      </property>
914 Access Controls
915 ---------------
917 A set of Permissions are built in to the security module by default:
919 - Edit (everything)
920 - View (everything)
922 The default interfaces define:
924 - Web Registration
925 - Web Access
926 - Web Roles
927 - Email Registration
928 - Email Access
930 These are hooked into the default Roles:
932 - Admin (Edit everything, View everything, Web Roles)
933 - User (Web Access, Email Access)
934 - Anonymous (Web Registration, Email Registration)
936 And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
937 gets the "Anonymous" assigned when the database is initialised on installation.
938 The two default schemas then define:
940 - Edit issue, View issue (both)
941 - Edit file, View file (both)
942 - Edit msg, View msg (both)
943 - Edit support, View support (extended only)
945 and assign those Permissions to the "User" Role. New users are assigned the
946 Roles defined in the config file as:
948 - NEW_WEB_USER_ROLES
949 - NEW_EMAIL_USER_ROLES
951 You may alter the configuration variables to change the Role that new web or
952 email users get, for example to not give them access to the web interface if
953 they register through email.
955 You may use the ``roundup-admin`` "``security``" command to display the
956 current Role and Permission configuration in your instance.
958 Adding a new Permission
959 ~~~~~~~~~~~~~~~~~~~~~~~
961 When adding a new Permission, you will need to:
963 1. add it to your instance's dbinit so it is created
964 2. enable it for the Roles that should have it (verify with
965    "``roundup-admin security``")
966 3. add it to the relevant HTML interface templates
967 4. add it to the appropriate xxxPermission methods on in your instance
968    interfaces module
972 -----------------
974 Back to `Table of Contents`_
976 .. _`Table of Contents`: index.html