-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathreference.txt
More file actions
4452 lines (3392 loc) · 167 KB
/
reference.txt
File metadata and controls
4452 lines (3392 loc) · 167 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.. meta::
:description:
Reference for the internals of Roundup. Includes background
information for cookbook and how-to examples.
Reference for the design and internals needed to understand
and extend the examples to meet new needs.
:tocdepth: 2
=================
Roundup Reference
=================
.. admonition:: Welcome
This document was part of the `customisation document`_. The
customisation document was getting large and unwieldy. The
combination of examples and internal information that made finding
information difficult. Questions raised on the mailing list were
well answered in the customisation document, but finding the info
was difficult.
The documentation is slowly being reorganized using the `Diataxis
framework`_. Help with the reorganization is welcome.
This document provides background for the tutorials
or how-tos in the customisation document.
.. _customisation document: customizing.html
.. _diataxis framework: https://diataxis.fr/
.. This document borrows from the ZopeBook section on ZPT. The original was at:
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
.. contents::
:depth: 2
:local:
Trackers in a Nutshell
======================
Trackers have the following structure:
.. index::
single: tracker; structure db directory
single: tracker; structure detectors directory
single: tracker; structure extensions directory
single: tracker; structure html directory
single: tracker; structure html directory
single: tracker; structure lib directory
.. table::
:class: valign-top
=================== ========================================================
Tracker File Description
=================== ========================================================
config.ini Holds the basic `tracker configuration`_
schema.py Holds the `tracker schema`_
initial_data.py Loads initial data into the tracker (status,
priority ...) when initializing the tracker (optional)
interfaces.py Allows `modifying the core of Roundup`_ (optional)
db/ Holds the tracker's database
db/files/ Holds the tracker's uploaded files and message
db/backend_name Names the database back-end for the tracker (obsolete).
Use the ``backend`` setting in the ``[rdbms]``
section of ``config.ini`` instead.
detectors/ `Auditors and reactors`_ for this tracker
extensions/ Additional `actions`_ and `templating utilities`_
html/ Web interface templates, images and style sheets
lib/ optional common imports for detectors and extensions
=================== ========================================================
.. index:: config.ini
.. index:: configuration; see config.ini
Tracker Configuration
=====================
The ``config.ini`` located in your tracker home contains the basic
configuration for the web and e-mail components of Roundup's interfaces.
The `tracker schema`_ defines the data captured by your tracker. It
also defines the permissions used when accessing the data: see the
`security / access controls`_ section. For example, you must grant the
"Anonymous" Role the "Email Access" Permission to allow users to
automatically register through the email interface,.
.. index::
single: config.ini; sections
see: configuration; config.ini
The following is taken from the `Python Library Reference`__ (July 18, 2018)
section "ConfigParser -- Configuration file parser":
The configuration file consists of sections, led by a [section] header
and followed by name: value entries, with continuations in the style
of RFC 822 (see section 3.1.1, “LONG HEADER FIELDS”); name=value is
also accepted. Note that leading whitespace is removed from
values. The optional values can contain format strings which refer to
other values in the same section, or values in a special DEFAULT
section. Additional defaults can be provided on initialization and
retrieval. Lines beginning with '#' or ';' are ignored and may be
used to provide comments.
For example::
[My Section]
foodir = %(dir)s/whatever
dir = frob
would resolve the "%(dir)s" to the value of "dir" ("frob" in this case)
resulting in "foodir" being "frob/whatever".
The reference above discusses using the ``[DEFAULT]`` section and
interpolation. For example::
[DEFAULT]
local_admin_email = admin@example.com
[main]
admin_email = %(local_admin_email)s
will set the admin_email setting. This works when running the
tracker. When upgrading Roundup using ``updateconfig`` to create
a new ``config.ini``, the ``DEFAULT`` section is not preserved
and interpolation tokens (e.g. ``%(local_admin_email)s`` are
replaced with their values (``admin@example.com``). This may be
fixed in a future release of Roundup.
Note that you can not reference settings in the ``DEFAULT``
section from Roundup. They are only useful when interpolated into
a defined setting.
__ https://docs.python.org/2/library/configparser.html
A default ``config.ini`` file broken into sections is shown below.
.. include:: tracker_config.txt
Additional notes:
The ``[rdbms]`` service defines the Connection Service for your
PostgreSQL connection when using a system-wide pg_service.conf or
~/.pg_service.conf as discussed in
https://www.postgresql.org/docs/current/libpq-pgservice.html.
Setting this to the name of the service allows different trackers to
connect to different services when running multiple trackers under one
Roundup server. If you are only running one tracker, you can set the
PGSERVICE environment variable. Note that other settings specified in
this file (rdbms: user, password, port, host, (db)name) will override
the corresponding connection service setting.
.. index:: single: roundup-admin; config.ini update
single: roundup-admin; config.ini create
single: config.ini; create
single: config.ini; update
You may generate a new default config file using the ``roundup-admin
genconfig`` command. You can generate a new config file merging in
existing settings using the ``roundup-admin updateconfig`` command.
Configuration variables may be referred to in lower or upper case. In code,
refer to variables not in the "main" section using their section and
name, so "domain" in the section "mail" becomes MAIL_DOMAIN.
.. index:: pair: configuration; extensions
pair: configuration; detectors
Extending the configuration file
--------------------------------
You can't add new variables to the config.ini file in the tracker home but
you can add two new config.ini files:
- a config.ini in the ``extensions`` directory is loaded and attached
to the config variable as "ext".
- a config.ini in the ``detectors`` directory is loaded and attached
to the config variable as "detectors".
These configuration files support the same operations as the main
``config.ini`` file. This included a ``DEFAULT`` section and
interpolation. Note that you can not reference settings in the
``DEFAULT`` section from Roundup. They can only be used for
interpolation.
For example, the following in ``detectors/config.ini``::
[main]
qa_recipients = email@example.com
is accessible as::
db.config.detectors['QA_RECIPIENTS']
Note that the name grouping applied to the main configuration file is
applied to the extension config files, so if you instead have::
[qa]
recipients = email@example.com
then the above ``db.config.detectors['QA_RECIPIENTS']`` will still work.
Unlike values in the tracker's main ``config.ini``, the values defined
in these config files are not validated. For example: a setting that
is supposed to be an integer value (e.g. 4) could be the word
"foo". If you are writing Python code that uses these settings, you
should expect to handle invalid values.
Also, incorrect values are discovered when the config setting is
used not set. This can be long after the tracker is
started and the error may not be seen in the logs.
It's possible to validate these settings. Validation involves calling
the ``update_options`` method on the configuration option. This can be
done by the ``init()`` function in the Python files implementing
extensions_ or detectors_.
As an example, adding the following to an extension::
from roundup.configuration import SecretMandatoryOption
def init(instance):
instance.config.ext.update_option('RECAPTCHA_SECRET',
SecretMandatoryOption,description="Secret securing reCaptcha.")
similarly for a detector::
from roundup.configuration import MailAddressOption
def init(db):
try:
db.config.detectors.update_option('QA_RECIPIENTS',
MailAddressOption,
description="Email used for QA comment followup.")
except KeyError:
# COMMENT_EMAIL setting is not found, but it's optional
# so continue
pass
will allow reading the secret from a file or append the tracker domain
to an email address if it does not have a domain.
Running ``roundup-admin -i tracker_home display user1`` will validate
the settings for both config.ini`s. Otherwise detector options are not
validated until the first request to the web interface (or email
gateway).
``update_option`` takes 4 arguments:
1. config setting name - string (positional, mandatory)
2. option type - Option derived class from configuration.py
(positional, mandatory)
3. default value - string (optional, named default)
4. description - string (optional, named description)
The first argument is the config setting name as described at the
beginning of this section.
The second argument is a class in the roundup.configuration module.
There are many of these classes: BooleanOption,
IntegerNumberOption, RegExpOption.... Please see the configuration
module for all Option validators and their descriptions. You can also
define your own custom validator in `interfaces.py`_.
The third and fourth arguments are optional strings. They are printed
when there is an error and may help the user correct the problem.
.. index:: ! schema
Tracker Schema
==============
.. note::
if you modify the schema, you'll most likely need to edit the
`web interface`_ HTML template files and `detectors`_ to reflect
your changes.
A tracker schema defines what data is stored in the tracker's database.
Schemas are defined using Python code in the ``schema.py`` module of your
tracker.
.. index:: schema; allowed changes
What you can/can't do to the schema
-----------------------------------
Your schema may be changed at any time before or after the tracker has been
initialised (or used). You may:
**Add new properties to classes, or add whole new classes**
This is painless and easy to do - there are generally no repercussions
from adding new information to a tracker's schema.
**Remove properties**
Removing properties is a little more tricky - you need to make sure that
the property is no longer used in the `web interface`_ *or* by the
detectors_.
You must never:
**Remove the user class**
This class is the only *required* class in Roundup.
**Remove the "username", "address", "password", "roles" or "realname" user properties**
Various parts of Roundup require these properties. Don't remove them.
**Change the type of a property**
Property types must *never* [1]_ be changed - the database simply
doesn't take this kind of action into account. Note that you can't
just remove a property and re-add it as a new type either. If you
wanted to make the assignedto property a Multilink, you'd need to
create a new property assignedto_list and remove the old assignedto
property.
.. [1] If you shut down the tracker, `export the database`_, modify the
exported csv property data to be compatible with the new type,
change the property type in the schema, and finally import the
changed exported data, you can change the property type. This is
not trivial nor for the faint of heart. But it can be done.
.. _export the database: admin_guide.html#using-roundup-admin
The ``schema.py`` and ``initial_data.py`` modules
-------------------------------------------------
The schema.py module is used to define what your tracker looks like
on the inside, the schema of the tracker. It defines the Classes
and properties on each class. It also defines the security for
those Classes. The next few sections describe how schemas work
and what you can do with them.
The initial_data.py module sets up the initial state of your
tracker. It’s called exactly once - by the ``roundup-admin initialise``
command. See the start of the section on `database content`_ for more
info about how this works.
.. index:: schema; classic - description of
The "classic" schema
--------------------
The "classic" schema looks like this (see section `setkey(property)`_
below for the meaning of ``'setkey'`` -- you may also want to look into
the sections `setlabelprop(property)`_ and `setorderprop(property)`_ for
specifying (default) labelling and ordering of classes.)::
pri = Class(db, "priority", name=String(), order=String())
pri.setkey("name")
stat = Class(db, "status", name=String(), order=String())
stat.setkey("name")
keyword = Class(db, "keyword", name=String())
keyword.setkey("name")
user = Class(db, "user", username=String(), organisation=String(),
password=String(), address=String(), realname=String(),
phone=String(), alternate_addresses=String(),
queries=Multilink('query'), roles=String(), timezone=String())
user.setkey("username")
msg = FileClass(db, "msg", author=Link("user"), summary=String(),
date=Date(), recipients=Multilink("user"),
files=Multilink("file"), messageid=String(), inreplyto=String())
file = FileClass(db, "file", name=String())
issue = IssueClass(db, "issue", keyword=Multilink("keyword"),
status=Link("status"), assignedto=Link("user"),
priority=Link("priority"))
issue.setkey('title')
.. index:: schema; classes and properties
Classes and Properties - creating a new information store
---------------------------------------------------------
The tracker above, defines 7 classes of information:
priority
Defines the possible levels of urgency for issues.
status
Defines the possible states of processing the issue may be in.
keyword
Initially empty, will hold keywords useful for searching issues.
user
Initially holding the "admin" user, will eventually have an entry
for all users using Roundup.
msg
Initially empty, will hold all e-mail messages sent to or
generated by Roundup.
file
Initially empty, will hold all files attached to issues.
issue
Initially empty, this is where the issue information is stored.
It defines the "priority" and "status" classes to allow two things:
1. reduction in the amount of information stored on the issue
2. more powerful, accurate searching of issues by priority and status
By requiring a link on the issue (stored as a single
number) the chance that someone mis-types a priority or
status - or makes a new one up is reduced.
Class names access items of that class in the `REST api`_
interface. The classic tracker was created before the REST interface
was added. It uses the single form (i.e. issue and user not issues and
users) for its classes. Most REST documentation suggests using plural
forms. However, to make your API consistent, use singular forms for
classes that you add.
.. index::
schema; classes
schema; items
Class and Items
~~~~~~~~~~~~~~~
A Class defines a particular class (or type) of data that will be stored
in the database. A class comprises one or more properties, which gives
the information about the class items.
The actual data entered into the database, using ``class.create()``, are
called items. They have a special immutable property called ``'id'``. We
sometimes refer to this as the *itemid*.
.. index:: schema; property types
Properties
~~~~~~~~~~
A Class consists of one or more properties of the following types:
Boolean
properties store on/off, yes/no, true/false values.
Date
properties store date-and-time stamps. Their values are Timestamp
objects.
Integer
properties store integer values. (Number can store real/float values.)
Interval
properties store time periods rather than absolute dates. For
example 2 hours.
Link
properties refers to a single item selected from a
specified class. The class is part of the property; the value is an
integer, the id of the chosen item.
Multilink
properties refer to one or more items in a specified
class. The value is a list of integers.
Number
properties store numeric values. There is an option to use
double-precision floating point numbers.
Password
properties store encoded arbitrary-length strings.
The default encoding is defined in the ``roundup.password.Password``
class.
String
properties store arbitrary-length strings.
Properties have attributes to change the default behaviour:
.. index:: triple: schema; property attributes; required
triple: schema; property attributes; default_value
triple: schema; property attributes; quiet
* All properties support the following attributes:
- ``required``: see `design documentation`_. Adds the property to
the list returned by calling get_required_props for the class.
- ``default_value``: see `design documentation`_ Sets the default
value if the property is not set.
- ``quiet``: see `design documentation`_. Suppresses reporting user
visible changes to this property. The property change is not reported:
- in the change feedback/confirmation message in the web
interface
- the property change section of the nosy email
- the web history at the bottom of an item's page
This is useful when storing the state of the user interface (e.g. the
names of elements that are collapsed or hidden from the
user). Properties updates as an indirect result of
a user's change (e.g. updating a blockers property, counting
number of times an issue is reopened or reassigned etc.) should
not be displayed to the user as they can be confusing.
.. index:: triple: schema; property attributes; indexme
* String properties have an ``indexme`` attribute. The default is
'no'. Setting it to 'yes' includes the property in the full text
index.
.. index:: triple: schema; property attributes; use_double
* Number properties can have a ``use_double`` attribute that, when set
to ``True``, will use double precision floating point in the database.
* Link and Multilink properties can have several attributes:
.. index:: triple: schema; property attributes; do_journal
- ``do_journal``: By default, every change of a link property is
recorded in the item being linked to (or being unlinked). A typical
use-case for setting ``do_journal='no'`` would be to turn off
journalling of nosy list, message author and message recipient link
and unlink events to prevent the journal from clogged with these
events.
.. index:: triple: schema; property attributes; try_id_parsing
- ``try_id_parsing`` is turned on by default. If a number is entered
into a Link or Multilink field, Roundup interprets this number as
an ID of the item to link to. Sometimes items can have numeric
names (e.g., product codes). For these Roundup needs to
match the numeric name and should never match an ID. In this case
you can set ``try_id_parsing='no'``.
.. index:: triple: schema; property attributes; rev_multilink
- The ``rev_multilink`` option takes a property name to be inserted
into the linked-to class. This property is a Multilink property that
links back to the current class. The new Multilink is read-only (it
is automatically modified if the Link or Multilink property defining
it is modified). The new property can be used in normal searches
using the "filter" method of the Class. This means it can be used
like other Multilink properties when searching (in an index
template) or via the REST and XMLRPC APIs.
As a example, suppose you want to group multiple issues into a
super issue. Each issue can be part of one super issue. It is
inefficient to find all of the issues linked to the super issue by
searching through all issues in the system looking at the part_of
link property. To make this more efficient, you can declare an
issue's part_of property as::
issue = IssueClass(db, "issue",
...
part_of = Link("issue", rev_multilink="components"),
... )
This automatically creates the ``components`` multilink on the issue
class. The ``components`` multilink is never explicitly declared in
the issue class, but it has the same effect as though you had
declared the class as::
issue = IssueClass(db, "issue",
...
part_of = Link("issue"),
components = Multilink("issue"),
... )
Then wrote a detector to update the components property on the
corresponding issue. Writing this detector can be tricky. There is
one other difference, you can not explicitly set/modify the
``components`` multilink.
The effect of setting ``part_of = 3456`` on issue1234
automatically adds "1234" to the ``components`` property on
issue3456. You can search the ``components`` multilink just like a
regular multilink, but you can't explicitly assign to it.
Another difference of reverse multilinks to normal multilinks
is that when a linked node is retired, the node vanishes from the
multilink. In the example above, when issue 1234 with ``part_of``
set to issue 5678 is retired, 1234 vanishes from the
``components`` multilink of issue 5678.
You can also link between different classes. If the issue
definition includes::
issue = IssueClass(db, "issue",
...
assigned_to = Link("user", rev_multilink="responsibleFor"),
... )
This makes it easy to list all issues that the user is responsible
for (aka assigned_to).
.. index:: triple: schema; property attributes; msg_header_property
- The ``msg_header_property`` is used by the mail gateway when sending
out messages. When a link or multilink property of an issue changes,
Roundup creates email headers of the form::
X-Roundup-issue-prop: value
where ``value`` is the ``name`` property for the linked item(s).
For example, if you have a multilink for attached_files in your
issue, you will see a header::
X-Roundup-issue-attached_files: MySpecialFile.doc, HisResume.txt
when the class for attached files is defined as::
file = FileClass(db, "file", name=String())
``MySpecialFile.doc`` is the name for the file object.
If you have an ``assigned_to`` property in your issue class that
links to the user class and you want to add a header::
X-Roundup-issue-assigned_to: ...
so that the mail recipients can filter emails where
``X-Roundup-issue-assigned_to: name`` contains their
username. The user class is defined as::
user = Class(db, "user",
username=String(),
password=Password(),
address=String(),
realname=String(),
phone=String(),
organisation=String(),
alternate_addresses=String(),
queries=Multilink('query'),
roles=String(), # comma-separated string of Role names
timezone=String())
Because the user class does not have a ``name`` parameter, no
header will be written. Setting::
assigned_to=Link("user", msg_header_property="username")
will make the mail gateway generate an ``X-Roundup-issue-assigned_to``
using the username property of the linked user.
Assume assigned_to for an issue is linked to the user with
username=joe_user, setting::
msg_header_property="username"
for the assigned_to property will generated message headers of the
form::
X-Roundup-issue-assigned_to: joe_user
for emails sent on issues where joe_user has been assigned to the issue.
If this property is set to the empty string "", no header will be
generated on outgoing mail.
.. index:: triple: schema; class property; creator
triple: schema; class property; creation
triple: schema; class property; actor
triple: schema; class property; activity
All Classes automatically have four properties by default:
*creator*
Link to the user that created the item.
*creation*
Date the item was created.
*actor*
Link to the user that last modified the item.
*activity*
Date the item was last modified.
.. index:: double: schema; class methods
Methods
~~~~~~~
All classes have the following methods.
.. index:: triple: schema; class method; setkey
setkey(property)
::::::::::::::::
.. index:: roundup-admin; setting assignedto on an issue
Set the key property of a class to a string property. The key property
must be unique. References to the items in the class can be done by
the content of the key property. For example, you can refer to users by
their username. Let's say that there's an issue in
Roundup, issue 23. There's also a user, richard, who happens to be
user 2. To assign an issue to him, we could do use::
roundup-admin set issue23 assignedto=2
or::
roundup-admin set issue23 assignedto=richard
Note, the same thing can be done in the web and e-mail interfaces.
.. index:: triple: schema; class method; setlabelprop
setlabelprop(property)
::::::::::::::::::::::
Select a property of the class to be the label property. The label
property is used whenever an item should be uniquely identified, e.g.,
when displaying a link to an item. If setlabelprop is not specified for
a class, the following values are tried for the label:
* the key of the class (see the `setkey(property)`_ section above)
* the "name" property
* the "title" property
* the first property from the sorted property name list
In most cases you can get away without specifying setlabelprop
explicitly.
Users should have View access to this property or the id property for
a class. If the property can not be viewed by a user, looping over
items in the class (e.g. messages attached to an issue) will not work.
.. index:: triple: schema; class method; setorderprop
setorderprop(property)
::::::::::::::::::::::
Select a property of the class to be the order property. The order
property is used whenever using a default sort order for the class,
e.g., when grouping or sorting class A by a link to class B in the user
interface, the order property of class B is used for sorting. If
setorderprop is not specified for a class, the following values are tried
for the order property:
* the property named "order"
* the label property (see `setlabelprop(property)`_ above)
Usually you can get away without specifying setorderprop
explicitly.
.. index:: triple: schema; class method; create
create(information)
:::::::::::::::::::
Create an item in the database. This is used to create items
in the :term:`definitional class` like "priority" and "status".
.. index:: triple: schema; class property; messages
triple: schema; class property; files
triple: schema; class property; nosy
triple: schema; class property; superseder
IssueClass
~~~~~~~~~~
IssueClass automatically includes the "messages", "files", "nosy", and
"superseder" properties.
The messages and files properties list the links to the messages and
files related to the issue. The nosy property is a list of links to
users to tell about changes to the issue. They get "CC'ed"
e-mails when messages are sent to or generated by the issue. The nosy
reactor (in the ``'detectors'`` directory) handles this action. The
superseder link indicates an issue which has superseded this one.
It is better described in the
`original document <original_overview.html#roundupdb>`_.
They also have the default "creation", "activity" and "creator"
properties.
The value of the "creation" property is the date when an item was
created. The value of the "activity" property is the date when any
property on the item was last edited (equivalently, these are the dates
on the first and last records in the item's journal). The "creator"
property holds a link to the user that created the issue.
.. index:: triple: schema; class property; content
triple: schema; class property; type
FileClass
~~~~~~~~~
FileClasses save their "content" attribute off in a separate file from
the rest of the database. This reduces the number of large entries in
the database, which makes databases more efficient. Also web servers,
image processing applications, and command line tools can operate on
the files. The content is stored in the ``files`` sub-directory of the
``'db'`` directory in your tracker. FileClasses also have a "type"
attribute to store the file's MIME type.
Roundup, by default, considers the contents of the file
immutable. This assists in maintaining an accurate record of
correspondence. The distributed tracker templates do not enforce
this. If you have access to the Roundup tracker directory, you can
edit the files (make sure to preserve mode, owner and group) to remove
information. You may need to do this if somebody includes a password
or you need to redact proprietary information. The journal for the
message/file won't report that the file has changed.
Best practice is to remove offending material and leave a
placeholder. E.G. replace a password with the text::
[password has been deleted 2020-12-02 --myname]
If you need to delete an entire file, replace the file contents with::
[file contents deleted due to spam 2020-10-21 --myname]
rather than deleting the file. If you actually delete the file Roundup
will report an error to the user and email the administrator. If you
empty the file, a user downloading the file using the direct URL
(e.g. ``tracker/msg22``) may be confused and think something is broken
when they receive an empty file. Retiring a file/msg does not prevent
access to the file using the direct URL. Retiring an item only removes
it when requesting a list of all items in the class. If you are
replacing the contents, you probably want to change the content type
of the file. E.G. from ``image/jpeg`` to ``text/plain``. You can do
this easily through the web interface, or using the ``roundup-admin``
command line interface.
You can also change the contents of a file or message using the REST
interface. Note that this will NOT result in an entry in the journal,
so again it allows a silent change. To do this you need to make two
rest requests. An example using curl is::
$ curl -u demo:demo -s
-H "X-requested-with: rest" \
-H "Referer: https://tracker.example.com/demo/" \
-X GET \
https://tracker.example.com/demo/rest/data/file/30/content
{
"data": {
"id": "30",
"type": "<class 'str'>",
"link": "https://tracker.example.com/demo/rest/data/file/30/content",
"data": "hello3",
"@etag": "\"3f2f8063dbce5b6bd43567e6f4f3c671\""
}
}
using the etag, overwrite the content with::
$ curl -u demo:demo -s
-H "X-requested-with: rest" \
-H "Referer: https://tracker.example.com/demo/" \
-H 'If-Match: "3f2f8063dbce5b6bd43567e6f4f3c671"' \
-X PUT \
-F "data=@hello" \
https://tracker.example.com/demo/rest/data/file/30/content
where ``hello`` is a file on local disk.
You can enforce immutability in your tracker by adding an auditor (see
detectors_) for the file/msg class that rejects changes to the content
property. The auditor could also add a journal entry so that a change
via the Roundup mechanism is reported. Using a mixin (see:
https://wiki.roundup-tracker.org/MixinClassFileClass) to augment the
file class allows for other possibilities including:
* signing the file,
* recording a checksum in the database and validating the file
contents at the time it gets read. This allows detection of changes
done on the filesystem outside of the Roundup mechanism.
* keeping multiple revisions of the file.
.. index:: schema; item ordering
A note about ordering
~~~~~~~~~~~~~~~~~~~~~
When we sort items in the hyperdb, we use one of three methods,
depending on the property type:
1. String, Integer, Number, Date or Interval property, sort the scalar
value of the property. Strings sort case-sensitively.
2. a Link property, sort by either the linked item's "order"
property (if it has one) or the linked item's "id".
3. Mulitlinks sort similar to #2, starting with the first Multilink
list item, and if they are the same, sort by the second item, and
so on.
Note that if an "order" property is defined for a class, all items of
that Class *must* have a value for the "order" property, or
sorting will result in random ordering.
Examples of adding to your schema
---------------------------------
See :ref:`CustomExamples` for examples.
The `Roundup wiki CategorySchema`_ provides a list of additional
examples of how to customize schemas to add new functionality.
.. _Roundup wiki CategorySchema:
https://wiki.roundup-tracker.org/CategorySchema
.. index:: !detectors
.. _detectors:
.. _Auditors and reactors:
Schema Integrity
----------------
There is a table in all SQL based schemas called ``schema``. It
contains a representation of the current schema and the current
Roundup schema version. Roundup will exit if the version is not supported
by the release. E.G. Roundup 2.1.0 will not work with a database
created by 2.3.0 as db version 8 used by 2.3.0 is not supported by
2.1.0.
The current schema representation is automatically updated whenever a
change is made to the schema via ``schema.py``. The schema version is
upgraded when running ``roundup-admin migrate`` although it can be
upgraded automatically in some cases by run a Roundup process (mailgw,
web interface). This information is kept in one large blob in the
table. To view this in a more understandable format, you can use the
commands below (requires the jq command):
Postgres
.. code::
psql -tq -d 'roundup_db' -U roundup_user -c \
'select schema from schema;' | \
python3 -c 'import json, sys; d = eval(sys.stdin.read()); \
print(json.dumps(d, indent=2));' | jq . | less
replace ``roundup_db``, ``roundup_user`` with the values from
``config.ini`` and use a ``~/.pgpass`` file or type the database
password when prompted.
SQLite
.. code::
sqlite3 demo/db/db 'select schema from schema;' | \
python3 -c 'import json, sys; d = eval(sys.stdin.read()); \
print(json.dumps(d, indent=2));' | jq . | less
Something similar for MySQL can be generated as well.
Replacing ``jq .`` with ``jq .version`` will display the schema
version.
Detectors - adding behaviour to your tracker
============================================
Detectors are Python modules that sit in your tracker's ``detectors``
directory. You are free to add and remove them any time, even after
the database is initialised via the ``roundup-admin initialise``
command.
There are two types of detectors:
1. *auditors* are run before changes are made to the database
2. *reactors* are run after the change has been committed to the database
.. index:: auditors; rules for use
single: reactors; rules for use
Auditor or Reactor?
-------------------
Generally speaking, you should observe the following rules:
**Auditors**
Are used for `vetoing creation of or changes to items`_. They might
also make automatic changes to item properties. They can raise the
``Reject`` or ``CheckId`` exceptions to control database changes.
**Reactors**
Detect changes in the database and react accordingly. They should avoid
making changes to the database where possible, as this could create
detector loops.
Detectors Installed by Default
------------------------------
You will have some detectors installed by default - have a look in the
``detectors`` subdirectory of your tracker home. You can write new
detectors or modify the existing ones. The existing detectors
installed for you are:
.. index:: detectors; installed
**nosyreaction.py**
This provides the automatic nosy list maintenance and email sending.
The nosy reactor (``nosyreaction``) fires when new messages are added
to issues. The nosy auditor (``updatenosy``) fires when issues are
changed, and figures out what changes need to be made to the nosy list
(such as adding new authors, etc.)
If you are running a tracker started with ``roundup-demo`` or the
``demo.py`` script, this detector will be missing.This is
intentional to prevent email from being sent from a demo
tracker. You can find the nosyreaction.py detector in the
:term:`template directory (meaning 3) <template>` and copy it into
your tracker if you want email to be sent.
**statusauditor.py**
This provides the ``chatty`` auditor which changes the issue status
from ``unread`` or ``closed`` to ``chatting`` if new messages appear.
It also provides the ``presetunread`` auditor which pre-sets the