Code

6ac834c6eb8e3161ed54bd5eb7c02abc1ddbb3ce
[roundup.git] / doc / customizing.txt
1 ===================
2 Customising Roundup
3 ===================
5 :Version: $Revision: 1.6 $
7 .. contents::
10 Instances have the following structure:
12 +-------------------+--------------------------------------------------------+
13 |instance_config.py |Holds the basic instance_configuration                  |
14 +-------------------+--------------------------------------------------------+
15 |dbinit.py          |Holds the instance_schema                               |
16 +-------------------+--------------------------------------------------------+
17 |interfaces.py      |Defines the Web and E-Mail interfaces for the instance  |
18 +-------------------+--------------------------------------------------------+
19 |select_db.py       |Selects the database back-end for the instance          |
20 +-------------------+--------------------------------------------------------+
21 |db/                |Holds the instance's database                           |
22 +-------------------+--------------------------------------------------------+
23 |db/files/          |Holds the instance's upload files and messages          |
24 +-------------------+--------------------------------------------------------+
25 |detectors/         |Auditors and reactors for this instance                 |
26 +-------------------+--------------------------------------------------------+
27 |html/              |Web interface templates, images and style sheets        |
28 +-------------------+--------------------------------------------------------+
30 Instance Configuration
31 ----------------------
33 The instance_config.py located in your instance home contains the basic
34 configuration for the web and e-mail components of roundup's interfaces. This
35 file is a Python module. The default instance_config.py is given below - as you
36 can see, the MAIL_DOMAIN must be edited before any interaction with the
37 instance is attempted.::
39   MAIL_DOMAIN=MAILHOST=HTTP_HOST=None
40   HTTP_PORT=0
42   # roundup home is this package's directory
43   INSTANCE_HOME=os.path.split(__file__)[0]
45   # The SMTP mail host that roundup will use to send mail
46   if not MAILHOST:
47       MAILHOST = 'localhost'
49   # The domain name used for email addresses.
50   if not MAIL_DOMAIN:
51       MAIL_DOMAIN = 'fill.me.in.'
53   # the next two are only used for the standalone HTTP server.
54   if not HTTP_HOST:
55       HTTP_HOST = ''
56   if not HTTP_PORT:
57       HTTP_PORT = 9080
59   # This is the directory that the database is going to be stored in
60   DATABASE = os.path.join(INSTANCE_HOME, 'db')
62   # This is the directory that the HTML templates reside in
63   TEMPLATES = os.path.join(INSTANCE_HOME, 'html')
65   # The email address that mail to roundup should go to
66   ISSUE_TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
68   # The web address that the instance is viewable at
69   ISSUE_TRACKER_WEB = 'http://some.useful.url/'
71   # The email address that roundup will complain to if it runs into trouble
72   ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
74   # Somewhere for roundup to log stuff internally sent to stdout or stderr
75   LOG = os.path.join(INSTANCE_HOME, 'roundup.log')
77   # Where to place the web filtering HTML on the index page
78   FILTER_POSITION = 'bottom'      # one of 'top', 'bottom', 'top and bottom'
80   # Deny or allow anonymous access to the web interface
81   ANONYMOUS_ACCESS = 'deny'       # either 'deny' or 'allow'
83   # Deny or allow anonymous users to register through the web interface
84   ANONYMOUS_REGISTER = 'deny'     # either 'deny' or 'allow'
86   # Deny or allow anonymous users to register through the mail interface
87   ANONYMOUS_REGISTER_MAIL = 'deny'    # either 'deny' or 'allow'
89   # Send nosy messages to the author of the message
90   MESSAGES_TO_AUTHOR = 'no'       # either 'yes' or 'no'
92   # Does the author of a message get placed on the nosy list automatically?
93   # If 'new' is used, then the author will only be added when a message
94   # creates a new issue. If 'yes', then the author will be added on followups
95   # too. If 'no', they're never added to the nosy.
96   ADD_AUTHOR_TO_NOSY = 'new'          # one of 'yes', 'no', 'new'
98   # Do the recipients (To:, Cc:) of a message get placed on the nosy list?
99   # If 'new' is used, then the recipients will only be added when a message
100   # creates a new issue. If 'yes', then the recipients will be added on
101   # followups
102   # too. If 'no', they're never added to the nosy.
103   ADD_RECIPIENTS_TO_NOSY = 'new'      # either 'yes', 'no', 'new'
105   # Where to place the email signature
106   EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
108   # Keep email citations
109   EMAIL_KEEP_QUOTED_TEXT = 'no'       # either 'yes' or 'no'
111   # Preserve the email body as is
112   EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
114   # Default class to use in the mailgw if one isn't supplied in email
115   # subjects. To disable, comment out the variable below or leave it blank.
116   # Examples:
117   MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
118   #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
121 Instance Schema
122 ---------------
124 Note: if you modify the schema, you'll most likely need to edit the
125       `web interface`_ HTML template files to reflect your changes.
127 An instance schema defines what data is stored in the instance's database. The
128 two schemas shipped with Roundup turn it into a typical software bug tracker
129 (the extended schema allowing for support issues as well as bugs). Schemas are
130 defined using Python code. The "classic" schema looks like this::
132     pri = Class(db, "priority", name=String(), order=String())
133     pri.setkey("name")
134     pri.create(name="critical", order="1")
135     pri.create(name="urgent", order="2")
136     pri.create(name="bug", order="3")
137     pri.create(name="feature", order="4")
138     pri.create(name="wish", order="5")
140     stat = Class(db, "status", name=String(), order=String())
141     stat.setkey("name")
142     stat.create(name="unread", order="1")
143     stat.create(name="deferred", order="2")
144     stat.create(name="chatting", order="3")
145     stat.create(name="need-eg", order="4")
146     stat.create(name="in-progress", order="5")
147     stat.create(name="testing", order="6")
148     stat.create(name="done-cbb", order="7")
149     stat.create(name="resolved", order="8")
151     keyword = Class(db, "keyword", name=String())
152     keyword.setkey("name")
154     user = Class(db, "user", username=String(), password=String(),
155         address=String(), realname=String(), phone=String(),
156         organisation=String())
157     user.setkey("username")
158     user.create(username="admin", password=adminpw,
159         address=instance_config.ADMIN_EMAIL)
161     msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
162         ("user"), date=Date(), summary=String(), files=Multilink("file"))
164     file = FileClass(db, "file", name=String(), type=String())
166     issue = IssueClass(db, "issue", assignedto=Link("user"),
167         topic=Multilink("keyword"), priority=Link("priority"), status=Link
168         ("status"))
169     issue.setkey('title')
171 Classes and Properties - creating a new information store
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174 In the instance above, we've defined 7 classes of information:
176   priority
177       Defines the possible levels of urgency for issues.
179   status
180       Defines the possible states of processing the issue may be in.
182   keyword
183       Initially empty, will hold keywords useful for searching issues.
185   user
186       Initially holding the "admin" user, will eventually have an entry for all
187       users using roundup.
189   msg
190       Initially empty, will all e-mail messages sent to or generated by
191       roundup.
193   file
194       Initially empty, will all files attached to issues.
196   issue
197       Initially emtyp, this is where the issue information is stored.
199 We define the "priority" and "status" classes to allow two things: reduction in
200 the amount of information stored on the issue and more powerful, accurate
201 searching of issues by priority and status. By only requiring a link on the
202 issue (which is stored as a single number) we reduce the chance that someone
203 mis-types a priority or status - or simply makes a new one up.
205 Class and Nodes
206 :::::::::::::::
208 A Class defines a particular class (or type) of data that will be stored in the
209 database. A class comprises one or more properties, which given the information
210 about the class nodes.
211 The actual data entered into the database, using class.create() are called
212 nodes. They have a special immutable property called id. We sometimes refer to
213 this as the nodeid.
215 Properties
216 ::::::::::
218 A Class is comprised of one or more properties of the following types:
219     * String properties are for storing arbitrary-length strings.
220     * Password properties are for storing encoded arbitrary-length strings. The
221       default encoding is defined on the roundup.password.Password class.
222     * Date properties store date-and-time stamps. Their values are Timestamp
223       objects.
224     * A Link property refers to a single other node selected from a specified
225       class. The class is part of the property; the value is an integer, the id
226       of the chosen node.
227     * A Multilink property refers to possibly many nodes in a specified class.
228       The value is a list of integers.
230 FileClass
231 :::::::::
233 FileClasses save their "content" attribute off in a separate file from the rest
234 of the database. This reduces the number of large entries in the database,
235 which generally makes databases more efficient, and also allows us to use
236 command-line tools to operate on the files. They are stored in the files sub-
237 directory of the db directory in your instance.
239 IssueClass
240 ::::::::::
242 IssueClasses automatically include the "messages", "files", "nosy", and
243 "superseder" properties.
244 The messages and files properties list the links to the messages and files
245 related to the issue. The nosy property is a list of links to users who wish to
246 be informed of changes to the issue - they get "CC'ed" e-mails when messages
247 are sent to or generated by the issue. The nosy reactor (in the detectors
248 directory) handles this action. The superceder link indicates an issue which
249 has superceded this one.
250 They also have the dynamically generated "creation", "activity" and "creator"
251 properties.
252 The value of the "creation" property is the date when a node was created, and
253 the value of the "activity" property is the date when any property on the node
254 was last edited (equivalently, these are the dates on the first and last
255 records in the node's journal). The "creator" property holds a link to the user
256 that created the issue.
258 setkey(property)
259 ::::::::::::::::
261 Select a String property of the class to be the key property. The key property
262 muse be unique, and allows references to the nodes in the class by the content
263 of the key property. That is, we can refer to users by their username, e.g.
264 let's say that there's an issue in roundup, issue 23. There's also a user,
265 richard who happens to be user 2. To assign an issue to him, we could do either
266 of::
268      roundup-admin set issue assignedto=2
270 or::
272      roundup-admin set issue assignedto=richard
274 Note, the same thing can be done in the web and e-mail interfaces.
276 create(information)
277 :::::::::::::::::::
279 Create a node in the database. This is generally used to create nodes in the
280 "definitional" classes like "priority" and "status".
283 Detectors - adding behaviour to your tracker
284 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286 Sample additional detectors that have been found useful will appear in the
287 ``detectors`` directory of the Roundup distribution:
289 newissuecopy.py
290   This detector sends an email to a team address whenever a new issue is
291   created. The address is hard-coded into the detector, so edit it before you
292   use it (look for the text 'team@team.host') or you'll get email errors!
295 Web Interface
296 -------------
298 The web interface works behind the cgi-bin/roundup.cgi or roundup-server
299 scripts. In both cases, the scripts determine which instance is being accessed
300 (the first part of the URL path inside the scope of the CGI handler) and pass
301 control on to the instance interfaces.Client class which handles the rest of
302 the access through its main() method. This means that you can do pretty much
303 anything you want as a web interface to your instance.
304 Most customisation of the web view can be done by modifying the templates in
305 the instance html directory. These are divided into index, item and newitem
306 views. The newitem view is optional - the item view will be used if the newitem
307 view doesn't exist.
309 Repurcussions of changing the instance schema
310 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
312 If you choose to change_the_instance_schema you will need to ensure the web
313 interface knows about it:
315    1. Index, item and filter pages for the relevant classes may need to have
316       properties added or removed,
317    2. The default page header relies on the existence of, and some values of
318       the priority, status, assignedto and activity classes. If you change any
319       of these (specifically if you remove any of the classes or their default
320       values) you will need to implement your own pagehead() method in your
321       instance's interfaces.py module.
323 Displaying Properties
324 ~~~~~~~~~~~~~~~~~~~~~
326 Properties appear in the user interface in three contexts: in indices, in
327 editors, and as filters. For each type of property, there are several display
328 possibilities. For example, in an index view, a string property may just be
329 printed as a plain string, but in an editor view, that property should be
330 displayed in an editable field.
332 The display of a property is handled by functions in the htmltemplate module.
333 Displayer functions are triggered by <display> tags in templates. The call
334 attribute of the tag provides a Python expression for calling the displayer
335 function. The three standard arguments are inserted in front of the arguments
336 given. For example, the occurrence of::
338          <display call="plain('status')">
340 in a template triggers a call the "plain" function. The displayer functions can
341 accept extra arguments to further specify details about the widgets that should
342 be generated. By defining new displayer functions, the user interface can be
343 highly customized.
345 +-----------------------------------------------------------------------------+
346 |The displayer functions are                                                  |
347 +---------+-------------------------------------------------------------------+
348 |plain    |Display a String property directly.                                |
349 |         |Display a Date property in a specified time zone with an option to |
350 |         |omit the time from the date stamp.                                 |
351 |         |For a Link or Multilink property, display the key strings of the   |
352 |         |linked nodes (or the ids if the linked class has no key property). |
353 |         |Options:                                                           |
354 |         |escape (boolean) - HTML-escape the resulting text.                 |
355 +---------+-------------------------------------------------------------------+
356 |field    |Display a property like the plain displayer above, but in a form   |
357 |         |field to be edited. Strings, Dates and Intervals use TEXT fields,  |
358 |         |Links use SELECT fields and Multilinks use SELECT MULTIPLE fields. |
359 |         |Options:                                                           |
360 |         |size (number) - width of TEXT fields.                              |
361 |         |height (number) - number of nows in SELECT MULTIPLE tags.          |
362 |         |showid (boolean) - true includes the id of linked nodes in the     |
363 |         |SELECT MULTIPLE fields.                                            |
364 +---------+-------------------------------------------------------------------+
365 |menu     |For a Links and Multilinks, display the same field as would be     |
366 |         |generated using field.                                             |
367 +---------+-------------------------------------------------------------------+
368 |link     |For a Link or Multilink property, display the names of the linked  |
369 |         |nodes, hyperlinked to the item views on those nodes.               |
370 |         |For other properties, link to this node with the property as the   |
371 |         |text.                                                              |
372 |         |Options:                                                           |
373 |         |property (property name) - the property to use in the second case. |
374 |         |showid - use the linked node id as the link text (linked node      |
375 |         |         "value" will be set as a tooltip)                         |
376 +---------+-------------------------------------------------------------------+
377 |count    |For a Multilink property, display a count of the number of links in|
378 |         |the list.                                                          |
379 |         |Arguments:                                                         |
380 |         |property (property name) - the property to use.                    |
381 +---------+-------------------------------------------------------------------+
382 |reldate  |Display a Date property in terms of an interval relative to the    |
383 |         |current date (e.g. "+ 3w", "- 2d").                                |
384 |         |Arguments:                                                         |
385 |         |property (property name) - the property to use.                    |
386 |         |Options:                                                           |
387 |         |pretty (boolean) - display the relative date in an English form.   |
388 +---------+-------------------------------------------------------------------+
389 |download |For a Link or Multilink property, display the names of the linked  |
390 |         |nodes, hyperlinked to the item views on those nodes.               |
391 |         |For other properties, link to this node with the property as the   |
392 |         |text.                                                              |
393 |         |In all cases, append the name (key property) of the item to the    |
394 |         |path so it is the name of the file being downloaded.               |
395 |         |Arguments:                                                         |
396 |         |property (property name) - the property to use.                    |
397 +---------+-------------------------------------------------------------------+
398 |checklist|For a Link or Multilink property, display checkboxes for the       |
399 |         |available choices to permit filtering.                             |
400 |         |Arguments:                                                         |
401 |         |property (property name) - the property to use.                    |
402 +---------+-------------------------------------------------------------------+
403 |note     |Display the special notes field, which is a text area for entering |
404 |         |a note to go along with a change.                                  |
405 +---------+-------------------------------------------------------------------+
406 |list     |List the nodes specified by property using the standard index for  |
407 |         |the class.                                                         |
408 |         |Arguments:                                                         |
409 |         |property (property name) - the property to use.                    |
410 +---------+-------------------------------------------------------------------+
411 |history  |List the history of the item.                                      |
412 +---------+-------------------------------------------------------------------+
413 |submit   |Add a submit button for the item.                                  |
414 +---------+-------------------------------------------------------------------+
417 Index Views
418 ~~~~~~~~~~~
420 An index view contains two sections: a filter section and an index section. The
421 filter section provides some widgets for selecting which items appear in the
422 index. The index section is a table of items.
424 Index View Specifiers
425 :::::::::::::::::::::
427 An index view specifier (URL fragment) looks like this (whitespace has been
428 added for clarity)::
430      /issue?status=unread,in-progress,resolved&
431              topic=security,ui&
432              :group=+priority&
433              :sort=-activity&
434              :filters=status,topic&
435              :columns=title,status,fixer
437 The index view is determined by two parts of the specifier: the layout part and
438 the filter part. The layout part consists of the query parameters that begin
439 with colons, and it determines the way that the properties of selected nodes
440 are displayed. The filter part consists of all the other query parameters, and
441 it determines the criteria by which nodes are selected for display.
442 The filter part is interactively manipulated with the form widgets displayed in
443 the filter section. The layout part is interactively manipulated by clicking on
444 the column headings in the table.
446 The filter part selects the union of the sets of items with values matching any
447 specified Link properties and the intersection of the sets of items with values
448 matching any specified Multilink properties.
450 The example specifies an index of "issue" nodes. Only items with a "status" of
451 either "unread" or "in-progres" or "resolved" are displayed, and only items
452 with "topic" values including both "security" and "ui" are displayed. The items
453 are grouped by priority, arranged in ascending order; and within groups, sorted
454 by activity, arranged in descending order. The filter section shows filters for
455 the "status" and "topic" properties, and the table includes columns for the
456 "title", "status", and "fixer" properties.
458 Associated with each item class is a default layout specifier. The layout
459 specifier in the above example is the default layout to be provided with the
460 default bug-tracker schema described above in section 4.4.
462 Filter Section
463 ::::::::::::::
465 The template for a filter section provides the filtering widgets at the top of
466 the index view. Fragments enclosed in <property>...</property> tags are
467 included or omitted depending on whether the view specifier requests a filter
468 for a particular property.
470 A property must appear in the filter template for it to be available as a
471 filter.
473 Here's a simple example of a filter template.::
475      <property name=status>
476          <display call="checklist('status')">
477      </property>
478      <br>
479      <property name=priority>
480          <display call="checklist('priority')">
481      </property>
482      <br>
483      <property name=fixer>
484          <display call="menu('fixer')">
485      </property>
487 The standard index generation code appends a section to the index pages which
488 allows selection of the filters - from those which are defined in the filter
489 template.
491 Index Section
492 :::::::::::::
494 The template for an index section describes one row of the index table.
495 Fragments enclosed in <property>...</property> tags are included or omitted
496 depending on whether the view specifier requests a column for a particular
497 property. The table cells should contain <display> tags to display the values
498 of the item's properties.
500 Here's a simple example of an index template.::
502      <tr>
503          <property name=title>
504              <td><display call="plain('title', max=50)"></td>
505          </property>
506          <property name=status>
507              <td><display call="plain('status')"></td>
508          </property>
509          <property name=fixer>
510              <td><display call="plain('fixer')"></td>
511          </property>
512      </tr>
514 Sorting
515 :::::::
517 String and Date values are sorted in the natural way. Link properties are
518 sorted according to the value of the "order" property on the linked nodes if it
519 is present; or otherwise on the key string of the linked nodes; or finally on
520 the node ids. Multilink properties are sorted according to how many links are
521 present.
523 Item Views
524 ~~~~~~~~~~
526 An item view contains an editor section and a spool section. At the top of an
527 item view, links to superseding and superseded items are always displayed.
529 Editor Section
530 ::::::::::::::
532 The editor section is generated from a template containing <display> tags to
533 insert the appropriate widgets for editing properties.
535 Here's an example of a basic editor template.::
537      <table>
538      <tr>
539          <td colspan=2>
540              <display call="field('title', size=60)">
541          </td>
542      </tr>
543      <tr>
544          <td>
545              <display call="field('fixer', size=30)">
546          </td>
547          <td>
548              <display call="menu('status')>
549          </td>
550      </tr>
551      <tr>
552          <td>
553              <display call="field('nosy', size=30)">
554          </td>
555          <td>
556              <display call="menu('priority')>
557          </td>
558      </tr>
559      <tr>
560          <td colspan=2>
561              <display call="note()">
562          </td>
563      </tr>
564      </table>
566 As shown in the example, the editor template can also request the display of a
567 "note" field, which is a text area for entering a note to go along with a
568 change.
570 The <property> tag used in the index may also be used here - it checks to see
571 if the nominated Multilink property has any entries. This can be used to
572 eliminate sections of the editor section if the property has no entries::
574   <td class="form-text">
575     <display call="field('superseder', size=40, showid=1)">
576     <display call="classhelp('issue', 'id,title', label='list', width=500)">
577     <property name="superseder">
578       <br>View: <display call="link('superseder', showid=1)">
579     </property>
580   </td>
582 The "View: " part with the links will only display if the superseder property
583 has values.
585 When a change is submitted, the system automatically generates a message
586 describing the changed properties.
588 If a note is given in the "note" field, the note is appended to the
589 description. The message is then added to the item's message spool (thus
590 triggering the standard detector to react by sending out this message to the
591 nosy list).
593 The message also displays all of the property values on the item and indicates
594 which ones have changed. An example of such a message might be this::
596      Polly's taken a turn for the worse - this is now really important!
597      -----
598      title: Polly Parrot is dead
599      priority: critical
600      status: unread -> in-progress
601      fixer: terry
602      keywords: parrot,plumage,perch,nailed,dead
604 Spool Section
605 :::::::::::::
607 The spool section lists messages in the item's "messages" property. The index
608 of messages displays the "date", "author", and "summary" properties on the
609 message nodes, and selecting a message takes you to its content.
611 The <property> tag used in the index may also be used here - it checks to see
612 if the nominated Multilink property has any entries. This can be used to
613 eliminate sections of the spool section if the property has no entries::
615      <property name="files">
616       <tr class="strong-header">
617        <td><b>Files</b></td>
618       </tr>
620       <tr>
621        <td><display call="list('files')"></td>
622       </tr>
623      </property>
625 -----------------
627 Back to `Table of Contents`_
629 .. _`Table of Contents`: index.html