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>&nbsp;</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>&nbsp;</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>

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