a2ad95e8affdbbffdda9d1fc5e94ccb8befa3d78
1 ===================
2 Customising Roundup
3 ===================
5 :Version: $Revision: 1.14 $
7 .. contents::
10 What You Can Do
11 ---------------
13 Customisation of Roundup can take one of four 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_
20 The third case is special because it takes two distinctly different forms
21 depending upon whether the instance has been initialised or not. The other two
22 may be done at any time, before or after instance initialisation. Yes, this
23 includes adding or removing properties from classes.
26 Instances in a Nutshell
27 -----------------------
29 Instances have the following structure:
31 +-------------------+--------------------------------------------------------+
32 |instance_config.py |Holds the basic instance_configuration |
33 +-------------------+--------------------------------------------------------+
34 |dbinit.py |Holds the instance_schema |
35 +-------------------+--------------------------------------------------------+
36 |interfaces.py |Defines the Web and E-Mail interfaces for the instance |
37 +-------------------+--------------------------------------------------------+
38 |select_db.py |Selects the database back-end for the instance |
39 +-------------------+--------------------------------------------------------+
40 |db/ |Holds the instance's database |
41 +-------------------+--------------------------------------------------------+
42 |db/files/ |Holds the instance's upload files and messages |
43 +-------------------+--------------------------------------------------------+
44 |detectors/ |Auditors and reactors for this instance |
45 +-------------------+--------------------------------------------------------+
46 |html/ |Web interface templates, images and style sheets |
47 +-------------------+--------------------------------------------------------+
49 Instance Configuration
50 ----------------------
52 The instance_config.py located in your instance home contains the basic
53 configuration for the web and e-mail components of roundup's interfaces. This
54 file is a Python module. The configuration variables available are:
56 **INSTANCE_HOME** - ``os.path.split(__file__)[0]``
57 The instance home directory. The above default code will automatically
58 determine the instance home for you.
60 **MAILHOST** - ``'localhost'``
61 The SMTP mail host that roundup will use to send e-mail.
63 **MAIL_DOMAIN** - ``'your.tracker.email.domain.example'``
64 The domain name used for email addresses.
66 **DATABASE** - ``os.path.join(INSTANCE_HOME, 'db')``
67 This is the directory that the database is going to be stored in. By default
68 it is in the instance home.
70 **TEMPLATES** - ``os.path.join(INSTANCE_HOME, 'html')``
71 This is the directory that the HTML templates reside in. By default they are
72 in the instance home.
74 **INSTANCE_NAME** - ``'Roundup issue tracker'``
75 A descriptive name for your roundup instance. This is sent out in e-mails and
76 appears in the heading of CGI pages.
78 **ISSUE_TRACKER_EMAIL** - ``'issue_tracker@%s'%MAIL_DOMAIN``
79 The email address that e-mail sent to roundup should go to. Think of it as the
80 instance's personal e-mail address.
82 **ISSUE_TRACKER_WEB** - ``'http://your.tracker.url.example/'``
83 The web address that the instance is viewable at. This will be included in
84 information sent to users of the tracker.
86 **ADMIN_EMAIL** - ``'roundup-admin@%s'%MAIL_DOMAIN``
87 The email address that roundup will complain to if it runs into trouble.
89 **FILTER_POSITION** - ``'top'``, ``'bottom'`` or ``'top and bottom'``
90 Where to place the web filtering HTML on the index page.
92 **ANONYMOUS_ACCESS** - ``'deny'`` or ``'allow'``
93 Deny or allow anonymous access to the web interface.
95 **ANONYMOUS_REGISTER** - ``'deny'`` or ``'allow'``
96 Deny or allow anonymous users to register through the web interface.
98 **ANONYMOUS_REGISTER_MAIL** - ``'deny'`` or ``'allow'``
99 Deny or allow anonymous users to register through the mail interface.
101 **MESSAGES_TO_AUTHOR** - ``'yes'`` or``'no'``
102 Send nosy messages to the author of the message.
104 **ADD_AUTHOR_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
105 Does the author of a message get placed on the nosy list automatically?
106 If ``'new'`` is used, then the author will only be added when a message
107 creates a new issue. If ``'yes'``, then the author will be added on followups
108 too. If ``'no'``, they're never added to the nosy.
110 **ADD_RECIPIENTS_TO_NOSY** - ``'new'``, ``'yes'`` or ``'no'``
111 Do the recipients (To:, Cc:) of a message get placed on the nosy list?
112 If ``'new'`` is used, then the recipients will only be added when a message
113 creates a new issue. If ``'yes'``, then the recipients will be added on
114 followups too. If ``'no'``, they're never added to the nosy.
116 **EMAIL_SIGNATURE_POSITION** - ``'top'``, ``'bottom'`` or ``'none'``
117 Where to place the email signature in messages that Roundup generates.
119 **EMAIL_KEEP_QUOTED_TEXT** - ``'yes'`` or ``'no'``
120 Keep email citations. Citations are the part of e-mail which the sender has
121 quoted in their reply to previous e-mail.
123 **EMAIL_LEAVE_BODY_UNCHANGED** - ``'no'``
124 Preserve the email body as is. Enabiling this will cause the entire message
125 body to be stored, including all citations and signatures. It should be
126 either ``'yes'`` or ``'no'``.
128 **MAIL_DEFAULT_CLASS** - ``'issue'`` or ``''``
129 Default class to use in the mailgw if one isn't supplied in email
130 subjects. To disable, comment out the variable below or leave it blank.
132 **HEADER_INDEX_LINKS** - ``['DEFAULT', 'UNASSIGNED', 'USER']``
133 Define what index links are available in the header, and what their
134 labels are. Each key is used to look up one of the index specifications
135 below - so ``'DEFAULT'`` will use ``'DEFAULT_INDEX'``.
137 Example ``DEFAULT_INDEX``::
139 {
140 'LABEL': 'All Issues',
141 'CLASS': 'issue',
142 'SORT': ['-activity'],
143 'GROUP': ['priority'],
144 'FILTER': ['status'],
145 'COLUMNS': ['id','activity','title','creator','assignedto'],
146 'FILTERSPEC': {
147 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
148 },
149 }
151 This defines one of the index links that appears in the
152 ``HEADER_INDEX_LINKS`` list.
154 **LABEL** - ``'All Issues'``
155 The text that appears as the link label.
156 **CLASS** - ``'issue'``
157 The class to display the index for.
158 **SORT** - ``['-activity']``
159 Sort by prop name, optionally preceeded with '-' to give descending or
160 nothing for ascending sorting.
161 **GROUP** - ``['priority']``
162 Group by prop name, optionally preceeded with '-' or to sort in descending
163 or nothing for ascending order.
164 **FILTER** - ``['status']``
165 Selects which props should be displayed in the filter section.
166 Default is all.
167 **COLUMNS** - ``['id','activity','title','creator','assignedto']``
168 Selects the columns that should be displayed. Default is all.
169 **FILTERSPEC** - *a dictionary giving the filter specification*
170 The ``FILTERSPEC`` gives the filtering arguments. This selects the values
171 the node properties given by propname must have.
173 Where the ``FILTERSPEC`` value is ``'CURRENT USER'``, it will be replaced
174 by the id of the logged-in user. For example::
176 'FILTERSPEC': {
177 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
178 'assignedto': 'CURRENT USER',
179 },
181 **HEADER_ADD_LINKS** - ``['issue']``
182 List the classes that users are able to add nodes to.
184 **HEADER_SEARCH_LINKS** - ``['issue']``
185 List the classes that users can search.
187 **SEARCH_FILTERS** - ``['ISSUE_FILTER', 'SUPPORT_FILTER']``
188 List search filters per class. Like the INDEX entries above, each key is
189 used to look up one of the filter specifications below - so ``'ISSUE'``
190 will use ``'ISSUE_FILTER'``.
192 Example ``ISSUE_FILTER``::
194 ISSUE_FILTER = {
195 'CLASS': 'issue',
196 'FILTER': ['status', 'priority', 'assignedto', 'creator']
197 }
199 **CLASS** - ``'issue'``
200 The class that the search page is for.
201 **FILTER** - ``['status', 'priority', 'assignedto', 'creator']``
202 Selects which props should be displayed on the filter page. Default is
203 all.
205 The default instance_config.py is given below - as you
206 can see, the MAIL_DOMAIN must be edited before any interaction with the
207 instance is attempted.::
209 # roundup home is this package's directory
210 INSTANCE_HOME=os.path.split(__file__)[0]
212 # The SMTP mail host that roundup will use to send mail
213 MAILHOST = 'localhost'
215 # The domain name used for email addresses.
216 MAIL_DOMAIN = 'your.tracker.email.domain.example'
218 # the next two are only used for the standalone HTTP server.
219 HTTP_HOST = ''
220 HTTP_PORT = 9080
222 # This is the directory that the database is going to be stored in
223 DATABASE = os.path.join(INSTANCE_HOME, 'db')
225 # This is the directory that the HTML templates reside in
226 TEMPLATES = os.path.join(INSTANCE_HOME, 'html')
228 # A descriptive name for your roundup instance
229 INSTANCE_NAME = 'Roundup issue tracker'
231 # The email address that mail to roundup should go to
232 ISSUE_TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
234 # The web address that the instance is viewable at
235 ISSUE_TRACKER_WEB = 'http://your.tracker.url.example/'
237 # The email address that roundup will complain to if it runs into trouble
238 ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
240 # Somewhere for roundup to log stuff internally sent to stdout or stderr
241 LOG = os.path.join(INSTANCE_HOME, 'roundup.log')
243 # Where to place the web filtering HTML on the index page
244 FILTER_POSITION = 'bottom' # one of 'top', 'bottom', 'top and bottom'
246 # Deny or allow anonymous access to the web interface
247 ANONYMOUS_ACCESS = 'deny' # either 'deny' or 'allow'
249 # Deny or allow anonymous users to register through the web interface
250 ANONYMOUS_REGISTER = 'deny' # either 'deny' or 'allow'
252 # Deny or allow anonymous users to register through the mail interface
253 ANONYMOUS_REGISTER_MAIL = 'deny' # either 'deny' or 'allow'
255 # Send nosy messages to the author of the message
256 MESSAGES_TO_AUTHOR = 'no' # either 'yes' or 'no'
258 # Does the author of a message get placed on the nosy list automatically?
259 # If 'new' is used, then the author will only be added when a message
260 # creates a new issue. If 'yes', then the author will be added on followups
261 # too. If 'no', they're never added to the nosy.
262 ADD_AUTHOR_TO_NOSY = 'new' # one of 'yes', 'no', 'new'
264 # Do the recipients (To:, Cc:) of a message get placed on the nosy list?
265 # If 'new' is used, then the recipients will only be added when a message
266 # creates a new issue. If 'yes', then the recipients will be added on followups
267 # too. If 'no', they're never added to the nosy.
268 ADD_RECIPIENTS_TO_NOSY = 'new' # either 'yes', 'no', 'new'
270 # Where to place the email signature
271 EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
273 # Keep email citations
274 EMAIL_KEEP_QUOTED_TEXT = 'no' # either 'yes' or 'no'
276 # Preserve the email body as is
277 EMAIL_LEAVE_BODY_UNCHANGED = 'no' # either 'yes' or 'no'
279 # Default class to use in the mailgw if one isn't supplied in email
280 # subjects. To disable, comment out the variable below or leave it blank.
281 # Examples:
282 MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default
283 #MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out)
285 # Define what index links are available in the header, and what their
286 # labels are. Each key is used to look up one of the index specifications
287 # below - so 'DEFAULT' will use 'DEFAULT_INDEX'.
288 # Where the FILTERSPEC has 'assignedto' with a value of None, it will be
289 # replaced by the id of the logged-in user.
290 HEADER_INDEX_LINKS = ['DEFAULT', 'UNASSIGNED', 'USER']
292 # list the classes that users are able to add nodes to
293 HEADER_ADD_LINKS = ['issue']
295 # list the classes that users can search
296 HEADER_SEARCH_LINKS = ['issue']
298 # list search filters per class
299 SEARCH_FILTERS = ['ISSUE_FILTER', 'SUPPORT_FILTER']
301 # Now the DEFAULT display specification. TODO: describe format
302 DEFAULT_INDEX = {
303 'LABEL': 'All Issues',
304 'CLASS': 'issue',
305 'SORT': ['-activity'],
306 'GROUP': ['priority'],
307 'FILTER': ['status'],
308 'COLUMNS': ['id','activity','title','creator','assignedto'],
309 'FILTERSPEC': {
310 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
311 },
312 }
314 # The "unsassigned issues" index
315 UNASSIGNED_INDEX = {
316 'LABEL': 'Unassigned Issues',
317 'CLASS': 'issue',
318 'SORT': ['-activity'],
319 'GROUP': ['priority'],
320 'FILTER': ['status', 'assignedto'],
321 'COLUMNS': ['id','activity','title','creator','status'],
322 'FILTERSPEC': {
323 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
324 'assignedto': ['-1'],
325 },
326 }
328 # The "my issues" index -- note that the user's id will replace the
329 # 'CURRENT USER' value of the "assignedto" filterspec
330 USER_INDEX = {
331 'LABEL': 'My Issues',
332 'CLASS': 'issue',
333 'SORT': ['-activity'],
334 'GROUP': ['priority'],
335 'FILTER': ['status', 'assignedto'],
336 'COLUMNS': ['id','activity','title','creator','status'],
337 'FILTERSPEC': {
338 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'],
339 'assignedto': 'CURRENT USER',
340 },
341 }
343 ISSUE_FILTER = {
344 'CLASS': 'issue',
345 'FILTER': ['status', 'priority', 'assignedto', 'creator']
346 }
348 SUPPORT_FILTER = {
349 'CLASS': 'issue',
350 'FILTER': ['status', 'priority', 'assignedto', 'creator']
351 }
354 Instance Schema
355 ---------------
357 Note: if you modify the schema, you'll most likely need to edit the
358 `web interface`_ HTML template files and `detectors`_ to reflect
359 your changes.
361 An instance schema defines what data is stored in the instance's database. The
362 two schemas shipped with Roundup turn it into a typical software bug tracker
363 (the extended schema allowing for support issues as well as bugs). Schemas are
364 defined using Python code. The "classic" schema looks like this::
366 pri = Class(db, "priority", name=String(), order=String())
367 pri.setkey("name")
368 pri.create(name="critical", order="1")
369 pri.create(name="urgent", order="2")
370 pri.create(name="bug", order="3")
371 pri.create(name="feature", order="4")
372 pri.create(name="wish", order="5")
374 stat = Class(db, "status", name=String(), order=String())
375 stat.setkey("name")
376 stat.create(name="unread", order="1")
377 stat.create(name="deferred", order="2")
378 stat.create(name="chatting", order="3")
379 stat.create(name="need-eg", order="4")
380 stat.create(name="in-progress", order="5")
381 stat.create(name="testing", order="6")
382 stat.create(name="done-cbb", order="7")
383 stat.create(name="resolved", order="8")
385 keyword = Class(db, "keyword", name=String())
386 keyword.setkey("name")
388 user = Class(db, "user", username=String(), password=String(),
389 address=String(), realname=String(), phone=String(),
390 organisation=String())
391 user.setkey("username")
392 user.create(username="admin", password=adminpw,
393 address=instance_config.ADMIN_EMAIL)
395 msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
396 ("user"), date=Date(), summary=String(), files=Multilink("file"))
398 file = FileClass(db, "file", name=String(), type=String())
400 issue = IssueClass(db, "issue", assignedto=Link("user"),
401 topic=Multilink("keyword"), priority=Link("priority"), status=Link
402 ("status"))
403 issue.setkey('title')
405 Classes and Properties - creating a new information store
406 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
408 In the instance above, we've defined 7 classes of information:
410 priority
411 Defines the possible levels of urgency for issues.
413 status
414 Defines the possible states of processing the issue may be in.
416 keyword
417 Initially empty, will hold keywords useful for searching issues.
419 user
420 Initially holding the "admin" user, will eventually have an entry for all
421 users using roundup.
423 msg
424 Initially empty, will all e-mail messages sent to or generated by
425 roundup.
427 file
428 Initially empty, will all files attached to issues.
430 issue
431 Initially emtyp, this is where the issue information is stored.
433 We define the "priority" and "status" classes to allow two things: reduction in
434 the amount of information stored on the issue and more powerful, accurate
435 searching of issues by priority and status. By only requiring a link on the
436 issue (which is stored as a single number) we reduce the chance that someone
437 mis-types a priority or status - or simply makes a new one up.
439 Class and Nodes
440 :::::::::::::::
442 A Class defines a particular class (or type) of data that will be stored in the
443 database. A class comprises one or more properties, which given the information
444 about the class nodes.
445 The actual data entered into the database, using class.create() are called
446 nodes. They have a special immutable property called id. We sometimes refer to
447 this as the nodeid.
449 Properties
450 ::::::::::
452 A Class is comprised of one or more properties of the following types:
453 * String properties are for storing arbitrary-length strings.
454 * Password properties are for storing encoded arbitrary-length strings. The
455 default encoding is defined on the roundup.password.Password class.
456 * Date properties store date-and-time stamps. Their values are Timestamp
457 objects.
458 * A Link property refers to a single other node selected from a specified
459 class. The class is part of the property; the value is an integer, the id
460 of the chosen node.
461 * A Multilink property refers to possibly many nodes in a specified class.
462 The value is a list of integers.
464 FileClass
465 :::::::::
467 FileClasses save their "content" attribute off in a separate file from the rest
468 of the database. This reduces the number of large entries in the database,
469 which generally makes databases more efficient, and also allows us to use
470 command-line tools to operate on the files. They are stored in the files sub-
471 directory of the db directory in your instance.
473 IssueClass
474 ::::::::::
476 IssueClasses automatically include the "messages", "files", "nosy", and
477 "superseder" properties.
478 The messages and files properties list the links to the messages and files
479 related to the issue. The nosy property is a list of links to users who wish to
480 be informed of changes to the issue - they get "CC'ed" e-mails when messages
481 are sent to or generated by the issue. The nosy reactor (in the detectors
482 directory) handles this action. The superceder link indicates an issue which
483 has superceded this one.
484 They also have the dynamically generated "creation", "activity" and "creator"
485 properties.
486 The value of the "creation" property is the date when a node was created, and
487 the value of the "activity" property is the date when any property on the node
488 was last edited (equivalently, these are the dates on the first and last
489 records in the node's journal). The "creator" property holds a link to the user
490 that created the issue.
492 setkey(property)
493 ::::::::::::::::
495 Select a String property of the class to be the key property. The key property
496 muse be unique, and allows references to the nodes in the class by the content
497 of the key property. That is, we can refer to users by their username, e.g.
498 let's say that there's an issue in roundup, issue 23. There's also a user,
499 richard who happens to be user 2. To assign an issue to him, we could do either
500 of::
502 roundup-admin set issue assignedto=2
504 or::
506 roundup-admin set issue assignedto=richard
508 Note, the same thing can be done in the web and e-mail interfaces.
510 create(information)
511 :::::::::::::::::::
513 Create a node in the database. This is generally used to create nodes in the
514 "definitional" classes like "priority" and "status".
517 Detectors - adding behaviour to your tracker
518 --------------------------------------------
519 .. _detectors:
521 The detectors in your instance fire before (*auditors*) and after (*reactors*)
522 changes to the contents of your database. They are Python modules that sit in
523 your instance's ``detectors`` directory. You will have some installed by
524 default - have a look. You can write new detectors or modify the existing
525 ones. The existing detectors installed for you are:
527 **nosyreaction.py**
528 This provides the automatic nosy list maintenance and email sending. The nosy
529 reactor (``nosyreaction``) fires when new messages are added to issues.
530 The nosy auditor (``updatenosy``) fires when issues are changed and figures
531 what changes need to be made to the nosy list (like adding new authors etc)
532 **statusauditor.py**
533 This provides the ``chatty`` auditor which changes the issue status from
534 ``unread`` or ``closed`` to ``chatting`` if new messages appear. It also
535 provides the ``presetunread`` auditor which pre-sets the status to
536 ``unread`` on new nodes if the status isn't explicitly defined.
538 See the detectors section in the `design document`__ for details of the
539 interface for detectors.
541 __ design.html
543 Sample additional detectors that have been found useful will appear in the
544 ``detectors`` directory of the Roundup distribution:
546 **newissuecopy.py**
547 This detector sends an email to a team address whenever a new issue is
548 created. The address is hard-coded into the detector, so edit it before you
549 use it (look for the text 'team@team.host') or you'll get email errors!
552 Database Content
553 ----------------
555 Note: if you modify the content of definitional classes, you'll most likely
556 need to edit the instance `detectors`_ to reflect your changes.
558 Customisation of the special "definitional" classes (eg. status, priority,
559 resolution, ...) may be done either before or after the instance is
560 initialised. The actual method of doing so is completely different in each
561 case though, so be careful to use the right one.
563 **Changing content before instance initialisation**
564 Edit the dbinit module in your instance to alter the nodes created in using
565 the create() methods.
568 **Changing content after instance initialisation**
569 Use the roundup-admin interface's create, set and retire methods to add,
570 alter or remove nodes from the classes in question.
574 Web Interface
575 -------------
577 The web interface works behind the cgi-bin/roundup.cgi or roundup-server
578 scripts. In both cases, the scripts determine which instance is being accessed
579 (the first part of the URL path inside the scope of the CGI handler) and pass
580 control on to the instance interfaces.Client class which handles the rest of
581 the access through its main() method. This means that you can do pretty much
582 anything you want as a web interface to your instance.
583 Most customisation of the web view can be done by modifying the templates in
584 the instance html directory. These are divided into index, item and newitem
585 views. The newitem view is optional - the item view will be used if the newitem
586 view doesn't exist. The header and footer that wrap the various views give the
587 pages an overall look.
589 Repurcussions of changing the instance schema
590 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
592 If you choose to change the `instance schema`_ you will need to ensure the web
593 interface knows about it:
595 1. Index, item and filter pages for the relevant classes may need to have
596 properties added or removed,
597 2. The default page header relies on the existence of, and some values of
598 the priority, status, assignedto and activity classes. If you change any
599 of these (specifically if you remove any of the classes or their default
600 values) you will need to implement your own pagehead() method in your
601 instance's interfaces.py module.
603 Overall Look - the Header and Footer
604 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
606 The header and footer are generated by Python code. The default code is in
607 roundup.cgi_client.Class. This class is mixed-in to your instance through the
608 instance's interfaces module. This means you can override the header and
609 footer with your own code. This allows you to use a sidebar navigation scheme,
610 for example.
613 Displaying Properties
614 ~~~~~~~~~~~~~~~~~~~~~
616 Properties appear in the user interface in three contexts: in indices, in
617 editors, and as filters. For each type of property, there are several display
618 possibilities. For example, in an index view, a string property may just be
619 printed as a plain string, but in an editor view, that property should be
620 displayed in an editable field.
622 The display of a property is handled by functions in the htmltemplate module.
623 Displayer functions are triggered by <display> tags in templates. The call
624 attribute of the tag provides a Python expression for calling the displayer
625 function. The three standard arguments are inserted in front of the arguments
626 given. For example, the occurrence of::
628 <display call="plain('status')">
630 in a template triggers a call the "plain" function. The displayer functions can
631 accept extra arguments to further specify details about the widgets that should
632 be generated. By defining new displayer functions, the user interface can be
633 highly customized.
635 +-----------------------------------------------------------------------------+
636 |The displayer functions are |
637 +---------+-------------------------------------------------------------------+
638 |plain |Display a String property directly. |
639 | |Display a Date property in a specified time zone with an option to |
640 | |omit the time from the date stamp. |
641 | |For a Link or Multilink property, display the key strings of the |
642 | |linked nodes (or the ids if the linked class has no key property). |
643 | |Options: |
644 | |escape (boolean) - HTML-escape the resulting text. |
645 +---------+-------------------------------------------------------------------+
646 |field |Display a property like the plain displayer above, but in a form |
647 | |field to be edited. Strings, Dates and Intervals use TEXT fields, |
648 | |Links use SELECT fields and Multilinks use SELECT MULTIPLE fields. |
649 | |Options: |
650 | |size (number) - width of TEXT fields. |
651 | |height (number) - number of nows in SELECT MULTIPLE tags. |
652 | |showid (boolean) - true includes the id of linked nodes in the |
653 | |SELECT MULTIPLE fields. |
654 +---------+-------------------------------------------------------------------+
655 |menu |For a Links and Multilinks, display the same field as would be |
656 | |generated using field. |
657 +---------+-------------------------------------------------------------------+
658 |link |For a Link or Multilink property, display the names of the linked |
659 | |nodes, hyperlinked to the item views on those nodes. |
660 | |For other properties, link to this node with the property as the |
661 | |text. |
662 | |Options: |
663 | |property (property name) - the property to use in the second case. |
664 | |showid - use the linked node id as the link text (linked node |
665 | |"value" will be set as a tooltip) |
666 +---------+-------------------------------------------------------------------+
667 |count |For a Multilink property, display a count of the number of links in|
668 | |the list. |
669 | |Arguments: |
670 | |property (property name) - the property to use. |
671 +---------+-------------------------------------------------------------------+
672 |reldate |Display a Date property in terms of an interval relative to the |
673 | |current date (e.g. "+ 3w", "- 2d"). |
674 | |Arguments: |
675 | |property (property name) - the property to use. |
676 | |Options: |
677 | |pretty (boolean) - display the relative date in an English form. |
678 +---------+-------------------------------------------------------------------+
679 |download |For a Link or Multilink property, display the names of the linked |
680 | |nodes, hyperlinked to the item views on those nodes. |
681 | |For other properties, link to this node with the property as the |
682 | |text. |
683 | |In all cases, append the name (key property) of the item to the |
684 | |path so it is the name of the file being downloaded. |
685 | |Arguments: |
686 | |property (property name) - the property to use. |
687 +---------+-------------------------------------------------------------------+
688 |checklist|For a Link or Multilink property, display checkboxes for the |
689 | |available choices to permit filtering. |
690 | |Arguments: |
691 | |property (property name) - the property to use. |
692 +---------+-------------------------------------------------------------------+
693 |note |Display the special notes field, which is a text area for entering |
694 | |a note to go along with a change. |
695 +---------+-------------------------------------------------------------------+
696 |list |List the nodes specified by property using the standard index for |
697 | |the class. |
698 | |Arguments: |
699 | |property (property name) - the property to use. |
700 +---------+-------------------------------------------------------------------+
701 |history |List the history of the item. |
702 +---------+-------------------------------------------------------------------+
703 |submit |Add a submit button for the item. |
704 +---------+-------------------------------------------------------------------+
707 Index Views
708 ~~~~~~~~~~~
710 An index view contains two sections: a filter section and an index section. The
711 filter section provides some widgets for selecting which items appear in the
712 index. The index section is a table of items.
714 Index View Specifiers
715 :::::::::::::::::::::
717 An index view specifier (URL fragment) looks like this (whitespace has been
718 added for clarity)::
720 /issue?status=unread,in-progress,resolved&
721 topic=security,ui&
722 :group=+priority&
723 :sort=-activity&
724 :filters=status,topic&
725 :columns=title,status,fixer
727 The index view is determined by two parts of the specifier: the layout part and
728 the filter part. The layout part consists of the query parameters that begin
729 with colons, and it determines the way that the properties of selected nodes
730 are displayed. The filter part consists of all the other query parameters, and
731 it determines the criteria by which nodes are selected for display.
732 The filter part is interactively manipulated with the form widgets displayed in
733 the filter section. The layout part is interactively manipulated by clicking on
734 the column headings in the table.
736 The filter part selects the union of the sets of items with values matching any
737 specified Link properties and the intersection of the sets of items with values
738 matching any specified Multilink properties.
740 The example specifies an index of "issue" nodes. Only items with a "status" of
741 either "unread" or "in-progres" or "resolved" are displayed, and only items
742 with "topic" values including both "security" and "ui" are displayed. The items
743 are grouped by priority, arranged in ascending order; and within groups, sorted
744 by activity, arranged in descending order. The filter section shows filters for
745 the "status" and "topic" properties, and the table includes columns for the
746 "title", "status", and "fixer" properties.
748 Associated with each item class is a default layout specifier. The layout
749 specifier in the above example is the default layout to be provided with the
750 default bug-tracker schema described above in section 4.4.
752 Filter Section
753 ::::::::::::::
755 The template for a filter section provides the filtering widgets at the top of
756 the index view. Fragments enclosed in <property>...</property> tags are
757 included or omitted depending on whether the view specifier requests a filter
758 for a particular property.
760 A property must appear in the filter template for it to be available as a
761 filter.
763 Here's a simple example of a filter template.::
765 <property name=status>
766 <display call="checklist('status')">
767 </property>
768 <br>
769 <property name=priority>
770 <display call="checklist('priority')">
771 </property>
772 <br>
773 <property name=fixer>
774 <display call="menu('fixer')">
775 </property>
777 The standard index generation code appends a section to the index pages which
778 allows selection of the filters - from those which are defined in the filter
779 template.
781 Index Section
782 :::::::::::::
784 The template for an index section describes one row of the index table.
785 Fragments enclosed in <property>...</property> tags are included or omitted
786 depending on whether the view specifier requests a column for a particular
787 property. The table cells should contain <display> tags to display the values
788 of the item's properties.
790 Here's a simple example of an index template.::
792 <tr>
793 <property name=title>
794 <td><display call="plain('title', max=50)"></td>
795 </property>
796 <property name=status>
797 <td><display call="plain('status')"></td>
798 </property>
799 <property name=fixer>
800 <td><display call="plain('fixer')"></td>
801 </property>
802 </tr>
804 Sorting
805 :::::::
807 String and Date values are sorted in the natural way. Link properties are
808 sorted according to the value of the "order" property on the linked nodes if it
809 is present; or otherwise on the key string of the linked nodes; or finally on
810 the node ids. Multilink properties are sorted according to how many links are
811 present.
813 Item Views
814 ~~~~~~~~~~
816 An item view contains an editor section and a spool section. At the top of an
817 item view, links to superseding and superseded items are always displayed.
819 Editor Section
820 ::::::::::::::
822 The editor section is generated from a template containing <display> tags to
823 insert the appropriate widgets for editing properties.
825 Here's an example of a basic editor template.::
827 <table>
828 <tr>
829 <td colspan=2>
830 <display call="field('title', size=60)">
831 </td>
832 </tr>
833 <tr>
834 <td>
835 <display call="field('fixer', size=30)">
836 </td>
837 <td>
838 <display call="menu('status')>
839 </td>
840 </tr>
841 <tr>
842 <td>
843 <display call="field('nosy', size=30)">
844 </td>
845 <td>
846 <display call="menu('priority')>
847 </td>
848 </tr>
849 <tr>
850 <td colspan=2>
851 <display call="note()">
852 </td>
853 </tr>
854 </table>
856 As shown in the example, the editor template can also request the display of a
857 "note" field, which is a text area for entering a note to go along with a
858 change.
860 The <property> tag used in the index may also be used here - it checks to see
861 if the nominated Multilink property has any entries. This can be used to
862 eliminate sections of the editor section if the property has no entries::
864 <td class="form-text">
865 <display call="field('superseder', size=40, showid=1)">
866 <display call="classhelp('issue', 'id,title', label='list', width=500)">
867 <property name="superseder">
868 <br>View: <display call="link('superseder', showid=1)">
869 </property>
870 </td>
872 The "View: " part with the links will only display if the superseder property
873 has values.
875 When a change is submitted, the system automatically generates a message
876 describing the changed properties.
878 If a note is given in the "note" field, the note is appended to the
879 description. The message is then added to the item's message spool (thus
880 triggering the standard detector to react by sending out this message to the
881 nosy list).
883 The message also displays all of the property values on the item and indicates
884 which ones have changed. An example of such a message might be this::
886 Polly's taken a turn for the worse - this is now really important!
887 -----
888 title: Polly Parrot is dead
889 priority: critical
890 status: unread -> in-progress
891 fixer: terry
892 keywords: parrot,plumage,perch,nailed,dead
894 Spool Section
895 :::::::::::::
897 The spool section lists messages in the item's "messages" property. The index
898 of messages displays the "date", "author", and "summary" properties on the
899 message nodes, and selecting a message takes you to its content.
901 The <property> tag used in the index may also be used here - it checks to see
902 if the nominated Multilink property has any entries. This can be used to
903 eliminate sections of the spool section if the property has no entries::
905 <property name="files">
906 <tr class="strong-header">
907 <td><b>Files</b></td>
908 </tr>
910 <tr>
911 <td><display call="list('files')"></td>
912 </tr>
913 </property>
916 Security
917 --------
919 A set of Permissions are built in to the security module by default:
921 - Edit (everything)
922 - View (everything)
924 The default interfaces define:
926 - Web Registration
927 - Email Registration
929 These are hooked into the default Roles:
931 - Admin (Edit everything, View everything)
932 - User ()
933 - Anonymous (Web Registration, Email Registration)
935 And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
936 gets the "Anonymous" assigned when the database is initialised on installation.
937 The two default schemas then define:
939 - Edit issue, View issue (both)
940 - Edit file, View file (both)
941 - Edit msg, View msg (both)
942 - Edit support, View support (extended only)
944 and assign those Permissions to the "User" Role. New users are assigned the
945 Roles defined in the config file as:
947 - NEW_WEB_USER_ROLES
948 - NEW_EMAIL_USER_ROLES
950 You may alter the configuration variables to change the Role that new web or
951 email users get, for example to not give them access to the web interface if
952 they register through email.
955 -----------------
957 Back to `Table of Contents`_
959 .. _`Table of Contents`: index.html