Mercurial > p > roundup > code
comparison doc/customizing.txt @ 1679:e2caeaa34ed4
fix timelog documentation, add new cgi special for variable doc
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Tue, 24 Jun 2003 00:46:21 +0000 |
| parents | 0807e3676133 |
| children | 86cc794cc3a0 |
comparison
equal
deleted
inserted
replaced
| 1678:2af054eafa24 | 1679:e2caeaa34ed4 |
|---|---|
| 1 =================== | 1 =================== |
| 2 Customising Roundup | 2 Customising Roundup |
| 3 =================== | 3 =================== |
| 4 | 4 |
| 5 :Version: $Revision: 1.89 $ | 5 :Version: $Revision: 1.90 $ |
| 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:: |
| 748 | 748 |
| 749 Determining web context | 749 Determining web context |
| 750 ----------------------- | 750 ----------------------- |
| 751 | 751 |
| 752 To determine the "context" of a request, we look at the URL and the | 752 To determine the "context" of a request, we look at the URL and the |
| 753 special request variable ``:template``. The URL path after the tracker | 753 special request variable ``@template``. The URL path after the tracker |
| 754 identifier is examined. Typical URL paths look like: | 754 identifier is examined. Typical URL paths look like: |
| 755 | 755 |
| 756 1. ``/tracker/issue`` | 756 1. ``/tracker/issue`` |
| 757 2. ``/tracker/issue1`` | 757 2. ``/tracker/issue1`` |
| 758 3. ``/tracker/_file/style.css`` | 758 3. ``/tracker/_file/style.css`` |
| 781 This raises a ``SendFile`` exception. | 781 This raises a ``SendFile`` exception. |
| 782 | 782 |
| 783 Both b. and e. stop before we bother to determine the template we're | 783 Both b. and e. stop before we bother to determine the template we're |
| 784 going to use. That's because they don't actually use templates. | 784 going to use. That's because they don't actually use templates. |
| 785 | 785 |
| 786 The template used is specified by the ``:template`` CGI variable, which | 786 The template used is specified by the ``@template`` CGI variable, which |
| 787 defaults to: | 787 defaults to: |
| 788 | 788 |
| 789 - only classname suplied: "index" | 789 - only classname suplied: "index" |
| 790 - full item designator supplied: "item" | 790 - full item designator supplied: "item" |
| 791 | 791 |
| 794 ---------------------------------- | 794 ---------------------------------- |
| 795 | 795 |
| 796 When a user requests a web page, they may optionally also request for an | 796 When a user requests a web page, they may optionally also request for an |
| 797 action to take place. As described in `how requests are processed`_, the | 797 action to take place. As described in `how requests are processed`_, the |
| 798 action is performed before the requested page is generated. Actions are | 798 action is performed before the requested page is generated. Actions are |
| 799 triggered by using a ``:action`` CGI variable, where the value is one | 799 triggered by using a ``@action`` CGI variable, where the value is one |
| 800 of: | 800 of: |
| 801 | 801 |
| 802 **login** | 802 **login** |
| 803 Attempt to log a user in. | 803 Attempt to log a user in. |
| 804 | 804 |
| 808 **register** | 808 **register** |
| 809 Attempt to create a new user based on the contents of the form and then | 809 Attempt to create a new user based on the contents of the form and then |
| 810 log them in. | 810 log them in. |
| 811 | 811 |
| 812 **edit** | 812 **edit** |
| 813 Perform an edit of an item in the database. There are some special form | 813 Perform an edit of an item in the database. There are some `special form |
| 814 elements you may use: | 814 variables`_ you may use. |
| 815 | |
| 816 :link=designator:property and :multilink=designator:property | |
| 817 The value specifies an item designator and the property on that item | |
| 818 to which *this* item should be added, as a link or multilink. | |
| 819 :note | |
| 820 Create a message and attach it to the current item's "messages" | |
| 821 property. | |
| 822 :file | |
| 823 Create a file and attach it to the current item's "files" property. | |
| 824 Attach the file to the message created from the ``:note`` if it's | |
| 825 supplied. | |
| 826 :required=property,property,... | |
| 827 The named properties are required to be filled in the form. | |
| 828 :remove:<propname>=id(s) | |
| 829 The ids will be removed from the multilink property. You may have | |
| 830 multiple ``:remove:<propname>`` form elements for a single <propname>. | |
| 831 :add:<propname>=id(s) | |
| 832 The ids will be added to the multilink property. You may have multiple | |
| 833 ``:add:<propname>`` form elements for a single <propname>. | |
| 834 | 815 |
| 835 **new** | 816 **new** |
| 836 Add a new item to the database. You may use the same special form | 817 Add a new item to the database. You may use the same `special form |
| 837 elements as in the "edit" action. | 818 variables`_ as in the "edit" action. |
| 838 | 819 |
| 839 **retire** | 820 **retire** |
| 840 Retire the item in the database. | 821 Retire the item in the database. |
| 841 | 822 |
| 842 **editCSV** | 823 **editCSV** |
| 889 Determine whether the user has permission to edit this class. Base | 870 Determine whether the user has permission to edit this class. Base |
| 890 behaviour is to check whether the user may edit this class. | 871 behaviour is to check whether the user may edit this class. |
| 891 **search** | 872 **search** |
| 892 Determine whether the user has permission to search this class. Base | 873 Determine whether the user has permission to search this class. Base |
| 893 behaviour is to check whether the user may view this class. | 874 behaviour is to check whether the user may view this class. |
| 875 | |
| 876 | |
| 877 Special form variables | |
| 878 ---------------------- | |
| 879 | |
| 880 Item properties and their values are edited with html FORM | |
| 881 variables and their values. You can: | |
| 882 | |
| 883 - Change the value of some property of the current item. | |
| 884 - Create a new item of any class, and edit the new item's | |
| 885 properties, | |
| 886 - Attach newly created items to a multilink property of the | |
| 887 current item. | |
| 888 - Remove items from a multilink property of the current item. | |
| 889 - Specify that some properties are required for the edit | |
| 890 operation to be successful. | |
| 891 | |
| 892 In the following, <bracketed> values are variable, "@" may be | |
| 893 either ":" or "@", and other text "required" is fixed. | |
| 894 | |
| 895 Most properties are specified as form variables: | |
| 896 | |
| 897 ``<propname>`` | |
| 898 property on the current context item | |
| 899 | |
| 900 ``<designator>"@"<propname>`` | |
| 901 property on the indicated item (for editing related information) | |
| 902 | |
| 903 Designators name a specific item of a class. | |
| 904 | |
| 905 ``<classname><N>`` | |
| 906 Name an existing item of class <classname>. | |
| 907 | |
| 908 ``<classname>"-"<N>`` | |
| 909 Name the <N>th new item of class <classname>. If the form | |
| 910 submission is successful, a new item of <classname> is | |
| 911 created. Within the submitted form, a particular | |
| 912 designator of this form always refers to the same new | |
| 913 item. | |
| 914 | |
| 915 Once we have determined the "propname", we look at it to see | |
| 916 if it's special: | |
| 917 | |
| 918 ``@required`` | |
| 919 The associated form value is a comma-separated list of | |
| 920 property names that must be specified when the form is | |
| 921 submitted for the edit operation to succeed. | |
| 922 | |
| 923 When the <designator> is missing, the properties are | |
| 924 for the current context item. When <designator> is | |
| 925 present, they are for the item specified by | |
| 926 <designator>. | |
| 927 | |
| 928 The "@required" specifier must come before any of the | |
| 929 properties it refers to are assigned in the form. | |
| 930 | |
| 931 ``@remove@<propname>=id(s)`` or ``@add@<propname>=id(s)`` | |
| 932 The "@add@" and "@remove@" edit actions apply only to | |
| 933 Multilink properties. The form value must be a | |
| 934 comma-separate list of keys for the class specified by | |
| 935 the simple form variable. The listed items are added | |
| 936 to (respectively, removed from) the specified | |
| 937 property. | |
| 938 | |
| 939 ``@link@<propname>=<designator>`` | |
| 940 If the edit action is "@link@", the simple form | |
| 941 variable must specify a Link or Multilink property. | |
| 942 The form value is a comma-separated list of | |
| 943 designators. The item corresponding to each | |
| 944 designator is linked to the property given by simple | |
| 945 form variable. | |
| 946 | |
| 947 None of the above (ie. just a simple form value) | |
| 948 The value of the form variable is converted | |
| 949 appropriately, depending on the type of the property. | |
| 950 | |
| 951 For a Link('klass') property, the form value is a | |
| 952 single key for 'klass', where the key field is | |
| 953 specified in dbinit.py. | |
| 954 | |
| 955 For a Multilink('klass') property, the form value is a | |
| 956 comma-separated list of keys for 'klass', where the | |
| 957 key field is specified in dbinit.py. | |
| 958 | |
| 959 Note that for simple-form-variables specifiying Link | |
| 960 and Multilink properties, the linked-to class must | |
| 961 have a key field. | |
| 962 | |
| 963 For a String() property specifying a filename, the | |
| 964 file named by the form value is uploaded. This means we | |
| 965 try to set additional properties "filename" and "type" (if | |
| 966 they are valid for the class). Otherwise, the property | |
| 967 is set to the form value. | |
| 968 | |
| 969 For Date(), Interval(), Boolean(), and Number() | |
| 970 properties, the form value is converted to the | |
| 971 appropriate | |
| 972 | |
| 973 Any of the form variables may be prefixed with a classname or | |
| 974 designator. | |
| 975 | |
| 976 Two special form values are supported for backwards compatibility: | |
| 977 | |
| 978 @note | |
| 979 This is equivalent to:: | |
| 980 | |
| 981 @link@messages=msg-1 | |
| 982 @msg-1@content=value | |
| 983 | |
| 984 except that in addition, the "author" and "date" properties of | |
| 985 "msg-1" are set to the userid of the submitter, and the current | |
| 986 time, respectively. | |
| 987 | |
| 988 @file | |
| 989 This is equivalent to:: | |
| 990 | |
| 991 @link@files=file-1 | |
| 992 @file-1@content=value | |
| 993 | |
| 994 The String content value is handled as described above for file | |
| 995 uploads. | |
| 996 | |
| 997 If both the "@note" and "@file" form variables are | |
| 998 specified, the action:: | |
| 999 | |
| 1000 @link@msg-1@files=file-1 | |
| 1001 | |
| 1002 is also performed. | |
| 1003 | |
| 1004 We also check that FileClass items have a "content" property with | |
| 1005 actual content, otherwise we remove them from all_props before | |
| 1006 returning. | |
| 1007 | |
| 894 | 1008 |
| 895 | 1009 |
| 896 Default templates | 1010 Default templates |
| 897 ----------------- | 1011 ----------------- |
| 898 | 1012 |
| 934 The *classic* template has a number of additional templates. | 1048 The *classic* template has a number of additional templates. |
| 935 | 1049 |
| 936 Note: Remember that you can create any template extension you want to, | 1050 Note: Remember that you can create any template extension you want to, |
| 937 so if you just want to play around with the templating for new issues, | 1051 so if you just want to play around with the templating for new issues, |
| 938 you can copy the current "issue.item" template to "issue.test", and then | 1052 you can copy the current "issue.item" template to "issue.test", and then |
| 939 access the test template using the ":template" URL argument:: | 1053 access the test template using the "@template" URL argument:: |
| 940 | 1054 |
| 941 http://your.tracker.example/tracker/issue?:template=test | 1055 http://your.tracker.example/tracker/issue?@template=test |
| 942 | 1056 |
| 943 and it won't affect your users using the "issue.item" template. | 1057 and it won't affect your users using the "issue.item" template. |
| 944 | 1058 |
| 945 | 1059 |
| 946 How the templates work | 1060 How the templates work |
| 1367 | 1481 |
| 1368 escape | 1482 escape |
| 1369 If true, escape the text so it is HTML safe (default: no). The | 1483 If true, escape the text so it is HTML safe (default: no). The |
| 1370 reason this defaults to off is that text is usually escaped | 1484 reason this defaults to off is that text is usually escaped |
| 1371 at a later stage by the TAL commands, unless the "structure" | 1485 at a later stage by the TAL commands, unless the "structure" |
| 1372 option is used in the template. The following are all | 1486 option is used in the template. The following ``tal:content`` |
| 1373 equivalent:: | 1487 expressions are all equivalent:: |
| 1374 | 1488 |
| 1375 <p tal:content="structure python:msg.content.plain(escape=1)" /> | 1489 "structure python:msg.content.plain(escape=1)" |
| 1376 <p tal:content="python:msg.content.plain()" /> | 1490 "python:msg.content.plain()" |
| 1377 <p tal:content="msg/content/plain" /> | 1491 "msg/content/plain" |
| 1378 <p tal:content="msg/content" /> | 1492 "msg/content" |
| 1379 | 1493 |
| 1380 Usually you'll only want to use the escape option in a | 1494 Usually you'll only want to use the escape option in a |
| 1381 complex expression. | 1495 complex expression. |
| 1382 | 1496 |
| 1383 hyperlink | 1497 hyperlink |
| 1384 If true, turn URLs, email addresses and hyperdb item | 1498 If true, turn URLs, email addresses and hyperdb item |
| 1385 designators in the text into hyperlinks (default: no). Note | 1499 designators in the text into hyperlinks (default: no). Note |
| 1386 that you'll need to use the "structure" TAL option if you | 1500 that you'll need to use the "structure" TAL option if you |
| 1387 want to use this:: | 1501 want to use this ``tal:content`` expression:: |
| 1388 | 1502 |
| 1389 <p tal:content="structure python:msg.content.plain(hyperlink=1)" /> | 1503 "structure python:msg.content.plain(hyperlink=1)" |
| 1390 | 1504 |
| 1391 Note also that the text is automatically HTML-escaped before | 1505 Note also that the text is automatically HTML-escaped before |
| 1392 the hyperlinking transformation. | 1506 the hyperlinking transformation. |
| 1393 | 1507 |
| 1394 field render an appropriate form edit field for the property - for | 1508 field render an appropriate form edit field for the property - for |
| 1415 format (eg. "yesterday") | 1529 format (eg. "yesterday") |
| 1416 menu only on Link and Multilink properties - render a form select | 1530 menu only on Link and Multilink properties - render a form select |
| 1417 list for this property | 1531 list for this property |
| 1418 reverse only on Multilink properties - produce a list of the linked | 1532 reverse only on Multilink properties - produce a list of the linked |
| 1419 items in reverse order | 1533 items in reverse order |
| 1420 ========= ===================================================================== | 1534 ========= ================================================================ |
| 1421 | 1535 |
| 1422 | 1536 |
| 1423 The request variable | 1537 The request variable |
| 1424 ~~~~~~~~~~~~~~~~~~~~ | 1538 ~~~~~~~~~~~~~~~~~~~~ |
| 1425 | 1539 |
| 1685 potentials then you will need to add the column to the appropriate | 1799 potentials then you will need to add the column to the appropriate |
| 1686 `index views`_ template so that it is actually displayed. | 1800 `index views`_ template so that it is actually displayed. |
| 1687 | 1801 |
| 1688 This is one of the class context views. The template used is typically | 1802 This is one of the class context views. The template used is typically |
| 1689 "*classname*.search". The form on this page should have "search" as its | 1803 "*classname*.search". The form on this page should have "search" as its |
| 1690 ``:action`` variable. The "search" action: | 1804 ``@action`` variable. The "search" action: |
| 1691 | 1805 |
| 1692 - sets up additional filtering, as well as performing indexed text | 1806 - sets up additional filtering, as well as performing indexed text |
| 1693 searching | 1807 searching |
| 1694 - sets the ``:filter`` variable correctly | 1808 - sets the ``:filter`` variable correctly |
| 1695 - saves the query off if ``:query_name`` is set. | 1809 - saves the query off if ``:query_name`` is set. |
| 1827 items to attach to the current item) | 1941 items to attach to the current item) |
| 1828 | 1942 |
| 1829 Once we have determined the "propname", we check to see if it is one of | 1943 Once we have determined the "propname", we check to see if it is one of |
| 1830 the special form values: | 1944 the special form values: |
| 1831 | 1945 |
| 1832 ``:required`` | 1946 ``@required`` |
| 1833 The named property values must be supplied or a ValueError will be | 1947 The named property values must be supplied or a ValueError will be |
| 1834 raised. | 1948 raised. |
| 1835 | 1949 |
| 1836 ``:remove:<propname>=id(s)`` | 1950 ``@remove@<propname>=id(s)`` |
| 1837 The ids will be removed from the multilink property. | 1951 The ids will be removed from the multilink property. |
| 1838 | 1952 |
| 1839 ``:add:<propname>=id(s)`` | 1953 ``:add:<propname>=id(s)`` |
| 1840 The ids will be added to the multilink property. | 1954 The ids will be added to the multilink property. |
| 1841 | 1955 |
| 1890 *where each journal entry is an HTMLJournalEntry.* | 2004 *where each journal entry is an HTMLJournalEntry.* |
| 1891 | 2005 |
| 1892 Defining new web actions | 2006 Defining new web actions |
| 1893 ------------------------ | 2007 ------------------------ |
| 1894 | 2008 |
| 1895 You may define new actions to be triggered by the ``:action`` form | 2009 You may define new actions to be triggered by the ``@action`` form |
| 1896 variable. These are added to the tracker ``interfaces.py`` as methods on | 2010 variable. These are added to the tracker ``interfaces.py`` as methods on |
| 1897 the ``Client`` class. | 2011 the ``Client`` class. |
| 1898 | 2012 |
| 1899 Adding action methods takes three steps; first you `define the new | 2013 Adding action methods takes three steps; first you `define the new |
| 1900 action method`_, then you `register the action method`_ with the cgi | 2014 action method`_, then you `register the action method`_ with the cgi |
| 1901 interface so it may be triggered by the ``:action`` form variable. | 2015 interface so it may be triggered by the ``@action`` form variable. |
| 1902 Finally you `use the new action`_ in your HTML form. | 2016 Finally you `use the new action`_ in your HTML form. |
| 1903 | 2017 |
| 1904 See "`setting up a "wizard" (or "druid") for controlled adding of | 2018 See "`setting up a "wizard" (or "druid") for controlled adding of |
| 1905 issues`_" for an example. | 2019 issues`_" for an example. |
| 1906 | 2020 |
| 1944 Use the new action | 2058 Use the new action |
| 1945 ~~~~~~~~~~~~~~~~~~ | 2059 ~~~~~~~~~~~~~~~~~~ |
| 1946 | 2060 |
| 1947 In your HTML form, add a hidden form element like so:: | 2061 In your HTML form, add a hidden form element like so:: |
| 1948 | 2062 |
| 1949 <input type="hidden" name=":action" value="myaction"> | 2063 <input type="hidden" name="@action" value="myaction"> |
| 1950 | 2064 |
| 1951 where "myaction" is the name you registered in the previous step. | 2065 where "myaction" is the name you registered in the previous step. |
| 1952 | 2066 |
| 1953 | 2067 |
| 1954 Examples | 2068 Examples |
| 2126 | 2240 |
| 2127 <p class="classblock" | 2241 <p class="classblock" |
| 2128 tal:condition="python:request.user.hasPermission('View', 'category')"> | 2242 tal:condition="python:request.user.hasPermission('View', 'category')"> |
| 2129 <b>Categories</b><br> | 2243 <b>Categories</b><br> |
| 2130 <a tal:condition="python:request.user.hasPermission('Edit', 'category')" | 2244 <a tal:condition="python:request.user.hasPermission('Edit', 'category')" |
| 2131 href="category?:template=item">New Category<br></a> | 2245 href="category?@template=item">New Category<br></a> |
| 2132 </p> | 2246 </p> |
| 2133 | 2247 |
| 2134 The first two lines is the classblock definition, which sets up a | 2248 The first two lines is the classblock definition, which sets up a |
| 2135 condition that only users who have "View" permission for the "category" | 2249 condition that only users who have "View" permission for the "category" |
| 2136 object will have this section included in their output. Next comes a | 2250 object will have this section included in their output. Next comes a |
| 2192 | 2306 |
| 2193 Next we define some code which sets up the minimum list of fields that | 2307 Next we define some code which sets up the minimum list of fields that |
| 2194 we require the user to enter. There will be only one field - "name" - so | 2308 we require the user to enter. There will be only one field - "name" - so |
| 2195 they better put something in it, otherwise the whole form is pointless:: | 2309 they better put something in it, otherwise the whole form is pointless:: |
| 2196 | 2310 |
| 2197 <input type="hidden" name=":required" value="name"> | 2311 <input type="hidden" name="@required" value="name"> |
| 2198 | 2312 |
| 2199 To get everything to line up properly we will put everything in a table, | 2313 To get everything to line up properly we will put everything in a table, |
| 2200 and put a nice big header on it so the user has an idea what is | 2314 and put a nice big header on it so the user has an idea what is |
| 2201 happening:: | 2315 happening:: |
| 2202 | 2316 |
| 2241 </td> | 2355 </td> |
| 2242 <td class="content" metal:fill-slot="content"> | 2356 <td class="content" metal:fill-slot="content"> |
| 2243 <form method="POST" onSubmit="return submit_once()" | 2357 <form method="POST" onSubmit="return submit_once()" |
| 2244 enctype="multipart/form-data"> | 2358 enctype="multipart/form-data"> |
| 2245 | 2359 |
| 2246 <input type="hidden" name=":required" value="name"> | 2360 <input type="hidden" name="@required" value="name"> |
| 2247 | 2361 |
| 2248 <table class="form"> | 2362 <table class="form"> |
| 2249 <tr><th class="header" colspan="2">Category</th></tr> | 2363 <tr><th class="header" colspan="2">Category</th></tr> |
| 2250 | 2364 |
| 2251 <tr> | 2365 <tr> |
| 2307 for issues based on their category, so that, for example, anyone working | 2421 for issues based on their category, so that, for example, anyone working |
| 2308 on the web server could look at all issues in the category "Web". | 2422 on the web server could look at all issues in the category "Web". |
| 2309 | 2423 |
| 2310 If you look for "Search Issues" in the 'html/page.html' file, you will | 2424 If you look for "Search Issues" in the 'html/page.html' file, you will |
| 2311 find that it looks something like | 2425 find that it looks something like |
| 2312 ``<a href="issue?:template=search">Search Issues</a>``. This shows us | 2426 ``<a href="issue?@template=search">Search Issues</a>``. This shows us |
| 2313 that when you click on "Search Issues" it will be looking for a | 2427 that when you click on "Search Issues" it will be looking for a |
| 2314 ``issue.search.html`` file to display. So that is the file that we will | 2428 ``issue.search.html`` file to display. So that is the file that we will |
| 2315 change. | 2429 change. |
| 2316 | 2430 |
| 2317 This file should begin to look familiar, by now. It is a simple HTML | 2431 This file should begin to look familiar, by now. It is a simple HTML |
| 2479 tal:content="string:msg${msg/id}"></a></td> | 2593 tal:content="string:msg${msg/id}"></a></td> |
| 2480 <td tal:content="msg/author">author</td> | 2594 <td tal:content="msg/author">author</td> |
| 2481 <td nowrap tal:content="msg/date/pretty">date</td> | 2595 <td nowrap tal:content="msg/date/pretty">date</td> |
| 2482 <td tal:content="msg/summary">summary</td> | 2596 <td tal:content="msg/summary">summary</td> |
| 2483 <td> | 2597 <td> |
| 2484 <a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit"> | 2598 <a tal:attributes="href string:?@remove@messages=${msg/id}&@action=edit"> |
| 2485 remove</a> | 2599 remove</a> |
| 2486 </td> | 2600 </td> |
| 2487 </tr> | 2601 </tr> |
| 2488 </table> | 2602 </table> |
| 2489 | 2603 |
| 2554 that category. The first page includes a table of help, explaining | 2668 that category. The first page includes a table of help, explaining |
| 2555 what the category names mean, and then the core of the form:: | 2669 what the category names mean, and then the core of the form:: |
| 2556 | 2670 |
| 2557 <form method="POST" onSubmit="return submit_once()" | 2671 <form method="POST" onSubmit="return submit_once()" |
| 2558 enctype="multipart/form-data"> | 2672 enctype="multipart/form-data"> |
| 2559 <input type="hidden" name=":template" value="add_page1"> | 2673 <input type="hidden" name="@template" value="add_page1"> |
| 2560 <input type="hidden" name=":action" value="page1submit"> | 2674 <input type="hidden" name="@action" value="page1submit"> |
| 2561 | 2675 |
| 2562 <strong>Category:</strong> | 2676 <strong>Category:</strong> |
| 2563 <tal:block tal:replace="structure context/category/menu" /> | 2677 <tal:block tal:replace="structure context/category/menu" /> |
| 2564 <input type="submit" value="Continue"> | 2678 <input type="submit" value="Continue"> |
| 2565 </form> | 2679 </form> |
| 2570 <form method="POST" onSubmit="return submit_once()" | 2684 <form method="POST" onSubmit="return submit_once()" |
| 2571 enctype="multipart/form-data" | 2685 enctype="multipart/form-data" |
| 2572 tal:condition="context/is_edit_ok" | 2686 tal:condition="context/is_edit_ok" |
| 2573 tal:define="cat request/form/category/value"> | 2687 tal:define="cat request/form/category/value"> |
| 2574 | 2688 |
| 2575 <input type="hidden" name=":template" value="add_page2"> | 2689 <input type="hidden" name="@template" value="add_page2"> |
| 2576 <input type="hidden" name=":required" value="title"> | 2690 <input type="hidden" name="@required" value="title"> |
| 2577 <input type="hidden" name="category" tal:attributes="value cat"> | 2691 <input type="hidden" name="category" tal:attributes="value cat"> |
| 2578 . | 2692 . |
| 2579 . | 2693 . |
| 2580 . | 2694 . |
| 2581 </form> | 2695 </form> |
| 2616 self.error_message.append('You must select a category of report') | 2730 self.error_message.append('You must select a category of report') |
| 2617 return | 2731 return |
| 2618 # everything's ok, move on to the next page | 2732 # everything's ok, move on to the next page |
| 2619 self.template = 'add_page2' | 2733 self.template = 'add_page2' |
| 2620 | 2734 |
| 2621 4. Use the usual "new" action as the ``:action`` on the final page, and | 2735 4. Use the usual "new" action as the ``@action`` on the final page, and |
| 2622 you're done (the standard context/submit method can do this for you). | 2736 you're done (the standard context/submit method can do this for you). |
| 2623 | 2737 |
| 2624 | 2738 |
| 2625 Using an external password validation source | 2739 Using an external password validation source |
| 2626 -------------------------------------------- | 2740 -------------------------------------------- |
| 2785 times=Multilink("timelog")) | 2899 times=Multilink("timelog")) |
| 2786 | 2900 |
| 2787 the "times" property is the new link to the "timelog" class. | 2901 the "times" property is the new link to the "timelog" class. |
| 2788 | 2902 |
| 2789 3. We'll need to let people add in times to the issue, so in the web | 2903 3. We'll need to let people add in times to the issue, so in the web |
| 2790 interface we'll have a new entry field, just below the change note | 2904 interface we'll have a new entry field. This is a special field |
| 2791 box:: | 2905 because unlike the other fields in the issue.item template, it |
| 2792 | 2906 affects a different item (a timelog item) and not the template's |
| 2793 <tr> | 2907 item, an issue. We have a special syntax for form fields that affect |
| 2794 <th nowrap>Time Log</th> | 2908 items other than the template default item (see the cgi |
| 2795 <td colspan="3"><input name=":timelog"> | 2909 documentation on `special form variables`_). In particular, we add a |
| 2796 (enter as "3y 1m 4d 2:40:02" or parts thereof) | 2910 field to capture a new timelog item's perdiod:: |
| 2797 </td> | 2911 |
| 2798 </tr> | 2912 <tr> |
| 2799 | 2913 <th nowrap>Time Log</th> |
| 2800 Note that we've made up a new form variable, but since we place a | 2914 <td colspan=3><input type="text" name="timelog-1@period" /> |
| 2801 colon ":" in front of it, it won't clash with any existing property | 2915 <br />(enter as '3y 1m 4d 2:40:02' or parts thereof) |
| 2802 variables. The names you *can't* use are ``:note``, ``:file``, | 2916 </td> |
| 2803 ``:action``, ``:required`` and ``:template``. These variables are | 2917 </tr> |
| 2804 described in the section `performing actions in web requests`_. | 2918 |
| 2805 | 2919 and another hidden field that links that new timelog item (new |
| 2806 4. We also need to handle this new field in the CGI interface - the way | 2920 because it's marked as having id "-1") to the issue item. It looks |
| 2807 to do this is through implementing a new form action (see `Setting up | 2921 like this:: |
| 2808 a "wizard" (or "druid") for controlled adding of issues`_ for another | 2922 |
| 2809 example where we implemented a new CGI form action). | 2923 <input type="hidden" name="@link@times" value="timelog-1" /> |
| 2810 | 2924 |
| 2811 In this case, we'll want our action to: | 2925 On submission, the "-1" timelog item will be created and assigned a |
| 2812 | 2926 real item id. The "times" property of the issue will have the new id |
| 2813 1. create a new "timelog" entry, | 2927 added to it. |
| 2814 2. fake that the issue's "times" property has been edited, and then | 2928 |
| 2815 3. call the normal CGI edit action handler. | 2929 4. We want to display a total of the time log times that have been |
| 2816 | |
| 2817 The code to do this is:: | |
| 2818 | |
| 2819 class Client(client.Client): | |
| 2820 ''' derives basic CGI implementation from the standard module, | |
| 2821 with any specific extensions | |
| 2822 ''' | |
| 2823 actions = client.Client.actions + ( | |
| 2824 ('edit_with_timelog', 'timelogEditAction'), | |
| 2825 ('new_with_timelog', 'timelogEditAction'), | |
| 2826 ) | |
| 2827 | |
| 2828 def timelogEditAction(self): | |
| 2829 ''' Handle the creation of a new time log entry if | |
| 2830 necessary. | |
| 2831 | |
| 2832 If we create a new entry, fake up a CGI form value for | |
| 2833 the altered "times" property of the issue being edited. | |
| 2834 | |
| 2835 Punt to the regular edit action when we're done. | |
| 2836 ''' | |
| 2837 # if there's a timelog value specified, create an entry | |
| 2838 if self.form.has_key(':timelog') and \ | |
| 2839 self.form[':timelog'].value.strip(): | |
| 2840 period = Interval(self.form[':timelog'].value) | |
| 2841 # create it | |
| 2842 newid = self.db.timelog.create(period=period) | |
| 2843 | |
| 2844 # if we're editing an existing item, get the old timelog | |
| 2845 # value | |
| 2846 if self.nodeid: | |
| 2847 l = self.db.issue.get(self.nodeid, 'times') | |
| 2848 l.append(newid) | |
| 2849 else: | |
| 2850 l = [newid] | |
| 2851 | |
| 2852 # now make the fake CGI form values | |
| 2853 for entry in l: | |
| 2854 self.form.list.append( | |
| 2855 MiniFieldStorage('times', entry)) | |
| 2856 | |
| 2857 # punt to the normal edit action | |
| 2858 if self.nodeid: | |
| 2859 return self.editItemAction() | |
| 2860 else: | |
| 2861 return self.newItemAction() | |
| 2862 | |
| 2863 you add this code to your Client class in your tracker's | |
| 2864 ``interfaces.py`` file. Locate the section that looks like:: | |
| 2865 | |
| 2866 class Client: | |
| 2867 ''' derives basic CGI implementation from the standard module, | |
| 2868 with any specific extensions | |
| 2869 ''' | |
| 2870 pass | |
| 2871 | |
| 2872 and insert this code in place of the ``pass`` statement. | |
| 2873 | |
| 2874 5. You'll also need to modify your ``issue.item`` form submit action so | |
| 2875 it calls the time logging action we just created. The current | |
| 2876 template will look like this:: | |
| 2877 | |
| 2878 <tr> | |
| 2879 <td> </td> | |
| 2880 <td colspan="3" tal:content="structure context/submit"> | |
| 2881 submit button will go here | |
| 2882 </td> | |
| 2883 </tr> | |
| 2884 | |
| 2885 replace it with this:: | |
| 2886 | |
| 2887 <tr> | |
| 2888 <td> </td> | |
| 2889 <td colspan="3"> | |
| 2890 <tal:block tal:condition="context/id"> | |
| 2891 <input type="hidden" name=":action" value="edit_with_timelog"> | |
| 2892 <input type="submit" name="submit" value="Submit Changes"> | |
| 2893 </tal:block> | |
| 2894 <tal:block tal:condition="not:context/id"> | |
| 2895 <input type="hidden" name=":action" value="new_with_timelog"> | |
| 2896 <input type="submit" name="submit" value="Submit New Issue"> | |
| 2897 </tal:block> | |
| 2898 </td> | |
| 2899 </tr> | |
| 2900 | |
| 2901 The important change is setting the action to "edit_with_timelog" for | |
| 2902 edit operations (where the item exists) and "new_with_timelog" for | |
| 2903 creations operations. | |
| 2904 | |
| 2905 6. We want to display a total of the time log times that have been | |
| 2906 accumulated for an issue. To do this, we'll need to actually write | 2930 accumulated for an issue. To do this, we'll need to actually write |
| 2907 some Python code, since it's beyond the scope of PageTemplates to | 2931 some Python code, since it's beyond the scope of PageTemplates to |
| 2908 perform such calculations. We do this by adding a method to the | 2932 perform such calculations. We do this by adding a method to the |
| 2909 TemplatingUtils class in our tracker ``interfaces.py`` module:: | 2933 TemplatingUtils class in our tracker ``interfaces.py`` module:: |
| 2910 | 2934 |
| 2924 Replace the ``pass`` line as we did in step 4 above with the Client | 2948 Replace the ``pass`` line as we did in step 4 above with the Client |
| 2925 class. As indicated in the docstrings, we will be able to access the | 2949 class. As indicated in the docstrings, we will be able to access the |
| 2926 ``totalTimeSpent`` method via the ``utils`` variable in our | 2950 ``totalTimeSpent`` method via the ``utils`` variable in our |
| 2927 templates. | 2951 templates. |
| 2928 | 2952 |
| 2929 7. Display the time log for an issue:: | 2953 5. Display the time log for an issue:: |
| 2930 | 2954 |
| 2931 <table class="otherinfo" tal:condition="context/times"> | 2955 <table class="otherinfo" tal:condition="context/times"> |
| 2932 <tr><th colspan="3" class="header">Time Log | 2956 <tr><th colspan="3" class="header">Time Log |
| 2933 <tal:block | 2957 <tal:block |
| 2934 tal:replace="python:utils.totalTimeSpent(context.times)" /> | 2958 tal:replace="python:utils.totalTimeSpent(context.times)" /> |
| 3093 | 3117 |
| 3094 This is pretty simple - all we need to do is copy the code from the | 3118 This is pretty simple - all we need to do is copy the code from the |
| 3095 example `displaying only message summaries in the issue display`_ into | 3119 example `displaying only message summaries in the issue display`_ into |
| 3096 our template alongside the summary display, and then introduce a switch | 3120 our template alongside the summary display, and then introduce a switch |
| 3097 that shows either one or the other. We'll use a new form variable, | 3121 that shows either one or the other. We'll use a new form variable, |
| 3098 ``:whole_messages`` to achieve this:: | 3122 ``@whole_messages`` to achieve this:: |
| 3099 | 3123 |
| 3100 <table class="messages" tal:condition="context/messages"> | 3124 <table class="messages" tal:condition="context/messages"> |
| 3101 <tal:block tal:condition="not:request/form/:whole_messages/value | python:0"> | 3125 <tal:block tal:condition="not:request/form/@whole_messages/value | python:0"> |
| 3102 <tr><th colspan="3" class="header">Messages</th> | 3126 <tr><th colspan="3" class="header">Messages</th> |
| 3103 <th colspan="2" class="header"> | 3127 <th colspan="2" class="header"> |
| 3104 <a href="?:whole_messages=yes">show entire messages</a> | 3128 <a href="?@whole_messages=yes">show entire messages</a> |
| 3105 </th> | 3129 </th> |
| 3106 </tr> | 3130 </tr> |
| 3107 <tr tal:repeat="msg context/messages"> | 3131 <tr tal:repeat="msg context/messages"> |
| 3108 <td><a tal:attributes="href string:msg${msg/id}" | 3132 <td><a tal:attributes="href string:msg${msg/id}" |
| 3109 tal:content="string:msg${msg/id}"></a></td> | 3133 tal:content="string:msg${msg/id}"></a></td> |
| 3110 <td tal:content="msg/author">author</td> | 3134 <td tal:content="msg/author">author</td> |
| 3111 <td nowrap tal:content="msg/date/pretty">date</td> | 3135 <td nowrap tal:content="msg/date/pretty">date</td> |
| 3112 <td tal:content="msg/summary">summary</td> | 3136 <td tal:content="msg/summary">summary</td> |
| 3113 <td> | 3137 <td> |
| 3114 <a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a> | 3138 <a tal:attributes="href string:?@remove@messages=${msg/id}&@action=edit">remove</a> |
| 3115 </td> | 3139 </td> |
| 3116 </tr> | 3140 </tr> |
| 3117 </tal:block> | 3141 </tal:block> |
| 3118 | 3142 |
| 3119 <tal:block tal:condition="request/form/:whole_messages/value | python:0"> | 3143 <tal:block tal:condition="request/form/@whole_messages/value | python:0"> |
| 3120 <tr><th colspan="2" class="header">Messages</th> | 3144 <tr><th colspan="2" class="header">Messages</th> |
| 3121 <th class="header"> | 3145 <th class="header"> |
| 3122 <a href="?:whole_messages=">show only summaries</a> | 3146 <a href="?@whole_messages=">show only summaries</a> |
| 3123 </th> | 3147 </th> |
| 3124 </tr> | 3148 </tr> |
| 3125 <tal:block tal:repeat="msg context/messages"> | 3149 <tal:block tal:repeat="msg context/messages"> |
| 3126 <tr> | 3150 <tr> |
| 3127 <th tal:content="msg/author">author</th> | 3151 <th tal:content="msg/author">author</th> |
| 3128 <th nowrap tal:content="msg/date/pretty">date</th> | 3152 <th nowrap tal:content="msg/date/pretty">date</th> |
| 3129 <th style="text-align: right"> | 3153 <th style="text-align: right"> |
| 3130 (<a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a>) | 3154 (<a tal:attributes="href string:?@remove@messages=${msg/id}&@action=edit">remove</a>) |
| 3131 </th> | 3155 </th> |
| 3132 </tr> | 3156 </tr> |
| 3133 <tr><td colspan="3" tal:content="msg/content"></td></tr> | 3157 <tr><td colspan="3" tal:content="msg/content"></td></tr> |
| 3134 </tal:block> | 3158 </tal:block> |
| 3135 </tal:block> | 3159 </tal:block> |
