Code

Plug a number of security holes:
[roundup.git] / doc / upgrading.txt
1 ======================================
2 Upgrading to newer versions of Roundup
3 ======================================
5 Please read each section carefully and edit your tracker home files
6 accordingly. Note that there is information about upgrade procedures in the
7 `administration guide`_.
9 If a specific version transition isn't mentioned here (eg. 0.6.7 to 0.6.8)
10 then you don't need to do anything. If you're upgrading from 0.5.6 to
11 0.6.8 though, you'll need to check the "0.5 to 0.6" and "0.6.x to 0.6.3"
12 steps.
14 .. contents::
17 Migrating from 1.4.x to 1.4.7
18 =============================
20 Several security issues were addressed in this release. Some aspects of your
21 trackers may no longer function depending on your local customisations. Core
22 functionality that will need to be modified:
24 Grant the "retire" permission to users for their queries
25 --------------------------------------------------------
27 Users will no longer be able to retire their own queries. To remedy this you
28 will need to add the following to your tracker's ``schema.py`` just under the
29 line that grants them permission to edit their own queries::
31    p = db.security.addPermission(name='Edit', klass='query', check=edit_query,
32       description="User is allowed to edit their queries")
33    db.security.addPermissionToRole('User', p)
34  + p = db.security.addPermission(name='Retire', klass='query', check=edit_query,
35  +    description="User is allowed to retire their queries")
36  + db.security.addPermissionToRole('User', p)
37    p = db.security.addPermission(name='Create', klass='query',
38       description="User is allowed to create queries")
39    db.security.addPermissionToRole('User', p)
41 The lines marked "+" should be added, minus the "+" sign.
44 Fix the "retire" link in the users list for admin users
45 -------------------------------------------------------
47 The "retire" link found in the file ``html/users.index.html``::
49   <td tal:condition="context/is_edit_ok">
50    <a tal:attributes="href string:user${user/id}?@action=retire&@template=index"
51     i18n:translate="">retire</a>
53 Should be replaced with::
55   <td tal:condition="context/is_retire_ok">
56      <form style="padding:0"
57            tal:attributes="action string:user${user/id}">
58       <input type="hidden" name="@template" value="index">
59       <input type="hidden" name="@action" value="retire">
60       <input type="submit" value="retire" i18n:attributes="value">
61      </form>
64 Trackers currently allowing HTML file uploading
65 -----------------------------------------------
67 Trackers which wish to continue to allow uploading of HTML content against issues
68 will need to set a new configuration variable in the ``[web]`` section of the
69 tracker's ``config.ini`` file:
71    # Setting this option enables Roundup to serve uploaded HTML
72    # file content *as HTML*. This is a potential security risk
73    # and is therefore disabled by default. Set to 'yes' if you
74    # trust *all* users uploading content to your tracker.
75    # Allowed values: yes, no
76    # Default: no
77    allow_html_file = no
81 Migrating from 1.4.2 to 1.4.3
82 =============================
84 If you are using the MySQL backend you will need to replace some indexes
85 that may have been created by version 1.4.2.
87 You should to access your MySQL database directly and remove any indexes
88 with a name ending in "_key_retired_idx". You should then re-add them with
89 the same spec except the key column name needs a size. So an index on
90 "_user (__retired, _name)" should become "_user (__retired, _name(255))".
93 Migrating from 1.4.x to 1.4.2
94 =============================
96 You should run the "roundup-admin migrate" command for your tracker once
97 you've installed the latest codebase. 
99 Do this before you use the web, command-line or mail interface and before
100 any users access the tracker.
102 This command will respond with either "Tracker updated" (if you've not
103 previously run it on an RDBMS backend) or "No migration action required"
104 (if you have run it, or have used another interface to the tracker,
105 or are using anydbm).
107 It's safe to run this even if it's not required, so just get into the
108 habit.
111 Migrating from 1.3.3 to 1.4.0
112 =============================
114 Value of the "refwd_re" tracker configuration option (section "mailgw")
115 is treated as UTF-8 string.  In previous versions, it was ISO8859-1.
117 If you have running trackers based on the classic template, please
118 update the messagesummary detector as follows::
120     --- detectors/messagesummary.py 17 Apr 2003 03:26:38 -0000      1.1
121     +++ detectors/messagesummary.py 3 Apr 2007 06:47:21 -0000       1.2
122     @@ -8,7 +8,7 @@
123      if newvalues.has_key('summary') or not newvalues.has_key('content'):
124          return
126     -    summary, content = parseContent(newvalues['content'], 1, 1)
127     +    summary, content = parseContent(newvalues['content'], config=db.config)
128      newvalues['summary'] = summary
130 In the latest version we have added some database indexes to the
131 SQL-backends (mysql, postgresql, sqlite) for speeding up building the
132 roundup-index for full-text search. We recommend that you create the
133 following database indexes on the database by hand::
135  CREATE INDEX words_by_id ON __words (_textid)
136  CREATE UNIQUE INDEX __textids_by_props ON __textids (_class, _itemid, _prop)
138 Migrating from 1.2.x to 1.3.0
139 =============================
141 1.3.0 Web interface changes
142 ---------------------------
144 Some of the HTML files in the "classic" and "minimal" tracker templates
145 were changed to fix some bugs and clean them up. You may wish to compare
146 them to the HTML files in your tracker and apply any changes.
149 Migrating from 1.1.2 to 1.2.0
150 =============================
152 1.2.0 Sorting and grouping by multiple properties
153 -------------------------------------------------
155 Starting with this version, sorting and grouping by multiple properties
156 is possible. This means that request.sort and request.group are now
157 lists. This is reflected in several places:
159  * ``renderWith`` now has list attributes for ``sort`` and ``group``,
160    where you previously wrote::
161    
162     renderWith(... sort=('-', 'activity'), group=('+', 'priority')
164    you write now::
166     renderWith(... sort=[('-', 'activity')], group=[('+', 'priority')]
168  * In templates that permit to edit sorting/grouping, request.sort and
169    request.group are (possibly empty) lists. You can now sort and group
170    by multiple attributes. For an example, see the classic template. You
171    may want search for the variable ``n_sort`` which can be set to the
172    number of sort/group properties.
174  * Templates that diplay new headlines for each group of items with
175    equal group properties can now use the modified ``batch.propchanged``
176    method that can take several properties which are checked for
177    changes. See the example in the classic template which makes use of
178    ``batch.propchanged``.
180 Migrating from 1.1.0 to 1.1.1
181 =============================
183 1.1.1 "Clear this message"
184 --------------------------
186 In 1.1.1, the standard ``page.html`` template includes a "clear this message"
187 link in the green "ok" message bar that appears after a successful edit
188 (or other) action.
190 To include this in your tracker, change the following in your ``page.html``
191 template::
193  <p tal:condition="options/ok_message | nothing" class="ok-message"
194     tal:repeat="m options/ok_message" tal:content="structure m">error</p>
196 to be::
198  <p tal:condition="options/ok_message | nothing" class="ok-message">
199    <span tal:repeat="m options/ok_message"
200       tal:content="structure string:$m <br/ > " />
201     <a class="form-small" tal:attributes="href request/current_url"
202        i18n:translate="">clear this message</a>
203  </p>
206 If you implemented the "clear this message" in your 1.1.0 tracker, then you
207 should change it to the above and it will work much better!
210 Migrating from 1.0.x to 1.1.0
211 =============================
213 1.1 Login "For Session Only"
214 ----------------------------
216 In 1.1, web logins are alive for the length of a session only, *unless* you
217 add the following to the login form in your tracker's ``page.html``::
219     <input type="checkbox" name="remember" id="remember">
220     <label for="remember" i18n:translate="">Remember me?</label><br>
222 See the classic tracker ``page.html`` if you're unsure where this should
223 go.
226 1.1 Query Display Name
227 ----------------------
229 The ``dispname`` web variable has been renamed ``@dispname`` to avoid
230 clashing with other variables of the same name. If you are using the
231 display name feature, you will need to edit your tracker's ``page.html``
232 and ``issue.index.html`` pages to change ``dispname`` to ``@dispname``.
234 A side-effect of this change is that the renderWith method used in the
235 ``home.html`` page may now take a dispname argument.
238 1.1 "Clear this message"
239 ------------------------
241 In 1.1, the standard ``page.html`` template includes a "clear this message"
242 link in the green "ok" message bar that appears after a successful edit
243 (or other) action.
245 To include this in your tracker, change the following in your ``page.html``
246 template::
248  <p tal:condition="options/ok_message | nothing" class="ok-message"
249     tal:repeat="m options/ok_message" tal:content="structure m">error</p>
251 to be::
253  <p tal:condition="options/ok_message | nothing" class="ok-message">
254    <span tal:repeat="m options/ok_message"
255       tal:content="structure string:$m <br/ > " />
256     <a class="form-small" tal:attributes="href string:issue${context/id}"
257        i18n:translate="">clear this message</a>
258  </p>
261 Migrating from 0.8.x to 1.0
262 ===========================
264 1.0 New Query Permissions
265 -------------------------
267 New permissions are defined for query editing and viewing. To include these
268 in your tracker, you need to add these lines to your tracker's
269 ``schema.py``::
271  # Users should be able to edit and view their own queries. They should also
272  # be able to view any marked as not private. They should not be able to
273  # edit others' queries, even if they're not private
274  def view_query(db, userid, itemid):
275      private_for = db.query.get(itemid, 'private_for')
276      if not private_for: return True
277      return userid == private_for
278  def edit_query(db, userid, itemid):
279      return userid == db.query.get(itemid, 'creator')
280  p = db.security.addPermission(name='View', klass='query', check=view_query,
281      description="User is allowed to view their own and public queries")
282  db.security.addPermissionToRole('User', p)
283  p = db.security.addPermission(name='Edit', klass='query', check=edit_query,
284      description="User is allowed to edit their queries")
285  db.security.addPermissionToRole('User', p)
286  p = db.security.addPermission(name='Create', klass='query',
287      description="User is allowed to create queries")
288  db.security.addPermissionToRole('User', p)
290 and then remove 'query' from the line::
292  # Assign the access and edit Permissions for issue, file and message
293  # to regular users now
294  for cl in 'issue', 'file', 'msg', 'query', 'keyword':
296 so it looks like::
298  for cl in 'issue', 'file', 'msg', 'keyword':
301 Migrating from 0.8.0 to 0.8.3
302 =============================
304 0.8.3 Nosy Handling Changes
305 ---------------------------
307 A change was made to fix a bug in the ``nosyreaction.py`` standard
308 detector. To incorporate this fix in your trackers, you will need to copy
309 the ``nosyreaction.py`` file from the ``templates/classic/detectors``
310 directory of the source to your tracker's ``templates`` directory.
312 If you have modified the ``nosyreaction.py`` file from the standard
313 version, you will need to roll your changes into the new file.
316 Migrating from 0.7.1 to 0.8.0
317 =============================
319 You *must* fully uninstall previous Roundup version before installing
320 Roundup 0.8.0.  If you don't do that, ``roundup-admin install``
321 command may fail to function properly.
323 0.8.0 Backend changes
324 ---------------------
326 Backends 'bsddb' and 'bsddb3' are removed.  If you are using one of these,
327 you *must* migrate to another backend before upgrading.
330 0.8.0 API changes
331 -----------------
333 Class.safeget() was removed from the API. Test your item ids before calling
334 Class.get() instead.
337 0.8.0 New tracker layout
338 ------------------------
340 The ``config.py`` file has been replaced by ``config.ini``. You may use the
341 roundup-admin command "genconfig" to generate a new config file::
343   roundup-admin genconfig <tracker home>/config.ini
345 and modify the values therein based on the contents of your old config.py.
346 In most cases, the names of the config variables are the same.
348 The ``select_db.py`` file has been replaced by a file in the ``db``
349 directory called ``backend_name``. As you might guess, this file contains
350 just the name of the backend. To figure what the contents of yours should
351 be, use the following table:
353   ================================ =========================
354   ``select_db.py`` contents        ``backend_name`` contents
355   ================================ =========================
356   from back_anydbm import ...      anydbm
357   from back_metakit import ...     metakit
358   from back_sqlite import ...      sqlite
359   from back_mysql import ...       mysql
360   from back_postgresql import ...  postgresql
361   ================================ =========================
363 The ``dbinit.py`` file has been split into two new files,
364 ``initial_data.py`` and ``schema.py``. The contents of this file are:
366 ``initial_data.py``
367   You don't need one of these as your tracker is already initialised.
369 ``schema.py``
370   Copy the body of the ``def open(name=None)`` function from your old
371   tracker's ``dbinit.py`` file to this file. As the lines you're copying
372   aren't part of a function definition anymore, one level of indentation
373   needs to be removed (remove only the leading four spaces on each
374   line). 
376   The first few lines -- those starting with ``from roundup.hyperdb
377   import ...`` and the ``db = Database(config, name)`` line -- don't
378   need to be copied. Neither do the last few lines -- those starting
379   with ``import detectors``, down to ``return db`` inclusive.
381 You may remove the ``__init__.py`` module from the "detectors" directory as
382 it is no longer used.
384 There's a new way to write extension code for Roundup. If you have code in
385 an ``interfaces.py`` file you should move it. See the `customisation
386 documentation`_ for information about how extensions are now written.
387 Note that some older trackers may use ``interfaces.py`` to customise the
388 mail gateway behaviour. You will need to keep your ``interfaces.py`` file
389 if this is the case.
392 0.8.0 Permissions Changes
393 -------------------------
395 The creation of a new item in the user interfaces is now controlled by the
396 "Create" Permission. You will need to add an assignment of this Permission
397 to your users who are allowed to create items. The most common form of this
398 is the following in your ``schema.py`` added just under the current
399 assignation of the Edit Permission::
401     for cl in 'issue', 'file', 'msg', 'query', 'keyword':
402         p = db.security.getPermission('Create', cl)
403         db.security.addPermissionToRole('User', p)
405 You will need to explicitly let anonymous users access the web interface so
406 that regular users are able to see the login form. Note that almost all
407 trackers will need this Permission. The only situation where it's not
408 required is in a tracker that uses an HTTP Basic Authenticated front-end.
409 It's enabled by adding to your ``schema.py``::
411     p = db.security.getPermission('Web Access')
412     db.security.addPermissionToRole('Anonymous', p)
414 Finally, you will need to enable permission for your users to edit their
415 own details by adding the following to ``schema.py``::
417     # Users should be able to edit their own details. Note that this
418     # permission is limited to only the situation where the Viewed or
419     # Edited item is their own.
420     def own_record(db, userid, itemid):
421         '''Determine whether the userid matches the item being accessed.'''
422         return userid == itemid
423     p = db.security.addPermission(name='View', klass='user', check=own_record,
424         description="User is allowed to view their own user details")
425     p = db.security.addPermission(name='Edit', klass='user', check=own_record,
426         description="User is allowed to edit their own user details")
427     db.security.addPermissionToRole('User', p)
430 0.8.0 Use of TemplatingUtils
431 ----------------------------
433 If you used custom python functions in TemplatingUtils, they must
434 be moved from interfaces.py to a new file in the ``extensions`` directory. 
436 Each Function that should be available through TAL needs to be defined
437 as a toplevel function in the newly created file. Furthermore you
438 add an inititialization function, that registers the functions with the 
439 tracker.
441 If you find this too tedious, donfu wrote an automatic init function that
442 takes an existing TemplatingUtils class, and registers all class methods
443 that do not start with an underscore. The following hack should be placed
444 in the ``extensions`` directory alongside other extensions::
446     class TemplatingUtils:
447          # copy from interfaces.py
449     def init(tracker):
450          util = TemplatingUtils()
452          def setClient(tu):
453              util.client = tu.client
454              return util
456          def execUtil(name):
457              return lambda tu, *args, **kwargs: \
458                      getattr(setClient(tu), name)(*args, **kwargs)
460          for name in dir(util):
461              if callable(getattr(util, name)) and not name.startswith('_'):
462                   tracker.registerUtil(name, execUtil(name))
465 0.8.0 Logging Configuration
466 ---------------------------
468 See the `administration guide`_ for information about configuring the new
469 logging implemented in 0.8.0.
472 Migrating from 0.7.2 to 0.7.3
473 =============================
475 0.7.3 Configuration
476 -------------------
478 If you choose, you may specify the directory from which static files are
479 served (those which use the URL component ``@@file``). Currently the
480 directory defaults to the ``TEMPLATES`` configuration variable. You may
481 define a new variable, ``STATIC_FILES`` which overrides this value for
482 static files.
485 Migrating from 0.7.0 to 0.7.2
486 =============================
488 0.7.2 DEFAULT_TIMEZONE is now required
489 --------------------------------------
491 The DEFAULT_TIMEZONE configuration variable is now required. Add the
492 following to your tracker's ``config.py`` file::
494     # You may specify a different default timezone, for use when users do not
495     # choose their own in their settings.
496     DEFAULT_TIMEZONE = 0            # specify as numeric hour offest
499 Migrating from 0.7.0 to 0.7.1
500 =============================
502 0.7.1 Permission assignments
503 ----------------------------
505 If you allow anonymous access to your tracker, you might need to assign
506 some additional View (or Edit if your tracker is that open) permissions
507 to the "anonymous" user. To do so, find the code in your ``dbinit.py`` that
508 says::
510     for cl in 'issue', 'file', 'msg', 'query', 'keyword':
511         p = db.security.getPermission('View', cl)
512         db.security.addPermissionToRole('User', p)
513         p = db.security.getPermission('Edit', cl)
514         db.security.addPermissionToRole('User', p)
515     for cl in 'priority', 'status':
516         p = db.security.getPermission('View', cl)
517         db.security.addPermissionToRole('User', p)
519 Add add a line::
521         db.security.addPermissionToRole('Anonymous', p)
523 next to the existing ``'User'`` lines for the Permissions you wish to
524 assign to the anonymous user.
527 Migrating from 0.6 to 0.7
528 =========================
530 0.7.0 Permission assignments
531 ----------------------------
533 Due to a change in the rendering of web widgets, permissions are now
534 checked on Classes where they previously weren't (this is a good thing).
536 You will need to add some additional Permission assignments for your
537 regular users, or some displays will break. After the following in your 
538 tracker's ``dbinit.py``::
540     # Assign the access and edit Permissions for issue, file and message
541     # to regular users now
542     for cl in 'issue', 'file', 'msg', 'query', 'keyword':
543         p = db.security.getPermission('View', cl)
544         db.security.addPermissionToRole('User', p)
545         p = db.security.getPermission('Edit', cl)
546         db.security.addPermissionToRole('User', p)
548 add::
550     for cl in 'priority', 'status':
551         p = db.security.getPermission('View', cl)
552         db.security.addPermissionToRole('User', p)
555 0.7.0 Getting the current user id
556 ---------------------------------
558 The Database.curuserid attribute has been removed.
560 Any code referencing this attribute should be replaced with a
561 call to Database.getuid().
564 0.7.0 ZRoundup changes
565 ----------------------
567 The templates in your tracker's html directory will need updating if you
568 wish to use ZRoundup. If you've not modified those files (or some of them),
569 you may just copy the new versions from the Roundup source in the
570 templates/classic/html directory.
572 If you have modified the html files, then you'll need to manually edit them
573 to change all occurances of special form variables from using the colon ":"
574 special character to the at "@" special character. That is, variables such
575 as::
577   :action :required :template :remove:messages ...
579 should become::
581   @action @required @template @remove@messages ...
583 Note that ``tal:`` statements are unaffected. So are TAL expression type
584 prefixes such as ``python:`` and ``string:``. Please ask on the
585 roundup-users mailing list for help if you're unsure.
588 0.7.0 Edit collision detection
589 ------------------------------
591 Roundup now detects collisions with editing in the web interface (that is,
592 two people editing the same item at the same time).
594 You must copy the ``_generic.collision.html`` file from Roundup source in
595 the ``templates/classic/html`` directory. to your tracker's ``html``
596 directory.
599 Migrating from 0.6.x to 0.6.3
600 =============================
602 0.6.3 Configuration
603 -------------------
605 You will need to copy the file::
607   templates/classic/detectors/__init__.py
609 to your tracker's ``detectors`` directory, replacing the one already there.
610 This fixes a couple of bugs in that file.
614 Migrating from 0.5 to 0.6
615 =========================
618 0.6.0 Configuration
619 -------------------
621 Introduced EMAIL_FROM_TAG config variable. This value is inserted into
622 the From: line of nosy email. If the sending user is "Foo Bar", the
623 From: line is usually::
625      "Foo Bar" <issue_tracker@tracker.example>
627 the EMAIL_FROM_TAG goes inside the "Foo Bar" quotes like so::
629      "Foo Bar EMAIL_FROM_TAG" <issue_tracker@tracker.example>
631 I've altered the mechanism in the detectors __init__.py module so that it
632 doesn't cross-import detectors from other trackers (if you run more than one
633 in a single roundup-server). This change means that you'll need to copy the
634 __init__.py from roundup/templates/classic/detectors/__init__.py to your
635 <tracker home>/detectors/__init__.py. Don't worry, the "classic" __init__ is a
636 one-size-fits-all, so it'll work even if you've added/removed detectors.
638 0.6.0 Templating changes
639 ------------------------
641 The ``user.item`` template (in the tracker home "templates" directory)
642 needs to have the following hidden variable added to its form (between the
643 ``<form...>`` and ``</form>`` tags::
645   <input type="hidden" name=":template" value="item">
648 0.6.0 Form handling changes
649 ---------------------------
651 Roundup's form handling capabilities have been significantly expanded. This
652 should not affect users of 0.5 installations - but if you find you're
653 getting errors from form submissions, please ask for help on the Roundup
654 users mailing list:
656   http://lists.sourceforge.net/lists/listinfo/roundup-users
658 See the customisation doc section on `Form Values`__ for documentation of the
659 new form variables possible.
661 __ customizing.html#form-values
664 0.6.0 Multilingual character set support
665 ----------------------------------------
667 Added internationalization support. This is done via encoding all data
668 stored in roundup database to utf-8 (unicode encoding). To support utf-8 in
669 web interface you should add the folowing line to your tracker's html/page
670 and html/_generic.help files inside <head> tag::
671   
672     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
674 Since latin characters in utf-8 have the same codes as in ASCII table, this
675 modification is optional for users who use only plain latin characters. 
677 After this modification, you will be able to see and enter any world
678 character via web interface. Data received via mail interface also converted
679 to utf-8, however only new messages will be converted. If your roundup
680 database contains some of non-ASCII characters in one of 8-bit encoding,
681 they will not be visible in new unicode environment. Some of such data (e.g.
682 user names, keywords, etc)  can be edited by administrator, the others
683 (e.g. messages' contents) is not editable via web interface. Currently there
684 is no tool for converting such data, the only solution is to close
685 appropriate old issues and create new ones with the same content.
688 0.6.0 User timezone support
689 ---------------------------
691 From version 0.6.0 roundup supports displaying of Date data in user' local
692 timezone if he/she has provided timezone information. To make it possible
693 some modification to tracker's schema and HTML templates are required.
694 First you must add string property 'timezone' to user class in dbinit.py
695 like this::
696   
697     user = Class(db, "user", 
698                     username=String(),   password=Password(),
699                     address=String(),    realname=String(), 
700                     phone=String(),      organisation=String(),
701                     alternate_addresses=String(),
702                     queries=Multilink('query'), roles=String(),
703                     timezone=String())
704   
705 And second - html interface. Add following lines to
706 $TRACKER_HOME/html/user.item template::
707   
708      <tr>
709       <th>Timezone</th>
710       <td tal:content="structure context/timezone/field">timezone</td>
711      </tr>
713 After that all users should be able to provide their timezone information.
714 Timezone should be a positive or negative integer - offset from GMT.
716 After providing timezone, roundup will show all dates values, found in web
717 and mail interfaces in local time. It will also accept any Date info in
718 local time, convert and store it in GMT.
721 0.6.0 Search page structure
722 ---------------------------
724 In order to accomodate query editing the search page has been restructured. If
725 you want to provide your users with query editing, you should update your
726 search page using the macros detailed in the customisation doc section
727 `Searching on categories`__.
729 __ customizing.html#searching-on-categories
731 Also, the url field in the query class no longer starts with a '?'. You'll need
732 to remove this question mark from the url field to support queries. There's
733 a script in the "tools" directory called ``migrate-queries.py`` that should
734 automatically change any existing queries for you. As always, make a backup
735 of your database before running such a script.
738 0.6.0 Notes for metakit backend users
739 -------------------------------------
741 Roundup 0.6.0 introduced searching on ranges of dates and intervals. To
742 support it, some modifications to interval storing routine were made. So if
743 your tracker uses metakit backend and your db schema contains intervals
744 property, searches on that property will not be accurate for db items that
745 was stored before roundup' upgrade. However all new records should be
746 searchable on intervals.
748 It is possible to convert your database to new format: you can export and
749 import back all your data (consult "Migrating backends" in "Maintenance"
750 documentation). After this operation all your interval properties should
751 become searchable.
753 Users of backends others than metakit should not worry about this issue.
756 Migrating from 0.4.x to 0.5.0
757 =============================
759 This has been a fairly major revision of Roundup:
761 1. Brand new, much more powerful, flexible, tasty and nutritious templating.
762    Unfortunately, this means all your current templates are useless. Hopefully
763    the new documentation and examples will be enough to help you make the
764    transition. Please don't hesitate to ask on roundup-users for help (or
765    complete conversions if you're completely stuck)!
766 2. The database backed got a lot more flexible, allowing Metakit and SQL
767    databases! The only decent SQL database implemented at present is sqlite,
768    but others shouldn't be a whole lot more work.
769 3. A brand new, highly flexible and much more robust security system including
770    a system of Permissions, Roles and Role assignments to users. You may now
771    define your own Permissions that may be checked in CGI transactions.
772 4. Journalling has been made less storage-hungry, so has been turned on
773    by default *except* for author, recipient and nosy link/unlink events. You
774    are advised to turn it off in your trackers too.
775 5. We've changed the terminology from "instance" to "tracker", to ease the
776    learning curve/impact for new users.
777 6. Because of the above changes, the tracker configuration has seen some
778    major changes. See below for the details.
780 Please, **back up your database** before you start the migration process. This
781 is as simple as copying the "db" directory and all its contents from your
782 tracker to somewhere safe.
785 0.5.0 Configuration
786 -------------------
788 First up, rename your ``instance_config.py`` file to just ``config.py``.
790 Then edit your tracker's ``__init__.py`` module. It'll currently look
791 like this::
793  from instance_config import *
794  try:
795      from dbinit import *
796  except ImportError:
797      pass # in installdir (probably :)
798  from interfaces import *
800 and it needs to be::
802  import config
803  from dbinit import open, init
804  from interfaces import Client, MailGW
806 Due to the new templating having a top-level ``page`` that defines links for
807 searching, indexes, adding items etc, the following variables are no longer
808 used:
810 - HEADER_INDEX_LINKS
811 - HEADER_ADD_LINKS
812 - HEADER_SEARCH_LINKS
813 - SEARCH_FILTERS
814 - DEFAULT_INDEX
815 - UNASSIGNED_INDEX
816 - USER_INDEX
817 - ISSUE_FILTER
819 The new security implementation will require additions to the dbinit module,
820 but also removes the need for the following tracker config variables:
822 - ANONYMOUS_ACCESS
823 - ANONYMOUS_REGISTER
825 but requires two new variables which define the Roles assigned to users who
826 register through the web and e-mail interfaces:
828 - NEW_WEB_USER_ROLES
829 - NEW_EMAIL_USER_ROLES
831 in both cases, 'User' is a good initial setting. To emulate
832 ``ANONYMOUS_ACCESS='deny'``, remove all "View" Permissions from the
833 "Anonymous" Role. To emulate ``ANONYMOUS_REGISTER='deny'``, remove the "Web
834 Registration" and/or the "Email Registration" Permission from the "Anonymous"
835 Role. See the section on customising security in the `customisation
836 documentation`_ for more information.
838 Finally, the following config variables have been renamed to make more sense:
840 - INSTANCE_HOME -> TRACKER_HOME
841 - INSTANCE_NAME -> TRACKER_NAME
842 - ISSUE_TRACKER_WEB -> TRACKER_WEB
843 - ISSUE_TRACKER_EMAIL -> TRACKER_EMAIL
846 0.5.0 Schema Specification
847 --------------------------
849 0.5.0 Database backend changes
850 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
852 Your select_db module in your tracker has changed a fair bit. Where it used
853 to contain::
855  # WARNING: DO NOT EDIT THIS FILE!!!
856  from roundup.backends.back_anydbm import Database
858 it must now contain::
860  # WARNING: DO NOT EDIT THIS FILE!!!
861  from roundup.backends.back_anydbm import Database, Class, FileClass, IssueClass
863 Yes, I realise the irony of the "DO NOT EDIT THIS FILE" statement :)
864 Note the addition of the Class, FileClass, IssueClass imports. These are very
865 important, as they're going to make the next change work too. You now need to
866 modify the top of the dbinit module in your tracker from::
868  import instance_config
869  from roundup import roundupdb
870  from select_db import Database
872  from roundup.roundupdb import Class, FileClass
874  class Database(roundupdb.Database, select_db.Database):
875      ''' Creates a hybrid database from:
876           . the selected database back-end from select_db
877           . the roundup extensions from roundupdb
878      '''
879      pass
881  class IssueClass(roundupdb.IssueClass):
882      ''' issues need the email information
883      '''
884      pass
886 to::
888  import config
889  from select_db import Database, Class, FileClass, IssueClass
891 Yes, remove the Database and IssueClass definitions and those other imports.
892 They're not needed any more!
894 Look for places in dbinit.py where ``instance_config`` is used too, and
895 rename them ``config``.
898 0.5.0 Journalling changes
899 ~~~~~~~~~~~~~~~~~~~~~~~~~
901 Journalling has been optimised for storage. Journalling of links has been
902 turned back on by default. If your tracker has a large user base, you may wish
903 to turn off journalling of nosy list, message author and message recipient
904 link and unlink events. You do this by adding ``do_journal='no'`` to the Class
905 initialisation in your dbinit. For example, your *msg* class initialisation
906 probably looks like this::
908     msg = FileClass(db, "msg",
909                     author=Link("user"), recipients=Multilink("user"),
910                     date=Date(),         summary=String(),
911                     files=Multilink("file"),
912                     messageid=String(),  inreplyto=String())
914 to turn off journalling of author and recipient link events, add
915 ``do_journal='no'`` to the ``author=Link("user")`` part of the statement,
916 like so::
918     msg = FileClass(db, "msg",
919                     author=Link("user", do_journal='no'),
920                     recipients=Multilink("user", do_journal='no'),
921                     date=Date(),         summary=String(),
922                     files=Multilink("file"),
923                     messageid=String(),  inreplyto=String())
925 Nosy list link event journalling is actually turned off by default now. If you
926 want to turn it on, change to your issue class' nosy list, change its
927 definition from::
929     issue = IssueClass(db, "issue",
930                     assignedto=Link("user"), topic=Multilink("keyword"),
931                     priority=Link("priority"), status=Link("status"))
933 to::
935     issue = IssueClass(db, "issue", nosy=Multilink("user", do_journal='yes'),
936                     assignedto=Link("user"), topic=Multilink("keyword"),
937                     priority=Link("priority"), status=Link("status"))
939 noting that your definition of the nosy Multilink will override the normal one.
942 0.5.0 User schema changes
943 ~~~~~~~~~~~~~~~~~~~~~~~~~
945 Users have two more properties, "queries" and "roles". You'll have something
946 like this in your dbinit module now::
948     user = Class(db, "user",
949                     username=String(),   password=Password(),
950                     address=String(),    realname=String(),
951                     phone=String(),      organisation=String(),
952                     alternate_addresses=String())
953     user.setkey("username")
955 and you'll need to add the new properties and the new "query" class to it
956 like so::
958     query = Class(db, "query",
959                     klass=String(),     name=String(),
960                     url=String())
961     query.setkey("name")
963     # Note: roles is a comma-separated string of Role names
964     user = Class(db, "user",
965                     username=String(),   password=Password(),
966                     address=String(),    realname=String(),
967                     phone=String(),      organisation=String(),
968                     alternate_addresses=String(),
969                     queries=Multilink('query'), roles=String())
970     user.setkey("username")
972 The "queries" property is used to store off the user's favourite database
973 queries. The "roles" property is explained below in `0.5.0 Security
974 Settings`_.
977 0.5.0 Security Settings
978 ~~~~~~~~~~~~~~~~~~~~~~~
980 See the `security documentation`_ for an explanation of how the new security
981 system works. In a nutshell though, the security is handled as a four step
982 process:
984 1. Permissions are defined as having a name and optionally a hyperdb class
985    they're specific to,
986 2. Roles are defined that have one or more Permissions,
987 3. Users are assigned Roles in their "roles" property, and finally
988 4. Roundup checks that users have appropriate Permissions at appropriate times
989    (like editing issues).
991 Your tracker dbinit module's *open* function now has to define any
992 Permissions that are specific to your tracker, and also the assignment
993 of Permissions to Roles. At the moment, your open function
994 ends with::
996     import detectors
997     detectors.init(db)
999     return db
1001 and what we need to do is insert some commands that will set up the security
1002 parameters. Right above the ``import detectors`` line, you'll want to insert
1003 these lines::
1005     #
1006     # SECURITY SETTINGS
1007     #
1008     # new permissions for this schema
1009     for cl in 'issue', 'file', 'msg', 'user':
1010         db.security.addPermission(name="Edit", klass=cl,
1011             description="User is allowed to edit "+cl)
1012         db.security.addPermission(name="View", klass=cl,
1013             description="User is allowed to access "+cl)
1015     # Assign the access and edit permissions for issue, file and message
1016     # to regular users now
1017     for cl in 'issue', 'file', 'msg':
1018         p = db.security.getPermission('View', cl)
1019         db.security.addPermissionToRole('User', p)
1020         p = db.security.getPermission('Edit', cl)
1021         db.security.addPermissionToRole('User', p)
1022     # and give the regular users access to the web and email interface
1023     p = db.security.getPermission('Web Access')
1024     db.security.addPermissionToRole('User', p)
1025     p = db.security.getPermission('Email Access')
1026     db.security.addPermissionToRole('User', p)
1028     # May users view other user information? Comment these lines out
1029     # if you don't want them to
1030     p = db.security.getPermission('View', 'user')
1031     db.security.addPermissionToRole('User', p)
1033     # Assign the appropriate permissions to the anonymous user's Anonymous
1034     # Role. Choices here are:
1035     # - Allow anonymous users to register through the web
1036     p = db.security.getPermission('Web Registration')
1037     db.security.addPermissionToRole('Anonymous', p)
1038     # - Allow anonymous (new) users to register through the email gateway
1039     p = db.security.getPermission('Email Registration')
1040     db.security.addPermissionToRole('Anonymous', p)
1041     # - Allow anonymous users access to the "issue" class of data
1042     #   Note: this also grants access to related information like files,
1043     #         messages, statuses etc that are linked to issues
1044     #p = db.security.getPermission('View', 'issue')
1045     #db.security.addPermissionToRole('Anonymous', p)
1046     # - Allow anonymous users access to edit the "issue" class of data
1047     #   Note: this also grants access to create related information like
1048     #         files and messages etc that are linked to issues
1049     #p = db.security.getPermission('Edit', 'issue')
1050     #db.security.addPermissionToRole('Anonymous', p)
1052     # oh, g'wan, let anonymous access the web interface too
1053     p = db.security.getPermission('Web Access')
1054     db.security.addPermissionToRole('Anonymous', p)
1056 Note in the comments there the places where you might change the permissions
1057 to restrict users or grant users more access. If you've created additional
1058 classes that users should be able to edit and view, then you should add them
1059 to the "new permissions for this schema" section at the start of the security
1060 block. Then add them to the "Assign the access and edit permissions" section
1061 too, so people actually have the new Permission you've created.
1063 One final change is needed that finishes off the security system's
1064 initialisation. We need to add a call to ``db.post_init()`` at the end of the
1065 dbinit open() function. Add it like this::
1067     import detectors
1068     detectors.init(db)
1070     # schema is set up - run any post-initialisation
1071     db.post_init()
1072     return db
1074 You may verify the setup of Permissions and Roles using the new
1075 "``roundup-admin security``" command.
1078 0.5.0 User changes
1079 ~~~~~~~~~~~~~~~~~~
1081 To support all those schema changes, you'll need to massage your user database
1082 a little too, to:
1084 1. make sure there's an "anonymous" user - this user is mandatory now and is
1085    the one that unknown users are logged in as.
1086 2. make sure all users have at least one Role.
1088 If you don't have the "anonymous" user, create it now with the command::
1090   roundup-admin create user username=anonymous roles=Anonymous
1092 making sure the capitalisation is the same as above. Once you've done that,
1093 you'll need to set the roles property on all users to a reasonable default.
1094 The admin user should get "Admin", the anonymous user "Anonymous"
1095 and all other users "User". The ``fixroles.py`` script in the tools directory
1096 will do this. Run it like so (where python is your python 2+ binary)::
1098   python tools/fixroles.py -i <tracker home> fixroles
1102 0.5.0 CGI interface changes
1103 ---------------------------
1105 The CGI interface code was completely reorganised and largely rewritten. The
1106 end result is that this section of your tracker interfaces module will need
1107 changing from::
1109  from roundup import cgi_client, mailgw
1110  from roundup.i18n import _
1111  
1112  class Client(cgi_client.Client):
1113      ''' derives basic CGI implementation from the standard module,
1114          with any specific extensions
1115      '''
1116      pass
1118 to::
1120  from roundup import mailgw
1121  from roundup.cgi import client
1122  
1123  class Client(client.Client): 
1124      ''' derives basic CGI implementation from the standard module,
1125          with any specific extensions
1126      '''
1127      pass
1129 You will also need to install the new version of roundup.cgi from the source
1130 cgi-bin directory if you're using it.
1133 0.5.0 HTML templating
1134 ---------------------
1136 You'll want to make a backup of your current tracker html directory. You
1137 should then copy the html directory from the Roundup source "classic" template
1138 and modify it according to your local schema changes.
1140 If you need help with the new templating system, please ask questions on the
1141 roundup-users mailing list (available through the roundup project page on
1142 sourceforge, http://roundup.sf.net/)
1145 0.5.0 Detectors
1146 ---------------
1148 The nosy reactor has been updated to handle the tracker not having an
1149 "assignedto" property on issues. You may want to copy it into your tracker's
1150 detectors directory. Chances are you've already fixed it though :)
1153 Migrating from 0.4.1 to 0.4.2
1154 =============================
1156 0.4.2 Configuration
1157 -------------------
1158 The USER_INDEX definition introduced in 0.4.1 was too restrictive in its
1159 allowing replacement of 'assignedto' with the user's userid. Users must change
1160 the None value of 'assignedto' to 'CURRENT USER' (the string, in quotes) for
1161 the replacement behaviour to occur now.
1163 The new configuration variables are:
1165 - EMAIL_KEEP_QUOTED_TEXT 
1166 - EMAIL_LEAVE_BODY_UNCHANGED
1167 - ADD_RECIPIENTS_TO_NOSY
1169 See the sample configuration files in::
1171  <roundup source>/roundup/templates/classic/instance_config.py
1173 and::
1175  <roundup source>/roundup/templates/extended/instance_config.py
1177 and the `customisation documentation`_ for information on how they're used.
1180 0.4.2 Changes to detectors
1181 --------------------------
1182 You will need to copy the detectors from the distribution into your instance
1183 home "detectors" directory. If you used the classic schema, the detectors
1184 are in::
1186  <roundup source>/roundup/templates/classic/detectors/
1188 If you used the extended schema, the detectors are in::
1190  <roundup source>/roundup/templates/extended/detectors/
1192 The change means that schema-specific code has been removed from the
1193 mail gateway and cgi interface and made into auditors:
1195 - nosyreactor.py has now got an updatenosy auditor which updates the nosy
1196   list with author, recipient and assignedto information.
1197 - statusauditor.py makes the unread or resolved -> chatting changes and
1198   presets the status of an issue to unread.
1200 There's also a bug or two fixed in the nosyreactor code.
1202 0.4.2 HTML templating changes
1203 -----------------------------
1204 The link() htmltemplate function now has a "showid" option for links and
1205 multilinks. When true, it only displays the linked item id as the anchor
1206 text. The link value is displayed as a tooltip using the title anchor
1207 attribute. To use in eg. the superseder field, have something like this::
1209    <td>
1210     <display call="field('superseder', showid=1)">
1211     <display call="classhelp('issue', 'id,title', label='list', width=500)">
1212     <property name="superseder">
1213      <br>View: <display call="link('superseder', showid=1)">
1214     </property>
1215    </td>
1217 The stylesheets have been cleaned up too. You may want to use the newer
1218 versions in::
1220  <roundup source>/roundup/templates/<template>/html/default.css
1224 Migrating from 0.4.0 to 0.4.1
1225 =============================
1227 0.4.1 Files storage
1228 -------------------
1230 Messages and files from newly created issues will be put into subdierectories
1231 in thousands e.g. msg123 will be put into files/msg/0/msg123, file2003
1232 will go into files/file/2/file2003. Previous messages are still found, but
1233 could be put into this structure.
1235 0.4.1 Configuration
1236 -------------------
1238 To allow more fine-grained access control, the variable used to check
1239 permission to auto-register users in the mail gateway is now called
1240 ANONYMOUS_REGISTER_MAIL rather than overloading ANONYMOUS_REGISTER. If the
1241 variable doesn't exist, then ANONYMOUS_REGISTER is tested as before.
1243 Configuring the links in the web header is now easier too. The following
1244 variables have been added to the classic instance_config.py::
1246   HEADER_INDEX_LINKS   - defines the "index" links to be made available
1247   HEADER_ADD_LINKS     - defines the "add" links
1248   DEFAULT_INDEX        - specifies the index view for DEFAULT
1249   UNASSIGNED_INDEX     - specifies the index view for UNASSIGNED
1250   USER_INDEX           - specifies the index view for USER
1252 See the <roundup source>/roundup/templates/classic/instance_config.py for more
1253 information - including how the variables are to be set up. Most users will
1254 just be able to copy the variables from the source to their instance home. If
1255 you've modified the header by changing the source of the interfaces.py file in
1256 the instance home, you'll need to remove that customisation and move it into
1257 the appropriate variables in instance_config.py.
1259 The extended schema has similar variables added too - see the source for more
1260 info.
1262 0.4.1 Alternate E-Mail Addresses
1263 --------------------------------
1265 If you add the property "alternate_addresses" to your user class, your users
1266 will be able to register alternate email addresses that they may use to
1267 communicate with roundup as. All email from roundup will continue to be sent
1268 to their primary address.
1270 If you have not edited the dbinit.py file in your instance home directory,
1271 you may simply copy the new dbinit.py file from the core code. If you used
1272 the classic schema, the interfaces file is in::
1274  <roundup source>/roundup/templates/classic/dbinit.py
1276 If you used the extended schema, the file is in::
1278  <roundup source>/roundup/templates/extended/dbinit.py 
1280 If you have modified your dbinit.py file, you need to edit the dbinit.py
1281 file in your instance home directory. Find the lines which define the user
1282 class::
1284     user = Class(db, "msg",
1285                     username=String(),   password=Password(),
1286                     address=String(),    realname=String(), 
1287                     phone=String(),      organisation=String(),
1288                     alternate_addresses=String())
1290 You will also want to add the property to the user's details page. The
1291 template for this is the "user.item" file in your instance home "html"
1292 directory. Similar to above, you may copy the file from the roundup source if
1293 you haven't modified it. Otherwise, add the following to the template::
1295    <display call="multiline('alternate_addresses')">
1297 with appropriate labelling etc. See the standard template for an idea.
1301 Migrating from 0.3.x to 0.4.0
1302 =============================
1304 0.4.0 Message-ID and In-Reply-To addition
1305 -----------------------------------------
1306 0.4.0 adds the tracking of messages by message-id and allows threading
1307 using in-reply-to. Most e-mail clients support threading using this
1308 feature, and we hope to add support for it to the web gateway. If you
1309 have not edited the dbinit.py file in your instance home directory, you may
1310 simply copy the new dbinit.py file from the core code. If you used the
1311 classic schema, the interfaces file is in::
1313  <roundup source>/roundup/templates/classic/dbinit.py
1315 If you used the extended schema, the file is in::
1317  <roundup source>/roundup/templates/extended/dbinit.py 
1319 If you have modified your dbinit.py file, you need to edit the dbinit.py
1320 file in your instance home directory. Find the lines which define the msg
1321 class::
1323     msg = FileClass(db, "msg",
1324                     author=Link("user"), recipients=Multilink("user"),
1325                     date=Date(),         summary=String(),
1326                     files=Multilink("file"))
1328 and add the messageid and inreplyto properties like so::
1330     msg = FileClass(db, "msg",
1331                     author=Link("user"), recipients=Multilink("user"),
1332                     date=Date(),         summary=String(),
1333                     files=Multilink("file"),
1334                     messageid=String(),  inreplyto=String())
1336 Also, configuration is being cleaned up. This means that your dbinit.py will
1337 also need to be changed in the open function. If you haven't changed your
1338 dbinit.py, the above copy will be enough. If you have, you'll need to change
1339 the line (round line 50)::
1341     db = Database(instance_config.DATABASE, name)
1343 to::
1345     db = Database(instance_config, name)
1348 0.4.0 Configuration
1349 --------------------
1350 ``TRACKER_NAME`` and ``EMAIL_SIGNATURE_POSITION`` have been added to the
1351 instance_config.py. The simplest solution is to copy the default values
1352 from template in the core source.
1354 The mail gateway now checks ``ANONYMOUS_REGISTER`` to see if unknown users
1355 are to be automatically registered with the tracker. If it is set to "deny"
1356 then unknown users will not have access. If it is set to "allow" they will be
1357 automatically registered with the tracker.
1360 0.4.0 CGI script roundup.cgi
1361 ----------------------------
1362 The CGI script has been updated with some features and a bugfix, so you should
1363 copy it from the roundup cgi-bin source directory again. Make sure you update
1364 the ROUNDUP_INSTANCE_HOMES after the copy.
1367 0.4.0 Nosy reactor
1368 ------------------
1369 The nosy reactor has also changed - copy the nosyreactor.py file from the core
1370 source::
1372    <roundup source>/roundup/templates/<template>/detectors/nosyreactor.py
1374 to your instance home "detectors" directory.
1377 0.4.0 HTML templating
1378 ---------------------
1379 The field() function was incorrectly implemented - links and multilinks now
1380 display as text fields when rendered using field(). To display a menu (drop-
1381 down or select box) you need to use the menu() function.
1385 Migrating from 0.2.x to 0.3.x
1386 =============================
1388 0.3.x Cookie Authentication changes
1389 -----------------------------------
1390 0.3.0 introduces cookie authentication - you will need to copy the
1391 interfaces.py file from the roundup source to your instance home to enable
1392 authentication. If you used the classic schema, the interfaces file is in::
1394  <roundup source>/roundup/templates/classic/interfaces.py
1396 If you used the extended schema, the file is in::
1398  <roundup source>/roundup/templates/extended/interfaces.py
1400 If you have modified your interfaces.Client class, you will need to take
1401 note of the login/logout functionality provided in roundup.cgi_client.Client
1402 (classic schema) or roundup.cgi_client.ExtendedClient (extended schema) and
1403 modify your instance code apropriately.
1406 0.3.x Password encoding
1407 -----------------------
1408 This release also introduces encoding of passwords in the database. If you
1409 have not edited the dbinit.py file in your instance home directory, you may
1410 simply copy the new dbinit.py file from the core code. If you used the
1411 classic schema, the interfaces file is in::
1413  <roundup source>/roundup/templates/classic/dbinit.py
1415 If you used the extended schema, the file is in::
1417  <roundup source>/roundup/templates/extended/dbinit.py
1420 If you have modified your dbinit.py file, you may use encoded passwords:
1422 1. Edit the dbinit.py file in your instance home directory
1423    a. At the first code line of the open() function::
1425        from roundup.hyperdb import String, Date, Link, Multilink
1427       alter to include Password, as so::
1429        from roundup.hyperdb import String, Password, Date, Link, Multilink
1431    b. Where the password property is defined (around line 66)::
1433        user = Class(db, "user", 
1434                        username=String(),   password=String(),
1435                        address=String(),    realname=String(), 
1436                        phone=String(),      organisation=String())
1437        user.setkey("username")
1439       alter the "password=String()" to "password=Password()"::
1441        user = Class(db, "user", 
1442                        username=String(),   password=Password(),
1443                        address=String(),    realname=String(), 
1444                        phone=String(),      organisation=String())
1445        user.setkey("username")
1447 2. Any existing passwords in the database will remain cleartext until they
1448    are edited. It is recommended that at a minimum the admin password be
1449    changed immediately::
1451       roundup-admin -i <instance home> set user1 password=<new password>
1454 0.3.x Configuration
1455 -------------------
1456 FILTER_POSITION, ANONYMOUS_ACCESS, ANONYMOUS_REGISTER have been added to
1457 the instance_config.py. Simplest solution is to copy the default values from
1458 template in the core source.
1460 MESSAGES_TO_AUTHOR has been added to the IssueClass in dbinit.py. Set to 'yes'
1461 to send nosy messages to the author. Default behaviour is to not send nosy
1462 messages to the author. You will need to add MESSAGES_TO_AUTHOR to your
1463 dbinit.py in your instance home.
1466 0.3.x CGI script roundup.cgi
1467 ----------------------------
1468 There have been some structural changes to the roundup.cgi script - you will
1469 need to install it again from the cgi-bin directory of the source
1470 distribution. Make sure you update the ROUNDUP_INSTANCE_HOMES after the
1471 copy.
1474 .. _`customisation documentation`: customizing.html
1475 .. _`security documentation`: security.html
1476 .. _`administration guide`: admin_guide.html