Mercurial > p > roundup > code
comparison doc/customizing.txt @ 1098:c5819344714c
more doc
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Tue, 10 Sep 2002 07:07:16 +0000 |
| parents | fa7df238e2d4 |
| children | f96deb4fb935 |
comparison
equal
deleted
inserted
replaced
| 1097:98f3d41f41d9 | 1098:c5819344714c |
|---|---|
| 1 =================== | 1 =================== |
| 2 Customising Roundup | 2 Customising Roundup |
| 3 =================== | 3 =================== |
| 4 | 4 |
| 5 :Version: $Revision: 1.25 $ | 5 :Version: $Revision: 1.26 $ |
| 6 | 6 |
| 7 .. This document borrows from the ZopeBook section on ZPT. The original is at: | 7 .. This document borrows from the ZopeBook section on ZPT. The original is at: |
| 8 http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx | 8 http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx |
| 9 | 9 |
| 10 .. contents:: | 10 .. contents:: |
| 11 | 11 |
| 12 | 12 |
| 13 What You Can Do | 13 What You Can Do |
| 14 --------------- | 14 =============== |
| 15 | 15 |
| 16 Customisation of Roundup can take one of five forms: | 16 Customisation of Roundup can take one of five forms: |
| 17 | 17 |
| 18 1. `tracker configuration`_ file changes | 18 1. `tracker configuration`_ file changes |
| 19 2. database, or `tracker schema`_ changes | 19 2. database, or `tracker schema`_ changes |
| 26 may be done at any time, before or after tracker initialisation. Yes, this | 26 may be done at any time, before or after tracker initialisation. Yes, this |
| 27 includes adding or removing properties from classes. | 27 includes adding or removing properties from classes. |
| 28 | 28 |
| 29 | 29 |
| 30 Trackers in a Nutshell | 30 Trackers in a Nutshell |
| 31 ---------------------- | 31 ====================== |
| 32 | 32 |
| 33 Trackers have the following structure: | 33 Trackers have the following structure: |
| 34 | 34 |
| 35 +-------------------+--------------------------------------------------------+ | 35 =================== ======================================================== |
| 36 |config.py |Holds the basic `tracker configuration`_ | | 36 Tracker File Description |
| 37 +-------------------+--------------------------------------------------------+ | 37 =================== ======================================================== |
| 38 |dbinit.py |Holds the `tracker schema`_ | | 38 config.py Holds the basic `tracker configuration`_ |
| 39 +-------------------+--------------------------------------------------------+ | 39 dbinit.py Holds the `tracker schema`_ |
| 40 |interfaces.py |Defines the Web and E-Mail interfaces for the tracker | | 40 interfaces.py Defines the Web and E-Mail interfaces for the tracker |
| 41 +-------------------+--------------------------------------------------------+ | 41 select_db.py Selects the database back-end for the tracker |
| 42 |select_db.py |Selects the database back-end for the tracker | | 42 db/ Holds the tracker's database |
| 43 +-------------------+--------------------------------------------------------+ | 43 db/files/ Holds the tracker's upload files and messages |
| 44 |db/ |Holds the tracker's database | | 44 detectors/ Auditors and reactors for this tracker |
| 45 +-------------------+--------------------------------------------------------+ | 45 html/ Web interface templates, images and style sheets |
| 46 |db/files/ |Holds the tracker's upload files and messages | | 46 =================== ======================================================== |
| 47 +-------------------+--------------------------------------------------------+ | |
| 48 |detectors/ |Auditors and reactors for this tracker | | |
| 49 +-------------------+--------------------------------------------------------+ | |
| 50 |html/ |Web interface templates, images and style sheets | | |
| 51 +-------------------+--------------------------------------------------------+ | |
| 52 | 47 |
| 53 Tracker Configuration | 48 Tracker Configuration |
| 54 --------------------- | 49 ===================== |
| 55 | 50 |
| 56 The config.py located in your tracker home contains the basic | 51 The config.py located in your tracker home contains the basic |
| 57 configuration for the web and e-mail components of roundup's interfaces. This | 52 configuration for the web and e-mail components of roundup's interfaces. This |
| 58 file is a Python module. The configuration variables available are: | 53 file is a Python module. The configuration variables available are: |
| 59 | 54 |
| 181 # Examples: | 176 # Examples: |
| 182 MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default | 177 MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default |
| 183 #MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out) | 178 #MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out) |
| 184 | 179 |
| 185 Tracker Schema | 180 Tracker Schema |
| 186 -------------- | 181 ============== |
| 187 | 182 |
| 188 Note: if you modify the schema, you'll most likely need to edit the | 183 Note: if you modify the schema, you'll most likely need to edit the |
| 189 `web interface`_ HTML template files and `detectors`_ to reflect | 184 `web interface`_ HTML template files and `detectors`_ to reflect |
| 190 your changes. | 185 your changes. |
| 191 | 186 |
| 239 issue.setkey('title') | 234 issue.setkey('title') |
| 240 | 235 |
| 241 XXX security definitions | 236 XXX security definitions |
| 242 | 237 |
| 243 Classes and Properties - creating a new information store | 238 Classes and Properties - creating a new information store |
| 244 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 239 --------------------------------------------------------- |
| 245 | 240 |
| 246 In the tracker above, we've defined 7 classes of information: | 241 In the tracker above, we've defined 7 classes of information: |
| 247 | 242 |
| 248 priority | 243 priority |
| 249 Defines the possible levels of urgency for issues. | 244 Defines the possible levels of urgency for issues. |
| 273 searching of issues by priority and status. By only requiring a link on the | 268 searching of issues by priority and status. By only requiring a link on the |
| 274 issue (which is stored as a single number) we reduce the chance that someone | 269 issue (which is stored as a single number) we reduce the chance that someone |
| 275 mis-types a priority or status - or simply makes a new one up. | 270 mis-types a priority or status - or simply makes a new one up. |
| 276 | 271 |
| 277 Class and Items | 272 Class and Items |
| 278 ::::::::::::::: | 273 ~~~~~~~~~~~~~~~ |
| 279 | 274 |
| 280 A Class defines a particular class (or type) of data that will be stored in the | 275 A Class defines a particular class (or type) of data that will be stored in the |
| 281 database. A class comprises one or more properties, which given the information | 276 database. A class comprises one or more properties, which given the information |
| 282 about the class items. | 277 about the class items. |
| 283 The actual data entered into the database, using class.create() are called | 278 The actual data entered into the database, using class.create() are called |
| 284 items. They have a special immutable property called id. We sometimes refer to | 279 items. They have a special immutable property called id. We sometimes refer to |
| 285 this as the itemid. | 280 this as the itemid. |
| 286 | 281 |
| 287 Properties | 282 Properties |
| 288 :::::::::: | 283 ~~~~~~~~~~ |
| 289 | 284 |
| 290 A Class is comprised of one or more properties of the following types: | 285 A Class is comprised of one or more properties of the following types: |
| 291 | 286 |
| 292 * String properties are for storing arbitrary-length strings. | 287 * String properties are for storing arbitrary-length strings. |
| 293 * Password properties are for storing encoded arbitrary-length strings. The | 288 * Password properties are for storing encoded arbitrary-length strings. The |
| 301 of the chosen item. | 296 of the chosen item. |
| 302 * A Multilink property refers to possibly many items in a specified class. | 297 * A Multilink property refers to possibly many items in a specified class. |
| 303 The value is a list of integers. | 298 The value is a list of integers. |
| 304 | 299 |
| 305 FileClass | 300 FileClass |
| 306 ::::::::: | 301 ~~~~~~~~~ |
| 307 | 302 |
| 308 FileClasses save their "content" attribute off in a separate file from the rest | 303 FileClasses save their "content" attribute off in a separate file from the rest |
| 309 of the database. This reduces the number of large entries in the database, | 304 of the database. This reduces the number of large entries in the database, |
| 310 which generally makes databases more efficient, and also allows us to use | 305 which generally makes databases more efficient, and also allows us to use |
| 311 command-line tools to operate on the files. They are stored in the files sub- | 306 command-line tools to operate on the files. They are stored in the files sub- |
| 312 directory of the db directory in your tracker. | 307 directory of the db directory in your tracker. |
| 313 | 308 |
| 314 IssueClass | 309 IssueClass |
| 315 :::::::::: | 310 ~~~~~~~~~~ |
| 316 | 311 |
| 317 IssueClasses automatically include the "messages", "files", "nosy", and | 312 IssueClasses automatically include the "messages", "files", "nosy", and |
| 318 "superseder" properties. | 313 "superseder" properties. |
| 319 The messages and files properties list the links to the messages and files | 314 The messages and files properties list the links to the messages and files |
| 320 related to the issue. The nosy property is a list of links to users who wish to | 315 related to the issue. The nosy property is a list of links to users who wish to |
| 329 was last edited (equivalently, these are the dates on the first and last | 324 was last edited (equivalently, these are the dates on the first and last |
| 330 records in the item's journal). The "creator" property holds a link to the user | 325 records in the item's journal). The "creator" property holds a link to the user |
| 331 that created the issue. | 326 that created the issue. |
| 332 | 327 |
| 333 setkey(property) | 328 setkey(property) |
| 334 :::::::::::::::: | 329 ~~~~~~~~~~~~~~~~ |
| 335 | 330 |
| 336 Select a String property of the class to be the key property. The key property | 331 Select a String property of the class to be the key property. The key property |
| 337 muse be unique, and allows references to the items in the class by the content | 332 muse be unique, and allows references to the items in the class by the content |
| 338 of the key property. That is, we can refer to users by their username, e.g. | 333 of the key property. That is, we can refer to users by their username, e.g. |
| 339 let's say that there's an issue in roundup, issue 23. There's also a user, | 334 let's say that there's an issue in roundup, issue 23. There's also a user, |
| 347 roundup-admin set issue assignedto=richard | 342 roundup-admin set issue assignedto=richard |
| 348 | 343 |
| 349 Note, the same thing can be done in the web and e-mail interfaces. | 344 Note, the same thing can be done in the web and e-mail interfaces. |
| 350 | 345 |
| 351 create(information) | 346 create(information) |
| 352 ::::::::::::::::::: | 347 ~~~~~~~~~~~~~~~~~~~ |
| 353 | 348 |
| 354 Create an item in the database. This is generally used to create items in the | 349 Create an item in the database. This is generally used to create items in the |
| 355 "definitional" classes like "priority" and "status". | 350 "definitional" classes like "priority" and "status". |
| 356 | 351 |
| 357 | 352 |
| 358 Examples of adding to your schema | 353 Examples of adding to your schema |
| 359 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 354 --------------------------------- |
| 360 | 355 |
| 361 TODO | 356 TODO |
| 362 | 357 |
| 363 | 358 |
| 364 Detectors - adding behaviour to your tracker | 359 Detectors - adding behaviour to your tracker |
| 365 -------------------------------------------- | 360 ============================================ |
| 366 .. _detectors: | 361 .. _detectors: |
| 367 | 362 |
| 368 The detectors in your tracker fire before (*auditors*) and after (*reactors*) | 363 The detectors in your tracker fire before (*auditors*) and after (*reactors*) |
| 369 changes to the contents of your database. They are Python modules that sit in | 364 changes to the contents of your database. They are Python modules that sit in |
| 370 your tracker's ``detectors`` directory. You will have some installed by | 365 your tracker's ``detectors`` directory. You will have some installed by |
| 397 | 392 |
| 398 XXX give the example here. | 393 XXX give the example here. |
| 399 | 394 |
| 400 | 395 |
| 401 Database Content | 396 Database Content |
| 402 ---------------- | 397 ================ |
| 403 | 398 |
| 404 Note: if you modify the content of definitional classes, you'll most likely | 399 Note: if you modify the content of definitional classes, you'll most likely |
| 405 need to edit the tracker `detectors`_ to reflect your changes. | 400 need to edit the tracker `detectors`_ to reflect your changes. |
| 406 | 401 |
| 407 Customisation of the special "definitional" classes (eg. status, priority, | 402 Customisation of the special "definitional" classes (eg. status, priority, |
| 420 | 415 |
| 421 XXX example | 416 XXX example |
| 422 | 417 |
| 423 | 418 |
| 424 Web Interface | 419 Web Interface |
| 425 ------------- | 420 ============= |
| 426 | 421 |
| 427 The web is provided by the roundup.cgi.client module and is used by | 422 The web is provided by the roundup.cgi.client module and is used by |
| 428 roundup.cgi, roundup-server and ZRoundup. | 423 roundup.cgi, roundup-server and ZRoundup. |
| 429 In all cases, we determine which tracker is being accessed | 424 In all cases, we determine which tracker is being accessed |
| 430 (the first part of the URL path inside the scope of the CGI handler) and pass | 425 (the first part of the URL path inside the scope of the CGI handler) and pass |
| 432 from roundup.cgi.client - which handles the rest of | 427 from roundup.cgi.client - which handles the rest of |
| 433 the access through its main() method. This means that you can do pretty much | 428 the access through its main() method. This means that you can do pretty much |
| 434 anything you want as a web interface to your tracker. | 429 anything you want as a web interface to your tracker. |
| 435 | 430 |
| 436 Repurcussions of changing the tracker schema | 431 Repurcussions of changing the tracker schema |
| 437 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 432 --------------------------------------------- |
| 438 | 433 |
| 439 If you choose to change the `tracker schema`_ you will need to ensure the web | 434 If you choose to change the `tracker schema`_ you will need to ensure the web |
| 440 interface knows about it: | 435 interface knows about it: |
| 441 | 436 |
| 442 1. Index, item and search pages for the relevant classes may need to have | 437 1. Index, item and search pages for the relevant classes may need to have |
| 443 properties added or removed, | 438 properties added or removed, |
| 444 2. The "page" template may require links to be changed, as might the "home" | 439 2. The "page" template may require links to be changed, as might the "home" |
| 445 page's content arguments. | 440 page's content arguments. |
| 446 | 441 |
| 447 How requests are processed | 442 How requests are processed |
| 448 ~~~~~~~~~~~~~~~~~~~~~~~~~~ | 443 -------------------------- |
| 449 | 444 |
| 450 The basic processing of a web request proceeds as follows: | 445 The basic processing of a web request proceeds as follows: |
| 451 | 446 |
| 452 1. figure out who we are, defaulting to the "anonymous" user | 447 1. figure out who we are, defaulting to the "anonymous" user |
| 453 2. figure out what the request is for - we call this the "context" | 448 2. figure out what the request is for - we call this the "context" |
| 467 granted for the action to take place | 462 granted for the action to take place |
| 468 - NotFound (raised wherever it needs to be) | 463 - NotFound (raised wherever it needs to be) |
| 469 this exception percolates up to the CGI interface that called the client | 464 this exception percolates up to the CGI interface that called the client |
| 470 | 465 |
| 471 Determining web context | 466 Determining web context |
| 472 ~~~~~~~~~~~~~~~~~~~~~~~ | 467 ----------------------- |
| 473 | 468 |
| 474 To determine the "context" of a request, we look at the URL and the special | 469 To determine the "context" of a request, we look at the URL and the special |
| 475 request variable ``:template``. The URL path after the tracker identifier | 470 request variable ``:template``. The URL path after the tracker identifier |
| 476 is examined. Typical URL paths look like: | 471 is examined. Typical URL paths look like: |
| 477 | 472 |
| 514 - only classname suplied: "index" | 509 - only classname suplied: "index" |
| 515 - full item designator supplied: "item" | 510 - full item designator supplied: "item" |
| 516 | 511 |
| 517 | 512 |
| 518 Performing actions in web requests | 513 Performing actions in web requests |
| 519 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 514 ---------------------------------- |
| 520 | 515 |
| 521 When a user requests a web page, they may optionally also request for an | 516 When a user requests a web page, they may optionally also request for an |
| 522 action to take place. As described in `how requests are processed`_, the | 517 action to take place. As described in `how requests are processed`_, the |
| 523 action is performed before the requested page is generated. Actions are | 518 action is performed before the requested page is generated. Actions are |
| 524 triggered by using a ``:action`` CGI variable, where the value is one of: | 519 triggered by using a ``:action`` CGI variable, where the value is one of: |
| 568 | 563 |
| 569 Each of the actions is implemented by a corresponding *actionAction* (where | 564 Each of the actions is implemented by a corresponding *actionAction* (where |
| 570 "action" is the name of the action) method on | 565 "action" is the name of the action) method on |
| 571 the roundup.cgi.Client class, which also happens to be in your tracker as | 566 the roundup.cgi.Client class, which also happens to be in your tracker as |
| 572 interfaces.Client. So if you need to define new actions, you may add them | 567 interfaces.Client. So if you need to define new actions, you may add them |
| 573 there (see `definining new web actions`_). | 568 there (see `defining new web actions`_). |
| 574 | 569 |
| 575 Each action also has a corresponding *actionPermission* (where | 570 Each action also has a corresponding *actionPermission* (where |
| 576 "action" is the name of the action) method which determines | 571 "action" is the name of the action) method which determines |
| 577 whether the action is permissible given the current user. The base permission | 572 whether the action is permissible given the current user. The base permission |
| 578 checks are: | 573 checks are: |
| 603 Determine whether the user has permission to search this class. | 598 Determine whether the user has permission to search this class. |
| 604 Base behaviour is to check the user can view this class. | 599 Base behaviour is to check the user can view this class. |
| 605 | 600 |
| 606 | 601 |
| 607 Default templates | 602 Default templates |
| 608 ~~~~~~~~~~~~~~~~~ | 603 ----------------- |
| 609 | 604 |
| 610 Most customisation of the web view can be done by modifying the templates in | 605 Most customisation of the web view can be done by modifying the templates in |
| 611 the tracker **html** directory. There are several types of files in there: | 606 the tracker **html** directory. There are several types of files in there: |
| 612 | 607 |
| 613 page | 608 page |
| 632 a special page just for the user class that renders the registration page | 627 a special page just for the user class that renders the registration page |
| 633 style.css | 628 style.css |
| 634 a static file that is served up as-is | 629 a static file that is served up as-is |
| 635 | 630 |
| 636 Overall Look - "page" template | 631 Overall Look - "page" template |
| 637 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 632 ------------------------------ |
| 638 | 633 |
| 639 XXX | 634 XXX |
| 640 | 635 |
| 641 How the templates work | 636 How the templates work |
| 642 ~~~~~~~~~~~~~~~~~~~~~~ | 637 ---------------------- |
| 643 | 638 |
| 644 Roundup's templates consist of special attributes on your template tags. These | 639 Roundup's templates consist of special attributes on your template tags. These |
| 645 attributes form the Template Attribute Language, or TAL. The commands are: | 640 attributes form the Template Attribute Language, or TAL. The commands are: |
| 646 | 641 |
| 647 | 642 |
| 759 variables are available, so ``python:item.status.checklist()`` would be | 754 variables are available, so ``python:item.status.checklist()`` would be |
| 760 equivalent to ``item/status/checklist``, assuming that ``checklist`` is | 755 equivalent to ``item/status/checklist``, assuming that ``checklist`` is |
| 761 a method. | 756 a method. |
| 762 | 757 |
| 763 Information available to templates | 758 Information available to templates |
| 764 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 759 ---------------------------------- |
| 765 | 760 |
| 766 The following variables are available to templates. | 761 The following variables are available to templates. |
| 767 | 762 |
| 768 .. taken from roundup.cgi.templating.RoundupPageTemplate docstring | 763 .. taken from roundup.cgi.templating.RoundupPageTemplate docstring |
| 769 | 764 |
| 789 XXX a special variable | 784 XXX a special variable |
| 790 *default* | 785 *default* |
| 791 XXX a special variable | 786 XXX a special variable |
| 792 | 787 |
| 793 The context variable | 788 The context variable |
| 794 :::::::::::::::::::: | 789 ~~~~~~~~~~~~~~~~~~~~ |
| 795 | 790 |
| 796 The *context* variable is one of three things based on the current context | 791 The *context* variable is one of three things based on the current context |
| 797 (see `determining web context`_ for how we figure this out): | 792 (see `determining web context`_ for how we figure this out): |
| 798 | 793 |
| 799 1. if we're looking at a "home" page, then it's None | 794 1. if we're looking at a "home" page, then it's None |
| 810 a real, or true value in the third. Thus we can determine whether we're | 805 a real, or true value in the third. Thus we can determine whether we're |
| 811 looking at a real item from the hyperdb by testing "context/id". | 806 looking at a real item from the hyperdb by testing "context/id". |
| 812 | 807 |
| 813 | 808 |
| 814 The request variable | 809 The request variable |
| 815 :::::::::::::::::::: | 810 ~~~~~~~~~~~~~~~~~~~~ |
| 816 | 811 |
| 817 The request variable is packed with information about the current request. | 812 The request variable is packed with information about the current request. |
| 818 | 813 |
| 819 .. taken from roundup.cgi.templating.HTMLRequest docstring | 814 .. taken from roundup.cgi.templating.HTMLRequest docstring |
| 820 | 815 |
| 836 =========== ================================================================ | 831 =========== ================================================================ |
| 837 Variable Holds | 832 Variable Holds |
| 838 =========== ================================================================ | 833 =========== ================================================================ |
| 839 columns dictionary of the columns to display in an index page | 834 columns dictionary of the columns to display in an index page |
| 840 show a convenience access to columns - request/show/colname will | 835 show a convenience access to columns - request/show/colname will |
| 841 be true if the columns should be displayed, false otherwise | 836 be true if the columns should be displayed, false otherwise |
| 842 sort index sort column (direction, column name) | 837 sort index sort column (direction, column name) |
| 843 group index grouping property (direction, column name) | 838 group index grouping property (direction, column name) |
| 844 filter properties to filter the index on | 839 filter properties to filter the index on |
| 845 filterspec values to filter the index on | 840 filterspec values to filter the index on |
| 846 search_text text to perform a full-text search on for an index | 841 search_text text to perform a full-text search on for an index |
| 847 =========== ================================================================ | 842 =========== ================================================================ |
| 848 | 843 |
| 849 | 844 |
| 850 Displaying Properties | 845 Displaying Properties |
| 851 ~~~~~~~~~~~~~~~~~~~~~ | 846 --------------------- |
| 852 | 847 |
| 853 Properties appear in the user interface in three contexts: in indices, in | 848 Properties appear in the user interface in three contexts: in indices, in |
| 854 editors, and as search arguments. | 849 editors, and as search arguments. |
| 855 For each type of property, there are several display possibilities. | 850 For each type of property, there are several display possibilities. |
| 856 For example, in an index view, a string property may just be | 851 For example, in an index view, a string property may just be |
| 857 printed as a plain string, but in an editor view, that property may be | 852 printed as a plain string, but in an editor view, that property may be |
| 858 displayed in an editable field. | 853 displayed in an editable field. |
| 859 | 854 |
| 860 | 855 |
| 861 Index Views | 856 Index Views |
| 862 ~~~~~~~~~~~ | 857 ----------- |
| 863 | 858 |
| 864 This is one of the class context views. It is also the default view for | 859 This is one of the class context views. It is also the default view for |
| 865 classes. The template used is "*classname*.index". | 860 classes. The template used is "*classname*.index". |
| 866 | 861 |
| 867 Index View Specifiers | 862 Index View Specifiers |
| 868 ::::::::::::::::::::: | 863 ~~~~~~~~~~~~~~~~~~~~~ |
| 869 | 864 |
| 870 An index view specifier (URL fragment) looks like this (whitespace has been | 865 An index view specifier (URL fragment) looks like this (whitespace has been |
| 871 added for clarity):: | 866 added for clarity):: |
| 872 | 867 |
| 873 /issue?status=unread,in-progress,resolved& | 868 /issue?status=unread,in-progress,resolved& |
| 874 topic=security,ui& | 869 topic=security,ui& |
| 875 :group=+priority& | 870 :group=+priority& |
| 876 :sort=-activity& | 871 :sort==activity& |
| 877 :filters=status,topic& | 872 :filters=status,topic& |
| 878 :columns=title,status,fixer | 873 :columns=title,status,fixer |
| 879 | 874 |
| 880 The index view is determined by two parts of the specifier: the layout part and | 875 The index view is determined by two parts of the specifier: the layout part and |
| 881 the filter part. The layout part consists of the query parameters that begin | 876 the filter part. The layout part consists of the query parameters that begin |
| 897 by activity, arranged in descending order. The filter section shows filters for | 892 by activity, arranged in descending order. The filter section shows filters for |
| 898 the "status" and "topic" properties, and the table includes columns for the | 893 the "status" and "topic" properties, and the table includes columns for the |
| 899 "title", "status", and "fixer" properties. | 894 "title", "status", and "fixer" properties. |
| 900 | 895 |
| 901 Filtering of indexes | 896 Filtering of indexes |
| 902 :::::::::::::::::::: | 897 ~~~~~~~~~~~~~~~~~~~~ |
| 903 | 898 |
| 904 TODO | 899 TODO |
| 905 | 900 |
| 906 Searching Views | 901 Searching Views |
| 907 ~~~~~~~~~~~~~~~ | 902 --------------- |
| 908 | 903 |
| 909 This is one of the class context views. The template used is typically | 904 This is one of the class context views. The template used is typically |
| 910 "*classname*.search". | 905 "*classname*.search". |
| 911 | 906 |
| 912 TODO | 907 TODO |
| 913 | 908 |
| 914 Item Views | 909 Item Views |
| 915 ~~~~~~~~~~ | 910 ---------- |
| 916 | 911 |
| 917 The basic view of a hyperdb item is provided by the "*classname*.item" | 912 The basic view of a hyperdb item is provided by the "*classname*.item" |
| 918 template. It generally has three sections; an "editor", a "spool" and a | 913 template. It generally has three sections; an "editor", a "spool" and a |
| 919 "history" section. | 914 "history" section. |
| 920 | 915 |
| 921 | 916 |
| 922 | 917 |
| 923 Editor Section | 918 Editor Section |
| 924 :::::::::::::: | 919 ~~~~~~~~~~~~~~ |
| 925 | 920 |
| 926 The editor section is used to manipulate the item - it may be a | 921 The editor section is used to manipulate the item - it may be a |
| 927 static display if the user doesn't have permission to edit the item. | 922 static display if the user doesn't have permission to edit the item. |
| 928 | 923 |
| 929 Here's an example of a basic editor template (this is the default "classic" | 924 Here's an example of a basic editor template (this is the default "classic" |
| 992 describing the changed properties. As shown in the example, the editor | 987 describing the changed properties. As shown in the example, the editor |
| 993 template can use the ":note" and ":file" fields, which are added to the | 988 template can use the ":note" and ":file" fields, which are added to the |
| 994 standard change note message generated by Roundup. | 989 standard change note message generated by Roundup. |
| 995 | 990 |
| 996 Spool Section | 991 Spool Section |
| 997 ::::::::::::: | 992 ~~~~~~~~~~~~~ |
| 998 | 993 |
| 999 The spool section lists related information like the messages and files of | 994 The spool section lists related information like the messages and files of |
| 1000 an issue. | 995 an issue. |
| 1001 | 996 |
| 1002 TODO | 997 TODO |
| 1003 | 998 |
| 1004 | 999 |
| 1005 History Section | 1000 History Section |
| 1006 ::::::::::::::: | 1001 ~~~~~~~~~~~~~~~ |
| 1007 | 1002 |
| 1008 The final section displayed is the history of the item - its database journal. | 1003 The final section displayed is the history of the item - its database journal. |
| 1009 This is generally generated with the template:: | 1004 This is generally generated with the template:: |
| 1010 | 1005 |
| 1011 <tal:block tal:replace="structure context/history" /> | 1006 <tal:block tal:replace="structure context/history" /> |
| 1019 a journal entry | 1014 a journal entry |
| 1020 </tal:block> | 1015 </tal:block> |
| 1021 | 1016 |
| 1022 *where each journal entry is an HTMLJournalEntry.* | 1017 *where each journal entry is an HTMLJournalEntry.* |
| 1023 | 1018 |
| 1019 Defining new web actions | |
| 1020 ------------------------ | |
| 1021 | |
| 1022 XXX | |
| 1023 | |
| 1024 | 1024 |
| 1025 Access Controls | 1025 Access Controls |
| 1026 --------------- | 1026 =============== |
| 1027 | 1027 |
| 1028 A set of Permissions are built in to the security module by default: | 1028 A set of Permissions are built in to the security module by default: |
| 1029 | 1029 |
| 1030 - Edit (everything) | 1030 - Edit (everything) |
| 1031 - View (everything) | 1031 - View (everything) |
| 1065 | 1065 |
| 1066 You may use the ``roundup-admin`` "``security``" command to display the | 1066 You may use the ``roundup-admin`` "``security``" command to display the |
| 1067 current Role and Permission configuration in your tracker. | 1067 current Role and Permission configuration in your tracker. |
| 1068 | 1068 |
| 1069 Adding a new Permission | 1069 Adding a new Permission |
| 1070 ~~~~~~~~~~~~~~~~~~~~~~~ | 1070 ----------------------- |
| 1071 | 1071 |
| 1072 When adding a new Permission, you will need to: | 1072 When adding a new Permission, you will need to: |
| 1073 | 1073 |
| 1074 1. add it to your tracker's dbinit so it is created | 1074 1. add it to your tracker's dbinit so it is created |
| 1075 2. enable it for the Roles that should have it (verify with | 1075 2. enable it for the Roles that should have it (verify with |
| 1078 4. add it to the appropriate xxxPermission methods on in your tracker | 1078 4. add it to the appropriate xxxPermission methods on in your tracker |
| 1079 interfaces module | 1079 interfaces module |
| 1080 | 1080 |
| 1081 | 1081 |
| 1082 | 1082 |
| 1083 ----------------- | 1083 An example of adding a new field to a roundup schema |
| 1084 ==================================================== | |
| 1085 | |
| 1086 Introduction | |
| 1087 ------------ | |
| 1088 | |
| 1089 To make the classic schema of roundup useful as a todo tracking system | |
| 1090 for a group of systems administrators, it needed an extra data field | |
| 1091 per issue: a category. | |
| 1092 | |
| 1093 This would let sysads quickly list all todos in their particular | |
| 1094 area of interest without having to do complex queries, and without | |
| 1095 relying on the spelling capabilities of other sysads (a losing | |
| 1096 proposition at best). | |
| 1097 | |
| 1098 Adding a field to the database | |
| 1099 ------------------------------ | |
| 1100 | |
| 1101 This is the easiest part of the change. The category would just be a plain | |
| 1102 string, nothing fancy. To change what is in the database you need to add | |
| 1103 some lines to the ``open()`` function in ``dbinit.py``:: | |
| 1104 | |
| 1105 category = Class(db, "category", name=String()) | |
| 1106 category.setkey("name") | |
| 1107 | |
| 1108 Here we are setting up a chunk of the database which we are calling | |
| 1109 "category". It contains a string, which we are refering to as "name" for | |
| 1110 lack of a more imaginative title. Then we are setting the key of this chunk | |
| 1111 of the database to be that "name". This is equivalent to an index for | |
| 1112 database types. This also means that there can only be one category with a | |
| 1113 given name. | |
| 1114 | |
| 1115 Adding the above lines allows us to create categories, but they're not tied | |
| 1116 to the issues that we are going to be creating. It's just a list of categories | |
| 1117 off on its own, which isn't much use. We need to link it in with the issues. | |
| 1118 To do that, find the lines in the ``open()`` function in ``dbinit.py`` which | |
| 1119 set up the "issue" class, and then add a link to the category:: | |
| 1120 | |
| 1121 issue = IssueClass(db, "issue", ... , category=Multilink("category"), ... ) | |
| 1122 | |
| 1123 The Multilink() means that each issue can have many categories. If you were | |
| 1124 adding something with a more one to one relationship use Link() instead. | |
| 1125 | |
| 1126 That is all you need to do to change the schema. The rest of the effort is | |
| 1127 fiddling around so you can actually use the new category. | |
| 1128 | |
| 1129 Setting up security on the new objects | |
| 1130 -------------------------------------- | |
| 1131 | |
| 1132 By default only the admin user can look at and change objects. This doesn't | |
| 1133 suit us, as we want any user to be able to create new categories as | |
| 1134 required, and obviously everyone needs to be able to view the categories of | |
| 1135 issues for it to be useful. | |
| 1136 | |
| 1137 We therefore need to change the security of the category objects. This is | |
| 1138 also done in the ``open()`` function of ``dbinit.py``. | |
| 1139 | |
| 1140 There are currently two loops which set up permissions and then assign them | |
| 1141 to various roles. Simply add the new "category" to both lists:: | |
| 1142 | |
| 1143 # new permissions for this schema | |
| 1144 for cl in 'issue', 'file', 'msg', 'user', 'category': | |
| 1145 db.security.addPermission(name="Edit", klass=cl, | |
| 1146 description="User is allowed to edit "+cl) | |
| 1147 db.security.addPermission(name="View", klass=cl, | |
| 1148 description="User is allowed to access "+cl) | |
| 1149 | |
| 1150 # Assign the access and edit permissions for issue, file and message | |
| 1151 # to regular users now | |
| 1152 for cl in 'issue', 'file', 'msg', 'category': | |
| 1153 p = db.security.getPermission('View', cl) | |
| 1154 db.security.addPermissionToRole('User', p) | |
| 1155 p = db.security.getPermission('Edit', cl) | |
| 1156 db.security.addPermissionToRole('User', p) | |
| 1157 | |
| 1158 So you are in effect doing the following:: | |
| 1159 | |
| 1160 db.security.addPermission(name="Edit", klass='category', | |
| 1161 description="User is allowed to edit "+'category') | |
| 1162 db.security.addPermission(name="View", klass='category', | |
| 1163 description="User is allowed to access "+'category') | |
| 1164 | |
| 1165 which is creating two permission types; that of editing and viewing | |
| 1166 "category" objects respectively. Then the following lines assign those new | |
| 1167 permissions to the "User" role, so that normal users can view and edit | |
| 1168 "category" objects:: | |
| 1169 | |
| 1170 p = db.security.getPermission('View', 'category') | |
| 1171 db.security.addPermissionToRole('User', p) | |
| 1172 | |
| 1173 p = db.security.getPermission('Edit', 'category') | |
| 1174 db.security.addPermissionToRole('User', p) | |
| 1175 | |
| 1176 This is all the work that needs to be done for the database. It will store | |
| 1177 categories, and let users view and edit them. Now on to the interface | |
| 1178 stuff. | |
| 1179 | |
| 1180 Changing the web left hand frame | |
| 1181 -------------------------------- | |
| 1182 | |
| 1183 We need to give the users the ability to create new categories, and the | |
| 1184 place to put the link to this functionality is in the left hand function | |
| 1185 bar, under the "Issues" area. The file that defines how this area looks is | |
| 1186 ``html/page``, which is what we are going to be editing next. | |
| 1187 | |
| 1188 If you look at this file you can see that it contains a lot of "classblock" | |
| 1189 sections which are chunks of HTML that will be included or excluded in the | |
| 1190 output depending on whether the condition in the classblock is met. Under | |
| 1191 the end of the classblock for issue is where we are going to add the | |
| 1192 category code:: | |
| 1193 | |
| 1194 <p class="classblock" | |
| 1195 tal:condition="python:request.user.hasPermission('View', 'category')"> | |
| 1196 <b>Categories</b><br> | |
| 1197 <a tal:condition="python:request.user.hasPermission('Edit', 'category')" | |
| 1198 href="category?:template=item">New Category<br></a> | |
| 1199 </p> | |
| 1200 | |
| 1201 The first two lines is the classblock definition, which sets up a condition | |
| 1202 that only users who have "View" permission to the "category" object will | |
| 1203 have this section included in their output. Next comes a plain "Categories" | |
| 1204 header in bold. Everyone who can view categories will get that. | |
| 1205 | |
| 1206 Next comes the link to the editing area of categories. This link will only | |
| 1207 appear if the condition is matched: that condition being that the user has | |
| 1208 "Edit" permissions for the "category" objects. If they do have permission | |
| 1209 then they will get a link to another page which will let the user add new | |
| 1210 categories. | |
| 1211 | |
| 1212 Note that if you have permission to view but not edit categories then all | |
| 1213 you will see is a "Categories" header with nothing underneath it. This is | |
| 1214 obviously not very good interface design, but will do for now. I just claim | |
| 1215 that it is so I can add more links in this section later on. However to fix | |
| 1216 the problem you could change the condition in the classblock statement, so | |
| 1217 that only users with "Edit" permission would see the "Categories" stuff. | |
| 1218 | |
| 1219 Setting up a page to edit categories | |
| 1220 ------------------------------------ | |
| 1221 | |
| 1222 We defined code in the previous section which let users with the | |
| 1223 appropriate permissions see a link to a page which would let them edit | |
| 1224 conditions. Now we have to write that page. | |
| 1225 | |
| 1226 The link was for the item template for the category object. This translates | |
| 1227 into the system looking for a file called ``category.item`` in the ``html`` | |
| 1228 tracker directory. This is the file that we are going to write now. | |
| 1229 | |
| 1230 First we add an id tag in a comment which doesn't affect the outcome | |
| 1231 of the code at all but is essential for managing the changes to this | |
| 1232 file. It is useful for debugging however, if you load a page in a | |
| 1233 browser and look at the page source, you can see which sections come | |
| 1234 from which files by looking for these comments:: | |
| 1235 | |
| 1236 <!-- dollarId: category.item,v 1.3 2002/05/22 00:32:34 me Exp dollar--> | |
| 1237 | |
| 1238 Next we need to setup up a standard HTML form, which is the whole | |
| 1239 purpose of this file. We link to some handy javascript which sends the form | |
| 1240 through only once. This is to stop users hitting the send button | |
| 1241 multiple times when they are impatient and thus having the form sent | |
| 1242 multiple times:: | |
| 1243 | |
| 1244 <form method="POST" onSubmit="return submit_once()" | |
| 1245 enctype="multipart/form-data"> | |
| 1246 | |
| 1247 Next we define some code which sets up the minimum list of fields that we | |
| 1248 require the user to enter. There will be only one field, that of "name", so | |
| 1249 they user better put something in it otherwise the whole form is pointless:: | |
| 1250 | |
| 1251 <input type="hidden" name=":required" value="name"> | |
| 1252 | |
| 1253 To get everything to line up properly we will put everything in a table, | |
| 1254 and put a nice big header on it so the user has an idea what is happening:: | |
| 1255 | |
| 1256 <table class="form"> | |
| 1257 <tr class="strong-header"><td colspan=2>Category</td></tr> | |
| 1258 | |
| 1259 Next we need the actual field that the user is going to enter the new | |
| 1260 category. The "context.name.field(size=60)" bit tells roundup to generate a | |
| 1261 normal HTML field of size 60, and the contents of that field will be the | |
| 1262 "name" variable of the current context (which is "category"). The upshot of | |
| 1263 this is that when the user types something in to the form, a new category | |
| 1264 will be created with that name:: | |
| 1265 | |
| 1266 <tr> | |
| 1267 <td nowrap>Name</td> | |
| 1268 <td tal:content="structure python:context.name.field(size=60)">name</td> | |
| 1269 </tr> | |
| 1270 | |
| 1271 Finally a submit button so that the user can submit the new category:: | |
| 1272 | |
| 1273 <tr> | |
| 1274 <td> </td> | |
| 1275 <td colspan=3 tal:content="structure context/submit"> | |
| 1276 submit button will go here | |
| 1277 </td> | |
| 1278 </tr> | |
| 1279 | |
| 1280 So putting it all together, and closing the table and form we get:: | |
| 1281 | |
| 1282 <!-- dollarId: category.item,v 1.3 2002/05/22 00:32:34 richard Exp dollar--> | |
| 1283 | |
| 1284 <form method="POST" onSubmit="return submit_once()" | |
| 1285 enctype="multipart/form-data"> | |
| 1286 | |
| 1287 <input type="hidden" name=":required" value="name"> | |
| 1288 | |
| 1289 <table class="form"> | |
| 1290 <tr class="strong-header"><td colspan=2>Category</td></tr> | |
| 1291 | |
| 1292 <tr> | |
| 1293 <td nowrap>Name</td> | |
| 1294 <td tal:content="structure python:context.name.field(size=60)">name</td> | |
| 1295 </tr> | |
| 1296 | |
| 1297 <tr> | |
| 1298 <td> </td> | |
| 1299 <td colspan=3 tal:content="structure context/submit"> | |
| 1300 submit button will go here | |
| 1301 </td> | |
| 1302 </tr> | |
| 1303 </table> | |
| 1304 </form> | |
| 1305 | |
| 1306 This is quite a lot to just ask the user one simple question, but | |
| 1307 there is a lot of setup for basically one line (the form line) to do | |
| 1308 its work. To add another field to "category" would involve one more line | |
| 1309 (well maybe a few extra to get the formatting correct). | |
| 1310 | |
| 1311 Adding the category to the issue | |
| 1312 -------------------------------- | |
| 1313 | |
| 1314 We now have the ability to create issues to our hearts content, but | |
| 1315 that is pointless unless we can assign categories to issues. Just like | |
| 1316 the ``html/category.item`` file was used to define how to add a new | |
| 1317 category, the ``html/issue.item`` is used to define how a new issue is | |
| 1318 created. | |
| 1319 | |
| 1320 Just like ``category.issue`` this file defines a form which has a table to lay | |
| 1321 things out. It doesn't matter where in the table we add new stuff, | |
| 1322 it is entirely up to your sense of aesthetics:: | |
| 1323 | |
| 1324 <th nowrap>Category</th> | |
| 1325 <td><span tal:replace="structure context/category/field" /> | |
| 1326 <span tal:replace="structure python:db.category.classhelp('name', | |
| 1327 label='list', width=500)" /> | |
| 1328 </td> | |
| 1329 | |
| 1330 First we define a nice header so that the user knows what the next section | |
| 1331 is, then the middle line does what we are most interested in. This | |
| 1332 ``context/category/field`` gets replaced with a field which contains the | |
| 1333 category in the current context (the current context being the new issue). | |
| 1334 | |
| 1335 The classhelp lines generate a link (labelled "list") to a popup window | |
| 1336 which contains the list of currently known categories. | |
| 1337 | |
| 1338 Searching on categories | |
| 1339 ----------------------- | |
| 1340 | |
| 1341 We can add categories, and create issues with categories. The next obvious | |
| 1342 thing that we would like to be would be to search issues based on their | |
| 1343 category, so that any one working on the web server could look at all | |
| 1344 issues in the category "Web" for example. | |
| 1345 | |
| 1346 If you look in the html/page file and look for the "Search Issues" you will | |
| 1347 see that it looks something like ``<a href="issue?:template=search">Search | |
| 1348 Issues</a>`` which shows us that when you click on "Search Issues" it will | |
| 1349 be looking for a ``issue.search`` file to display. So that is indeed the file | |
| 1350 that we are going to change. | |
| 1351 | |
| 1352 If you look at this file it should be starting to seem familiar. It is a | |
| 1353 simple HTML form using a table to define structure. You can add the new | |
| 1354 category search code anywhere you like within that form:: | |
| 1355 | |
| 1356 <tr> | |
| 1357 <th>Category:</th> | |
| 1358 <td> | |
| 1359 <select name="category"> | |
| 1360 <option value="">don't care</option> | |
| 1361 <option value="">------------</option> | |
| 1362 <option tal:repeat="s db/category/list" tal:attributes="value s/name" | |
| 1363 tal:content="s/name">category to filter on</option> | |
| 1364 </select> | |
| 1365 </td> | |
| 1366 <td><input type="checkbox" name=":columns" value="category" checked></td> | |
| 1367 <td><input type="radio" name=":sort" value="category"></td> | |
| 1368 <td><input type="radio" name=":group" value="category"></td> | |
| 1369 </tr> | |
| 1370 | |
| 1371 Most of this is straightforward to anyone who knows HTML. It is just | |
| 1372 setting up a select list followed by a checkbox and a couple of radio | |
| 1373 buttons. | |
| 1374 | |
| 1375 The ``tal:repeat`` part repeats the tag for every item in the "category" | |
| 1376 table and setting "s" to be each category in turn. | |
| 1377 | |
| 1378 The ``tal:attributes`` part is setting up the ``value=`` part of the option tag | |
| 1379 to be the name part of "s" which is the current category in the loop. | |
| 1380 | |
| 1381 The ``tal:content`` part is setting the contents of the option tag to be the | |
| 1382 name part of "s" again. For objects more complex than category, obviously | |
| 1383 you would put an id in the value, and the descriptive part in the content; | |
| 1384 but for category they are the same. | |
| 1385 | |
| 1386 Adding category to the default view | |
| 1387 ----------------------------------- | |
| 1388 | |
| 1389 We can now add categories, add issues with categories, and search issues | |
| 1390 based on categories. This is everything that we need to do, however there | |
| 1391 is some more icing that we would like. I think the category of an issue is | |
| 1392 important enough that it should be displayed by default when listing all | |
| 1393 the issues. | |
| 1394 | |
| 1395 Unfortunately, this is a bit less obvious than the previous steps. The code | |
| 1396 defining how the issues look is in ``html/issue.index``. This is a large table | |
| 1397 with a form down the bottom for redisplaying and so forth. | |
| 1398 | |
| 1399 Firstly we need to add an appropriate header to the start of the table:: | |
| 1400 | |
| 1401 <th tal:condition="request/show/category">Category</th> | |
| 1402 | |
| 1403 The condition part of this statement is so that if the user has selected | |
| 1404 not to see the Category column then they won't. | |
| 1405 | |
| 1406 The rest of the table is a loop which will go through every issue that | |
| 1407 matches the display criteria. The loop variable is "i" - which means that | |
| 1408 every issue gets assigned to "i" in turn. | |
| 1409 | |
| 1410 The new part of code to display the category will look like this:: | |
| 1411 | |
| 1412 <td tal:condition="request/show/category" tal:content="i/category"></td> | |
| 1413 | |
| 1414 The condition is the same as above: only display the condition when the | |
| 1415 user hasn't asked for it to be hidden. The next part is to set the content | |
| 1416 of the cell to be the category part of "i" - the current issue. | |
| 1417 | |
| 1418 Finally we have to edit ``html/page`` again. This time to tell it that when the | |
| 1419 user clicks on "Unnasigned Issues" or "All Issues" that the category should | |
| 1420 be displayed. If you scroll down the page file, you can see the links with | |
| 1421 lots of options. The option that we are interested in is the ``:columns=`` one | |
| 1422 which tells roundup which fields of the issue to display. Simply add | |
| 1423 "category" to that list and it all should work. | |
| 1424 | |
| 1425 | |
| 1426 ------------------- | |
| 1084 | 1427 |
| 1085 Back to `Table of Contents`_ | 1428 Back to `Table of Contents`_ |
| 1086 | 1429 |
| 1087 .. _`Table of Contents`: index.html | 1430 .. _`Table of Contents`: index.html |
| 1088 | 1431 |
