comparison doc/reference.txt @ 7464:82bbb95e5690 issue2550923_computed_property

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

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