Code

documentation updates
[roundup.git] / doc / customizing.txt
1 ===================
2 Customising Roundup
3 ===================
5 :Version: $Revision: 1.12 $
7 .. contents::
10 What You Can Do
11 ---------------
13 Customisation of Roundup can take one of three forms:
15 1. `instance configuration`_ file changes
16 2. `instance schema`_ changes
17 3. "definition" class `database content`_ changes
19 The third case is special because it takes two distinctly different forms
20 depending upon whether the instance has been initialised or not. The other two
21 may be done at any time, before or after instance initialisation. Yes, this
22 includes adding or removing properties from classes.
25 Instances in a Nutshell
26 -----------------------
28 Instances have the following structure:
30 +-------------------+--------------------------------------------------------+
31 |instance_config.py |Holds the basic instance_configuration                  |
32 +-------------------+--------------------------------------------------------+
33 |dbinit.py          |Holds the instance_schema                               |
34 +-------------------+--------------------------------------------------------+
35 |interfaces.py      |Defines the Web and E-Mail interfaces for the instance  |
36 +-------------------+--------------------------------------------------------+
37 |select_db.py       |Selects the database back-end for the instance          |
38 +-------------------+--------------------------------------------------------+
39 |db/                |Holds the instance's database                           |
40 +-------------------+--------------------------------------------------------+
41 |db/files/          |Holds the instance's upload files and messages          |
42 +-------------------+--------------------------------------------------------+
43 |detectors/         |Auditors and reactors for this instance                 |
44 +-------------------+--------------------------------------------------------+
45 |html/              |Web interface templates, images and style sheets        |
46 +-------------------+--------------------------------------------------------+
48 Instance Configuration
49 ----------------------
51 The instance_config.py located in your instance home contains the basic
52 configuration for the web and e-mail components of roundup's interfaces. This
53 file is a Python module. The configuration variables available are:
55 **INSTANCE_HOME** - ``os.path.split(__file__)[0]``
56  The instance home directory. The above default code will automatically
57  determine the instance home for you.
59 **MAILHOST** - ``'localhost'``
60  The SMTP mail host that roundup will use to send e-mail.
62 **MAIL_DOMAIN** - ``'your.tracker.email.domain.example'``
63  The domain name used for email addresses.
65 **DATABASE** - ``os.path.join(INSTANCE_HOME, 'db')``
66  This is the directory that the database is going to be stored in. By default
67  it is in the instance home.
69 **TEMPLATES** - ``os.path.join(INSTANCE_HOME, 'html')``
70  This is the directory that the HTML templates reside in. By default they are
71  in the instance home.
73 **INSTANCE_NAME** - ``'Roundup issue tracker'``
74  A descriptive name for your roundup instance. This is sent out in e-mails and
75  appears in the heading of CGI pages.
77 **ISSUE_TRACKER_EMAIL** - ``'issue_tracker@%s'%MAIL_DOMAIN``
78  The email address that e-mail sent to roundup should go to. Think of it as the
79  instance's personal e-mail address.
81 **ISSUE_TRACKER_WEB** - ``'http://your.tracker.url.example/'``
82  The web address that the instance is viewable at. This will be included in
83  information sent to users of the tracker.
85 **ADMIN_EMAIL** - ``'roundup-admin@%s'%MAIL_DOMAIN``
86  The email address that roundup will complain to if it runs into trouble.
88 **FILTER_POSITION** - ``'top'``, ``'bottom'`` or ``'top and bottom'``
89  Where to place the web filtering HTML on the index page.
91 **ANONYMOUS_ACCESS** - ``'deny'`` or ``'allow'``
92  Deny or allow anonymous access to the web interface.
94 **ANONYMOUS_REGISTER** - ``'deny'`` or ``'allow'``
95  Deny or allow anonymous users to register through the web interface.
97 **ANONYMOUS_REGISTER_MAIL** - ``'deny'`` or ``'allow'``
98  Deny or allow anonymous users to register through the mail interface.
100 **MESSAGES_TO_AUTHOR** - ``'yes'`` or``'no'``
101  Send nosy messages to the author of the message.
103 **ADD_AUTHOR_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
104  Does the author of a message get placed on the nosy list automatically?
105  If ``'new'`` is used, then the author will only be added when a message
106  creates a new issue. If ``'yes'``, then the author will be added on followups
107  too. If ``'no'``, they're never added to the nosy.
109 **ADD_RECIPIENTS_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
110  Do the recipients (To:, Cc:) of a message get placed on the nosy list?
111  If ``'new'`` is used, then the recipients will only be added when a message
112  creates a new issue. If ``'yes'``, then the recipients will be added on
113  followups too. If ``'no'``, they're never added to the nosy.
115 **EMAIL_SIGNATURE_POSITION** - ``'top'``, ``'bottom'`` or ``'none'``
116  Where to place the email signature in messages that Roundup generates.
118 **EMAIL_KEEP_QUOTED_TEXT** - ``'yes'`` or ``'no'``
119  Keep email citations. Citations are the part of e-mail which the sender has
120  quoted in their reply to previous e-mail.
122 **EMAIL_LEAVE_BODY_UNCHANGED** - ``'no'``
123  Preserve the email body as is. Enabiling this will cause the entire message
124  body to be stored, including all citations and signatures. It should be
125  either ``'yes'`` or ``'no'``.
127 **MAIL_DEFAULT_CLASS** - ``'issue'`` or ``''``
128  Default class to use in the mailgw if one isn't supplied in email
129  subjects. To disable, comment out the variable below or leave it blank.
131 **HEADER_INDEX_LINKS** - ``['DEFAULT', 'UNASSIGNED', 'USER']``
132  Define what index links are available in the header, and what their
133  labels are. Each key is used to look up one of the index specifications
134  below - so ``'DEFAULT'`` will use ``'DEFAULT_INDEX'``.
136  Example ``DEFAULT_INDEX``::
138   {
139    'LABEL': 'All Issues',
140    'CLASS': 'issue',
141    'SORT': ['-activity'],
142    'GROUP': ['priority'],
143    'FILTER': ['status'],
144    'COLUMNS': ['id','activity','title','creator','assignedto'],
145    'FILTERSPEC': {
146      'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
147    },
148   }
150  This defines one of the index links that appears in the
151  ``HEADER_INDEX_LINKS`` list.
153  **LABEL** - ``'All Issues'``
154   The text that appears as the link label.
155  **CLASS** - ``'issue'``
156   The class to display the index for.
157  **SORT** - ``['-activity']``
158   Sort by prop name, optionally preceeded with '-' to give descending or
159   nothing for ascending sorting.
160  **GROUP** - ``['priority']``
161   Group by prop name, optionally preceeded with '-' or to sort in descending
162   or nothing for ascending order.
163  **FILTER** - ``['status']``
164   Selects which props should be displayed in the filter section.
165   Default is all. 
166  **COLUMNS** - ``['id','activity','title','creator','assignedto']``
167   Selects the columns that should be displayed. Default is all.
168  **FILTERSPEC** - *a dictionary giving the filter specification*
169   The ``FILTERSPEC`` gives the filtering arguments. This selects the values
170   the node properties given by propname must have.
172   Where the ``FILTERSPEC`` value is ``'CURRENT USER'``, it will be replaced
173   by the id of the logged-in user. For example::
175    'FILTERSPEC': {
176      'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
177      'assignedto': 'CURRENT USER',
178    },
180 **HEADER_ADD_LINKS** - ``['issue']``
181  List the classes that users are able to add nodes to.
183 **HEADER_SEARCH_LINKS** - ``['issue']``
184  List the classes that users can search.
186 **SEARCH_FILTERS** - ``['ISSUE_FILTER', 'SUPPORT_FILTER']``
187  List search filters per class. Like the INDEX entries above, each key is
188  used to look up one of the filter specifications below - so ``'ISSUE'``
189  will use ``'ISSUE_FILTER'``.
191  Example ``ISSUE_FILTER``::
193   ISSUE_FILTER = {
194     'CLASS': 'issue',
195     'FILTER': ['status', 'priority', 'assignedto', 'creator']
196   }
198   **CLASS** - ``'issue'``
199    The class that the search page is for.
200   **FILTER** - ``['status', 'priority', 'assignedto', 'creator']``
201    Selects which props should be displayed on the filter page. Default is
202    all.
204 The default instance_config.py is given below - as you
205 can see, the MAIL_DOMAIN must be edited before any interaction with the
206 instance is attempted.::
208     # roundup home is this package's directory
209     INSTANCE_HOME=os.path.split(__file__)[0]
211     # The SMTP mail host that roundup will use to send mail
212     MAILHOST = 'localhost'
214     # The domain name used for email addresses.
215     MAIL_DOMAIN = 'your.tracker.email.domain.example'
217     # the next two are only used for the standalone HTTP server.
218     HTTP_HOST = ''
219     HTTP_PORT = 9080
221     # This is the directory that the database is going to be stored in
222     DATABASE = os.path.join(INSTANCE_HOME, 'db')
224     # This is the directory that the HTML templates reside in
225     TEMPLATES = os.path.join(INSTANCE_HOME, 'html')
227     # A descriptive name for your roundup instance
228     INSTANCE_NAME = 'Roundup issue tracker'
230     # The email address that mail to roundup should go to
231     ISSUE_TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
233     # The web address that the instance is viewable at
234     ISSUE_TRACKER_WEB = 'http://your.tracker.url.example/'
236     # The email address that roundup will complain to if it runs into trouble
237     ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
239     # Somewhere for roundup to log stuff internally sent to stdout or stderr
240     LOG = os.path.join(INSTANCE_HOME, 'roundup.log')
242     # Where to place the web filtering HTML on the index page
243     FILTER_POSITION = 'bottom'          # one of 'top', 'bottom', 'top and bottom'
245     # Deny or allow anonymous access to the web interface
246     ANONYMOUS_ACCESS = 'deny'           # either 'deny' or 'allow'
248     # Deny or allow anonymous users to register through the web interface
249     ANONYMOUS_REGISTER = 'deny'         # either 'deny' or 'allow'
251     # Deny or allow anonymous users to register through the mail interface
252     ANONYMOUS_REGISTER_MAIL = 'deny'    # either 'deny' or 'allow'
254     # Send nosy messages to the author of the message
255     MESSAGES_TO_AUTHOR = 'no'           # either 'yes' or 'no'
257     # Does the author of a message get placed on the nosy list automatically?
258     # If 'new' is used, then the author will only be added when a message
259     # creates a new issue. If 'yes', then the author will be added on followups
260     # too. If 'no', they're never added to the nosy.
261     ADD_AUTHOR_TO_NOSY = 'new'          # one of 'yes', 'no', 'new'
263     # Do the recipients (To:, Cc:) of a message get placed on the nosy list?
264     # If 'new' is used, then the recipients will only be added when a message
265     # creates a new issue. If 'yes', then the recipients will be added on followups
266     # too. If 'no', they're never added to the nosy.
267     ADD_RECIPIENTS_TO_NOSY = 'new'      # either 'yes', 'no', 'new'
269     # Where to place the email signature
270     EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
272     # Keep email citations
273     EMAIL_KEEP_QUOTED_TEXT = 'no'       # either 'yes' or 'no'
275     # Preserve the email body as is
276     EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
278     # Default class to use in the mailgw if one isn't supplied in email
279     # subjects. To disable, comment out the variable below or leave it blank.
280     # Examples:
281     MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
282     #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
284     # Define what index links are available in the header, and what their
285     # labels are. Each key is used to look up one of the index specifications
286     # below - so 'DEFAULT' will use 'DEFAULT_INDEX'.
287     # Where the FILTERSPEC has 'assignedto' with a value of None, it will be
288     # replaced by the id of the logged-in user.
289     HEADER_INDEX_LINKS = ['DEFAULT', 'UNASSIGNED', 'USER']
291     # list the classes that users are able to add nodes to
292     HEADER_ADD_LINKS = ['issue']
294     # list the classes that users can search
295     HEADER_SEARCH_LINKS = ['issue']
297     # list search filters per class
298     SEARCH_FILTERS = ['ISSUE_FILTER', 'SUPPORT_FILTER']
300     # Now the DEFAULT display specification. TODO: describe format
301     DEFAULT_INDEX = {
302       'LABEL': 'All Issues',
303       'CLASS': 'issue',
304       'SORT': ['-activity'],
305       'GROUP': ['priority'],
306       'FILTER': ['status'],
307       'COLUMNS': ['id','activity','title','creator','assignedto'],
308       'FILTERSPEC': {
309         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
310       },
311     }
313     # The "unsassigned issues" index
314     UNASSIGNED_INDEX = {
315       'LABEL': 'Unassigned Issues',
316       'CLASS': 'issue',
317       'SORT': ['-activity'],
318       'GROUP': ['priority'],
319       'FILTER': ['status', 'assignedto'],
320       'COLUMNS': ['id','activity','title','creator','status'],
321       'FILTERSPEC': {
322         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
323         'assignedto': ['-1'],
324       },
325     }
327     # The "my issues" index -- note that the user's id will replace the
328     # 'CURRENT USER' value of the "assignedto" filterspec
329     USER_INDEX = {
330       'LABEL': 'My Issues',
331       'CLASS': 'issue',
332       'SORT': ['-activity'],
333       'GROUP': ['priority'],
334       'FILTER': ['status', 'assignedto'],
335       'COLUMNS': ['id','activity','title','creator','status'],
336       'FILTERSPEC': {
337         'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
338         'assignedto': 'CURRENT USER',
339       },
340     }
342     ISSUE_FILTER = {
343       'CLASS': 'issue',
344       'FILTER': ['status', 'priority', 'assignedto', 'creator']
345     }
347     SUPPORT_FILTER = {
348       'CLASS': 'issue',
349       'FILTER': ['status', 'priority', 'assignedto', 'creator']
350     }
353 Instance Schema
354 ---------------
356 Note: if you modify the schema, you'll most likely need to edit the
357       `web interface`_ HTML template files and `detectors`_ to reflect
358       your changes.
360 An instance schema defines what data is stored in the instance's database. The
361 two schemas shipped with Roundup turn it into a typical software bug tracker
362 (the extended schema allowing for support issues as well as bugs). Schemas are
363 defined using Python code. The "classic" schema looks like this::
365     pri = Class(db, "priority", name=String(), order=String())
366     pri.setkey("name")
367     pri.create(name="critical", order="1")
368     pri.create(name="urgent", order="2")
369     pri.create(name="bug", order="3")
370     pri.create(name="feature", order="4")
371     pri.create(name="wish", order="5")
373     stat = Class(db, "status", name=String(), order=String())
374     stat.setkey("name")
375     stat.create(name="unread", order="1")
376     stat.create(name="deferred", order="2")
377     stat.create(name="chatting", order="3")
378     stat.create(name="need-eg", order="4")
379     stat.create(name="in-progress", order="5")
380     stat.create(name="testing", order="6")
381     stat.create(name="done-cbb", order="7")
382     stat.create(name="resolved", order="8")
384     keyword = Class(db, "keyword", name=String())
385     keyword.setkey("name")
387     user = Class(db, "user", username=String(), password=String(),
388         address=String(), realname=String(), phone=String(),
389         organisation=String())
390     user.setkey("username")
391     user.create(username="admin", password=adminpw,
392         address=instance_config.ADMIN_EMAIL)
394     msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
395         ("user"), date=Date(), summary=String(), files=Multilink("file"))
397     file = FileClass(db, "file", name=String(), type=String())
399     issue = IssueClass(db, "issue", assignedto=Link("user"),
400         topic=Multilink("keyword"), priority=Link("priority"), status=Link
401         ("status"))
402     issue.setkey('title')
404 Classes and Properties - creating a new information store
405 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
407 In the instance above, we've defined 7 classes of information:
409   priority
410       Defines the possible levels of urgency for issues.
412   status
413       Defines the possible states of processing the issue may be in.
415   keyword
416       Initially empty, will hold keywords useful for searching issues.
418   user
419       Initially holding the "admin" user, will eventually have an entry for all
420       users using roundup.
422   msg
423       Initially empty, will all e-mail messages sent to or generated by
424       roundup.
426   file
427       Initially empty, will all files attached to issues.
429   issue
430       Initially emtyp, this is where the issue information is stored.
432 We define the "priority" and "status" classes to allow two things: reduction in
433 the amount of information stored on the issue and more powerful, accurate
434 searching of issues by priority and status. By only requiring a link on the
435 issue (which is stored as a single number) we reduce the chance that someone
436 mis-types a priority or status - or simply makes a new one up.
438 Class and Nodes
439 :::::::::::::::
441 A Class defines a particular class (or type) of data that will be stored in the
442 database. A class comprises one or more properties, which given the information
443 about the class nodes.
444 The actual data entered into the database, using class.create() are called
445 nodes. They have a special immutable property called id. We sometimes refer to
446 this as the nodeid.
448 Properties
449 ::::::::::
451 A Class is comprised of one or more properties of the following types:
452     * String properties are for storing arbitrary-length strings.
453     * Password properties are for storing encoded arbitrary-length strings. The
454       default encoding is defined on the roundup.password.Password class.
455     * Date properties store date-and-time stamps. Their values are Timestamp
456       objects.
457     * A Link property refers to a single other node selected from a specified
458       class. The class is part of the property; the value is an integer, the id
459       of the chosen node.
460     * A Multilink property refers to possibly many nodes in a specified class.
461       The value is a list of integers.
463 FileClass
464 :::::::::
466 FileClasses save their "content" attribute off in a separate file from the rest
467 of the database. This reduces the number of large entries in the database,
468 which generally makes databases more efficient, and also allows us to use
469 command-line tools to operate on the files. They are stored in the files sub-
470 directory of the db directory in your instance.
472 IssueClass
473 ::::::::::
475 IssueClasses automatically include the "messages", "files", "nosy", and
476 "superseder" properties.
477 The messages and files properties list the links to the messages and files
478 related to the issue. The nosy property is a list of links to users who wish to
479 be informed of changes to the issue - they get "CC'ed" e-mails when messages
480 are sent to or generated by the issue. The nosy reactor (in the detectors
481 directory) handles this action. The superceder link indicates an issue which
482 has superceded this one.
483 They also have the dynamically generated "creation", "activity" and "creator"
484 properties.
485 The value of the "creation" property is the date when a node was created, and
486 the value of the "activity" property is the date when any property on the node
487 was last edited (equivalently, these are the dates on the first and last
488 records in the node's journal). The "creator" property holds a link to the user
489 that created the issue.
491 setkey(property)
492 ::::::::::::::::
494 Select a String property of the class to be the key property. The key property
495 muse be unique, and allows references to the nodes in the class by the content
496 of the key property. That is, we can refer to users by their username, e.g.
497 let's say that there's an issue in roundup, issue 23. There's also a user,
498 richard who happens to be user 2. To assign an issue to him, we could do either
499 of::
501      roundup-admin set issue assignedto=2
503 or::
505      roundup-admin set issue assignedto=richard
507 Note, the same thing can be done in the web and e-mail interfaces.
509 create(information)
510 :::::::::::::::::::
512 Create a node in the database. This is generally used to create nodes in the
513 "definitional" classes like "priority" and "status".
516 Detectors - adding behaviour to your tracker
517 --------------------------------------------
518 .. _`detectors`:
520 The detectors in your instance fire before (*auditors*) and after (*reactors*)
521 changes to the contents of your database. They are Python modules that sit in
522 your instance's ``detectors`` directory. You will have some installed by
523 default - have a look. You can write new detectors or modify the existing
524 ones. The existing detectors installed for you are:
526 **nosyreaction.py**
527   This provides the automatic nosy list maintenance and email sending. The nosy
528   reactor (``nosyreaction``) fires when new messages are added to issues.
529   The nosy auditor (``updatenosy``) fires when issues are changed and figures
530   what changes need to be made to the nosy list (like adding new authors etc)
531 **statusauditor.py**
532   This provides the ``chatty`` auditor which changes the issue status from
533   ``unread`` or ``closed`` to ``chatting`` if new messages appear. It also
534   provides the ``presetunread`` auditor which pre-sets the status to
535   ``unread`` on new nodes if the status isn't explicitly defined.
537 See the detectors section in the `design document`__ for details of the
538 interface for detectors.
540 __ spec.html
542 Sample additional detectors that have been found useful will appear in the
543 ``detectors`` directory of the Roundup distribution:
545 **newissuecopy.py**
546   This detector sends an email to a team address whenever a new issue is
547   created. The address is hard-coded into the detector, so edit it before you
548   use it (look for the text 'team@team.host') or you'll get email errors!
551 Database Content
552 ----------------
554 Note: if you modify the content of definitional classes, you'll most likely
555        need to edit the instance `detectors`_ to reflect your changes.
557 Customisation of the special "definitional" classes (eg. status, priority,
558 resolution, ...) may be done either before or after the instance is
559 initialised. The actual method of doing so is completely different in each
560 case though, so be careful to use the right one.
562 **Changing content before instance initialisation**
563     Edit the dbinit module in your instance to alter the nodes created in using
564     the create() methods.
567 **Changing content after instance initialisation**
568     Use the roundup-admin interface's create, set and retire methods to add,
569     alter or remove nodes from the classes in question.
573 Web Interface
574 -------------
576 The web interface works behind the cgi-bin/roundup.cgi or roundup-server
577 scripts. In both cases, the scripts determine which instance is being accessed
578 (the first part of the URL path inside the scope of the CGI handler) and pass
579 control on to the instance interfaces.Client class which handles the rest of
580 the access through its main() method. This means that you can do pretty much
581 anything you want as a web interface to your instance.
582 Most customisation of the web view can be done by modifying the templates in
583 the instance html directory. These are divided into index, item and newitem
584 views. The newitem view is optional - the item view will be used if the newitem
585 view doesn't exist. The header and footer that wrap the various views give the
586 pages an overall look.
588 Repurcussions of changing the instance schema
589 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
591 If you choose to change the `instance schema`_ you will need to ensure the web
592 interface knows about it:
594 1. Index, item and filter pages for the relevant classes may need to have
595    properties added or removed,
596 2. The default page header relies on the existence of, and some values of
597    the priority, status, assignedto and activity classes. If you change any
598    of these (specifically if you remove any of the classes or their default
599    values) you will need to implement your own pagehead() method in your
600    instance's interfaces.py module.
602 Overall Look - the Header and Footer
603 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
605 The header and footer are generated by Python code. The default code is in
606 roundup.cgi_client.Class. This class is mixed-in to your instance through the
607 instance's interfaces module. This means you can override the header and
608 footer with your own code. This allows you to use a sidebar navigation scheme,
609 for example.
612 Displaying Properties
613 ~~~~~~~~~~~~~~~~~~~~~
615 Properties appear in the user interface in three contexts: in indices, in
616 editors, and as filters. For each type of property, there are several display
617 possibilities. For example, in an index view, a string property may just be
618 printed as a plain string, but in an editor view, that property should be
619 displayed in an editable field.
621 The display of a property is handled by functions in the htmltemplate module.
622 Displayer functions are triggered by <display> tags in templates. The call
623 attribute of the tag provides a Python expression for calling the displayer
624 function. The three standard arguments are inserted in front of the arguments
625 given. For example, the occurrence of::
627          <display call="plain('status')">
629 in a template triggers a call the "plain" function. The displayer functions can
630 accept extra arguments to further specify details about the widgets that should
631 be generated. By defining new displayer functions, the user interface can be
632 highly customized.
634 +-----------------------------------------------------------------------------+
635 |The displayer functions are                                                  |
636 +---------+-------------------------------------------------------------------+
637 |plain    |Display a String property directly.                                |
638 |         |Display a Date property in a specified time zone with an option to |
639 |         |omit the time from the date stamp.                                 |
640 |         |For a Link or Multilink property, display the key strings of the   |
641 |         |linked nodes (or the ids if the linked class has no key property). |
642 |         |Options:                                                           |
643 |         |escape (boolean) - HTML-escape the resulting text.                 |
644 +---------+-------------------------------------------------------------------+
645 |field    |Display a property like the plain displayer above, but in a form   |
646 |         |field to be edited. Strings, Dates and Intervals use TEXT fields,  |
647 |         |Links use SELECT fields and Multilinks use SELECT MULTIPLE fields. |
648 |         |Options:                                                           |
649 |         |size (number) - width of TEXT fields.                              |
650 |         |height (number) - number of nows in SELECT MULTIPLE tags.          |
651 |         |showid (boolean) - true includes the id of linked nodes in the     |
652 |         |SELECT MULTIPLE fields.                                            |
653 +---------+-------------------------------------------------------------------+
654 |menu     |For a Links and Multilinks, display the same field as would be     |
655 |         |generated using field.                                             |
656 +---------+-------------------------------------------------------------------+
657 |link     |For a Link or Multilink property, display the names of the linked  |
658 |         |nodes, hyperlinked to the item views on those nodes.               |
659 |         |For other properties, link to this node with the property as the   |
660 |         |text.                                                              |
661 |         |Options:                                                           |
662 |         |property (property name) - the property to use in the second case. |
663 |         |showid - use the linked node id as the link text (linked node      |
664 |         |"value" will be set as a tooltip)                                  |
665 +---------+-------------------------------------------------------------------+
666 |count    |For a Multilink property, display a count of the number of links in|
667 |         |the list.                                                          |
668 |         |Arguments:                                                         |
669 |         |property (property name) - the property to use.                    |
670 +---------+-------------------------------------------------------------------+
671 |reldate  |Display a Date property in terms of an interval relative to the    |
672 |         |current date (e.g. "+ 3w", "- 2d").                                |
673 |         |Arguments:                                                         |
674 |         |property (property name) - the property to use.                    |
675 |         |Options:                                                           |
676 |         |pretty (boolean) - display the relative date in an English form.   |
677 +---------+-------------------------------------------------------------------+
678 |download |For a Link or Multilink property, display the names of the linked  |
679 |         |nodes, hyperlinked to the item views on those nodes.               |
680 |         |For other properties, link to this node with the property as the   |
681 |         |text.                                                              |
682 |         |In all cases, append the name (key property) of the item to the    |
683 |         |path so it is the name of the file being downloaded.               |
684 |         |Arguments:                                                         |
685 |         |property (property name) - the property to use.                    |
686 +---------+-------------------------------------------------------------------+
687 |checklist|For a Link or Multilink property, display checkboxes for the       |
688 |         |available choices to permit filtering.                             |
689 |         |Arguments:                                                         |
690 |         |property (property name) - the property to use.                    |
691 +---------+-------------------------------------------------------------------+
692 |note     |Display the special notes field, which is a text area for entering |
693 |         |a note to go along with a change.                                  |
694 +---------+-------------------------------------------------------------------+
695 |list     |List the nodes specified by property using the standard index for  |
696 |         |the class.                                                         |
697 |         |Arguments:                                                         |
698 |         |property (property name) - the property to use.                    |
699 +---------+-------------------------------------------------------------------+
700 |history  |List the history of the item.                                      |
701 +---------+-------------------------------------------------------------------+
702 |submit   |Add a submit button for the item.                                  |
703 +---------+-------------------------------------------------------------------+
706 Index Views
707 ~~~~~~~~~~~
709 An index view contains two sections: a filter section and an index section. The
710 filter section provides some widgets for selecting which items appear in the
711 index. The index section is a table of items.
713 Index View Specifiers
714 :::::::::::::::::::::
716 An index view specifier (URL fragment) looks like this (whitespace has been
717 added for clarity)::
719      /issue?status=unread,in-progress,resolved&
720              topic=security,ui&
721              :group=+priority&
722              :sort=-activity&
723              :filters=status,topic&
724              :columns=title,status,fixer
726 The index view is determined by two parts of the specifier: the layout part and
727 the filter part. The layout part consists of the query parameters that begin
728 with colons, and it determines the way that the properties of selected nodes
729 are displayed. The filter part consists of all the other query parameters, and
730 it determines the criteria by which nodes are selected for display.
731 The filter part is interactively manipulated with the form widgets displayed in
732 the filter section. The layout part is interactively manipulated by clicking on
733 the column headings in the table.
735 The filter part selects the union of the sets of items with values matching any
736 specified Link properties and the intersection of the sets of items with values
737 matching any specified Multilink properties.
739 The example specifies an index of "issue" nodes. Only items with a "status" of
740 either "unread" or "in-progres" or "resolved" are displayed, and only items
741 with "topic" values including both "security" and "ui" are displayed. The items
742 are grouped by priority, arranged in ascending order; and within groups, sorted
743 by activity, arranged in descending order. The filter section shows filters for
744 the "status" and "topic" properties, and the table includes columns for the
745 "title", "status", and "fixer" properties.
747 Associated with each item class is a default layout specifier. The layout
748 specifier in the above example is the default layout to be provided with the
749 default bug-tracker schema described above in section 4.4.
751 Filter Section
752 ::::::::::::::
754 The template for a filter section provides the filtering widgets at the top of
755 the index view. Fragments enclosed in <property>...</property> tags are
756 included or omitted depending on whether the view specifier requests a filter
757 for a particular property.
759 A property must appear in the filter template for it to be available as a
760 filter.
762 Here's a simple example of a filter template.::
764      <property name=status>
765          <display call="checklist('status')">
766      </property>
767      <br>
768      <property name=priority>
769          <display call="checklist('priority')">
770      </property>
771      <br>
772      <property name=fixer>
773          <display call="menu('fixer')">
774      </property>
776 The standard index generation code appends a section to the index pages which
777 allows selection of the filters - from those which are defined in the filter
778 template.
780 Index Section
781 :::::::::::::
783 The template for an index section describes one row of the index table.
784 Fragments enclosed in <property>...</property> tags are included or omitted
785 depending on whether the view specifier requests a column for a particular
786 property. The table cells should contain <display> tags to display the values
787 of the item's properties.
789 Here's a simple example of an index template.::
791      <tr>
792          <property name=title>
793              <td><display call="plain('title', max=50)"></td>
794          </property>
795          <property name=status>
796              <td><display call="plain('status')"></td>
797          </property>
798          <property name=fixer>
799              <td><display call="plain('fixer')"></td>
800          </property>
801      </tr>
803 Sorting
804 :::::::
806 String and Date values are sorted in the natural way. Link properties are
807 sorted according to the value of the "order" property on the linked nodes if it
808 is present; or otherwise on the key string of the linked nodes; or finally on
809 the node ids. Multilink properties are sorted according to how many links are
810 present.
812 Item Views
813 ~~~~~~~~~~
815 An item view contains an editor section and a spool section. At the top of an
816 item view, links to superseding and superseded items are always displayed.
818 Editor Section
819 ::::::::::::::
821 The editor section is generated from a template containing <display> tags to
822 insert the appropriate widgets for editing properties.
824 Here's an example of a basic editor template.::
826      <table>
827      <tr>
828          <td colspan=2>
829              <display call="field('title', size=60)">
830          </td>
831      </tr>
832      <tr>
833          <td>
834              <display call="field('fixer', size=30)">
835          </td>
836          <td>
837              <display call="menu('status')>
838          </td>
839      </tr>
840      <tr>
841          <td>
842              <display call="field('nosy', size=30)">
843          </td>
844          <td>
845              <display call="menu('priority')>
846          </td>
847      </tr>
848      <tr>
849          <td colspan=2>
850              <display call="note()">
851          </td>
852      </tr>
853      </table>
855 As shown in the example, the editor template can also request the display of a
856 "note" field, which is a text area for entering a note to go along with a
857 change.
859 The <property> tag used in the index may also be used here - it checks to see
860 if the nominated Multilink property has any entries. This can be used to
861 eliminate sections of the editor section if the property has no entries::
863   <td class="form-text">
864     <display call="field('superseder', size=40, showid=1)">
865     <display call="classhelp('issue', 'id,title', label='list', width=500)">
866     <property name="superseder">
867       <br>View: <display call="link('superseder', showid=1)">
868     </property>
869   </td>
871 The "View: " part with the links will only display if the superseder property
872 has values.
874 When a change is submitted, the system automatically generates a message
875 describing the changed properties.
877 If a note is given in the "note" field, the note is appended to the
878 description. The message is then added to the item's message spool (thus
879 triggering the standard detector to react by sending out this message to the
880 nosy list).
882 The message also displays all of the property values on the item and indicates
883 which ones have changed. An example of such a message might be this::
885      Polly's taken a turn for the worse - this is now really important!
886      -----
887      title: Polly Parrot is dead
888      priority: critical
889      status: unread -> in-progress
890      fixer: terry
891      keywords: parrot,plumage,perch,nailed,dead
893 Spool Section
894 :::::::::::::
896 The spool section lists messages in the item's "messages" property. The index
897 of messages displays the "date", "author", and "summary" properties on the
898 message nodes, and selecting a message takes you to its content.
900 The <property> tag used in the index may also be used here - it checks to see
901 if the nominated Multilink property has any entries. This can be used to
902 eliminate sections of the spool section if the property has no entries::
904      <property name="files">
905       <tr class="strong-header">
906        <td><b>Files</b></td>
907       </tr>
909       <tr>
910        <td><display call="list('files')"></td>
911       </tr>
912      </property>
914 -----------------
916 Back to `Table of Contents`_
918 .. _`Table of Contents`: index.html