comparison doc/reference.txt @ 7352:f3c9ba5db30b

split reference out from customizing and fix all links. also reformat a couple of lists. Still to do verify that references to customizing.txt from the rest of the docs should still point there.
author John Rouillard <rouilj@ieee.org>
date Tue, 16 May 2023 00:56:00 -0400
parents 2a735d785836
children 0cb4541bad71
comparison
equal deleted inserted replaced
7351:1f8e41b0e97f 7352:f3c9ba5db30b
29 was difficult. 29 was difficult.
30 30
31 The documentation is slowly being reorganized using the `Diataxis 31 The documentation is slowly being reorganized using the `Diataxis
32 framework`_. Help with the reorganization is welcome. 32 framework`_. Help with the reorganization is welcome.
33 33
34
35 This is the document you should look at for background on a tutorial 34 This is the document you should look at for background on a tutorial
36 or how-to guide in the customization document. 35 or how-to guide in the customization document.
37 36
38 .. _customization document: customizing.html 37 .. _customization document: customizing.html
39 .. _diataxis framework: https://diataxis.fr/ 38 .. _diataxis framework: https://diataxis.fr/
39
40 Trackers in a Nutshell
41 ======================
42
43 Trackers have the following structure:
44
45 .. index::
46 single: tracker; structure db directory
47 single: tracker; structure detectors directory
48 single: tracker; structure extensions directory
49 single: tracker; structure html directory
50 single: tracker; structure html directory
51 single: tracker; structure lib directory
52
53 =================== ========================================================
54 Tracker File Description
55 =================== ========================================================
56 config.ini Holds the basic `tracker configuration`_
57 schema.py Holds the `tracker schema`_
58 initial_data.py Holds any data to be entered into the database when the
59 tracker is initialised (optional)
60 interfaces.py Allows `modifying the core of Roundup`_ (optional)
61 db/ Holds the tracker's database
62 db/files/ Holds the tracker's upload files and messages
63 db/backend_name Names the database back-end for the tracker (obsolete).
64 Use the ``backend`` setting in the ``[rdbms]``
65 section of ``config.ini`` instead.
66 detectors/ `Auditors and reactors`_ for this tracker
67 extensions/ Additional `actions`_ and `templating utilities`_
68 html/ Web interface templates, images and style sheets
69 lib/ optional common imports for detectors and extensions
70 =================== ========================================================
71
72
73 .. index:: config.ini
74 .. index:: configuration; see config.ini
75
76 Tracker Configuration
77 =====================
78
79 The ``config.ini`` located in your tracker home contains the basic
80 configuration for the web and e-mail components of Roundup's interfaces.
81
82 Changes to the data captured by your tracker is controlled by the `tracker
83 schema`_. Some configuration is also performed using permissions - see the
84 `security / access controls`_ section. For example, to allow users to
85 automatically register through the email interface, you must grant the
86 "Anonymous" Role the "Email Access" Permission.
87
88 .. index::
89 single: config.ini; sections
90 see: configuration; config.ini
91
92 The following is taken from the `Python Library Reference`__ (July 18, 2018)
93 section "ConfigParser -- Configuration file parser":
94
95 The configuration file consists of sections, led by a [section] header
96 and followed by name: value entries, with continuations in the style
97 of RFC 822 (see section 3.1.1, “LONG HEADER FIELDS”); name=value is
98 also accepted. Note that leading whitespace is removed from
99 values. The optional values can contain format strings which refer to
100 other values in the same section, or values in a special DEFAULT
101 section. Additional defaults can be provided on initialization and
102 retrieval. Lines beginning with '#' or ';' are ignored and may be
103 used to provide comments.
104
105 For example::
106
107 [My Section]
108 foodir = %(dir)s/whatever
109 dir = frob
110
111 would resolve the "%(dir)s" to the value of "dir" ("frob" in this case)
112 resulting in "foodir" being "frob/whatever".
113
114 __ https://docs.python.org/2/library/configparser.html
115
116 Example configuration settings are below. This is a partial
117 list. Documentation on all the settings is included in the
118 ``config.ini`` file.
119
120 .. index:: config.ini; sections main
121
122 Section **main**
123 database -- ``db``
124 Database directory path. The path may be either absolute or relative
125 to the directory containig this config file.
126
127 templates -- ``html``
128 Path to the HTML templates directory. The path may be either absolute
129 or relative to the directory containing this config file.
130
131 static_files -- default *blank*
132 A list of space separated directory paths (or a single directory).
133 These directories hold additional static files available via Web UI.
134 These directories may contain sitewide images, CSS stylesheets etc. If
135 a '-' is included, the list processing ends and the TEMPLATES
136 directory is not searched after the specified directories. If this
137 option is not set, all static files are taken from the TEMPLATES
138 directory.
139
140 admin_email -- ``roundup-admin``
141 Email address that roundup will complain to if it runs into trouble. If
142 the email address doesn't contain an ``@`` part, the MAIL_DOMAIN defined
143 below is used.
144
145 dispatcher_email -- ``roundup-admin``
146 The 'dispatcher' is a role that can get notified of new items to the
147 database. It is used by the ERROR_MESSAGES_TO config setting. If the
148 email address doesn't contain an ``@`` part, the MAIL_DOMAIN defined
149 below is used.
150
151 email_from_tag -- default *blank*
152 Additional text to include in the "name" part of the From: address used
153 in nosy messages. If the sending user is "Foo Bar", the From: line
154 is usually: ``"Foo Bar" <issue_tracker@tracker.example>``
155 the EMAIL_FROM_TAG goes inside the "Foo Bar" quotes like so:
156 ``"Foo Bar EMAIL_FROM_TAG" <issue_tracker@tracker.example>``
157
158 new_web_user_roles -- ``User``
159 Roles that a user gets when they register with Web User Interface.
160 This is a comma-separated list of role names (e.g. ``Admin,User``).
161
162 new_email_user_roles -- ``User``
163 Roles that a user gets when they register with Email Gateway.
164 This is a comma-separated string of role names (e.g. ``Admin,User``).
165
166 error_messages_to -- ``user``
167 Send error message emails to the ``dispatcher``, ``user``, or ``both``?
168 The dispatcher is configured using the DISPATCHER_EMAIL setting.
169 Allowed values: ``dispatcher``, ``user``, or ``both``
170
171 html_version -- ``html4``
172 HTML version to generate. The templates are ``html4`` by default.
173 If you wish to make them xhtml, then you'll need to change this
174 var to ``xhtml`` too so all auto-generated HTML is compliant.
175 Allowed values: ``html4``, ``xhtml``
176
177 timezone -- ``0``
178 Numeric timezone offset used when users do not choose their own
179 in their settings.
180
181 instant_registration -- ``yes``
182 Register new users instantly, or require confirmation via
183 email?
184 Allowed values: ``yes``, ``no``
185
186 email_registration_confirmation -- ``yes``
187 Offer registration confirmation by email or only through the web?
188 Allowed values: ``yes``, ``no``
189
190 indexer_stopwords -- default *blank*
191 Additional stop-words for the full-text indexer specific to
192 your tracker. See the indexer source for the default list of
193 stop-words (e.g. ``A,AND,ARE,AS,AT,BE,BUT,BY, ...``).
194
195 umask -- ``02``
196 Defines the file creation mode mask.
197
198 csv_field_size -- ``131072``
199 Maximum size of a csv-field during import. Roundup's export
200 format is a csv (comma separated values) variant. The csv
201 reader has a limit on the size of individual fields
202 starting with python 2.5. Set this to a higher value if you
203 get the error 'Error: field larger than field limit' during
204 import.
205
206 .. index:: config.ini; sections tracker
207
208 Section **tracker**
209 name -- ``Roundup issue tracker``
210 A descriptive name for your Roundup instance.
211
212 web -- ``http://host.example/demo/``
213 The web address that the tracker is viewable at.
214 This will be included in information sent to users of the tracker.
215 The URL MUST include the cgi-bin part or anything else
216 that is required to get to the home page of the tracker.
217 You MUST include a trailing '/' in the URL.
218
219 email -- ``issue_tracker``
220 Email address that mail to Roundup should go to.
221
222 language -- default *blank*
223 Default locale name for this tracker. If this option is not set, the
224 language is determined by the environment variable LANGUAGE, LC_ALL,
225 LC_MESSAGES, or LANG, in that order of preference.
226
227 .. index:: config.ini; sections web
228
229 Section **web**
230 allow_html_file -- ``no``
231 Setting this option enables Roundup to serve uploaded HTML
232 file content *as HTML*. This is a potential security risk
233 and is therefore disabled by default. Set to 'yes' if you
234 trust *all* users uploading content to your tracker.
235
236 http_auth -- ``yes``
237 Whether to use HTTP Basic Authentication, if present.
238 Roundup will use either the REMOTE_USER or HTTP_AUTHORIZATION
239 variables supplied by your web server (in that order).
240 Set this option to 'no' if you do not wish to use HTTP Basic
241 Authentication in your web interface.
242
243 use_browser_language -- ``yes``
244 Whether to use HTTP Accept-Language, if present.
245 Browsers send a language-region preference list.
246 It's usually set in the client's browser or in their
247 Operating System.
248 Set this option to 'no' if you want to ignore it.
249
250 debug -- ``no``
251 Setting this option makes Roundup display error tracebacks
252 in the user's browser rather than emailing them to the
253 tracker admin."),
254
255 .. index:: config.ini; sections rdbms
256 single: config.ini; database settings
257
258 Section **rdbms**
259 Settings in this section are used to set the backend and configure
260 addition settings needed by RDBMs like SQLite, Postgresql and
261 MySQL backends.
262
263 .. index::
264 single: postgres; select backend in config.ini
265 single: mysql; select backend in config.ini
266 single: sqlite; select backend in config.ini
267 single: anydbm; select backend in config.ini
268 see: database; postgres
269 see: database; mysql
270 see: database; sqlite
271 see: database; anydbm
272
273 backend -- set to value by init
274 The database backend such as anydbm, sqlite, mysql or postgres.
275
276 name -- ``roundup``
277 Name of the database to use.
278
279 host -- ``localhost``
280 Database server host.
281
282 port -- default *blank*
283 TCP port number of the database server. Postgresql usually resides on
284 port 5432 (if any), for MySQL default port number is 3306. Leave this
285 option empty to use backend default.
286
287 user -- ``roundup``
288 Database user name that Roundup should use.
289
290 password -- ``roundup``
291 Database user password.
292
293 read_default_file -- ``~/.my.cnf``
294 Name of the MySQL defaults file. Only used in MySQL connections.
295
296 read_default_group -- ``roundup``
297 Name of the group to use in the MySQL defaults file. Only used in
298 MySQL connections.
299
300 .. index::
301 single: sqlite; lock timeout
302
303 sqlite_timeout -- ``30``
304 Number of seconds to wait when the SQLite database is locked.
305 Used only for SQLite.
306
307 cache_size -- `100`
308 Size of the node cache (in elements) used to keep most recently used
309 data in memory.
310
311 .. index:: config.ini; sections logging
312 see: logging; config.ini, sections logging
313
314 Section **logging**
315 config -- default *blank*
316 Path to configuration file for standard Python logging module. If this
317 option is set, logging configuration is loaded from specified file;
318 options 'filename' and 'level' in this section are ignored. The path may
319 be either absolute or relative to the directory containig this config file.
320
321 filename -- default *blank*
322 Log file name for minimal logging facility built into Roundup. If no file
323 name specified, log messages are written on stderr. If above 'config'
324 option is set, this option has no effect. The path may be either absolute
325 or relative to the directory containig this config file.
326
327 level -- ``ERROR``
328 Minimal severity level of messages written to log file. If above 'config'
329 option is set, this option has no effect.
330 Allowed values: ``DEBUG``, ``INFO``, ``WARNING``, ``ERROR``
331
332 .. index:: config.ini; sections mail
333
334 Section **mail**
335 Outgoing email options. Used for nosy messages, password reset and
336 registration approval requests.
337
338 domain -- ``localhost``
339 Domain name used for email addresses.
340
341 host -- default *blank*
342 SMTP mail host that Roundup will use to send mail
343
344 username -- default *blank*
345 SMTP login name. Set this if your mail host requires authenticated access.
346 If username is not empty, password (below) MUST be set!
347
348 password -- default *blank*
349 SMTP login password.
350 Set this if your mail host requires authenticated access.
351
352 port -- default *25*
353 SMTP port on mail host.
354 Set this if your mail host runs on a different port.
355
356 local_hostname -- default *blank*
357 The fully qualified domain name (FQDN) to use during SMTP sessions. If left
358 blank, the underlying SMTP library will attempt to detect your FQDN. If your
359 mail host requires something specific, specify the FQDN to use.
360
361 tls -- ``no``
362 If your SMTP mail host provides or requires TLS (Transport Layer Security)
363 then you may set this option to 'yes'.
364 Allowed values: ``yes``, ``no``
365
366 tls_keyfile -- default *blank*
367 If TLS is used, you may set this option to the name of a PEM formatted
368 file that contains your private key. The path may be either absolute or
369 relative to the directory containig this config file.
370
371 tls_certfile -- default *blank*
372 If TLS is used, you may set this option to the name of a PEM formatted
373 certificate chain file. The path may be either absolute or relative
374 to the directory containig this config file.
375
376 charset -- utf-8
377 Character set to encode email headers with. We use utf-8 by default, as
378 it's the most flexible. Some mail readers (eg. Eudora) can't cope with
379 that, so you might need to specify a more limited character set
380 (eg. iso-8859-1).
381
382 debug -- default *blank*
383 Setting this option makes Roundup to write all outgoing email messages
384 to this file *instead* of sending them. This option has the same effect
385 as environment variable SENDMAILDEBUG. Environment variable takes
386 precedence. The path may be either absolute or relative to the directory
387 containig this config file.
388
389 add_authorinfo -- ``yes``
390 Add a line with author information at top of all messages send by
391 Roundup.
392
393 add_authoremail -- ``yes``
394 Add the mail address of the author to the author information at the
395 top of all messages. If this is false but add_authorinfo is true,
396 only the name of the actor is added which protects the mail address
397 of the actor from being exposed at mail archives, etc.
398
399 .. index:: config.ini; sections mailgw
400 single: mailgw; config
401 see: mail gateway; mailgw
402
403 Section **mailgw**
404 Roundup Mail Gateway options
405
406 keep_quoted_text -- ``yes``
407 Keep email citations when accepting messages. Setting this to ``no`` strips
408 out "quoted" text from the message. Signatures are also stripped.
409 Allowed values: ``yes``, ``no``
410
411 leave_body_unchanged -- ``no``
412 Preserve the email body as is - that is, keep the citations *and*
413 signatures.
414 Allowed values: ``yes``, ``no``
415
416 default_class -- ``issue``
417 Default class to use in the mailgw if one isn't supplied in email subjects.
418 To disable, leave the value blank.
419
420 language -- default *blank*
421 Default locale name for the tracker mail gateway. If this option is
422 not set, mail gateway will use the language of the tracker instance.
423
424 subject_prefix_parsing -- ``strict``
425 Controls the parsing of the [prefix] on subject lines in incoming emails.
426 ``strict`` will return an error to the sender if the [prefix] is not
427 recognised. ``loose`` will attempt to parse the [prefix] but just
428 pass it through as part of the issue title if not recognised. ``none``
429 will always pass any [prefix] through as part of the issue title.
430
431 subject_suffix_parsing -- ``strict``
432 Controls the parsing of the [suffix] on subject lines in incoming emails.
433 ``strict`` will return an error to the sender if the [suffix] is not
434 recognised. ``loose`` will attempt to parse the [suffix] but just
435 pass it through as part of the issue title if not recognised. ``none``
436 will always pass any [suffix] through as part of the issue title.
437
438 subject_suffix_delimiters -- ``[]``
439 Defines the brackets used for delimiting the commands suffix in a subject
440 line.
441
442 subject_content_match -- ``always``
443 Controls matching of the incoming email subject line against issue titles
444 in the case where there is no designator [prefix]. ``never`` turns off
445 matching. ``creation + interval`` or ``activity + interval`` will match
446 an issue for the interval after the issue's creation or last activity.
447 The interval is a standard Roundup interval.
448
449 subject_updates_title -- ``yes``
450 Update issue title if incoming subject of email is different.
451 Setting this to ``no`` will ignore the title part of
452 the subject of incoming email messages.
453
454 refwd_re -- ``(\s*\W?\s*(fw|fwd|re|aw|sv|ang)\W)+``
455 Regular expression matching a single reply or forward prefix
456 prepended by the mailer. This is explicitly stripped from the
457 subject during parsing. Value is Python Regular Expression
458 (UTF8-encoded).
459
460 origmsg_re -- `` ^[>|\s]*-----\s?Original Message\s?-----$``
461 Regular expression matching start of an original message if quoted
462 in the body. Value is Python Regular Expression (UTF8-encoded).
463
464 sign_re -- ``^[>|\s]*-- ?$``
465 Regular expression matching the start of a signature in the message
466 body. Value is Python Regular Expression (UTF8-encoded).
467
468 eol_re -- ``[\r\n]+``
469 Regular expression matching end of line. Value is Python Regular
470 Expression (UTF8-encoded).
471
472 blankline_re -- ``[\r\n]+\s*[\r\n]+``
473 Regular expression matching a blank line. Value is Python Regular
474 Expression (UTF8-encoded).
475
476 ignore_alternatives -- ``no``
477 When parsing incoming mails, Roundup uses the first
478 text/plain part it finds. If this part is inside a
479 multipart/alternative, and this option is set, all other
480 parts of the multipart/alternative are ignored. The default
481 is to keep all parts and attach them to the issue.
482
483 .. index:: config.ini; sections php
484
485 Section **pgp**
486 OpenPGP mail processing options
487
488 enable -- ``no``
489 Enable PGP processing. Requires gpg.
490
491 roles -- default *blank*
492 If specified, a comma-separated list of roles to perform PGP
493 processing on. If not specified, it happens for all users.
494
495 homedir -- default *blank*
496 Location of PGP directory. Defaults to $HOME/.gnupg if not
497 specified.
498
499
500 .. index:: config.ini; sections nosy
501
502 Section **nosy**
503 Nosy messages sending
504
505 messages_to_author -- ``no``
506 Send nosy messages to the author of the message.
507 If ``yes`` is used, then messages are sent to the author
508 even if not on the nosy list, same for ``new`` (but only for new messages).
509 When set to ``nosy``, the nosy list controls sending messages to the author.
510 Allowed values: ``yes``, ``no``, ``new``, ``nosy``
511
512 signature_position -- ``bottom``
513 Where to place the email signature.
514 Allowed values: ``top``, ``bottom``, ``none``
515
516 add_author -- ``new``
517 Does the author of a message get placed on the nosy list automatically?
518 If ``new`` is used, then the author will only be added when a message
519 creates a new issue. If ``yes``, then the author will be added on
520 followups too. If ``no``, they're never added to the nosy.
521 Allowed values: ``yes``, ``no``, ``new``
522
523 add_recipients -- ``new``
524 Do the recipients (``To:``, ``Cc:``) of a message get placed on the nosy
525 list? If ``new`` is used, then the recipients will only be added when a
526 message creates a new issue. If ``yes``, then the recipients will be added
527 on followups too. If ``no``, they're never added to the nosy.
528 Allowed values: ``yes``, ``no``, ``new``
529
530 email_sending -- ``single``
531 Controls the email sending from the nosy reactor. If ``multiple`` then
532 a separate email is sent to each recipient. If ``single`` then a single
533 email is sent with each recipient as a CC address.
534
535 max_attachment_size -- ``2147483647``
536 Attachments larger than the given number of bytes won't be attached
537 to nosy mails. They will be replaced by a link to the tracker's
538 download page for the file.
539
540
541 .. index:: single: roundup-admin; config.ini update
542 single: roundup-admin; config.ini create
543 single: config.ini; create
544 single: config.ini; update
545
546 You may generate a new default config file using the ``roundup-admin
547 genconfig`` command. You can generate a new config file merging in
548 existing settings using the ``roundup-admin updateconfig`` command.
549
550 Configuration variables may be referred to in lower or upper case. In code,
551 variables not in the "main" section are referred to using their section and
552 name, so "domain" in the section "mail" becomes MAIL_DOMAIN.
553
554 .. index:: pair: configuration; extensions
555 pair: configuration; detectors
556
557 Extending the configuration file
558 --------------------------------
559
560 You can't add new variables to the config.ini file in the tracker home but
561 you can add two new config.ini files:
562
563 - a config.ini in the ``extensions`` directory will be loaded and attached
564 to the config variable as "ext".
565 - a config.ini in the ``detectors`` directory will be loaded and attached
566 to the config variable as "detectors".
567
568 For example, the following in ``detectors/config.ini``::
569
570 [main]
571 qa_recipients = email@example.com
572
573 is accessible as::
574
575 db.config.detectors['QA_RECIPIENTS']
576
577 Note that the name grouping applied to the main configuration file is
578 applied to the extension config files, so if you instead have::
579
580 [qa]
581 recipients = email@example.com
582
583 then the above ``db.config.detectors['QA_RECIPIENTS']`` will still work.
584
585 Unlike values in the tracker's main ``config.ini``, the values defined
586 in these config files are not validated. For example: a setting that
587 is supposed to be an integer value (e.g. 4) could be the word
588 "foo". If you are writing Python code that uses these settings, you
589 should expect to handle invalid values.
590
591 Also, incorrect values aren't discovered until the config setting is
592 used. This can be long after the tracker is started and the error may
593 not be seen in the logs.
594
595 It is possible to validate these settings. Validation involves calling
596 the ``update_options`` method on the configuration option. This can be
597 done from the ``init()`` function in the Python files implementing
598 extensions_ or detectors_.
599
600 As an example, adding the following to an extension::
601
602 from roundup.configuration import SecretMandatoryOption
603
604 def init(instance):
605 instance.config.ext.update_option('RECAPTCHA_SECRET',
606 SecretMandatoryOption,description="Secret securing reCaptcha.")
607
608 similarly for a detector::
609
610 from roundup.configuration import MailAddressOption
611
612 def init(db):
613 try:
614 db.config.detectors.update_option('QA_RECIPIENTS',
615 MailAddressOption,
616 description="Email used for QA comment followup.")
617 except KeyError:
618 # COMMENT_EMAIL setting is not found, but it's optional
619 # so continue
620 pass
621
622 will allow reading the secret from a file or append the tracker domain
623 to an email address if it does not have a domain.
624
625 Running ``roundup-admin -i tracker_home display user1`` will validate
626 the settings for both config.ini`s. Otherwise detector options are not
627 validated until the first request to the web interface (or email
628 gateway).
629
630 There are 4 arguments for ``update_option``:
631
632 1. config setting name - string (positional, mandatory)
633 2. option type - Option derived class from configuration.py
634 (positional, mandatory)
635 3. default value - string (optional, named default)
636 4. description - string (optional, named description)
637
638 The first argument is the config setting name as described at the
639 beginning of this section.
640
641 The second argument is a class in the roundup.configuration module.
642 There are a number of these classes: BooleanOption,
643 IntegerNumberOption, RegExpOption.... Please see the configuration
644 module for all Option validators and their descriptions. You can also
645 define your own custom validator in `interfaces.py`_.
646
647 The third and fourth arguments are strings and are optional. They are
648 printed if there is an error and may help the user correct the problem.
649
650 .. index:: ! schema
651
652 Tracker Schema
653 ==============
654
655 .. note::
656 if you modify the schema, you'll most likely need to edit the
657 `web interface`_ HTML template files and `detectors`_ to reflect
658 your changes.
659
660 A tracker schema defines what data is stored in the tracker's database.
661 Schemas are defined using Python code in the ``schema.py`` module of your
662 tracker.
663
664 The ``schema.py`` and ``initial_data.py`` modules
665 -------------------------------------------------
666
667 The schema.py module is used to define what your tracker looks like
668 on the inside, the schema of the tracker. It defines the Classes
669 and properties on each class. It also defines the security for
670 those Classes. The next few sections describe how schemas work
671 and what you can do with them.
672
673 The initial_data.py module sets up the initial state of your
674 tracker. It’s called exactly once - by the ``roundup-admin initialise``
675 command. See the start of the section on `database content`_ for more
676 info about how this works.
677
678 .. index:: schema; classic - description of
679
680 The "classic" schema
681 --------------------
682
683 The "classic" schema looks like this (see section `setkey(property)`_
684 below for the meaning of ``'setkey'`` -- you may also want to look into
685 the sections `setlabelprop(property)`_ and `setorderprop(property)`_ for
686 specifying (default) labelling and ordering of classes.)::
687
688 pri = Class(db, "priority", name=String(), order=String())
689 pri.setkey("name")
690
691 stat = Class(db, "status", name=String(), order=String())
692 stat.setkey("name")
693
694 keyword = Class(db, "keyword", name=String())
695 keyword.setkey("name")
696
697 user = Class(db, "user", username=String(), organisation=String(),
698 password=String(), address=String(), realname=String(),
699 phone=String(), alternate_addresses=String(),
700 queries=Multilink('query'), roles=String(), timezone=String())
701 user.setkey("username")
702
703 msg = FileClass(db, "msg", author=Link("user"), summary=String(),
704 date=Date(), recipients=Multilink("user"),
705 files=Multilink("file"), messageid=String(), inreplyto=String())
706
707 file = FileClass(db, "file", name=String())
708
709 issue = IssueClass(db, "issue", keyword=Multilink("keyword"),
710 status=Link("status"), assignedto=Link("user"),
711 priority=Link("priority"))
712 issue.setkey('title')
713
714 .. index:: schema; allowed changes
715
716 What you can't do to the schema
717 -------------------------------
718
719 You must never:
720
721 **Remove the user class**
722 This class is the only *required* class in Roundup.
723
724 **Remove the "username", "address", "password" or "realname" user properties**
725 Various parts of Roundup require these properties. Don't remove them.
726
727 **Change the type of a property**
728 Property types must *never* be changed - the database simply doesn't take
729 this kind of action into account. Note that you can't just remove a
730 property and re-add it as a new type either. If you wanted to make the
731 assignedto property a Multilink, you'd need to create a new property
732 assignedto_list and remove the old assignedto property.
733
734
735 What you can do to the schema
736 -----------------------------
737
738 Your schema may be changed at any time before or after the tracker has been
739 initialised (or used). You may:
740
741 **Add new properties to classes, or add whole new classes**
742 This is painless and easy to do - there are generally no repercussions
743 from adding new information to a tracker's schema.
744
745 **Remove properties**
746 Removing properties is a little more tricky - you need to make sure that
747 the property is no longer used in the `web interface`_ *or* by the
748 detectors_.
749
750
751
752 Classes and Properties - creating a new information store
753 ---------------------------------------------------------
754
755 In the tracker above, we've defined 7 classes of information:
756
757 priority
758 Defines the possible levels of urgency for issues.
759
760 status
761 Defines the possible states of processing the issue may be in.
762
763 keyword
764 Initially empty, will hold keywords useful for searching issues.
765
766 user
767 Initially holding the "admin" user, will eventually have an entry
768 for all users using Roundup.
769
770 msg
771 Initially empty, will hold all e-mail messages sent to or
772 generated by Roundup.
773
774 file
775 Initially empty, will hold all files attached to issues.
776
777 issue
778 Initially empty, this is where the issue information is stored.
779
780 We define the "priority" and "status" classes to allow two things:
781
782 1. reduction in the amount of information stored on the issue
783 2. more powerful, accurate searching of issues by priority and status
784
785 By only requiring a link on the issue (which is stored as a single
786 number) we reduce the chance that someone mis-types a priority or
787 status - or simply makes a new one up.
788
789 Class names are used to access items of that class in the `REST api`_
790 interface. The classic tracker was created before the REST interface
791 was added. It uses the single form (i.e. issue and user not issues and
792 users) for its classes. Most REST documentation suggests using plural
793 forms. However, to make your API consistent, use singular forms for
794 classes that you add.
795
796 Class and Items
797 ~~~~~~~~~~~~~~~
798
799 A Class defines a particular class (or type) of data that will be stored
800 in the database. A class comprises one or more properties, which gives
801 the information about the class items.
802
803 The actual data entered into the database, using ``class.create()``, are
804 called items. They have a special immutable property called ``'id'``. We
805 sometimes refer to this as the *itemid*.
806
807
808 .. index:: schema; property types
809
810 Properties
811 ~~~~~~~~~~
812
813 A Class is comprised of one or more properties of the following types:
814
815 String
816 properties are for storing arbitrary-length strings.
817 Password
818 properties are for storing encoded arbitrary-length strings.
819 The default encoding is defined on the ``roundup.password.Password``
820 class.
821 Date
822 properties store date-and-time stamps. Their values are Timestamp
823 objects.
824 Interval
825 properties store time periods rather than absolute dates. For
826 example 2 hours.
827 Integer
828 properties store integer values. (Number can store real/float values.)
829 Number
830 properties store numeric values. There is an option to use
831 double-precision floating point numbers.
832 Boolean
833 properties store on/off, yes/no, true/false values.
834 Link
835 properties refers to a single other item selected from a
836 specified class. The class is part of the property; the value is an
837 integer, the id of the chosen item.
838 Multilink
839 properties refer to possibly many items in a specified
840 class. The value is a list of integers.
841
842 Properties can have additional attributes to change the default
843 behaviour:
844
845 .. index:: triple: schema; property attributes; required
846 triple: schema; property attributes; default_value
847 triple: schema; property attributes; quiet
848
849 * All properties support the following attributes:
850
851 - ``required``: see `design documentation`_. Adds the property to
852 the list returned by calling get_required_props for the class.
853 - ``default_value``: see `design documentation`_ Sets the default
854 value if the property is not set.
855 - ``quiet``: see `design documentation`_. Suppresses user visible
856 to changes to this property. The property change is not reported:
857
858 - in the change feedback/confirmation message in the web
859 interface
860 - the property change section of the nosy email
861 - the web history at the bottom of an item's page
862
863 This can be used to store state of the user interface (e.g. the
864 names of elements that are collapsed or hidden from the
865 user). Making properties that are updated as an indirect result of
866 a user's change (e.g. updating a blockers property, counting
867 number of times an issue was reopened or reassigned etc.) should
868 not be displayed to the user as they can be confusing.
869
870 .. index:: triple: schema; property attributes; indexme
871
872 * String properties can have an ``indexme`` attribute that defines if the
873 property should be part of the full text index. The default is 'no' but this
874 can be set to 'yes' to allow a property's contents to be in the full
875 text index.
876
877 .. index:: triple: schema; property attributes; use_double
878
879 * Number properties can have a ``use_double`` attribute that, when set
880 to ``True``, will use double precision floating point in the database.
881 * Link and Multilink properties can have several attributes:
882
883 .. index:: triple: schema; property attributes; do_journal
884
885 - ``do_journal``: By default, every change of a link property is
886 recorded in the item being linked to (or being unlinked). A typical
887 use-case for setting ``do_journal='no'`` would be to turn off
888 journalling of nosy list, message author and message recipient link
889 and unlink events to prevent the journal from clogged with these
890 events.
891
892 .. index:: triple: schema; property attributes; try_id_parsing
893
894 - ``try_id_parsing`` is turned on by default. If entering a number
895 into a Link or Multilink field, Roundup interprets this number as an
896 ID of the item to link to. Sometimes items can have numeric names
897 (like, e.g., product codes). For these Roundup needs to match the
898 numeric name and should never match an ID. In this case you can set
899 ``try_id_parsing='no'``.
900
901 .. index:: triple: schema; property attributes; rev_multilink
902
903 - The ``rev_multilink`` option takes a property name to be inserted
904 into the linked-to class. This property is a Multilink property that
905 links back to the current class. The new Multilink is read-only (it
906 is automatically modified if the Link or Multilink property defining
907 it is modified). The new property can be used in normal searches
908 using the "filter" method of the Class. This means it can be used
909 like other Multilink properties when searching (in an index
910 template) or via the REST and XMLRPC APIs.
911
912 As a example, suppose you want to group multiple issues into a
913 super issue. Each issue can be part of only one super issue. It is
914 inefficient to find all of the issues that are part of the
915 super issue by searching through all issues in the system looking
916 at the part_of link property. To make this more efficient, you
917 can declare an issue's part_of property as::
918
919 issue = IssueClass(db, "issue",
920 ...
921 part_of = Link("issue", rev_multilink="components"),
922 ... )
923
924 This automatically creates the ``components`` multilink on the issue
925 class. The ``components`` multilink is never explicitly declared in
926 the issue class, but it has the same effect as though you had
927 declared the class as::
928
929 issue = IssueClass(db, "issue",
930 ...
931 part_of = Link("issue"),
932 components = Multilink("issue"),
933 ... )
934
935 Then wrote a detector to update the components property on the
936 corresponding issue. Writing this detector can be tricky. There is
937 one other difference, you can not explicitly set/modify the
938 ``components`` multilink.
939
940 The effect of setting ``part_of = 3456`` on issue1234
941 automatically adds "1234" to the ``components`` property on
942 issue3456. You can search the ``components`` multilink just like a
943 regular multilink, but you can't explicitly assign to it.
944 Another difference of reverse multilinks to normal multilinks
945 is that when a linked node is retired, the node vanishes from the
946 multilink, e.g. in the example above, if an issue with ``part_of``
947 set to another issue is retired this issue vanishes from the
948 ``components`` multilink of the other issue.
949
950 You can also link between different classes. So you can modify
951 the issue definition to include::
952
953 issue = IssueClass(db, "issue",
954 ...
955 assigned_to = Link("user", rev_multilink="responsibleFor"),
956 ... )
957
958 This makes it easy to list all issues that the user is responsible
959 for (aka assigned_to).
960
961 .. index:: triple: schema; property attributes; msg_header_property
962
963 - The ``msg_header_property`` is used by the mail gateway when sending
964 out messages. When a link or multilink property of an issue changes,
965 Roundup creates email headers of the form::
966
967 X-Roundup-issue-prop: value
968
969 where ``value`` is the ``name`` property for the linked item(s).
970 For example, if you have a multilink for attached_files in your
971 issue, you will see a header::
972
973 X-Roundup-issue-attached_files: MySpecialFile.doc, HisResume.txt
974
975 when the class for attached files is defined as::
976
977 file = FileClass(db, "file", name=String())
978
979 ``MySpecialFile.doc`` is the name for the file object.
980
981 If you have an ``assigned_to`` property in your issue class that
982 links to the user class and you want to add a header::
983
984 X-Roundup-issue-assigned_to: ...
985
986 so that the mail recipients can filter emails where
987 ``X-Roundup-issue-assigned_to: name`` that contains their
988 username. The user class is defined as::
989
990 user = Class(db, "user",
991 username=String(),
992 password=Password(),
993 address=String(),
994 realname=String(),
995 phone=String(),
996 organisation=String(),
997 alternate_addresses=String(),
998 queries=Multilink('query'),
999 roles=String(), # comma-separated string of Role names
1000 timezone=String())
1001
1002 Because there is no ``name`` parameter for the user class, there
1003 will be no header. However setting::
1004
1005 assigned_to=Link("user", msg_header_property="username")
1006
1007 will make the mail gateway generate an ``X-Roundup-issue-assigned_to``
1008 using the username property of the linked user.
1009
1010 Assume assigned_to for an issue is linked to the user with
1011 username=joe_user, setting::
1012
1013 msg_header_property="username"
1014
1015 for the assigned_to property will generated message headers of the
1016 form::
1017
1018 X-Roundup-issue-assigned_to: joe_user
1019
1020 for emails sent on issues where joe_user has been assigned to the issue.
1021
1022 If this property is set to the empty string "", it will prevent
1023 the header from being generated on outgoing mail.
1024
1025 .. index:: triple: schema; class property; creator
1026 triple: schema; class property; creation
1027 triple: schema; class property; actor
1028 triple: schema; class property; activity
1029
1030 All Classes automatically have a number of properties by default:
1031
1032 *creator*
1033 Link to the user that created the item.
1034 *creation*
1035 Date the item was created.
1036 *actor*
1037 Link to the user that last modified the item.
1038 *activity*
1039 Date the item was last modified.
1040
1041
1042 .. index:: triple: schema; class property; content
1043 triple: schema; class property; type
1044
1045 FileClass
1046 ~~~~~~~~~
1047
1048 FileClasses save their "content" attribute off in a separate file from
1049 the rest of the database. This reduces the number of large entries in
1050 the database, which generally makes databases more efficient, and also
1051 allows us to use command-line tools to operate on the files. They are
1052 stored in the files sub-directory of the ``'db'`` directory in your
1053 tracker. FileClasses also have a "type" attribute to store the MIME
1054 type of the file.
1055
1056 Roundup by default considers the contents of the file immutable. This
1057 is to assist in maintaining an accurate record of correspondence. The
1058 distributed tracker templates do not enforce this. So if you have
1059 access to the Roundup tracker directory, you can edit the files (make
1060 sure to preserve mode, owner and group) to remove information (e.g. if
1061 somebody includes a password or you need to redact proprietary
1062 information). Obviously the journal for the message/file will not
1063 report that the file has changed.
1064
1065 Best practice is to remove offending material and leave a
1066 placeholder. E.G. replace a password with the text::
1067
1068 [password has been deleted 2020-12-02 --myname]
1069
1070 If you need to delete an entire file, replace the file contents with::
1071
1072 [file contents deleted due to spam 2020-10-21 --myname]
1073
1074 rather than deleting the file. If you actually delete the file Roundup
1075 will report an error to the user and email the administrator. If you
1076 empty the file, a user downloading the file using the direct URL
1077 (e.g. ``tracker/msg22``) may be confused and think something is broken
1078 when they receive an empty file. Retiring a file/msg does not prevent
1079 access to the file using the direct URL. Retiring an item only removes
1080 it when requesting a list of all items in the class. If you are
1081 replacing the contents, you probably want to change the content type
1082 of the file. E.G. from ``image/jpeg`` to ``text/plain``. You can do
1083 this easily through the web interface, or using the ``roundup-admin``
1084 command line interface.
1085
1086 You can also change the contents of a file or message using the REST
1087 interface. Note that this will NOT result in an entry in the journal,
1088 so again it allows a silent change. To do this you need to make two
1089 rest requests. An example using curl is::
1090
1091 $ curl -u demo:demo -s
1092 -H "X-requested-with: rest" \
1093 -H "Referer: https://tracker.example.com/demo/" \
1094 -X GET \
1095 https://tracker.example.com/demo/rest/data/file/30/content
1096 {
1097 "data": {
1098 "id": "30",
1099 "type": "<class 'str'>",
1100 "link": "https://tracker.example.com/demo/rest/data/file/30/content",
1101 "data": "hello3",
1102 "@etag": "\"3f2f8063dbce5b6bd43567e6f4f3c671\""
1103 }
1104 }
1105
1106 using the etag, overwrite the content with::
1107
1108 $ curl -u demo:demo -s
1109 -H "X-requested-with: rest" \
1110 -H "Referer: https://tracker.example.com/demo/" \
1111 -H 'If-Match: "3f2f8063dbce5b6bd43567e6f4f3c671"' \
1112 -X PUT \
1113 -F "data=@hello" \
1114 https://tracker.example.com/demo/rest/data/file/30/content
1115
1116 where ``hello`` is a file on local disk.
1117
1118 You can enforce immutability in your tracker by adding an auditor (see
1119 detectors_) for the file/msg class that rejects changes to the content
1120 property. The auditor could also add a journal entry so that a change
1121 via the Roundup mechanism is reported. Using a mixin (see:
1122 https://wiki.roundup-tracker.org/MixinClassFileClass) to augment the
1123 file class allows for other possibilities including signing the file, or
1124 recording a checksum in the database and validating the file contents
1125 at the time it gets read. This allows detection of changes done on the
1126 filesystem outside of the Roundup mechanism.
1127
1128 .. index:: triple: schema; class property; messages
1129 triple: schema; class property; files
1130 triple: schema; class property; nosy
1131 triple: schema; class property; superseder
1132
1133 IssueClass
1134 ~~~~~~~~~~
1135
1136 IssueClasses automatically include the "messages", "files", "nosy", and
1137 "superseder" properties.
1138
1139 The messages and files properties list the links to the messages and
1140 files related to the issue. The nosy property is a list of links to
1141 users who wish to be informed of changes to the issue - they get "CC'ed"
1142 e-mails when messages are sent to or generated by the issue. The nosy
1143 reactor (in the ``'detectors'`` directory) handles this action. The
1144 superseder link indicates an issue which has superseded this one.
1145
1146 They also have the dynamically generated "creation", "activity" and
1147 "creator" properties.
1148
1149 The value of the "creation" property is the date when an item was
1150 created, and the value of the "activity" property is the date when any
1151 property on the item was last edited (equivalently, these are the dates
1152 on the first and last records in the item's journal). The "creator"
1153 property holds a link to the user that created the issue.
1154
1155 .. index: triple: schema; class method; setkey
1156
1157 setkey(property)
1158 ~~~~~~~~~~~~~~~~
1159
1160 .. index:: roundup-admin; setting assignedto on an issue
1161
1162 Select a String property of the class to be the key property. The key
1163 property must be unique, and allows references to the items in the class
1164 by the content of the key property. That is, we can refer to users by
1165 their username: for example, let's say that there's an issue in Roundup,
1166 issue 23. There's also a user, richard, who happens to be user 2. To
1167 assign an issue to him, we could do either of::
1168
1169 roundup-admin set issue23 assignedto=2
1170
1171 or::
1172
1173 roundup-admin set issue23 assignedto=richard
1174
1175 Note, the same thing can be done in the web and e-mail interfaces.
1176
1177 .. index: triple: schema; class method; setlabelprop
1178
1179 setlabelprop(property)
1180 ~~~~~~~~~~~~~~~~~~~~~~
1181
1182 Select a property of the class to be the label property. The label
1183 property is used whereever an item should be uniquely identified, e.g.,
1184 when displaying a link to an item. If setlabelprop is not specified for
1185 a class, the following values are tried for the label:
1186
1187 * the key of the class (see the `setkey(property)`_ section above)
1188 * the "name" property
1189 * the "title" property
1190 * the first property from the sorted property name list
1191
1192 So in most cases you can get away without specifying setlabelprop
1193 explicitly.
1194
1195 You should make sure that users have View access to this property or
1196 the id property for a class. If the property can not be viewed by a
1197 user, looping over items in the class (e.g. messages attached to an
1198 issue) will not work.
1199
1200 .. index: triple: schema; class method; setorderprop
1201
1202 setorderprop(property)
1203 ~~~~~~~~~~~~~~~~~~~~~~
1204
1205 Select a property of the class to be the order property. The order
1206 property is used whenever using a default sort order for the class,
1207 e.g., when grouping or sorting class A by a link to class B in the user
1208 interface, the order property of class B is used for sorting. If
1209 setorderprop is not specified for a class, the following values are tried
1210 for the order property:
1211
1212 * the property named "order"
1213 * the label property (see `setlabelprop(property)`_ above)
1214
1215 So in most cases you can get away without specifying setorderprop
1216 explicitly.
1217
1218 .. index: triple: schema; class method; create
1219
1220 create(information)
1221 ~~~~~~~~~~~~~~~~~~~
1222
1223 Create an item in the database. This is generally used to create items
1224 in the "definitional" classes like "priority" and "status".
1225
1226 .. index: schema; item ordering
1227
1228 A note about ordering
1229 ~~~~~~~~~~~~~~~~~~~~~
1230
1231 When we sort items in the hyperdb, we use one of a number of methods,
1232 depending on the properties being sorted on:
1233
1234 1. If it's a String, Integer, Number, Date or Interval property, we
1235 just sort the scalar value of the property. Strings are sorted
1236 case-sensitively.
1237 2. If it's a Link property, we sort by either the linked item's "order"
1238 property (if it has one) or the linked item's "id".
1239 3. Mulitlinks sort similar to #2, but we start with the first Multilink
1240 list item, and if they're the same, we sort by the second item, and
1241 so on.
1242
1243 Note that if an "order" property is defined on a Class that is used for
1244 sorting, all items of that Class *must* have a value against the "order"
1245 property, or sorting will result in random ordering.
1246
1247
1248 Examples of adding to your schema
1249 ---------------------------------
1250
1251 Some examples are in the :ref:`CustomExamples` section.
1252
1253 Also you can start with `Roundup wiki CategorySchema`_ to see a list
1254 of additional examples of how schemas can be customised to add new
1255 functionality.
1256
1257 .. _Roundup wiki CategorySchema:
1258 https://wiki.roundup-tracker.org/CategorySchema
1259
1260 .. index:: !detectors
1261 .. _detectors:
1262 .. _Auditors and reactors:
1263
1264 Detectors - adding behaviour to your tracker
1265 ============================================
1266
1267 Detectors are initialised every time you open your tracker database, so
1268 you're free to add and remove them any time, even after the database is
1269 initialised via the ``roundup-admin initialise`` command.
1270
1271 The detectors in your tracker fire *before* (**auditors**) and *after*
1272 (**reactors**) changes to the contents of your database. They are Python
1273 modules that sit in your tracker's ``detectors`` directory. You will
1274 have some installed by default - have a look. You can write new
1275 detectors or modify the existing ones. The existing detectors installed
1276 for you are:
1277
1278 .. index:: detectors; installed
1279
1280 **nosyreaction.py**
1281 This provides the automatic nosy list maintenance and email sending.
1282 The nosy reactor (``nosyreaction``) fires when new messages are added
1283 to issues. The nosy auditor (``updatenosy``) fires when issues are
1284 changed, and figures out what changes need to be made to the nosy list
1285 (such as adding new authors, etc.)
1286 **statusauditor.py**
1287 This provides the ``chatty`` auditor which changes the issue status
1288 from ``unread`` or ``closed`` to ``chatting`` if new messages appear.
1289 It also provides the ``presetunread`` auditor which pre-sets the
1290 status to ``unread`` on new items if the status isn't explicitly
1291 defined.
1292 **messagesummary.py**
1293 Generates the ``summary`` property for new messages based on the message
1294 content.
1295 **userauditor.py**
1296 Verifies the content of some of the user fields (email addresses and
1297 roles lists).
1298
1299 If you don't want this default behaviour, you're completely free to change
1300 or remove these detectors.
1301
1302 See the detectors section in the `design document`__ for details of the
1303 interface for detectors.
1304
1305 __ design.html
1306
1307
1308 .. index:: detectors; writing api
1309
1310 Detector API
1311 ------------
1312
1313 .. index:: pair: detectors; auditors
1314 single: auditors; function signature
1315 single: auditors; defining
1316 single: auditors; arguments
1317
1318 Auditors are called with the arguments::
1319
1320 audit(db, cl, itemid, newdata)
1321
1322 where ``db`` is the database, ``cl`` is an instance of Class or
1323 IssueClass within the database, and ``newdata`` is a dictionary mapping
1324 property names to values.
1325
1326 For a ``create()`` operation, the ``itemid`` argument is None and
1327 newdata contains all of the initial property values with which the item
1328 is about to be created.
1329
1330 For a ``set()`` operation, newdata contains only the names and values of
1331 properties that are about to be changed.
1332
1333 For a ``retire()`` or ``restore()`` operation, newdata is None.
1334
1335 .. index:: pair: detectors; reactor
1336 single: reactors; function signature
1337 single: reactors; defining
1338 single: reactors; arguments
1339
1340 Reactors are called with the arguments::
1341
1342 react(db, cl, itemid, olddata)
1343
1344 where ``db`` is the database, ``cl`` is an instance of Class or
1345 IssueClass within the database, and ``olddata`` is a dictionary mapping
1346 property names to values.
1347
1348 For a ``create()`` operation, the ``itemid`` argument is the id of the
1349 newly-created item and ``olddata`` is None.
1350
1351 For a ``set()`` operation, ``olddata`` contains the names and previous
1352 values of properties that were changed.
1353
1354 For a ``retire()`` or ``restore()`` operation, ``itemid`` is the id of
1355 the retired or restored item and ``olddata`` is None.
1356
1357 .. index:: detectors; additional
1358
1359 Additional Detectors Ready For Use
1360 ----------------------------------
1361
1362 Sample additional detectors that have been found useful will appear in
1363 the ``'detectors'`` directory of the Roundup distribution. If you want
1364 to use one, copy it to the ``'detectors'`` of your tracker instance:
1365
1366 **irker.py**
1367 This detector sends notification on IRC through an irker daemon
1368 (http://www.catb.org/esr/irker/) when issues are created or messages
1369 are added. In order to use it you need to install irker, start the
1370 irkerd daemon, and add an ``[irker]`` section in ``detectors/config.ini``
1371 that contains a comma-separated list of channels where the messages should
1372 be sent, e.g. ``channels = irc://chat.freenode.net/channelname``.
1373 **newissuecopy.py**
1374 This detector sends an email to a team address whenever a new issue is
1375 created. The address is hard-coded into the detector, so edit it
1376 before you use it (look for the text 'team@team.host') or you'll get
1377 email errors!
1378 **creator_resolution.py**
1379 Catch attempts to set the status to "resolved" - if the assignedto
1380 user isn't the creator, then set the status to "confirm-done". Note that
1381 "classic" Roundup doesn't have that status, so you'll have to add it. If
1382 you don't want to though, it'll just use "in-progress" instead.
1383 **email_auditor.py**
1384 If a file added to an issue is of type message/rfc822, we tack on the
1385 extension .eml.
1386 The reason for this is that Microsoft Internet Explorer will not open
1387 things with a .eml attachment, as they deem it 'unsafe'. Worse yet,
1388 they'll just give you an incomprehensible error message. For more
1389 information, see the detector code - it has a lengthy explanation.
1390
1391
1392 .. index:: auditors; rules for use
1393 single: reactors; rules for use
1394
1395 Auditor or Reactor?
1396 -------------------
1397
1398 Generally speaking, the following rules should be observed:
1399
1400 **Auditors**
1401 Are used for `vetoing creation of or changes to items`_. They might
1402 also make automatic changes to item properties.
1403 **Reactors**
1404 Detect changes in the database and react accordingly. They should avoid
1405 making changes to the database where possible, as this could create
1406 detector loops.
1407
1408
1409 Vetoing creation of or changes to items
1410 ---------------------------------------
1411
1412 Auditors may raise the ``Reject`` exception to prevent the creation of
1413 or changes to items in the database. The mail gateway, for example, will
1414 not attach files or messages to issues when the creation of those files or
1415 messages are prevented through the ``Reject`` exception. It'll also not create
1416 users if that creation is ``Reject``'ed too.
1417
1418 To use, simply add at the top of your auditor::
1419
1420 from roundup.exceptions import Reject
1421
1422 And then when your rejection criteria have been detected, simply::
1423
1424 raise Reject('Description of error')
1425
1426 Error messages raised with ``Reject`` automatically have any HTML content
1427 escaped before being displayed to the user. To display an error message to the
1428 user without performing any HTML escaping the ``RejectRaw`` should be used. All
1429 security implications should be carefully considering before using
1430 ``RejectRaw``.
1431
1432
1433 Generating email from Roundup
1434 -----------------------------
1435
1436 The module ``roundup.mailer`` contains most of the nuts-n-bolts required
1437 to generate email messages from Roundup.
1438
1439 In addition, the ``IssueClass`` methods ``nosymessage()`` and
1440 ``send_message()`` are used to generate nosy messages, and may generate
1441 messages which only consist of a change note (ie. the message id parameter
1442 is not required - this is referred to as a "System Message" because it
1443 comes from "the system" and not a user).
1444
1445
1446 .. index:: extensions
1447 .. index:: extensions; add python functions to tracker
1448 .. _extensions:
1449 .. _actions:
1450
1451 Extensions - adding capabilities to your tracker
1452 ================================================
1453
1454 While detectors_ add new behavior by reacting to changes in tracked
1455 objects, `extensions` add new actions and utilities to Roundup, which
1456 are mostly used to enhance web interface.
1457
1458 You can create an extension by creating Python file in your tracker
1459 ``extensions`` directory. All files from this dir are loaded when
1460 tracker instance is created, at which point it calls ``init(instance)``
1461 from each file supplying itself as a first argument.
1462
1463 Note that at this point web interface is not loaded, but extensions still
1464 can register actions for in tracker instance. This may be fixed in
1465 Roundup 1.6 by introducing ``init_web(client)`` callback or a more
1466 flexible extension point mechanism.
1467
1468 * ``instance.registerUtil`` is used for adding `templating utilities`_
1469 (see `adding a time log to your issues
1470 <customizing.html#adding-a-time-log-to-your-issues-4>`_ for an example)
1471
1472 * ``instance.registerAction`` is used to add more actions to instance
1473 and to web interface. See `Defining new web actions`_ for details.
1474 Generic action can be added by inheriting from ``action.Action``
1475 instead of ``cgi.action.Action``.
1476
1477 .. _interfaces.py:
1478 .. _modifying the core of Roundup:
1479
1480 interfaces.py - hooking into the core of Roundup
1481 ================================================
1482
1483 There is a magic trick for hooking into the core of Roundup. Using
1484 this you can:
1485
1486 * modify class data structures
1487 * monkey patch core code to add new functionality
1488 * modify the email gateway
1489 * add new rest endpoints
1490
1491 but with great power comes great responsibility.
1492
1493 Interfaces.py has been around since the earliest releases of Roundup
1494 and used to be the main way to get a lot of customization done. In
1495 modern Roundup, the extensions_ mechanism is used to `add actions
1496 <#defining-new-web-actions>`_ and `templating utilities`_. But there are
1497 places where interfaces.py is still useful. Note that the tracker
1498 directories are not on the Python system path when interfaces.py is
1499 evaluated. You need to add library directories explictly by
1500 modifying sys.path.
1501
1502 See `Changing How the Core Code Works
1503 <customizing.html#changing-how-the-core-code-works>`_ for examples.
1504
1505 Database Content
1506 ================
1507
1508 .. note::
1509 If you modify the content of definitional classes, you will
1510 need to edit the tracker `detectors`_ if they reference a value
1511 of a definitional class. (E.g. if a detector checks to see if an
1512 issue is "open", and you change the "open" definition to be
1513 "working".)
1514
1515 Customisation of the special "definitional" classes (eg. status,
1516 priority, resolution, ...) may be done either before or after the
1517 tracker is initialised. The actual method of doing so is completely
1518 different in each case though, so be careful to use the right one.
1519
1520 **Changing content before tracker initialisation**
1521 Edit the initial_data.py module in your tracker to alter the items
1522 created using the ``create( ... )`` methods.
1523
1524 **Changing content after tracker initialisation**
1525 As the "admin" user, click on the "class list" link in the web
1526 interface to bring up a list of all database classes. Click on the
1527 name of the class you wish to change the content of.
1528
1529 You may also use the ``roundup-admin`` interface's create, set and
1530 retire methods to add, alter or remove items from the classes in
1531 question.
1532
1533 See "`adding a new field to the classic schema
1534 <customizing.html#adding-a-new-field-to-the-classic-schema>`_" for an
1535 example that requires database content changes.
1536
1537
1538 Security / Access Controls
1539 ==========================
1540
1541 A set of Permissions is built into the security module by default:
1542
1543 - Create (everything)
1544 - Edit (everything)
1545 - Search (everything) (used if View does not permit access)
1546 - View (everything)
1547 - Register (User class only)
1548
1549 These are assigned to the "Admin" Role by default, and allow a user to do
1550 anything. Every Class you define in your `tracker schema`_ also gets an
1551 Create, Edit and View Permission of its own. The web and email interfaces
1552 also define:
1553
1554 *Email Access*
1555 If defined, the user may use the email interface. Used by default to deny
1556 Anonymous users access to the email interface. When granted to the
1557 Anonymous user, they will be automatically registered by the email
1558 interface (see also the ``new_email_user_roles`` configuration option).
1559 *Web Access*
1560 If defined, the user may use the web interface. All users are able to see
1561 the login form, regardless of this setting (thus enabling logging in).
1562 *Web Roles*
1563 Controls user access to editing the "roles" property of the "user" class.
1564 TODO: deprecate in favour of a property-based control.
1565 *Rest Access* and *Xmlrpc Access*
1566 These control access to the Rest and Xmlrpc endpoints. The Admin and User
1567 roles have these by default in the classic tracker. See the
1568 `directions in the rest interface documentation`_ and the
1569 `xmlrpc interface documentation`_.
1570
1571 These are hooked into the default Roles:
1572
1573 - Admin (Create, Edit, Search, View and everything; Web Roles)
1574 - User (Web Access; Email Access)
1575 - Anonymous (Web Access)
1576
1577 And finally, the "admin" user gets the "Admin" Role, and the "anonymous"
1578 user gets "Anonymous" assigned when the tracker is installed.
1579
1580 For the "User" Role, the "classic" tracker defines:
1581
1582 - Create, Edit and View issue, file, msg, query, keyword
1583 - View priority, status
1584 - View user
1585 - Edit their own user record
1586
1587 And the "Anonymous" Role is defined as:
1588
1589 - Web interface access
1590 - Register user (for registration)
1591 - View issue, file, msg, query, keyword, priority, status
1592
1593 Put together, these settings appear in the tracker's ``schema.py`` file::
1594
1595 #
1596 # TRACKER SECURITY SETTINGS
1597 #
1598 # See the configuration and customisation document for information
1599 # about security setup.
1600
1601 #
1602 # REGULAR USERS
1603 #
1604 # Give the regular users access to the web and email interface
1605 db.security.addPermissionToRole('User', 'Web Access')
1606 db.security.addPermissionToRole('User', 'Email Access')
1607 db.security.addPermissionToRole('User', 'Rest Access')
1608 db.security.addPermissionToRole('User', 'Xmlrpc Access')
1609
1610 # Assign the access and edit Permissions for issue, file and message
1611 # to regular users now
1612 for cl in 'issue', 'file', 'msg', 'keyword':
1613 db.security.addPermissionToRole('User', 'View', cl)
1614 db.security.addPermissionToRole('User', 'Edit', cl)
1615 db.security.addPermissionToRole('User', 'Create', cl)
1616 for cl in 'priority', 'status':
1617 db.security.addPermissionToRole('User', 'View', cl)
1618
1619 # May users view other user information? Comment these lines out
1620 # if you don't want them to
1621 p = db.security.addPermission(name='View', klass='user',
1622 properties=('id', 'organisation', 'phone', 'realname', 'timezone',
1623 'username'))
1624 db.security.addPermissionToRole('User', p)
1625
1626 # Users should be able to edit their own details -- this permission is
1627 # limited to only the situation where the Viewed or Edited item is their own.
1628 def own_record(db, userid, itemid, **ctx):
1629 '''Determine whether the userid matches the item being accessed.'''
1630 return userid == itemid
1631 p = db.security.addPermission(name='View', klass='user', check=own_record,
1632 description="User is allowed to view their own user details")
1633 db.security.addPermissionToRole('User', p)
1634 p = db.security.addPermission(name='Edit', klass='user', check=own_record,
1635 properties=('username', 'password', 'address', 'realname', 'phone',
1636 'organisation', 'alternate_addresses', 'queries', 'timezone'),
1637 description="User is allowed to edit their own user details")
1638 db.security.addPermissionToRole('User', p)
1639
1640 # Users should be able to edit and view their own queries. They should also
1641 # be able to view any marked as not private. They should not be able to
1642 # edit others' queries, even if they're not private
1643 def view_query(db, userid, itemid):
1644 private_for = db.query.get(itemid, 'private_for')
1645 if not private_for: return True
1646 return userid == private_for
1647 def edit_query(db, userid, itemid):
1648 return userid == db.query.get(itemid, 'creator')
1649 p = db.security.addPermission(name='View', klass='query', check=view_query,
1650 description="User is allowed to view their own and public queries")
1651 db.security.addPermissionToRole('User', p)
1652 p = db.security.addPermission(name='Search', klass='query')
1653 db.security.addPermissionToRole('User', p)
1654 p = db.security.addPermission(name='Edit', klass='query', check=edit_query,
1655 description="User is allowed to edit their queries")
1656 db.security.addPermissionToRole('User', p)
1657 p = db.security.addPermission(name='Retire', klass='query', check=edit_query,
1658 description="User is allowed to retire their queries")
1659 db.security.addPermissionToRole('User', p)
1660 p = db.security.addPermission(name='Restore', klass='query', check=edit_query,
1661 description="User is allowed to restore their queries")
1662 db.security.addPermissionToRole('User', p)
1663 p = db.security.addPermission(name='Create', klass='query',
1664 description="User is allowed to create queries")
1665 db.security.addPermissionToRole('User', p)
1666
1667 #
1668 # ANONYMOUS USER PERMISSIONS
1669 #
1670 # Let anonymous users access the web interface. Note that almost all
1671 # trackers will need this Permission. The only situation where it's not
1672 # required is in a tracker that uses an HTTP Basic Authenticated front-end.
1673 db.security.addPermissionToRole('Anonymous', 'Web Access')
1674
1675 # Let anonymous users access the email interface (note that this implies
1676 # that they will be registered automatically, hence they will need the
1677 # "Create" user Permission below)
1678 # This is disabled by default to stop spam from auto-registering users on
1679 # public trackers.
1680 #db.security.addPermissionToRole('Anonymous', 'Email Access')
1681
1682 # Assign the appropriate permissions to the anonymous user's Anonymous
1683 # Role. Choices here are:
1684 # - Allow anonymous users to register
1685 db.security.addPermissionToRole('Anonymous', 'Register', 'user')
1686
1687 # Allow anonymous users access to view issues (and the related, linked
1688 # information)
1689 for cl in 'issue', 'file', 'msg', 'keyword', 'priority', 'status':
1690 db.security.addPermissionToRole('Anonymous', 'View', cl)
1691
1692 # Allow the anonymous user to use the "Show Unassigned" search.
1693 # It acts like "Show Open" if this permission is not available.
1694 # If you are running a tracker that does not allow read access for
1695 # anonymous, you should remove this entry as it can be used to perform
1696 # a username guessing attack against a roundup install.
1697 p = db.security.addPermission(name='Search', klass='user')
1698 db.security.addPermissionToRole ('Anonymous', p)
1699
1700 # [OPTIONAL]
1701 # Allow anonymous users access to create or edit "issue" items (and the
1702 # related file and message items)
1703 #for cl in 'issue', 'file', 'msg':
1704 # db.security.addPermissionToRole('Anonymous', 'Create', cl)
1705 # db.security.addPermissionToRole('Anonymous', 'Edit', cl)
1706
1707 .. index::
1708 single: roundup-admin; view class permissions
1709
1710 You can use ``roundup-admin security`` to verify the permissions
1711 defined in the schema. It also verifies that properties specified in
1712 permissions are valid for the class. This helps detect typos that can
1713 cause baffling permission issues.
1714
1715 Automatic Permission Checks
1716 ---------------------------
1717
1718 Permissions are automatically checked when information is rendered
1719 through the web. This includes:
1720
1721 1. View checks for properties when being rendered via the ``plain()`` or
1722 similar methods. If the check fails, the text "[hidden]" will be
1723 displayed.
1724 2. Edit checks for properties when the edit field is being rendered via
1725 the ``field()`` or similar methods. If the check fails, the property
1726 will be rendered via the ``plain()`` method (see point 1. for subsequent
1727 checking performed)
1728 3. View checks are performed in index pages for each item being displayed
1729 such that if the user does not have permission, the row is not rendered.
1730 4. View checks are performed at the top of item pages for the Item being
1731 displayed. If the user does not have permission, the text "You are not
1732 allowed to view this page." will be displayed.
1733 5. View checks are performed at the top of index pages for the Class being
1734 displayed. If the user does not have permission, the text "You are not
1735 allowed to view this page." will be displayed.
1736
1737
1738 New User Roles
1739 --------------
1740
1741 New users are assigned the Roles defined in the config file as:
1742
1743 - NEW_WEB_USER_ROLES
1744 - NEW_EMAIL_USER_ROLES
1745
1746 The `users may only edit their issues
1747 <customizing.html#users-may-only-edit-their-issues>`_ example shows
1748 customisation of these parameters.
1749
1750
1751 Changing Access Controls
1752 ------------------------
1753
1754 You may alter the configuration variables to change the Role that new
1755 web or email users get, for example to not give them access to the web
1756 interface if they register through email.
1757
1758 You may use the ``roundup-admin`` "``security``" command to display the
1759 current Role and Permission configuration in your tracker.
1760
1761
1762 Adding a new Permission
1763 ~~~~~~~~~~~~~~~~~~~~~~~
1764
1765 When adding a new Permission, you will need to:
1766
1767 1. add it to your tracker's ``schema.py`` so it is created, using
1768 ``security.addPermission``, for example::
1769
1770 db.security.addPermission(name="View", klass='frozzle',
1771 description="User is allowed to access frozzles")
1772
1773 will set up a new "View" permission on the Class "frozzle".
1774 2. enable it for the Roles that should have it (verify with
1775 "``roundup-admin security``")
1776 3. add it to the relevant HTML interface templates
1777 4. add it to the appropriate xxxPermission methods on in your tracker
1778 interfaces module
1779
1780 The ``addPermission`` method takes a few optional parameters:
1781
1782 **properties**
1783 A sequence of property names that are the only properties to apply the
1784 new Permission to (eg. ``... klass='user', properties=('name',
1785 'email') ...``)
1786 **props_only**
1787 A boolean value (set to false by default) that is a new feature
1788 in Roundup 1.6.
1789 A permission defined using:
1790
1791 ``properties=('list', 'of', 'property', 'names')``
1792
1793 is used to determine access for things other than just those
1794 properties. For example a check for View permission on the issue
1795 class or an issue item can use any View permission for the issue
1796 class even if that permission has a property list. This can be
1797 confusing and surprising as you would think that a permission
1798 including properties would be used only for determining the
1799 access permission for those properties.
1800
1801 ``roundup-admin security`` will report invalid properties for the
1802 class. For example a permission with an invalid summary property is
1803 presented as::
1804
1805 Allowed to see content of object regardless of spam status
1806 (View for "file": ('content', 'summary') only)
1807
1808 **Invalid properties for file: ['summary']
1809
1810 Setting ``props_only=True`` will make the permission valid only for
1811 those properties.
1812
1813 If you use a lot of permissions with property checks, it can be
1814 difficult to change all of them. Calling the function:
1815
1816 db.security.set_props_only_default(True)
1817
1818 at the top of ``schema.py`` will make every permission creation
1819 behave as though props_only was set to True. It is expected that the
1820 default of True will become the default in a future Roundup release.
1821 **check**
1822 A function to be executed which returns boolean determining whether
1823 the Permission is allowed. If it returns True, the permission is
1824 allowed, if it returns False the permission is denied. The function
1825 can have one of two signatures::
1826
1827 check(db, userid, itemid)
1828
1829 or::
1830
1831 check(db, userid, itemid, **ctx)
1832
1833 where ``db`` is a handle on the open database, ``userid`` is
1834 the user attempting access and ``itemid`` is the specific item being
1835 accessed. If the second form is used the ``ctx`` dictionary is
1836 defined with the following values::
1837
1838 ctx['property'] the name of the property being checked or None if
1839 it's a class check.
1840
1841 ctx['classname'] the name of the class that is being checked
1842 (issue, query ....).
1843
1844 ctx['permission'] the name of the permission (e.g. View, Edit...).
1845
1846 The second form is preferred as it makes it easier to implement more
1847 complex permission schemes. An example of the use of ``ctx`` can be
1848 found in the ``upgrading.txt`` or `upgrading.html`_ document.
1849
1850 .. _`upgrading.html`: upgrading.html
1851
1852 Example Scenarios
1853 ~~~~~~~~~~~~~~~~~
1854
1855 See the `examples <customizing.html#examples>`_ section for longer
1856 examples of customisation.
1857
1858 **anonymous access through the e-mail gateway**
1859 Give the "anonymous" user the "Email Access", ("Edit", "issue") and
1860 ("Create", "msg") Permissions but do not not give them the ("Create",
1861 "user") Permission. This means that when an unknown user sends email
1862 into the tracker, they're automatically logged in as "anonymous".
1863 Since they don't have the ("Create", "user") Permission, they won't
1864 be automatically registered, but since "anonymous" has permission to
1865 use the gateway, they'll still be able to submit issues. Note that
1866 the Sender information - their email address - will not be available
1867 - they're *anonymous*.
1868
1869 **automatic registration of users in the e-mail gateway**
1870 By giving the "anonymous" user the ("Register", "user") Permission, any
1871 unidentified user will automatically be registered with the tracker
1872 (with no password, so they won't be able to log in through
1873 the web until an admin sets their password). By default new Roundup
1874 trackers don't allow this as it opens them up to spam. It may be enabled
1875 by uncommenting the appropriate addPermissionToRole in your tracker's
1876 ``schema.py`` file. The new user is given the Roles list defined in the
1877 "new_email_user_roles" config variable.
1878
1879 **only developers may be assigned issues**
1880 Create a new Permission called "Fixer" for the "issue" class. Create a
1881 new Role "Developer" which has that Permission, and assign that to the
1882 appropriate users. Filter the list of users available in the assignedto
1883 list to include only those users. Enforce the Permission with an
1884 auditor. See the example
1885 `restricting the list of users that are assignable to a task
1886 <customizing.html#restricting-the-list-of-users-that-are-assignable-to-a-task>`_.
1887
1888 **only managers may sign off issues as complete**
1889 Create a new Permission called "Closer" for the "issue" class. Create a
1890 new Role "Manager" which has that Permission, and assign that to the
1891 appropriate users. In your web interface, only display the "resolved"
1892 issue state option when the user has the "Closer" Permissions. Enforce
1893 the Permission with an auditor. This is very similar to the previous
1894 example, except that the web interface check would look like::
1895
1896 <option tal:condition="python:request.user.hasPermission('Closer')"
1897 value="resolved">Resolved</option>
1898
1899 **don't give web access to users who register through email**
1900 Create a new Role called "Email User" which has all the Permissions of
1901 the normal "User" Role minus the "Web Access" Permission. This will
1902 allow users to send in emails to the tracker, but not access the web
1903 interface.
1904
1905 **let some users edit the details of all users**
1906 Create a new Role called "User Admin" which has the Permission for
1907 editing users::
1908
1909 db.security.addRole(name='User Admin', description='Managing users')
1910 p = db.security.getPermission('Edit', 'user')
1911 db.security.addPermissionToRole('User Admin', p)
1912
1913 and assign the Role to the users who need the permission.
1914
1915
1916 Web Interface
1917 =============
1918
1919 .. contents::
1920 :local:
1921
1922 The web interface is provided by the ``roundup.cgi.client`` module and
1923 is used by ``roundup.cgi``, ``roundup-server`` and ``ZRoundup``
1924 (``ZRoundup`` is broken, until further notice). In all cases, we
1925 determine which tracker is being accessed (the first part of the URL
1926 path inside the scope of the CGI handler) and pass control on to the
1927 ``roundup.cgi.client.Client`` class - which handles the rest of the
1928 access through its ``main()`` method. This means that you can do pretty
1929 much anything you want as a web interface to your tracker.
1930
1931
1932
1933 Repercussions of changing the tracker schema
1934 ---------------------------------------------
1935
1936 If you choose to change the `tracker schema`_ you will need to ensure
1937 the web interface knows about it:
1938
1939 1. Index, item and search pages for the relevant classes may need to
1940 have properties added or removed,
1941 2. The "page" template may require links to be changed, as might the
1942 "home" page's content arguments.
1943
1944
1945 How requests are processed
1946 --------------------------
1947
1948 The basic processing of a web request proceeds as follows:
1949
1950 1. figure out who we are, defaulting to the "anonymous" user
1951 2. figure out what the request is for - we call this the "context"
1952 3. handle any requested action (item edit, search, ...)
1953 4. render the template requested by the context, resulting in HTML
1954 output
1955
1956 In some situations, exceptions occur:
1957
1958 - HTTP Redirect (generally raised by an action)
1959 - SendFile (generally raised by ``determine_context``)
1960 here we serve up a FileClass "content" property
1961 - SendStaticFile (generally raised by ``determine_context``)
1962 here we serve up a file from the tracker "html" directory
1963 - Unauthorised (generally raised by an action)
1964 here the action is cancelled, the request is rendered and an error
1965 message is displayed indicating that permission was not granted for
1966 the action to take place
1967 - NotFound (raised wherever it needs to be)
1968 this exception percolates up to the CGI interface that called the
1969 client
1970
1971
1972 Roundup URL design
1973 ------------------
1974
1975 Each tracker has several hardcoded URLs. These three are equivalent and
1976 lead to the main tracker page:
1977
1978 1. ``/``
1979 2. ``/index``
1980 3. ``/home``
1981
1982 The following prefix is used to access static resources:
1983
1984 4. ``/@@file/``
1985
1986 Two additional url's are used for the API's.
1987 The `REST api`_ is accessed via:
1988
1989 5. ``/rest/``
1990
1991 .. _`REST api`: rest.html
1992
1993 and the `XMLRPC api`_ is available at:
1994
1995 6. ``/xmlrpc``
1996
1997 .. _`XMLRPC api`: xmlrpc.html
1998
1999 All other URLs depend on the classes configured in Roundup database.
2000 Each class receives two URLs - one for the class itself and another
2001 for specific items of that class. Example for class URL:
2002
2003 7. ``/issue``
2004
2005 This is usually used to show listings of class items. The URL for
2006 for specific object of issue class with id 1 will look like:
2007
2008 8. ``/issue1``
2009
2010 .. _strip_zeros:
2011
2012 Note that a leading string of 0's will be stripped from the id part of
2013 the object designator in the URL. E.G. ``/issue001`` is the same as
2014 ``/issue1``. Similarly for ``/file01`` etc. However you should
2015 generate URL's without the extra zeros.
2016
2017 Determining web context
2018 -----------------------
2019
2020 To determine the "context" of a request (what request is for), we look at
2021 the URL path after the tracker root and at ``@template`` request
2022 parameter. Typical URL paths look like:
2023
2024 1. ``/tracker/issue``
2025 2. ``/tracker/issue1``
2026 3. ``/tracker/@@file/style.css``
2027 4. ``/cgi-bin/roundup.cgi/tracker/file1``
2028 5. ``/cgi-bin/roundup.cgi/tracker/file1/kitten.png``
2029
2030 where tracker root is ``/tracker/`` or ``/cgi-bin/roundup.cgi/tracker/``
2031 We're looking at "issue", "issue1", "@@file/style.css", "file1" and
2032 "file1/kitten.png" in the cases above.
2033
2034 1. with is no path we are in the "home" context. See `the "home"
2035 context`_ below for details. "index" or "home" paths may also be used
2036 to switch into "home" context.
2037 2. for paths starting with "@@file" the additional path entry ("style.css"
2038 in the example above) specifies the static file to be served
2039 from the tracker TEMPLATES directory (or STATIC_FILES, if configured).
2040 This is usually the tracker's "html" directory. Internally this works
2041 by raising SendStaticFile exception.
2042 3. if there is something in the path (as in example 1, "issue"), it
2043 identifies the tracker class to display.
2044 4. if the path is an item designator (as in examples 2 and 4, "issue1"
2045 and "file1"), then we're to display a specific item.
2046 :ref:`Note. <strip_zeros>`
2047 5. if the path starts with an item designator and is longer than one
2048 entry (as in example 5, "file1/kitten.png"), then we're assumed to be
2049 handling an item of a ``FileClass``, and the extra path information
2050 gives the filename that the client is going to label the download
2051 with (i.e. "file1/kitten.png" is nicer to download than "file1").
2052 This raises a ``SendFile`` exception.
2053
2054 Neither 2. or 5. use templates and stop before the template is
2055 determined. For other contexts the template used is specified by the
2056 ``@template`` variable, which defaults to:
2057
2058 - only classname supplied: "index"
2059 - full item designator supplied: "item"
2060
2061
2062 The "home" Context
2063 ------------------
2064
2065 The "home" context is special because it allows you to add templated
2066 pages to your tracker that don't rely on a class or item (ie. an issues
2067 list or specific issue).
2068
2069 Let's say you wish to add frames to control the layout of your tracker's
2070 interface. You'd probably have:
2071
2072 - A top-level frameset page. This page probably wouldn't be templated, so
2073 it could be served as a static file (see `serving static content`_)
2074 - A sidebar frame that is templated. Let's call this page
2075 "home.navigation.html" in your tracker's "html" directory. To load that
2076 page up, you use the URL:
2077
2078 <tracker url>/home?@template=navigation
2079
2080
2081 Serving static content
2082 ----------------------
2083
2084 See the previous section `determining web context`_ where it describes
2085 ``@@file`` paths.
2086
2087 These files are served without any permission checks. Any user on the
2088 internet with the url can download the file.
2089
2090 This is rarely an issue since the html templates are just source code
2091 and much of it can be found in the Roundup repository. Other
2092 decoration (logos, stylesheets) are similarly not security sensitive.
2093 You can use the static_files setting in config.ini to eliminate
2094 access to the templates directory if desired.
2095
2096 If a file resolves to a symbolic link, it is not served.
2097
2098 Performing actions in web requests
2099 ----------------------------------
2100
2101 When a user requests a web page, they may optionally also request for an
2102 action to take place. As described in `how requests are processed`_, the
2103 action is performed before the requested page is generated. Actions are
2104 triggered by using a ``@action`` CGI variable, where the value is one
2105 of:
2106
2107 **login**
2108 Attempt to log a user in.
2109
2110 **logout**
2111 Log the user out - make them "anonymous".
2112
2113 **register**
2114 Attempt to create a new user based on the contents of the form and then
2115 log them in.
2116
2117 **edit**
2118 Perform an edit of an item in the database. There are some `special form
2119 variables`_ you may use. Also you can set the ``__redirect_to`` form
2120 variable to the URL that should be displayed after the edit is succesfully
2121 completed. If you wanted to edit a sequence of issues, users etc. this
2122 could be used to display the next item in the sequence to the user.
2123
2124 **new**
2125 Add a new item to the database. You may use the same `special form
2126 variables`_ as in the "edit" action. Also you can set the
2127 ``__redirect_to`` form variable to the URL that should be displayed after
2128 the new item is created. This is useful if you want to create another
2129 item rather than edit the newly created item.
2130
2131 **retire**
2132 Retire the item in the database.
2133
2134 **editCSV**
2135 Performs an edit of all of a class' items in one go. See also the
2136 *class*.csv templating method which generates the CSV data to be
2137 edited, and the ``'_generic.index'`` template which uses both of these
2138 features.
2139
2140 **search**
2141 Mangle some of the form variables:
2142
2143 - Set the form ":filter" variable based on the values of the filter
2144 variables - if they're set to anything other than "dontcare" then add
2145 them to :filter.
2146
2147 - Also handle the ":queryname" variable and save off the query to the
2148 user's query list.
2149
2150 Each of the actions is implemented by a corresponding ``*XxxAction*`` (where
2151 "Xxx" is the name of the action) class in the ``roundup.cgi.actions`` module.
2152 These classes are registered with ``roundup.cgi.client.Client``. If you need
2153 to define new actions, you may add them there (see `defining new
2154 web actions`_).
2155
2156 Each action class also has a ``*permission*`` method which determines whether
2157 the action is permissible given the current user. The base permission checks
2158 for each action are:
2159
2160 **login**
2161 Determine whether the user has the "Web Access" Permission.
2162 **logout**
2163 No permission checks are made.
2164 **register**
2165 Determine whether the user has the ("Create", "user") Permission.
2166 **edit**
2167 Determine whether the user has permission to edit this item. If we're
2168 editing the "user" class, users are allowed to edit their own details -
2169 unless they try to edit the "roles" property, which requires the
2170 special Permission "Web Roles".
2171 **new**
2172 Determine whether the user has permission to create this item. No
2173 additional property checks are made. Additionally, new user items may
2174 be created if the user has the ("Create", "user") Permission.
2175 **editCSV**
2176 Determine whether the user has permission to edit this class.
2177 **search**
2178 Determine whether the user has permission to view this class.
2179
2180 Protecting users from web application attacks
2181 ---------------------------------------------
2182
2183 There is a class of attacks known as Cross Site Request Forgeries
2184 (CSRF). Malicious code running in the browser can making a
2185 request to Roundup while you are logged into Roundup. The
2186 malicious code piggy backs on your existing Roundup session to
2187 make changes without your knowledge. Roundup 1.6 has support for
2188 defending against this by analyzing the
2189
2190 * Referer,
2191 * Origin, and
2192 * Host or
2193 * X-Forwarded-Host
2194
2195 HTTP headers. It compares the headers to the value of the web setting
2196 in the [tracker] section of the tracker's ``config.ini``.
2197
2198 Also a per form token (also called a nonce) can be enabled for
2199 the tracker using the ``csrf_enforce_token`` option in
2200 config.ini. When enabled, Roundup will validate a hidden form
2201 field called ``@csrf``. If the validation fails (or the token
2202 is used more than once) the request is rejected. The ``@csrf``
2203 input field is added automatically by calling the ``submit``
2204 function/path. It can also be added manually by calling
2205 anti_csrf_nonce() directly. For example::
2206
2207 <input name="@csrf" type="hidden"
2208 tal:attributes="value python:utils.anti_csrf_nonce(lifetime=10)">
2209
2210 By default a nonce lifetime is 2 weeks. However the lifetime (in
2211 minutes) can be set by passing a lifetime argument as shown
2212 above. The example above makes the nonce lifetime 10 minutes.
2213
2214 Search for @csrf in this document for more examples. There are
2215 more examples and information in ``upgrading.txt``.
2216
2217 The token protects you because malicious code supplied by another
2218 site is unable to obtain the token. Thus many attempts they make
2219 to submit a request are rejected.
2220
2221 The protection on the xmlrpc interface is untested, but is based
2222 on a valid header check against the Roundup url and the presence
2223 of the ``X-REQUESTED-WITH`` header. Work to improve this is a
2224 future project after the 1.6 release.
2225
2226 The enforcement levels can be modified in ``config.ini``. Refer to
2227 that file for details.
2228
2229 Special form variables
2230 ----------------------
2231
2232 Item properties and their values are edited with html FORM
2233 variables and their values. You can:
2234
2235 - Change the value of some property of the current item.
2236 - Create a new item of any class, and edit the new item's
2237 properties,
2238 - Attach newly created items to a multilink property of the
2239 current item.
2240 - Remove items from a multilink property of the current item.
2241 - Specify that some properties are required for the edit
2242 operation to be successful.
2243 - Redirect to a different page after creating a new item (new action
2244 only, not edit action). Usually you end up on the page for the
2245 created item.
2246 - Set up user interface locale.
2247
2248 These operations will only take place if the form action (the
2249 ``@action`` variable) is "edit" or "new".
2250
2251 In the following, <bracketed> values are variable, "@" may be
2252 either ":" or "@", and other text "required" is fixed.
2253
2254 Two special form variables are used to specify user language preferences:
2255
2256 ``@language``
2257 value may be locale name or ``none``. If this variable is set to
2258 locale name, web interface language is changed to given value
2259 (provided that appropriate translation is available), the value
2260 is stored in the browser cookie and will be used for all following
2261 requests. If value is ``none`` the cookie is removed and the
2262 language is changed to the tracker default, set up in the tracker
2263 configuration or OS environment.
2264
2265 ``@charset``
2266 value may be character set name or ``none``. Character set name
2267 is stored in the browser cookie and sets output encoding for all
2268 HTML pages generated by Roundup. If value is ``none`` the cookie
2269 is removed and HTML output is reset to Roundup internal encoding
2270 (UTF-8).
2271
2272 Most properties are specified as form variables:
2273
2274 ``<propname>``
2275 property on the current context item
2276
2277 ``<designator>"@"<propname>``
2278 property on the indicated item (for editing related information)
2279
2280 Designators name a specific item of a class.
2281
2282 ``<classname><N>``
2283 Name an existing item of class <classname>.
2284
2285 ``<classname>"-"<N>``
2286 Name the <N>th new item of class <classname>. If the form
2287 submission is successful, a new item of <classname> is
2288 created. Within the submitted form, a particular
2289 designator of this form always refers to the same new
2290 item.
2291
2292 Once we have determined the "propname", we look at it to see
2293 if it's special:
2294
2295 ``@required``
2296 The associated form value is a comma-separated list of
2297 property names that must be specified when the form is
2298 submitted for the edit operation to succeed.
2299
2300 When the <designator> is missing, the properties are
2301 for the current context item. When <designator> is
2302 present, they are for the item specified by
2303 <designator>.
2304
2305 The "@required" specifier must come before any of the
2306 properties it refers to are assigned in the form.
2307
2308 ``@remove@<propname>=id(s)`` or ``@add@<propname>=id(s)``
2309 The "@add@" and "@remove@" edit actions apply only to
2310 Multilink properties. The form value must be a
2311 comma-separate list of keys for the class specified by
2312 the simple form variable. The listed items are added
2313 to (respectively, removed from) the specified
2314 property.
2315
2316 ``@link@<propname>=<designator>``
2317 If the edit action is "@link@", the simple form
2318 variable must specify a Link or Multilink property.
2319 The form value is a comma-separated list of
2320 designators. The item corresponding to each
2321 designator is linked to the property given by simple
2322 form variable.
2323
2324 None of the above (ie. just a simple form value)
2325 The value of the form variable is converted
2326 appropriately, depending on the type of the property.
2327
2328 For a Link('klass') property, the form value is a
2329 single key for 'klass', where the key field is
2330 specified in schema.py.
2331
2332 For a Multilink('klass') property, the form value is a
2333 comma-separated list of keys for 'klass', where the
2334 key field is specified in schema.py.
2335
2336 Note that for simple-form-variables specifiying Link
2337 and Multilink properties, the linked-to class must
2338 have a key field.
2339
2340 For a String() property specifying a filename, the
2341 file named by the form value is uploaded. This means we
2342 try to set additional properties "filename" and "type" (if
2343 they are valid for the class). Otherwise, the property
2344 is set to the form value.
2345
2346 For Date(), Interval(), Boolean(), Integer() and Number()
2347 properties, the form value is converted to the
2348 appropriate value.
2349
2350 Any of the form variables may be prefixed with a classname or
2351 designator.
2352
2353 Setting the form variable: ``__redirect_to=`` to a url when
2354 @action=new redirects the user to the specified url after successfully
2355 creating the new item. This is useful if you want the user to create
2356 another item rather than edit the newly created item. Note that the
2357 url assigned to ``__redirect_to`` must be url encoded/quoted and be
2358 under the tracker's base url. If the base_url uses http, you can set
2359 the url to https.
2360
2361 Two special form values are supported for backwards compatibility:
2362
2363 @note
2364 This is equivalent to::
2365
2366 @link@messages=msg-1
2367 msg-1@content=value
2368
2369 which is equivalent to the html::
2370
2371 <textarea name="msg-1@content"></textarea>
2372 <input type="hidden" name="@link@messages" value="msg-1">
2373
2374 except that in addition, the "author" and "date" properties of
2375 "msg-1" are set to the userid of the submitter, and the current
2376 time, respectively.
2377
2378 @file
2379 This is equivalent to::
2380
2381 @link@files=file-1
2382 file-1@content=value
2383
2384 by adding the HTML::
2385
2386 <input type="file" name="file-1@content">
2387 <input type="hidden" name="@link@files" value="file-1">
2388
2389 The String content value is handled as described above for file
2390 uploads.
2391
2392 If both the "@note" and "@file" form variables are
2393 specified, the action::
2394
2395 msg-1@link@files=file-1
2396
2397 is also performed. This would be expressed in HTML with::
2398
2399 <input type="hidden" name="msg-1@link@files" value="file-1">
2400
2401 We also check that FileClass items have a "content" property with
2402 actual content, otherwise we remove them from all_props before
2403 returning.
2404
2405
2406 Default templates
2407 -----------------
2408
2409 The default templates are html4 compliant. If you wish to change them to be
2410 xhtml compliant, you'll need to change the ``html_version`` configuration
2411 variable in ``config.ini`` to ``'xhtml'`` instead of ``'html4'``.
2412
2413 Most customisation of the web view can be done by modifying the
2414 templates in the tracker ``'html'`` directory. There are several types
2415 of files in there. The *minimal* template includes:
2416
2417 **page.html**
2418 This template usually defines the overall look of your tracker. When
2419 you view an issue, it appears inside this template. When you view an
2420 index, it also appears inside this template. This template defines a
2421 macro called "icing" which is used by almost all other templates as a
2422 coating for their content, using its "content" slot. It also defines
2423 the "head_title" and "body_title" slots to allow setting of the page
2424 title.
2425 **home.html**
2426 the default page displayed when no other page is indicated by the user
2427 **home.classlist.html**
2428 a special version of the default page that lists the classes in the
2429 tracker
2430 **classname.item.html**
2431 displays an item of the *classname* class
2432 **classname.index.html**
2433 displays a list of *classname* items
2434 **classname.search.html**
2435 displays a search page for *classname* items
2436 **_generic.index.html**
2437 used to display a list of items where there is no
2438 ``*classname*.index`` available
2439 **_generic.help.html**
2440 used to display a "class help" page where there is no
2441 ``*classname*.help``
2442 **user.register.html**
2443 a special page just for the user class, that renders the registration
2444 page
2445 **style.css**
2446 a static file that is served up as-is
2447
2448 The *classic* template has a number of additional templates.
2449
2450 Remember that you can create any template extension you want to,
2451 so if you just want to play around with the templating for new issues,
2452 you can copy the current "issue.item" template to "issue.test", and then
2453 access the test template using the "@template" URL argument::
2454
2455 http://your.tracker.example/tracker/issue?@template=test
2456
2457 and it won't affect your users using the "issue.item" template.
2458
2459 You can also put templates into a subdirectory of the template
2460 directory. So if you specify::
2461
2462 http://your.tracker.example/tracker/issue?@template=test/item
2463
2464 you will use the template at: ``test/issue.item.html``. If that
2465 template doesn't exit it will try to use
2466 ``test/_generic.item.html``. If that template doesn't exist
2467 it will return an error.
2468
2469 Implementing Modal Editing Using @template
2470 ------------------------------------------
2471
2472 Many item templates allow you to edit the item. They contain
2473 code that renders edit boxes if the user has edit permissions.
2474 Otherwise the template will just display the item information.
2475
2476 In some cases you want to do a modal edit. The user has to take some
2477 action (click a button or follow a link) to shift from display mode to
2478 edit mode. When the changes are submitted, ending the edit mode,
2479 the user is returned to display mode.
2480
2481 Modal workflows usually slow things down and are not implemented by
2482 default templates. However for some workflows a modal edit is useful.
2483 For example a batch edit mode that allows the user to edit a number of
2484 issues all from one form could be implemented as a modal workflow of:
2485
2486 * search for issues to modify
2487 * switch to edit mode and change values
2488 * exit back to the results of the search
2489
2490 To implement the modal edit, assume you have an issue.edit.html
2491 template that implements an edit form. On the display page (a version
2492 of issue.item.html modified to only display information) add a link
2493 that calls the display url, but adds ``@template=edit`` to the link.
2494
2495 This will now display the edit page. On the edit page you want to add
2496 a hidden text field to your form named ``@template`` with the value:
2497 ``item|edit``. When the form is submitted it is validated. If the
2498 form is correct the user will see the item rendered using the item
2499 template. If there is an error (validation failed) the item will be
2500 rendered using the edit template. The edit template that is rendered
2501 will display all the changes that the user made to the form before it
2502 was submitted. The user can correct the error and resubmit the changes
2503 until the form validates.
2504
2505 If the form failed to validate but the ``@template`` field had the
2506 value ``item`` the user would still see the error, but all of the data
2507 the user entered would be discarded. The user would have to redo all
2508 the edits again.
2509
2510
2511 How the templates work
2512 ----------------------
2513
2514
2515 Templating engines
2516 ~~~~~~~~~~~~~~~~~~
2517
2518 Since version 1.4.20 Roundup supports two templating engines:
2519
2520 * the original `Template Attribute Language`_ (TAL) engine from Zope
2521 * the standalone Chameleon templating engine. Chameleon is intended
2522 as a replacement for the original TAL engine, and supports the
2523 same syntax, but they are not 100% compatible. The major (and most
2524 likely the only) incompatibility is the default expression type being
2525 ``python:`` instead of ``path:``. See also "Incompatibilities and
2526 differences" section of `Chameleon documentation`__.
2527
2528 Version 1.5.0 added experimental support for the `jinja2`_ templating
2529 language. You must install the `jinja2`_ module in order to use it. The
2530 ``jinja2`` template supplied with Roundup has the templates rewritten
2531 to use ``jinja2`` rather than TAL. A number of trackers are running
2532 using ``jinja2`` templating so it is considered less experimental than
2533 Chameleon templating.
2534
2535 .. _jinja2: https://palletsprojects.com/p/jinja/
2536
2537
2538 **NOTE1**: For historical reasons, examples given below assumes path
2539 expression as default expression type. With Chameleon you have to manually
2540 resolve the path expressions. A Chameleon-based, z3c.pt, that is fully
2541 compatible with the old TAL implementation, is planned to be included in a
2542 future release.
2543
2544 **NOTE2**: As of 1.4.20 Chameleon support is highly experimental and **not**
2545 recommended for production use.
2546
2547 .. _Chameleon:
2548 https://pypi.org/project/Chameleon/
2549 .. _z3c.pt:
2550 https://pypi.org/project/z3c.pt/
2551 __ https://chameleon.readthedocs.io/en/latest/reference.html?highlight=differences#incompatibilities-and-differences
2552 .. _TAL:
2553 .. _Template Attribute Language:
2554 https://pagetemplates.readthedocs.io/en/latest/history/TALSpecification14.html
2555
2556
2557 Basic Templating Actions
2558 ~~~~~~~~~~~~~~~~~~~~~~~~
2559
2560 Roundup's templates consist of special attributes on the HTML tags.
2561 These attributes form the **Template Attribute Language**, or TAL.
2562 The basic TAL commands are:
2563
2564 **tal:define="variable expression; variable expression; ..."**
2565 Define a new variable that is local to this tag and its contents. For
2566 example::
2567
2568 <html tal:define="title request/description">
2569 <head><title tal:content="title"></title></head>
2570 </html>
2571
2572 In this example, the variable "title" is defined as the result of the
2573 expression "request/description". The "tal:content" command inside the
2574 <html> tag may then use the "title" variable.
2575
2576 **tal:condition="expression"**
2577 Only keep this tag and its contents if the expression is true. For
2578 example::
2579
2580 <p tal:condition="python:request.user.hasPermission('View', 'issue')">
2581 Display some issue information.
2582 </p>
2583
2584 In the example, the <p> tag and its contents are only displayed if
2585 the user has the "View" permission for issues. We consider the number
2586 zero, a blank string, an empty list, and the built-in variable
2587 nothing to be false values. Nearly every other value is true,
2588 including non-zero numbers, and strings with anything in them (even
2589 spaces!).
2590
2591 **tal:repeat="variable expression"**
2592 Repeat this tag and its contents for each element of the sequence
2593 that the expression returns, defining a new local variable and a
2594 special "repeat" variable for each element. For example::
2595
2596 <tr tal:repeat="u user/list">
2597 <td tal:content="u/id"></td>
2598 <td tal:content="u/username"></td>
2599 <td tal:content="u/realname"></td>
2600 </tr>
2601
2602 The example would iterate over the sequence of users returned by
2603 "user/list" and define the local variable "u" for each entry. Using
2604 the repeat command creates a new variable called "repeat" which you
2605 may access to gather information about the iteration. See the section
2606 below on `the repeat variable`_.
2607
2608 **tal:replace="expression"**
2609 Replace this tag with the result of the expression. For example::
2610
2611 <span tal:replace="request/user/realname" />
2612
2613 The example would replace the <span> tag and its contents with the
2614 user's realname. If the user's realname was "Bruce", then the
2615 resultant output would be "Bruce".
2616
2617 **tal:content="expression"**
2618 Replace the contents of this tag with the result of the expression.
2619 For example::
2620
2621 <span tal:content="request/user/realname">user's name appears here
2622 </span>
2623
2624 The example would replace the contents of the <span> tag with the
2625 user's realname. If the user's realname was "Bruce" then the
2626 resultant output would be "<span>Bruce</span>".
2627
2628 **tal:attributes="attribute expression; attribute expression; ..."**
2629 Set attributes on this tag to the results of expressions. For
2630 example::
2631
2632 <a tal:attributes="href string:user${request/user/id}">My Details</a>
2633
2634 In the example, the "href" attribute of the <a> tag is set to the
2635 value of the "string:user${request/user/id}" expression, which will
2636 be something like "user123".
2637
2638 **tal:omit-tag="expression"**
2639 Remove this tag (but not its contents) if the expression is true. For
2640 example::
2641
2642 <span tal:omit-tag="python:1">Hello, world!</span>
2643
2644 would result in output of::
2645
2646 Hello, world!
2647
2648 Note that the commands on a given tag are evaulated in the order above,
2649 so *define* comes before *condition*, and so on.
2650
2651 Additionally, you may include tags such as <tal:block>, which are
2652 removed from output. Its content is kept, but the tag itself is not (so
2653 don't go using any "tal:attributes" commands on it). This is useful for
2654 making arbitrary blocks of HTML conditional or repeatable (very handy
2655 for repeating multiple table rows, which would otherwise require an
2656 illegal tag placement to effect the repeat).
2657
2658
2659 Templating Expressions
2660 ~~~~~~~~~~~~~~~~~~~~~~
2661
2662 Templating Expressions are covered by `Template Attribute Language
2663 Expression Syntax`_, or TALES. The expressions you may use in the
2664 attribute values may be one of the following forms:
2665
2666 **Path Expressions** - eg. ``item/status/checklist``
2667 These are object attribute / item accesses. Roughly speaking, the
2668 path ``item/status/checklist`` is broken into parts ``item``,
2669 ``status`` and ``checklist``. The ``item`` part is the root of the
2670 expression. We then look for a ``status`` attribute on ``item``, or
2671 failing that, a ``status`` item (as in ``item['status']``). If that
2672 fails, the path expression fails. When we get to the end, the object
2673 we're left with is evaluated to get a string - if it is a method, it
2674 is called; if it is an object, it is stringified. Path expressions
2675 may have an optional ``path:`` prefix, but they are the default
2676 expression type, so it's not necessary.
2677
2678 If an expression evaluates to ``default``, then the expression is
2679 "cancelled" - whatever HTML already exists in the template will
2680 remain (tag content in the case of ``tal:content``, attributes in the
2681 case of ``tal:attributes``).
2682
2683 If an expression evaluates to ``nothing`` then the target of the
2684 expression is removed (tag content in the case of ``tal:content``,
2685 attributes in the case of ``tal:attributes`` and the tag itself in
2686 the case of ``tal:replace``).
2687
2688 If an element in the path may not exist, then you can use the ``|``
2689 operator in the expression to provide an alternative. So, the
2690 expression ``request/form/foo/value | default`` would simply leave
2691 the current HTML in place if the "foo" form variable doesn't exist.
2692
2693 You may use the python function ``path``, as in
2694 ``path("item/status")``, to embed path expressions in Python
2695 expressions.
2696
2697 **String Expressions** - eg. ``string:hello ${user/name}``
2698 These expressions are simple string interpolations - though they can
2699 be just plain strings with no interpolation if you want. The
2700 expression in the ``${ ... }`` is just a path expression as above.
2701
2702 **Python Expressions** - eg. ``python: 1+1``
2703 These expressions give the full power of Python. All the "root level"
2704 variables are available, so ``python:item.status.checklist()`` would
2705 be equivalent to ``item/status/checklist``, assuming that
2706 ``checklist`` is a method.
2707
2708 Modifiers:
2709
2710 **structure** - eg. ``structure python:msg.content.plain(hyperlink=1)``
2711 The result of expressions are normally *escaped* to be safe for HTML
2712 display (all "<", ">" and "&" are turned into special entities). The
2713 ``structure`` expression modifier turns off this escaping - the
2714 result of the expression is now assumed to be HTML, which is passed
2715 to the web browser for rendering.
2716
2717 **not:** - eg. ``not:python:1=1``
2718 This simply inverts the logical true/false value of another
2719 expression.
2720
2721 .. _TALES:
2722 .. _Template Attribute Language Expression Syntax:
2723 https://pagetemplates.readthedocs.io/en/latest/history/TALESSpecification13.html
2724
2725
2726 Template Macros
2727 ~~~~~~~~~~~~~~~
2728
2729 Macros are used in Roundup to save us from repeating the same common
2730 page stuctures over and over. The most common (and probably only) macro
2731 you'll use is the "icing" macro defined in the "page" template.
2732
2733 Macros are generated and used inside your templates using special
2734 attributes similar to the `basic templating actions`_. In this case,
2735 though, the attributes belong to the `Macro Expansion Template
2736 Attribute Language`_, or METAL. The macro commands are:
2737
2738 **metal:define-macro="macro name"**
2739 Define that the tag and its contents are now a macro that may be
2740 inserted into other templates using the *use-macro* command. For
2741 example::
2742
2743 <html metal:define-macro="page">
2744 ...
2745 </html>
2746
2747 defines a macro called "page" using the ``<html>`` tag and its
2748 contents. Once defined, macros are stored on the template they're
2749 defined on in the ``macros`` attribute. You can access them later on
2750 through the ``templates`` variable, eg. the most common
2751 ``templates/page/macros/icing`` to access the "page" macro of the
2752 "page" template.
2753
2754 **metal:use-macro="path expression"**
2755 Use a macro, which is identified by the path expression (see above).
2756 This will replace the current tag with the identified macro contents.
2757 For example::
2758
2759 <tal:block metal:use-macro="templates/page/macros/icing">
2760 ...
2761 </tal:block>
2762
2763 will replace the tag and its contents with the "page" macro of the
2764 "page" template.
2765
2766 **metal:define-slot="slot name"** and **metal:fill-slot="slot name"**
2767 To define *dynamic* parts of the macro, you define "slots" which may
2768 be filled when the macro is used with a *use-macro* command. For
2769 example, the ``templates/page/macros/icing`` macro defines a slot like
2770 so::
2771
2772 <title metal:define-slot="head_title">title goes here</title>
2773
2774 In your *use-macro* command, you may now use a *fill-slot* command
2775 like this::
2776
2777 <title metal:fill-slot="head_title">My Title</title>
2778
2779 where the tag that fills the slot completely replaces the one defined
2780 as the slot in the macro.
2781
2782 Note that you may not mix `METAL`_ and `TAL`_ commands on the same tag, but
2783 TAL commands may be used freely inside METAL-using tags (so your
2784 *fill-slots* tags may have all manner of TAL inside them).
2785
2786 .. _METAL:
2787 .. _Macro Expansion Template Attribute Language:
2788 https://pagetemplates.readthedocs.io/en/latest/history/TALESSpecification13.html
2789
2790 Information available to templates
2791 ----------------------------------
2792
2793 This is implemented by ``roundup.cgi.templating.RoundupPageTemplate``
2794
2795 The following variables are available to templates.
2796
2797 **context**
2798 The current context. This is either None, a `hyperdb class wrapper`_
2799 or a `hyperdb item wrapper`_
2800
2801 **request**
2802 Includes information about the current request, including:
2803
2804 - the current index information (``filterspec``, ``filter``
2805 args, ``properties``, etc) parsed out of the form.
2806 - methods for easy filterspec link generation
2807 - "form"
2808 The current CGI form information as a mapping of form argument name
2809 to value (specifically a cgi.FieldStorage)
2810 - "env" the CGI environment variables
2811 - "base" the base URL for this instance
2812 - "user" a HTMLItem instance for the current user
2813 - "language" as determined by the browser or config
2814 - "classname" the current classname (possibly None)
2815 - "template" the current template (suffix, also possibly None)
2816 **config**
2817 This variable holds all the values defined in the tracker config.ini
2818 file (eg. TRACKER_NAME, etc.)
2819 **db**
2820 The current database, used to access arbitrary database items.
2821 **templates**
2822 Access to all the tracker templates by name. Used mainly in
2823 *use-macro* commands.
2824 **utils**
2825 This variable makes available some utility functions like batching.
2826 **nothing**
2827 This is a special variable - if an expression evaluates to this, then
2828 the tag (in the case of a ``tal:replace``), its contents (in the case
2829 of ``tal:content``) or some attributes (in the case of
2830 ``tal:attributes``) will not appear in the the output. So, for
2831 example::
2832
2833 <span tal:attributes="class nothing">Hello, World!</span>
2834
2835 would result in::
2836
2837 <span>Hello, World!</span>
2838
2839 **default**
2840 Also a special variable - if an expression evaluates to this, then the
2841 existing HTML in the template will not be replaced or removed, it will
2842 remain. So::
2843
2844 <span tal:replace="default">Hello, World!</span>
2845
2846 would result in::
2847
2848 <span>Hello, World!</span>
2849
2850 **true**, **false**
2851 Boolean constants that may be used in `templating expressions`_
2852 instead of ``python:1`` and ``python:0``.
2853 **i18n**
2854 Internationalization service, providing two string translation methods:
2855
2856 **gettext** (*message*)
2857 Return the localized translation of message
2858 **ngettext** (*singular*, *plural*, *number*)
2859 Like ``gettext()``, but consider plural forms. If a translation
2860 is found, apply the plural formula to *number*, and return the
2861 resulting message (some languages have more than two plural forms).
2862 If no translation is found, return singular if *number* is 1;
2863 return plural otherwise.
2864
2865 The context variable
2866 ~~~~~~~~~~~~~~~~~~~~
2867
2868 The *context* variable is one of three things based on the current
2869 context (see `determining web context`_ for how we figure this out):
2870
2871 1. if we're looking at a "home" page, then it's None
2872 2. if we're looking at a specific hyperdb class, it's a
2873 `hyperdb class wrapper`_.
2874 3. if we're looking at a specific hyperdb item, it's a
2875 `hyperdb item wrapper`_.
2876
2877 If the context is not None, we can access the properties of the class or
2878 item. The only real difference between cases 2 and 3 above are:
2879
2880 1. the properties may have a real value behind them, and this will
2881 appear if the property is displayed through ``context/property`` or
2882 ``context/property/field``.
2883 2. the context's "id" property will be a false value in the second case,
2884 but a real, or true value in the third. Thus we can determine whether
2885 we're looking at a real item from the hyperdb by testing
2886 "context/id".
2887
2888 Hyperdb class wrapper
2889 :::::::::::::::::::::
2890
2891 This is implemented by the ``roundup.cgi.templating.HTMLClass``
2892 class.
2893
2894 This wrapper object provides access to a hyperdb class. It is used
2895 primarily in both index view and new item views, but it's also usable
2896 anywhere else that you wish to access information about a class, or the
2897 items of a class, when you don't have a specific item of that class in
2898 mind.
2899
2900 We allow access to properties. There will be no "id" property. The value
2901 accessed through the property will be the current value of the same name
2902 from the CGI form.
2903
2904 There are several methods available on these wrapper objects:
2905
2906 =========== =============================================================
2907 Method Description
2908 =========== =============================================================
2909 properties return a `hyperdb property wrapper`_ for all of this class's
2910 properties that are searchable by the user. You can use
2911 the argument cansearch=False to get all properties.
2912 list lists all of the active (not retired) items in the class.
2913 csv return the items of this class as a chunk of CSV text.
2914 propnames lists the names of the properties of this class.
2915 filter lists of items from this class, filtered and sorted. Two
2916 options are available for sorting:
2917
2918 1. by the current *request* filterspec/filter/sort/group args
2919 2. by the "filterspec", "sort" and "group" keyword args.
2920 "filterspec" is ``{propname: value(s)}``. "sort" and
2921 "group" are an optionally empty list ``[(dir, prop)]``
2922 where dir is '+', '-' or None
2923 and prop is a prop name or None.
2924
2925 The propname in filterspec and prop in a sort/group spec
2926 may be transitive, i.e., it may contain properties of
2927 the form link.link.link.name.
2928
2929 eg. All issues with a priority of "1" with messages added in
2930 the last week, sorted by activity date:
2931 ``issue.filter(filterspec={"priority": "1",
2932 'messages.creation' : '.-1w;'}, sort=[('activity', '+')])``
2933
2934 Note that when searching for Link and Multilink values, the
2935 special value '-1' searches for empty Link or Multilink
2936 values. For both, Links and Multilinks, multiple values
2937 given in a filter call are combined with 'OR' by default.
2938 For Multilinks a postfix expression syntax using negative ID
2939 numbers (as strings) as operators is supported. Each
2940 non-negative number (or '-1') is pushed on an operand stack.
2941 A negative number pops the required number of arguments from
2942 the stack, applies the operator, and pushes the result. The
2943 following operators are supported:
2944
2945 - '-2' stands for 'NOT' and takes one argument
2946 - '-3' stands for 'AND' and takes two arguments
2947 - '-4' stands for 'OR' and takes two arguments
2948
2949 Note that this special handling of ID arguments is applied only
2950 when a negative number smaller than -1 is encountered as an ID
2951 in the filter call. Otherwise the implicit OR default
2952 applies.
2953 Examples of using Multilink expressions would be
2954
2955 - '1', '2', '-4', '3', '4', '-4', '-3'
2956 would search for IDs (1 or 2) and (3 or 4)
2957 - '-1' '-2' would search for all non-empty Multilinks
2958
2959 filter_sql **Only in SQL backends**
2960
2961 Lists the items that match the SQL provided. The SQL is a
2962 complete "select" statement.
2963
2964 The SQL select must include the item id as the first column.
2965
2966 This function **does not** filter out retired items, add
2967 on a where clause "__retired__ <> 1" if you don't want
2968 retired nodes.
2969
2970 classhelp display a link to a javascript popup containing this class'
2971 "help" template.
2972
2973 This generates a link to a popup window which displays the
2974 properties indicated by "properties" of the class named by
2975 "classname". The "properties" should be a comma-separated list
2976 (eg. 'id,name,description'). Properties defaults to all the
2977 properties of a class (excluding id, creator, created and
2978 activity).
2979
2980 You may optionally override the "label" displayed, the "width",
2981 the "height", the number of items per page ("pagesize") and
2982 the field on which the list is sorted ("sort").
2983
2984 With the "filter" arg it is possible to specify a filter for
2985 which items are supposed to be displayed. It has to be of
2986 the format "<field>=<values>;<field>=<values>;...".
2987
2988 The popup window will be resizable and scrollable.
2989
2990 If the "property" arg is given, it's passed through to the
2991 javascript help_window function. This allows updating of a
2992 property in the calling HTML page.
2993
2994 If the "form" arg is given, it's passed through to the
2995 javascript help_window function - it's the name of the form
2996 the "property" belongs to.
2997
2998 submit generate a submit button (and action and @csrf hidden elements)
2999 renderWith render this class with the given template.
3000 history returns 'New node - no history' :)
3001 is_edit_ok is the user allowed to Edit the current class?
3002 is_view_ok is the user allowed to View the current class?
3003 =========== =============================================================
3004
3005 Note that if you have a property of the same name as one of the above
3006 methods, you'll need to access it using a python "item access"
3007 expression. For example::
3008
3009 python:context['list']
3010
3011 will access the "list" property, rather than the list method.
3012
3013
3014 Hyperdb item wrapper
3015 ::::::::::::::::::::
3016
3017 This is implemented by the ``roundup.cgi.templating.HTMLItem``
3018 class.
3019
3020 This wrapper object provides access to a hyperdb item.
3021
3022 We allow access to properties. There will be no "id" property. The value
3023 accessed through the property will be the current value of the same name
3024 from the CGI form.
3025
3026 There are several methods available on these wrapper objects:
3027
3028 =============== ========================================================
3029 Method Description
3030 =============== ========================================================
3031 submit generate a submit button (and action and @csrf hidden elements)
3032 journal return the journal of the current item (**not
3033 implemented**)
3034 history render the journal of the current item as
3035 HTML. By default properties marked as "quiet" (see
3036 `design documentation`_) are not shown unless the
3037 function is called with the showall=True parameter.
3038 Properties that are not Viewable to the user are not
3039 shown.
3040 renderQueryForm specific to the "query" class - render the search form
3041 for the query
3042 hasPermission specific to the "user" class - determine whether the
3043 user has a Permission. The signature is::
3044
3045 hasPermission(self, permission, [classname=],
3046 [property=], [itemid=])
3047
3048 where the classname defaults to the current context.
3049 hasRole specific to the "user" class - determine whether the
3050 user has a Role. The signature is::
3051
3052 hasRole(self, rolename)
3053
3054 is_edit_ok is the user allowed to Edit the current item?
3055 is_view_ok is the user allowed to View the current item?
3056 is_retired is the item retired?
3057 download_url generate a url-quoted link for download of FileClass
3058 item contents (ie. file<id>/<name>)
3059 copy_url generate a url-quoted link for creating a copy
3060 of this item. By default, the copy will acquire
3061 all properties of the current item except for
3062 ``messages`` and ``files``. This can be overridden
3063 by passing ``exclude`` argument which contains a list
3064 (or any iterable) of property names that shall not be
3065 copied. Database-driven properties like ``id`` or
3066 ``activity`` cannot be copied.
3067 =============== ========================================================
3068
3069 Note that if you have a property of the same name as one of the above
3070 methods, you'll need to access it using a python "item access"
3071 expression. For example::
3072
3073 python:context['journal']
3074
3075 will access the "journal" property, rather than the journal method.
3076
3077
3078 Hyperdb property wrapper
3079 ::::::::::::::::::::::::
3080
3081 This is implemented by subclasses of the
3082 ``roundup.cgi.templating.HTMLProperty`` class (``HTMLStringProperty``,
3083 ``HTMLNumberProperty``, and so on).
3084
3085 This wrapper object provides access to a single property of a class. Its
3086 value may be either:
3087
3088 1. if accessed through a `hyperdb item wrapper`_, then it's a value from
3089 the hyperdb
3090 2. if access through a `hyperdb class wrapper`_, then it's a value from
3091 the CGI form
3092
3093
3094 The property wrapper has some useful attributes:
3095
3096 =============== ========================================================
3097 Attribute Description
3098 =============== ========================================================
3099 _name the name of the property
3100 _value the value of the property if any - this is the actual
3101 value retrieved from the hyperdb for this property
3102 =============== ========================================================
3103
3104 There are several methods available on these wrapper objects:
3105
3106 =========== ================================================================
3107 Method Description
3108 =========== ================================================================
3109 plain render a "plain" representation of the property. This method
3110 may take two arguments:
3111
3112 escape
3113 If true, escape the text so it is HTML safe (default: no). The
3114 reason this defaults to off is that text is usually escaped
3115 at a later stage by the TAL commands, unless the "structure"
3116 option is used in the template. The following ``tal:content``
3117 expressions are all equivalent::
3118
3119 "structure python:msg.content.plain(escape=1)"
3120 "python:msg.content.plain()"
3121 "msg/content/plain"
3122 "msg/content"
3123
3124 Usually you'll only want to use the escape option in a
3125 complex expression.
3126
3127 hyperlink
3128 If true, turn URLs, email addresses and hyperdb item
3129 designators in the text into hyperlinks (default: no). Note
3130 that you'll need to use the "structure" TAL option if you
3131 want to use this ``tal:content`` expression::
3132
3133 "structure python:msg.content.plain(hyperlink=1)"
3134
3135 The text is automatically HTML-escaped before the hyperlinking
3136 transformation done in the plain() method.
3137
3138 hyperlinked The same as msg.content.plain(hyperlink=1), but nicer::
3139
3140 "structure msg/content/hyperlinked"
3141
3142 field render an appropriate form edit field for the property - for
3143 most types this is a text entry box, but for Booleans it's a
3144 tri-state yes/no/neither selection. This method may take some
3145 arguments:
3146
3147 size
3148 Sets the width in characters of the edit field
3149
3150 format (Date properties only)
3151 Sets the format of the date in the field - uses the same
3152 format string argument as supplied to the ``pretty`` method
3153 below.
3154
3155 popcal (Date properties only)
3156 Include the Javascript-based popup calendar for date
3157 selection. Defaults to on.
3158
3159 stext only on String properties - render the value of the property
3160 as StructuredText (requires the StructureText module to be
3161 installed separately)
3162 multiline only on String properties - render a multiline form edit
3163 field for the property
3164 email only on String properties - render the value of the property
3165 as an obscured email address
3166 url_quote only on String properties. It quotes any characters in the
3167 string so it is safe to use in a url. E.G. a space is
3168 replaced with %20.
3169 confirm only on Password properties - render a second form edit field
3170 for the property, used for confirmation that the user typed
3171 the password correctly. Generates a field with name
3172 "name:confirm".
3173 now only on Date properties - return the current date as a new
3174 property
3175 reldate only on Date properties - render the interval between the date
3176 and now
3177 local only on Date properties - return this date as a new property
3178 with some timezone offset, for example::
3179
3180 python:context.creation.local(10)
3181
3182 will render the date with a +10 hour offset.
3183 pretty Date properties - render the date as "dd Mon YYYY" (eg. "19
3184 Mar 2004"). Takes an optional format argument, for example::
3185
3186 python:context.activity.pretty('%Y-%m-%d')
3187
3188 Will format as "2004-03-19" instead.
3189
3190 Interval properties - render the interval in a pretty
3191 format (eg. "yesterday"). The format arguments are those used
3192 in the standard ``strftime`` call (see the `Python Library
3193 Reference: time module`__)
3194
3195 Number properties - takes a printf style format argument
3196 (default: '%0.3f') and formats the number accordingly.
3197 If the value can't be converted, '' is returned if the
3198 value is ``None`` otherwise it is converted to a string.
3199 popcal Generate a link to a popup calendar which may be used to
3200 edit the date field, for example::
3201
3202 <span tal:replace="structure context/due/popcal" />
3203
3204 you still need to include the ``field`` for the property, so
3205 typically you'd have::
3206
3207 <span tal:replace="structure context/due/field" />
3208 <span tal:replace="structure context/due/popcal" />
3209
3210 menu only on Link and Multilink properties - render a form select
3211 list for this property. Takes a number of optional arguments
3212
3213 size
3214 is used to limit the length of the list labels
3215 height
3216 is used to set the <select> tag's "size" attribute
3217 showid
3218 includes the item ids in the list labels
3219 additional
3220 lists properties which should be included in the label
3221 sort_on
3222 indicates the property to sort the list on as (direction,
3223 (direction, property) where direction is '+' or '-'. A
3224 single string with the direction prepended may be used.
3225 For example: ('-', 'order'), '+name'.
3226 value
3227 gives a default value to preselect in the menu
3228
3229 The remaining keyword arguments are used as conditions for
3230 filtering the items in the list - they're passed as the
3231 "filterspec" argument to a Class.filter() call. For example::
3232
3233 <span tal:replace="structure context/status/menu" />
3234
3235 <span tal:replace="python:context.status.menu(order='+name",
3236 value='chatting',
3237 filterspec={'status': '1,2,3,4'}" />
3238
3239 sorted only on Multilink properties - produce a list of the linked
3240 items sorted by some property, for example::
3241
3242 python:context.files.sorted('creation')
3243
3244 Will list the files by upload date. While::
3245
3246 python:context.files.sorted('creation', reverse=True)
3247
3248 Will list the files by upload date in reverse order from
3249 the prior example. If the property can be unset, you can
3250 use the ``NoneFirst`` parameter to sort the None/Unset
3251 values at the front or the end of the list. For example::
3252
3253 python:context.files.sorted('creation', NoneFirst=True)
3254
3255 will sort files by creation date with files missing a
3256 creation date at the start of the list. The default for
3257 ``NoneFirst`` is False so these files will sort at the end
3258 by default. (Note creation date is never unset, but you
3259 get the idea.) If you combine NoneFirst with
3260 ``reverse=True`` the meaning of NoneFirst is inverted:
3261 True sorts None/unset at the end and False sorts at the
3262 beginning.
3263 reverse only on Multilink properties - produce a list of the linked
3264 items in reverse order
3265 isset returns True if the property has been set to a value
3266 =========== ================================================================
3267
3268 __ https://docs.python.org/2/library/time.html
3269
3270 All of the above functions perform checks for permissions required to
3271 display or edit the data they are manipulating. The simplest case is
3272 editing an issue title. Including the expression::
3273
3274 context/title/field
3275
3276 Will present the user with an edit field, if they have edit permission. If
3277 not, then they will be presented with a static display if they have view
3278 permission. If they don't even have view permission, then an error message
3279 is raised, preventing the display of the page, indicating that they don't
3280 have permission to view the information.
3281
3282
3283 The request variable
3284 ~~~~~~~~~~~~~~~~~~~~
3285
3286 This is implemented by the ``roundup.cgi.templating.HTMLRequest``
3287 class.
3288
3289 The request variable is packed with information about the current
3290 request.
3291
3292 .. taken from ``roundup.cgi.templating.HTMLRequest`` docstring
3293
3294 =========== ============================================================
3295 Variable Holds
3296 =========== ============================================================
3297 form the CGI form as a cgi.FieldStorage
3298 env the CGI environment variables
3299 base the base URL for this tracker
3300 user a HTMLUser instance for this user
3301 classname the current classname (possibly None)
3302 template the current template (suffix, also possibly None)
3303 form the current CGI form variables in a FieldStorage
3304 =========== ============================================================
3305
3306 **Index page specific variables (indexing arguments)**
3307
3308 =========== ============================================================
3309 Variable Holds
3310 =========== ============================================================
3311 columns dictionary of the columns to display in an index page
3312 show a convenience access to columns - request/show/colname will
3313 be true if the columns should be displayed, false otherwise
3314 sort index sort columns [(direction, column name)]
3315 group index grouping properties [(direction, column name)]
3316 filter properties to filter the index on
3317 filterspec values to filter the index on (property=value, eg
3318 ``priority=1`` or ``messages.author=42``
3319 search_text text to perform a full-text search on for an index
3320 =========== ============================================================
3321
3322 There are several methods available on the request variable:
3323
3324 =============== ========================================================
3325 Method Description
3326 =============== ========================================================
3327 description render a description of the request - handle for the
3328 page title
3329 indexargs_form render the current index args as form elements
3330 indexargs_url render the current index args as a URL
3331 base_javascript render some javascript that is used by other components
3332 of the templating
3333 batch run the current index args through a filter and return a
3334 list of items (see `hyperdb item wrapper`_, and
3335 `batching`_)
3336 =============== ========================================================
3337
3338 The form variable
3339 :::::::::::::::::
3340
3341 The form variable is a bit special because it's actually a python
3342 FieldStorage object. That means that you have two ways to access its
3343 contents. For example, to look up the CGI form value for the variable
3344 "name", use the path expression::
3345
3346 request/form/name/value
3347
3348 or the python expression::
3349
3350 python:request.form['name'].value
3351
3352 Note the "item" access used in the python case, and also note the
3353 explicit "value" attribute we have to access. That's because the form
3354 variables are stored as MiniFieldStorages. If there's more than one
3355 "name" value in the form, then the above will break since
3356 ``request/form/name`` is actually a *list* of MiniFieldStorages. So it's
3357 best to know beforehand what you're dealing with.
3358
3359
3360 The db variable
3361 ~~~~~~~~~~~~~~~
3362
3363 This is implemented by the ``roundup.cgi.templating.HTMLDatabase``
3364 class.
3365
3366 Allows access to all hyperdb classes as attributes of this variable. If
3367 you want access to the "user" class, for example, you would use::
3368
3369 db/user
3370 python:db.user
3371
3372 Also, the current id of the current user is available as
3373 ``db.getuid()``. This isn't so useful in templates (where you have
3374 ``request/user``), but it can be useful in detectors or interfaces.
3375
3376 The access results in a `hyperdb class wrapper`_.
3377
3378
3379 The templates variable
3380 ~~~~~~~~~~~~~~~~~~~~~~
3381
3382 This was implemented by the ``roundup.cgi.templating.Templates``
3383 class before 1.4.20. In later versions it is the instance of appropriate
3384 template engine loader class.
3385
3386 This variable is used to access other templates in expressions and
3387 template macros. It doesn't have any useful methods defined. The
3388 templates can be accessed using the following path expression::
3389
3390 templates/name
3391
3392 or the python expression::
3393
3394 templates[name]
3395
3396 where "name" is the name of the template you wish to access. The
3397 template has one useful attribute, namely "macros". To access a specific
3398 macro (called "macro_name"), use the path expression::
3399
3400 templates/name/macros/macro_name
3401
3402 or the python expression::
3403
3404 templates[name].macros[macro_name]
3405
3406 The repeat variable
3407 ~~~~~~~~~~~~~~~~~~~
3408
3409 The repeat variable holds an entry for each active iteration. That is, if
3410 you have a ``tal:repeat="user db/users"`` command, then there will be a
3411 repeat variable entry called "user". This may be accessed as either::
3412
3413 repeat/user
3414 python:repeat['user']
3415
3416 The "user" entry has a number of methods available for information:
3417
3418 =============== =========================================================
3419 Method Description
3420 =============== =========================================================
3421 first True if the current item is the first in the sequence.
3422 last True if the current item is the last in the sequence.
3423 even True if the current item is an even item in the sequence.
3424 odd True if the current item is an odd item in the sequence.
3425 number Current position in the sequence, starting from 1.
3426 letter Current position in the sequence as a letter, a through
3427 z, then aa through zz, and so on.
3428 Letter Same as letter(), except uppercase.
3429 roman Current position in the sequence as lowercase roman
3430 numerals.
3431 Roman Same as roman(), except uppercase.
3432 =============== =========================================================
3433
3434 .. _templating utilities:
3435
3436 The utils variable
3437 ~~~~~~~~~~~~~~~~~~
3438
3439 This is implemented by the
3440 ``roundup.cgi.templating.TemplatingUtils`` class, which may be extended
3441 with additional methods by extensions_.
3442
3443 =============== ========================================================
3444 Method Description
3445 =============== ========================================================
3446 Batch return a batch object using the supplied list
3447 url_quote quote some text as safe for a URL (ie. space, %, ...)
3448 html_quote quote some text as safe in HTML (ie. <, >, ...)
3449 html_calendar renders an HTML calendar used by the
3450 ``_generic.calendar.html`` template (itself invoked by
3451 the popupCalendar DateHTMLProperty method
3452 anti_csrf_nonce returns the random noncue generated for this session
3453 =============== ========================================================
3454
3455
3456 Batching
3457 ::::::::
3458
3459 Use Batch to turn a list of items, or item ids of a given class, into a
3460 series of batches. Its usage is::
3461
3462 python:utils.Batch(sequence, size, start, end=0, orphan=0,
3463 overlap=0)
3464
3465 or, to get the current index batch::
3466
3467 request/batch
3468
3469 The parameters are:
3470
3471 ========= ==============================================================
3472 Parameter Usage
3473 ========= ==============================================================
3474 sequence a list of HTMLItems
3475 size how big to make the sequence.
3476 start where to start (0-indexed) in the sequence.
3477 end where to end (0-indexed) in the sequence.
3478 orphan if the next batch would contain less items than this value,
3479 then it is combined with this batch
3480 overlap the number of items shared between adjacent batches
3481 ========= ==============================================================
3482
3483 All of the parameters are assigned as attributes on the batch object. In
3484 addition, it has several more attributes:
3485
3486 =============== ========================================================
3487 Attribute Description
3488 =============== ========================================================
3489 start indicates the start index of the batch. *Unlike
3490 the argument, is a 1-based index (I know, lame)*
3491 first indicates the start index of the batch *as a 0-based
3492 index*
3493 length the actual number of elements in the batch
3494 sequence_length the length of the original, unbatched, sequence.
3495 =============== ========================================================
3496
3497 And several methods:
3498
3499 =============== ========================================================
3500 Method Description
3501 =============== ========================================================
3502 previous returns a new Batch with the previous batch settings
3503 next returns a new Batch with the next batch settings
3504 propchanged detect if the named property changed on the current item
3505 when compared to the last item
3506 =============== ========================================================
3507
3508 An example of batching::
3509
3510 <table class="otherinfo">
3511 <tr><th colspan="4" class="header">Existing Keywords</th></tr>
3512 <tr tal:define="keywords db/keyword/list"
3513 tal:repeat="start python:range(0, len(keywords), 4)">
3514 <td tal:define="batch python:utils.Batch(keywords, 4, start)"
3515 tal:repeat="keyword batch" tal:content="keyword/name">
3516 keyword here</td>
3517 </tr>
3518 </table>
3519
3520 ... which will produce a table with four columns containing the items of
3521 the "keyword" class (well, their "name" anyway).
3522
3523
3524 Translations
3525 ~~~~~~~~~~~~
3526
3527 Should you wish to enable multiple languages in template content that you
3528 create you'll need to add new locale files in the tracker home under a
3529 ``locale`` directory. Use the `translation instructions in the
3530 developer's guide <developers.html#extracting-translatable-messages>`_ to
3531 create the locale files.
3532
3533
3534 Displaying Properties
3535 ---------------------
3536
3537 Properties appear in the user interface in three contexts: in indices,
3538 in editors, and as search arguments. For each type of property, there
3539 are several display possibilities. For example, in an index view, a
3540 string property may just be printed as a plain string, but in an editor
3541 view, that property may be displayed in an editable field.
3542
3543
3544 Index Views
3545 -----------
3546
3547 This is one of the class context views. It is also the default view for
3548 classes. The template used is "*classname*.index".
3549
3550
3551 Index View Specifiers
3552 ~~~~~~~~~~~~~~~~~~~~~
3553
3554 An index view specifier (URL fragment) looks like this (whitespace has
3555 been added for clarity)::
3556
3557 /issue?status=unread,in-progress,resolved&
3558 keyword=security,ui&
3559 @group=priority,-status&
3560 @sort=-activity&
3561 @filters=status,keyword&
3562 @columns=title,status,fixer
3563
3564 The index view is determined by two parts of the specifier: the layout
3565 part and the filter part. The layout part consists of the query
3566 parameters that begin with colons, and it determines the way that the
3567 properties of selected items are displayed. The filter part consists of
3568 all the other query parameters, and it determines the criteria by which
3569 items are selected for display. The filter part is interactively
3570 manipulated with the form widgets displayed in the filter section. The
3571 layout part is interactively manipulated by clicking on the column
3572 headings in the table.
3573
3574 The filter part selects the union of the sets of items with values
3575 matching any specified Link properties and the intersection of the sets
3576 of items with values matching any specified Multilink properties.
3577
3578 The example specifies an index of "issue" items. Only items with a
3579 "status" of either "unread" or "in-progress" or "resolved" are
3580 displayed, and only items with "keyword" values including both "security"
3581 and "ui" are displayed. The items are grouped by priority arranged in
3582 ascending order and in descending order by status; and within
3583 groups, sorted by activity, arranged in descending order. The filter
3584 section shows filters for the "status" and "keyword" properties, and the
3585 table includes columns for the "title", "status", and "fixer"
3586 properties.
3587
3588 ============ =============================================================
3589 Argument Description
3590 ============ =============================================================
3591 @sort sort by prop name, optionally preceeded with '-' to give
3592 descending or nothing for ascending sorting. Several
3593 properties can be specified delimited with comma.
3594 Internally a search-page using several sort properties may
3595 use @sort0, @sort1 etc. with option @sortdir0, @sortdir1
3596 etc. for the direction of sorting (a non-empty value of
3597 sortdir0 specifies reverse order).
3598 @group group by prop name, optionally preceeded with '-' or to sort
3599 in descending or nothing for ascending order. Several
3600 properties can be specified delimited with comma.
3601 Internally a search-page using several grouping properties may
3602 use @group0, @group1 etc. with option @groupdir0, @groupdir1
3603 etc. for the direction of grouping (a non-empty value of
3604 groupdir0 specifies reverse order).
3605 @columns selects the columns that should be displayed. Default is
3606 all.
3607 @filter indicates which properties are being used in filtering.
3608 Default is none.
3609 propname selects the values the item properties given by propname must
3610 have (very basic search/filter).
3611 @search_text if supplied, performs a full-text search (message bodies,
3612 issue titles, etc)
3613 ============ =============================================================
3614
3615
3616 Searching Views
3617 ---------------
3618
3619 .. note::
3620 if you add a new column to the ``@columns`` form variable potentials
3621 then you will need to add the column to the appropriate `index views`_
3622 template so that it is actually displayed.
3623
3624 This is one of the class context views. The template used is typically
3625 "*classname*.search". The form on this page should have "search" as its
3626 ``@action`` variable. The "search" action:
3627
3628 - sets up additional filtering, as well as performing indexed text
3629 searching
3630 - sets the ``@filter`` variable correctly
3631 - saves the query off if ``@query_name`` is set.
3632
3633 The search page should lay out any fields that you wish to allow the
3634 user to search on. If your schema contains a large number of properties,
3635 you should be wary of making all of those properties available for
3636 searching, as this can cause confusion. If the additional properties are
3637 Strings, consider having their value indexed, and then they will be
3638 searchable using the full text indexed search. This is both faster, and
3639 more useful for the end user.
3640
3641 If the search view does specify the "search" ``@action``, then it may also
3642 provide an additional argument:
3643
3644 ============ =============================================================
3645 Argument Description
3646 ============ =============================================================
3647 @query_name if supplied, the index parameters (including @search_text)
3648 will be saved off as a the query item and registered against
3649 the user's queries property. Note that the *classic* template
3650 schema has this ability, but the *minimal* template schema
3651 does not.
3652 ============ =============================================================
3653
3654
3655 Item Views
3656 ----------
3657
3658 The basic view of a hyperdb item is provided by the "*classname*.item"
3659 template. It generally has three sections; an "editor", a "spool" and a
3660 "history" section.
3661
3662
3663 Editor Section
3664 ~~~~~~~~~~~~~~
3665
3666 The editor section is used to manipulate the item - it may be a static
3667 display if the user doesn't have permission to edit the item.
3668
3669 Here's an example of a basic editor template (this is the default
3670 "classic" template issue item edit form - from the "issue.item.html"
3671 template)::
3672
3673 <table class="form">
3674 <tr>
3675 <th>Title</th>
3676 <td colspan="3" tal:content="structure python:context.title.field(size=60)">title</td>
3677 </tr>
3678
3679 <tr>
3680 <th>Priority</th>
3681 <td tal:content="structure context/priority/menu">priority</td>
3682 <th>Status</th>
3683 <td tal:content="structure context/status/menu">status</td>
3684 </tr>
3685
3686 <tr>
3687 <th>Superseder</th>
3688 <td>
3689 <span tal:replace="structure python:context.superseder.field(showid=1, size=20)" />
3690 <span tal:replace="structure python:db.issue.classhelp('id,title')" />
3691 <span tal:condition="context/superseder">
3692 <br>View: <span tal:replace="structure python:context.superseder.link(showid=1)" />
3693 </span>
3694 </td>
3695 <th>Nosy List</th>
3696 <td>
3697 <span tal:replace="structure context/nosy/field" />
3698 <span tal:replace="structure python:db.user.classhelp('username,realname,address,phone')" />
3699 </td>
3700 </tr>
3701
3702 <tr>
3703 <th>Assigned To</th>
3704 <td tal:content="structure context/assignedto/menu">
3705 assignedto menu
3706 </td>
3707 <td>&nbsp;</td>
3708 <td>&nbsp;</td>
3709 </tr>
3710
3711 <tr>
3712 <th>Change Note</th>
3713 <td colspan="3">
3714 <textarea name=":note" wrap="hard" rows="5" cols="60"></textarea>
3715 </td>
3716 </tr>
3717
3718 <tr>
3719 <th>File</th>
3720 <td colspan="3"><input type="file" name=":file" size="40"></td>
3721 </tr>
3722
3723 <tr>
3724 <td>&nbsp;</td>
3725 <td colspan="3" tal:content="structure context/submit">
3726 submit button will go here
3727 </td>
3728 </tr>
3729 </table>
3730
3731
3732 When a change is submitted, the system automatically generates a message
3733 describing the changed properties. As shown in the example, the editor
3734 template can use the ":note" and ":file" fields, which are added to the
3735 standard changenote message generated by Roundup.
3736
3737
3738 Form values
3739 :::::::::::
3740
3741 We have a number of ways to pull properties out of the form in order to
3742 meet the various needs of:
3743
3744 1. editing the current item (perhaps an issue item)
3745 2. editing information related to the current item (eg. messages or
3746 attached files)
3747 3. creating new information to be linked to the current item (eg. time
3748 spent on an issue)
3749
3750 In the following, ``<bracketed>`` values are variable, ":" may be one of
3751 ":" or "@", and other text ("required") is fixed.
3752
3753 Properties are specified as form variables:
3754
3755 ``<propname>``
3756 property on the current context item
3757
3758 ``<designator>:<propname>``
3759 property on the indicated item (for editing related information)
3760
3761 ``<classname>-<N>:<propname>``
3762 property on the Nth new item of classname (generally for creating new
3763 items to attach to the current item)
3764
3765 Once we have determined the "propname", we check to see if it is one of
3766 the special form values:
3767
3768 ``@required``
3769 The named property values must be supplied or a ValueError will be
3770 raised.
3771
3772 ``@remove@<propname>=id(s)``
3773 The ids will be removed from the multilink property.
3774
3775 ``:add:<propname>=id(s)``
3776 The ids will be added to the multilink property.
3777
3778 ``:link:<propname>=<designator>``
3779 Used to add a link to new items created during edit. These are
3780 collected and returned in ``all_links``. This will result in an
3781 additional linking operation (either Link set or Multilink append)
3782 after the edit/create is done using ``all_props`` in ``_editnodes``.
3783 The <propname> on the current item will be set/appended the id of the
3784 newly created item of class <designator> (where <designator> must be
3785 <classname>-<N>).
3786
3787 Any of the form variables may be prefixed with a classname or
3788 designator.
3789
3790 Two special form values are supported for backwards compatibility:
3791
3792 ``:note``
3793 create a message (with content, author and date), linked to the
3794 context item. This is ALWAYS designated "msg-1".
3795 ``:file``
3796 create a file, attached to the current item and any message created by
3797 :note. This is ALWAYS designated "file-1".
3798
3799
3800 Spool Section
3801 ~~~~~~~~~~~~~
3802
3803 The spool section lists related information like the messages and files
3804 of an issue.
3805
3806 TODO
3807
3808
3809 History Section
3810 ~~~~~~~~~~~~~~~
3811
3812 The final section displayed is the history of the item - its database
3813 journal. This is generally generated with the template::
3814
3815 <tal:block tal:replace="structure context/history" />
3816
3817 or::
3818
3819 <tal:block
3820 tal:replace="structure python:context.history(showall=True)" />
3821
3822 if you want to show history entries for quiet properties.
3823
3824 *To be done:*
3825
3826 *The actual history entries of the item may be accessed for manual
3827 templating through the "journal" method of the item*::
3828
3829 <tal:block tal:repeat="entry context/journal">
3830 a journal entry
3831 </tal:block>
3832
3833 *where each journal entry is an HTMLJournalEntry.*
3834
3835
3836 Defining new web actions
3837 ------------------------
3838
3839 You may define new actions to be triggered by the ``@action`` form variable.
3840 These are added to the tracker ``extensions`` directory and registered
3841 using ``instance.registerAction``.
3842
3843 All the existing Actions are defined in ``roundup.cgi.actions``.
3844
3845 Adding action classes takes three steps; first you `define the new
3846 action class`_, then you `register the action class`_ with the cgi
3847 interface so it may be triggered by the ``@action`` form variable.
3848 Finally you `use the new action`_ in your HTML form.
3849
3850 See `setting up a "wizard" (or "druid") for controlled adding of
3851 issues
3852 <customizing.html#setting-up-a-wizard-or-druid-for-controlled-adding-of-issues>`_ for an example.
3853
3854
3855 Define the new action class
3856 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3857
3858 Create a new action class in your tracker's ``extensions`` directory, for
3859 example ``myaction.py``::
3860
3861 from roundup.cgi.actions import Action
3862
3863 class MyAction(Action):
3864 def handle(self):
3865 ''' Perform some action. No return value is required.
3866 '''
3867
3868 The *self.client* attribute is an instance of ``roundup.cgi.client.Client``.
3869 See the docstring of that class for details of what it can do.
3870
3871 The method will typically check the ``self.form`` variable's contents.
3872 It may then:
3873
3874 - add information to ``self.client._ok_message``
3875 or ``self.client._error_message`` (by using ``self.client.add_ok_message``
3876 or ``self.client.add_error_message``, respectively)
3877 - change the ``self.client.template`` variable to alter what the user will see
3878 next
3879 - raise Unauthorised, SendStaticFile, SendFile, NotFound or Redirect
3880 exceptions (import them from roundup.cgi.exceptions)
3881
3882
3883 Register the action class
3884 ~~~~~~~~~~~~~~~~~~~~~~~~~~
3885
3886 The class is now written, but isn't available to the user until you register
3887 it with the following code appended to your ``myaction.py`` file::
3888
3889 def init(instance):
3890 instance.registerAction('myaction', myActionClass)
3891
3892 This maps the action name "myaction" to the action class we defined.
3893
3894
3895 Use the new action
3896 ~~~~~~~~~~~~~~~~~~
3897
3898 In your HTML form, add a hidden form element like so::
3899
3900 <input type="hidden" name="@action" value="myaction">
3901
3902 where "myaction" is the name you registered in the previous step.
3903
3904 Actions may return content to the user
3905 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3906
3907 Actions generally perform some database manipulation and then pass control
3908 on to the rendering of a template in the current context (see `Determining
3909 web context`_ for how that works.) Some actions will want to generate the
3910 actual content returned to the user. Action methods may return their own
3911 content string to be displayed to the user, overriding the templating step.
3912 In this situation, we assume that the content is HTML by default. You may
3913 override the content type indicated to the user by calling ``setHeader``::
3914
3915 self.client.setHeader('Content-Type', 'text/csv')
3916
3917 This example indicates that the value sent back to the user is actually
3918 comma-separated value content (eg. something to be loaded into a
3919 spreadsheet or database).
3920
3921
3922 8-bit character set support in Web interface
3923 --------------------------------------------
3924
3925 The web interface uses UTF-8 default. It may be overridden in both forms
3926 and a browser cookie.
3927
3928 - In forms, use the ``@charset`` variable.
3929 - To use the cookie override, have the ``roundup_charset`` cookie set.
3930
3931 In both cases, the value is a valid charset name (eg. ``utf-8`` or
3932 ``kio8-r``).
3933
3934 Inside Roundup, all strings are stored and processed in utf-8.
3935 Unfortunately, some older browsers do not work properly with
3936 utf-8-encoded pages (e.g. Netscape Navigator 4 displays wrong
3937 characters in form fields). This version allows one to change
3938 the character set for http transfers. To do so, you may add
3939 the following code to your ``page.html`` template::
3940
3941 <tal:block define="uri string:${request/base}${request/env/PATH_INFO}">
3942 <a tal:attributes="href python:request.indexargs_url(uri,
3943 {'@charset':'utf-8'})">utf-8</a>
3944 <a tal:attributes="href python:request.indexargs_url(uri,
3945 {'@charset':'koi8-r'})">koi8-r</a>
3946 </tal:block>
3947
3948 (substitute ``koi8-r`` with appropriate charset for your language).
3949 Charset preference is kept in the browser cookie ``roundup_charset``.
3950
3951 ``meta http-equiv`` lines added to the tracker templates in version 0.6.0
3952 should be changed to include actual character set name::
3953
3954 <meta http-equiv="Content-Type"
3955 tal:attributes="content string:text/html;; charset=${request/client/charset}"
3956 />
3957
3958 The charset is also sent in the http header.
3959
3960
3961
3962
3963
3964 Debugging Trackers
3965 ==================
3966
3967 There are three switches in tracker configs that turn on debugging in
3968 Roundup:
3969
3970 1. web :: debug
3971 2. mail :: debug
3972 3. logging :: level
3973
3974 See the config.ini file or the `tracker configuration`_ section for
3975 more information.
3976
3977 Additionally, the ``roundup-server.py`` script has its own debugging mode
3978 in which it reloads edited templates immediately when they are changed,
3979 rather than requiring a web server restart.
3980
3981
3982 .. _`design documentation`: design.html
3983 .. _change the rate limiting method: rest.html#creating-custom-rate-limits
3984 .. _`directions in the rest interface documentation`: rest.html#enabling-the-rest-api
3985 .. _`xmlrpc interface documentation`: xmlrpc.html#through-roundup

Roundup Issue Tracker: http://roundup-tracker.org/