Mercurial > p > roundup > code
comparison doc/customizing.txt @ 2915:7d97c75e7cba
more docs
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Tue, 16 Nov 2004 23:36:59 +0000 |
| parents | c75a19894d3e |
| children | ad4fb8a14a97 |
comparison
equal
deleted
inserted
replaced
| 2914:c75a19894d3e | 2915:7d97c75e7cba |
|---|---|
| 1 =================== | 1 =================== |
| 2 Customising Roundup | 2 Customising Roundup |
| 3 =================== | 3 =================== |
| 4 | 4 |
| 5 :Version: $Revision: 1.156 $ | 5 :Version: $Revision: 1.157 $ |
| 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:: |
| 883 The web interface is provided by the ``roundup.cgi.client`` module and | 883 The web interface is provided by the ``roundup.cgi.client`` module and |
| 884 is used by ``roundup.cgi``, ``roundup-server`` and ``ZRoundup`` | 884 is used by ``roundup.cgi``, ``roundup-server`` and ``ZRoundup`` |
| 885 (``ZRoundup`` is broken, until further notice). In all cases, we | 885 (``ZRoundup`` is broken, until further notice). In all cases, we |
| 886 determine which tracker is being accessed (the first part of the URL | 886 determine which tracker is being accessed (the first part of the URL |
| 887 path inside the scope of the CGI handler) and pass control on to the | 887 path inside the scope of the CGI handler) and pass control on to the |
| 888 tracker ``interfaces.Client`` class - which uses the ``Client`` class | 888 ``roundup.cgi.client.Client`` class - which handles the rest of the |
| 889 from ``roundup.cgi.client`` - which handles the rest of the access | 889 access through its ``main()`` method. This means that you can do pretty |
| 890 through its ``main()`` method. This means that you can do pretty much | 890 much anything you want as a web interface to your tracker. |
| 891 anything you want as a web interface to your tracker. | 891 |
| 892 | 892 |
| 893 | 893 |
| 894 Repercussions of changing the tracker schema | 894 Repercussions of changing the tracker schema |
| 895 --------------------------------------------- | 895 --------------------------------------------- |
| 896 | 896 |
| 1049 - Also handle the ":queryname" variable and save off the query to the | 1049 - Also handle the ":queryname" variable and save off the query to the |
| 1050 user's query list. | 1050 user's query list. |
| 1051 | 1051 |
| 1052 Each of the actions is implemented by a corresponding ``*XxxAction*`` (where | 1052 Each of the actions is implemented by a corresponding ``*XxxAction*`` (where |
| 1053 "Xxx" is the name of the action) class in the ``roundup.cgi.actions`` module. | 1053 "Xxx" is the name of the action) class in the ``roundup.cgi.actions`` module. |
| 1054 These classes are registered with ``roundup.cgi.client.Client`` which also | 1054 These classes are registered with ``roundup.cgi.client.Client``. If you need |
| 1055 happens to be available in your tracker instance as ``interfaces.Client``. So | 1055 to define new actions, you may add them there (see `defining new |
| 1056 if you need to define new actions, you may add them there (see `defining new | |
| 1057 web actions`_). | 1056 web actions`_). |
| 1058 | 1057 |
| 1059 Each action class also has a ``*permission*`` method which determines whether | 1058 Each action class also has a ``*permission*`` method which determines whether |
| 1060 the action is permissible given the current user. The base permission checks | 1059 the action is permissible given the current user. The base permission checks |
| 1061 are: | 1060 are: |
| 1993 url_quote quote some text as safe for a URL (ie. space, %, ...) | 1992 url_quote quote some text as safe for a URL (ie. space, %, ...) |
| 1994 html_quote quote some text as safe in HTML (ie. <, >, ...) | 1993 html_quote quote some text as safe in HTML (ie. <, >, ...) |
| 1995 =============== ======================================================== | 1994 =============== ======================================================== |
| 1996 | 1995 |
| 1997 You may add additional utility methods by writing them in your tracker | 1996 You may add additional utility methods by writing them in your tracker |
| 1998 ``extensions`` directory and registering them with the templating system. | 1997 ``extensions`` directory and registering them with the templating system |
| 1998 using ``instance.registerUtil`` (see `adding a time log to your issues`_ for | |
| 1999 an example of this). | |
| 1999 | 2000 |
| 2000 | 2001 |
| 2001 Batching | 2002 Batching |
| 2002 :::::::: | 2003 :::::::: |
| 2003 | 2004 |
| 2334 | 2335 |
| 2335 Defining new web actions | 2336 Defining new web actions |
| 2336 ------------------------ | 2337 ------------------------ |
| 2337 | 2338 |
| 2338 You may define new actions to be triggered by the ``@action`` form variable. | 2339 You may define new actions to be triggered by the ``@action`` form variable. |
| 2339 These are added to the tracker ``interfaces.py`` as ``Action`` classes that get | 2340 These are added to the tracker ``extensions`` directory and registered |
| 2340 called by the the ``Client`` class. | 2341 using ``instance.registerAction``. |
| 2342 | |
| 2343 All the existing Actions are defined in ``roundup.cgi.actions``. | |
| 2341 | 2344 |
| 2342 Adding action classes takes three steps; first you `define the new | 2345 Adding action classes takes three steps; first you `define the new |
| 2343 action class`_, then you `register the action class`_ with the cgi | 2346 action class`_, then you `register the action class`_ with the cgi |
| 2344 interface so it may be triggered by the ``@action`` form variable. | 2347 interface so it may be triggered by the ``@action`` form variable. |
| 2345 Finally you `use the new action`_ in your HTML form. | 2348 Finally you `use the new action`_ in your HTML form. |
| 2349 | 2352 |
| 2350 | 2353 |
| 2351 Define the new action class | 2354 Define the new action class |
| 2352 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 2355 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 2353 | 2356 |
| 2354 The action classes have the following interface:: | 2357 Create a new action class in your tracker's ``extensions`` directory, for |
| 2358 example ``myaction.py``:: | |
| 2355 | 2359 |
| 2356 class MyAction(Action): | 2360 class MyAction(Action): |
| 2357 def handle(self): | 2361 def handle(self): |
| 2358 ''' Perform some action. No return value is required. | 2362 ''' Perform some action. No return value is required. |
| 2359 ''' | 2363 ''' |
| 2360 | 2364 |
| 2361 The *self.client* attribute is an instance of your tracker ``instance.Client`` | 2365 The *self.client* attribute is an instance of ``roundup.cgi.client.Client``. |
| 2362 class - thus it's mostly implemented by ``roundup.cgi.client.Client``. See the | 2366 See the docstring of that class for details of what it can do. |
| 2363 docstring of that class for details of what it can do. | |
| 2364 | 2367 |
| 2365 The method will typically check the ``self.form`` variable's contents. | 2368 The method will typically check the ``self.form`` variable's contents. |
| 2366 It may then: | 2369 It may then: |
| 2367 | 2370 |
| 2368 - add information to ``self.client.ok_message`` or ``self.client.error_message`` | 2371 - add information to ``self.client.ok_message`` or ``self.client.error_message`` |
| 2373 | 2376 |
| 2374 | 2377 |
| 2375 Register the action class | 2378 Register the action class |
| 2376 ~~~~~~~~~~~~~~~~~~~~~~~~~~ | 2379 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 2377 | 2380 |
| 2378 XXX update for new extensions setup (then search for interfaces.py and make | 2381 The class is now written, but isn't available to the user until you register |
| 2379 sure there's no examples that use the old style) | 2382 it with the following code appended to your ``myaction.py`` file:: |
| 2380 | 2383 |
| 2381 The class is now written, but isn't available to the user until you add it to | 2384 def init(instance): |
| 2382 the ``instance.Client`` class ``actions`` variable, like so:: | 2385 instance.registerAction('myaction', myActionClass) |
| 2383 | |
| 2384 actions = client.Client.actions + ( | |
| 2385 ('myaction', myActionClass), | |
| 2386 ) | |
| 2387 | 2386 |
| 2388 This maps the action name "myaction" to the action class we defined. | 2387 This maps the action name "myaction" to the action class we defined. |
| 2388 | |
| 2389 | 2389 |
| 2390 Use the new action | 2390 Use the new action |
| 2391 ~~~~~~~~~~~~~~~~~~ | 2391 ~~~~~~~~~~~~~~~~~~ |
| 2392 | 2392 |
| 2393 In your HTML form, add a hidden form element like so:: | 2393 In your HTML form, add a hidden form element like so:: |
| 2936 added to it. | 2936 added to it. |
| 2937 | 2937 |
| 2938 4. We want to display a total of the time log times that have been | 2938 4. We want to display a total of the time log times that have been |
| 2939 accumulated for an issue. To do this, we'll need to actually write | 2939 accumulated for an issue. To do this, we'll need to actually write |
| 2940 some Python code, since it's beyond the scope of PageTemplates to | 2940 some Python code, since it's beyond the scope of PageTemplates to |
| 2941 perform such calculations. We do this by adding a method to the | 2941 perform such calculations. We do this by adding a module ``timespent.py`` |
| 2942 TemplatingUtils class in our tracker ``interfaces.py`` module:: | 2942 to the ``extensions`` directory in our tracker:: |
| 2943 | 2943 |
| 2944 class TemplatingUtils: | 2944 def totalTimeSpent(times): |
| 2945 ''' Methods implemented on this class will be available to HTML | 2945 ''' Call me with a list of timelog items (which have an |
| 2946 templates through the 'utils' variable. | 2946 Interval "period" property) |
| 2947 ''' | 2947 ''' |
| 2948 def totalTimeSpent(self, times): | 2948 total = Interval('0d') |
| 2949 ''' Call me with a list of timelog items (which have an | 2949 for time in times: |
| 2950 Interval "period" property) | 2950 total += time.period._value |
| 2951 ''' | 2951 return total |
| 2952 total = Interval('0d') | 2952 |
| 2953 for time in times: | 2953 def init(instance): |
| 2954 total += time.period._value | 2954 instance.registerUtil('totalTimeSpent', totalTimeSpent) |
| 2955 return total | 2955 |
| 2956 | 2956 We will now be able to access the ``totalTimeSpent`` function via the |
| 2957 XXX update this example for TemplatingUtils -> extensions | 2957 ``utils`` variable in our templates, as shown in the next step. |
| 2958 | |
| 2959 Replace the ``pass`` line if one appears in your TemplatingUtils | |
| 2960 class. As indicated in the docstrings, we will be able to access the | |
| 2961 ``totalTimeSpent`` method via the ``utils`` variable in our templates. | |
| 2962 | 2958 |
| 2963 5. Display the time log for an issue:: | 2959 5. Display the time log for an issue:: |
| 2964 | 2960 |
| 2965 <table class="otherinfo" tal:condition="context/times"> | 2961 <table class="otherinfo" tal:condition="context/times"> |
| 2966 <tr><th colspan="3" class="header">Time Log | 2962 <tr><th colspan="3" class="header">Time Log |
| 3058 admin:aamrgyQfDFSHw | 3054 admin:aamrgyQfDFSHw |
| 3059 | 3055 |
| 3060 Each user of Roundup must still have their information stored in the Roundup | 3056 Each user of Roundup must still have their information stored in the Roundup |
| 3061 database - we just use the passwd file to check their password. To do this, we | 3057 database - we just use the passwd file to check their password. To do this, we |
| 3062 need to override the standard ``verifyPassword`` method defined in | 3058 need to override the standard ``verifyPassword`` method defined in |
| 3063 ``roundup.cgi.actions.LoginAction`` and register the new class with our | 3059 ``roundup.cgi.actions.LoginAction`` and register the new class. The |
| 3064 ``Client`` class in the tracker home ``interfaces.py`` module:: | 3060 following is added as ``externapassword.py`` in the tracker ``extensions`` |
| 3061 directory:: | |
| 3065 | 3062 |
| 3066 from roundup.cgi.actions import LoginAction | 3063 from roundup.cgi.actions import LoginAction |
| 3067 | 3064 |
| 3068 class ExternalPasswordLoginAction(LoginAction): | 3065 class ExternalPasswordLoginAction(LoginAction): |
| 3069 def verifyPassword(self, userid, password): | 3066 def verifyPassword(self, userid, password): |
| 3067 '''Look through the file, line by line, looking for a | |
| 3068 name that matches. | |
| 3069 ''' | |
| 3070 # get the user's username | 3070 # get the user's username |
| 3071 username = self.db.user.get(userid, 'username') | 3071 username = self.db.user.get(userid, 'username') |
| 3072 | 3072 |
| 3073 # the passwords are stored in the "passwd.txt" file in the | 3073 # the passwords are stored in the "passwd.txt" file in the |
| 3074 # tracker home | 3074 # tracker home |
| 3081 return crypt.crypt(password, ent[1][:2]) == ent[1] | 3081 return crypt.crypt(password, ent[1][:2]) == ent[1] |
| 3082 | 3082 |
| 3083 # user doesn't exist in the file | 3083 # user doesn't exist in the file |
| 3084 return 0 | 3084 return 0 |
| 3085 | 3085 |
| 3086 class Client(client.Client): | 3086 def init(instance): |
| 3087 actions = ( | 3087 instance.registerAction('login', ExternalPasswordLoginAction) |
| 3088 ('login', ExternalPasswordLoginAction), | 3088 |
| 3089 ) + client.Client.actions | 3089 You should also remove the redundant password fields from the ``user.item`` |
| 3090 | |
| 3091 What this does is look through the file, line by line, looking for a | |
| 3092 name that matches. | |
| 3093 | |
| 3094 We also remove the redundant password fields from the ``user.item`` | |
| 3095 template. | 3090 template. |
| 3096 | 3091 |
| 3097 | 3092 |
| 3098 Using a UN*X passwd file as the user database | 3093 Using a UN*X passwd file as the user database |
| 3099 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 3094 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4087 ... the above section will only be displayed if the category is one | 4082 ... the above section will only be displayed if the category is one |
| 4088 of 6, 10, 13, 14, 15, 16 or 17. | 4083 of 6, 10, 13, 14, 15, 16 or 17. |
| 4089 | 4084 |
| 4090 3. Determine what actions need to be taken between the pages - these are | 4085 3. Determine what actions need to be taken between the pages - these are |
| 4091 usually to validate user choices and determine what page is next. Now encode | 4086 usually to validate user choices and determine what page is next. Now encode |
| 4092 those actions in a new ``Action`` class and insert hooks to those actions in | 4087 those actions in a new ``Action`` class (see `defining new web actions`_):: |
| 4093 the "actions" attribute on on the ``interfaces.Client`` class, like so (see | 4088 |
| 4094 `defining new web actions`_):: | 4089 from roundup.cgi.actions import Action |
| 4095 | 4090 |
| 4096 class Page1SubmitAction(Action): | 4091 class Page1SubmitAction(Action): |
| 4097 def handle(self): | 4092 def handle(self): |
| 4098 ''' Verify that the user has selected a category, and then move | 4093 ''' Verify that the user has selected a category, and then move |
| 4099 on to page 2. | 4094 on to page 2. |
| 4103 self.error_message.append('You must select a category of report') | 4098 self.error_message.append('You must select a category of report') |
| 4104 return | 4099 return |
| 4105 # everything's ok, move on to the next page | 4100 # everything's ok, move on to the next page |
| 4106 self.template = 'add_page2' | 4101 self.template = 'add_page2' |
| 4107 | 4102 |
| 4108 actions = client.Client.actions + ( | 4103 def init(instance): |
| 4109 ('page1_submit', Page1SubmitAction), | 4104 instance.registerAction('page1_submit', Page1SubmitAction) |
| 4110 ) | |
| 4111 | 4105 |
| 4112 4. Use the usual "new" action as the ``@action`` on the final page, and | 4106 4. Use the usual "new" action as the ``@action`` on the final page, and |
| 4113 you're done (the standard context/submit method can do this for you). | 4107 you're done (the standard context/submit method can do this for you). |
| 4114 | 4108 |
| 4115 | 4109 |
