Code

merge from maintenance branch
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 3 Oct 2002 06:56:30 +0000 (06:56 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 3 Oct 2002 06:56:30 +0000 (06:56 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1311 57a73879-2fb5-44c3-a270-3262357dd7e2

30 files changed:
CHANGES.txt
TODO.txt
doc/.cvsignore
doc/Makefile
doc/announcement.txt
doc/customizing.txt
doc/design.txt
doc/developers.txt
doc/getting_started.txt [deleted file]
doc/index.txt
doc/installation.txt
roundup/__init__.py
roundup/admin.py
roundup/backends/__init__.py
roundup/backends/back_bsddb3.py
roundup/backends/back_gadfly.py
roundup/backends/portalocker.py
roundup/backends/rdbms_common.py
roundup/cgi/TAL/TALGenerator.py
roundup/cgi/client.py
roundup/cgi/templating.py
roundup/hyperdb.py
roundup/mailgw.py
roundup/templates/classic/html/issue.index
roundup/templates/classic/html/query.item
roundup/templates/classic/html/style.css
roundup/templates/classic/html/user.index
roundup/templates/minimal/html/style.css
roundup/templates/minimal/html/user.index
test/test_db.py

index e968842f9558fc00867538ef0c59ff45fb9f596c..597a754977475297cd8057fbae88be976c816425 100644 (file)
@@ -1,6 +1,19 @@
 This file contains the changes to the Roundup system over time. The entries
 are given with the most recent entry first.
 
+2002-10-?? 0.5.1
+- highlight rows in groups of three
+- metakit cleanups
+- nicer "navigation" style in index views
+
+
+2002-10-02 0.5.0
+- fixed style for alternating rows in user lists
+- fixed query edit form so it doesn't barf
+- #617133 ] 0.5.0pr1 uses nonexistent renderTemplate
+- merged Zope Collector #539 fix from ZPT CVS trunk
+
+
 2002-09-27 0.5.0 pr1
 - handling of None for Date/Interval/Password values in export/import
 - handling of journal values in export/import
@@ -48,6 +61,7 @@ are given with the most recent entry first.
   tracker interfaces module
 - fixed login attempt by user that doesn't exist
 
+
 2002-09-13 0.5.0 beta2
 -  all backends now have a .close() method, and it's used everywhere
 -  fixed bug in detectors __init__
index 598ea05ac8091e3877fdcd19b892c4ffe5cbc0d6..f02a0b0f3e3ff28d409870dd48d77bef67238458 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -30,6 +30,8 @@ pending mailgw    Allow multiple email addresses at one gw with different
 
 pending mailgw    Identification of users should have a configurable degree of
                   strictness (ie. turn off username==address matching)
+pending mailgw    Use in-reply-to for determining message lineage when subject
+                  line lets us down
 pending project   switch to a Roundup instance for Roundup bug/feature tracking
 pending security  authenticate over a secure connection
 pending security  optionally auth with Basic HTTP auth instead of cookies
@@ -44,15 +46,18 @@ pending web       Quick help links next to the property labels giving a
 pending web       clicking on a group header should filter for that type of
                   entry
 pending web       re-enable auth basic http auth
-pending web       search "refinement" - pre-fill the search page with the
-                  current search parameters
-pending web       UNIX init.d script for roundup-server
 pending web       allow multilink selections to select a "none" element to allow
                   people with broken browsers to select nothing?
 pending web       automagically link designators
 pending web       add checkbox-based removal/addition for multilink entries
                   (eg "add me"/"remove me" for nosy list)
+pending web       multilink item removal action (with retirement)
+pending web       search "refinement" - pre-fill the search page with the
+                  current search parameters
+pending web       column-heading sort stuff isn't implemented
 
+active  web       UNIX init.d script for roundup-server
 bug     docs      need to mention somewhere how sorting works
+bug     web       query editing isn't fully implemented
 ======= ========= =============================================================
 
index 21d8992392d0ec321cad37da8605cc3d9cd6adb2..a6565df5598ae4e936d8dd7a955cbd29a0985a21 100644 (file)
@@ -1,7 +1,6 @@
 announcement.html
 customizing.html
 developers.html
-getting_started.html
 implementation.html
 index.html
 installation.html
index 75d4f2b08b44d9d8d464c37f266f2d0f950df46c..987dfe7071249eaccb6ccda635c58dea3b3599e5 100644 (file)
@@ -2,7 +2,7 @@ PYTHON = /usr/bin/python2
 STXTOHTML = -c "from docutils.core import publish;publish(writer_name='html')"
 
 SOURCE = announcement.txt customizing.txt developers.txt FAQ.txt features.txt \
-    getting_started.txt glossary.txt implementation.txt index.txt \
+    glossary.txt implementation.txt index.txt design.txt \
     installation.txt security.txt upgrading.txt user_guide.txt \
     maintenance.txt
 
index e9c0646fb7d629b0646da4c3d08b4e002de826b0..b2602f2f710ddfd40428741a8a0e5488f1f6d339 100644 (file)
@@ -1,76 +1,37 @@
-===========================================================
-SC-Track Roundup 0.5 pre-release - an issue tracking system
-===========================================================
+=================================================
+SC-Track Roundup 0.5.0 - an issue tracking system
+=================================================
 
-Note: This is the final pre-release of the newest version of Roundup. It is
-      strongly recommended that you maintain your existing 0.4 installation if
-      you have one, and run 0.5 on a copy of the database. If you are
-      upgrading from 0.4, you must read doc/upgrading.txt!
+Note: If you are upgrading, you *must* read doc/upgrading.txt!
 
 Roundup requires python 2.1.1 for correct operation. Support for dumbdbm
 requires python 2.1.2 or 2.2. 2.1.3 and 2.2.1 are recommended.
 
-This release fixes the following specific problems:
-
-- fixes to import/export
-- password edit now has a confirmation field
-- cleanups and fixes to the shipped classic template
-- new backend for sqlite (and it rocks :)
-- many performance improvements in dbm and sql backends
-- cgi.client base URL is now obtained from the config TRACKER_WEB (as a result
-  request.url has gone away - there's too much magic in trying to figure
-  what it should be)
-- cgi-bin script redirects to https now if the request was https
-- FileClass "content" property wasn't being returned by getprops() in most
-  backends
-- we now verify instance attributes on instance open and throw a useful error
-  if they're not all there
-- sf bug 611217 ] menu() has problems when labelprop==None
-- verify contents of tracker module when the tracker is opened
-- fixes to value parsing from edit forms
-- mailgw was missing an "import sys" (!)
-- setup now installs scripts with python -O flag, doubling performance in some
-  cases (there's a lot of __debug__ use)
-- added getItem to HTMLClass so you can access arbitrary items in templates
-- replaced the content() callback ickiness with Page Template macro usage
-- changed the default CSS style to be less offensive to some ;)
-- better handling of Page Template compilation errors
-- sf bug 614188 ] Exception in mailgw.py
-- sf bug 613310 ] traceback on onexistant items
-- sf bug 613291 ] typos in nosy list
-- handle stupid mailers that QUOTE their Re; 'Re: "[issue1] bla blah"'
-- giving a user a Role that doesn't exist doesn't break stuff any more
-- revamped user guide, customisation guide, added basic maintenance guide
-- merged some bugfixes from the Zope Page Templates trunk
-- added the "minimal" template
-
-A lot has been done since 0.4:
+A lot has been done since 0.4.4:
 
 - new backend for metakit (thanks Gordon McMillan)
 - new backend for sqlite
 - new backend for gadfly (it's as done as it's going to get)
-- further split the dbm backends from the core code, allowing easier
-  non-dict-like backends (eg metakit, RDB)
 - added Boolean and Number types
-- fixed the journal bloat
+- fixed the journal bloat, re-enabling useful link journal events
 - full-text search may also search certain String properties
-- entire database export and import (incl files)
-- implemented and used the new access control mechanisms (Permissions, Roles)
+- entire database export and import (including files)
+- implemented new per-user access control mechanisms (Permissions, Roles)
 - switched templating to use Zope's PageTemplates giving much more flexibility
+- made web interface more generic, robust, give nicer errors, ...
 - revamped look and feel in web interface including cleaned up CSS usage
-- re-worked cgi interface to abstract out the explicit "issue" interface
-- switched to sessions for web authentication
+- switched to cookie-based sessions for web authentication
 - saving of named search queries
-- updated design document for new access controls
-- updated customisation document, including more examples
-- added maintenance guide
-- better mailgw help message (feature request #558562)
-- we handle "not found", access and item page render errors better
-- fixed double-submit by having new-item-submit redirect at end
+- lots of documentation cleanups including an updated customisation document
+  with los of examples and a new maintenance guide
 - roundup-server may be a daemon now (fork, logfile, pidfile)
-- renamed "instance" to "tracker" everywhere, and "node" to "item" in most
-  places
-- many more bug fixes, cleanups and minor improvements
+- many, many more bug fixes, cleanups and minor improvements (see CHANGES.txt)
+
+This final 0.5.0 release fixes the following problems:
+
+- fixed style for alternating rows in user lists
+- a couple of other minor bugs
+- updated demo to use 0.5 codebase
 
 Source and documentation is available at the website:
      http://roundup.sourceforge.net/
index 55d7acba758a240a37b15252600d5dad8eaff1c1..96e52ca9854619812441f9d44066ccb0e01e4528 100644 (file)
@@ -2,7 +2,7 @@
 Customising Roundup
 ===================
 
-:Version: $Revision: 1.50 $
+:Version: $Revision: 1.51 $
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -13,6 +13,9 @@ Customising Roundup
 What You Can Do
 ===============
 
+Before you get too far, it's probably worth having a quick read of the Roundup
+`design documentation`_.
+
 Customisation of Roundup can take one of five forms:
 
 1. `tracker configuration`_ file changes
@@ -20,6 +23,7 @@ Customisation of Roundup can take one of five forms:
 3. "definition" class `database content`_ changes
 4. behavioural changes, through detectors_
 5. `access controls`_
+6. change the `web interface`_
 
 The third case is special because it takes two distinctly different forms
 depending upon whether the tracker has been initialised or not. The other two
@@ -190,45 +194,27 @@ tracker. The "classic" schema looks like this::
 
     pri = Class(db, "priority", name=String(), order=String())
     pri.setkey("name")
-    pri.create(name="critical", order="1")
-    pri.create(name="urgent", order="2")
-    pri.create(name="bug", order="3")
-    pri.create(name="feature", order="4")
-    pri.create(name="wish", order="5")
 
     stat = Class(db, "status", name=String(), order=String())
     stat.setkey("name")
-    stat.create(name="unread", order="1")
-    stat.create(name="deferred", order="2")
-    stat.create(name="chatting", order="3")
-    stat.create(name="need-eg", order="4")
-    stat.create(name="in-progress", order="5")
-    stat.create(name="testing", order="6")
-    stat.create(name="done-cbb", order="7")
-    stat.create(name="resolved", order="8")
 
     keyword = Class(db, "keyword", name=String())
     keyword.setkey("name")
 
-    user = Class(db, "user", username=String(), password=String(),
-        address=String(), realname=String(), phone=String(),
-        organisation=String())
+    user = Class(db, "user", username=String(), organisation=String(),
+        password=String(), address=String(), realname=String(), phone=String())
     user.setkey("username")
-    user.create(username="admin", password=adminpw,
-        address=config.ADMIN_EMAIL)
 
-    msg = FileClass(db, "msg", author=Link("user"), recipients=Multilink
-        ("user"), date=Date(), summary=String(), files=Multilink("file"))
+    msg = FileClass(db, "msg", author=Link("user"), summary=String(),
+        date=Date(), recipients=Multilink("user"), files=Multilink("file"))
 
     file = FileClass(db, "file", name=String(), type=String())
 
-    issue = IssueClass(db, "issue", assignedto=Link("user"),
-        topic=Multilink("keyword"), priority=Link("priority"), status=Link
-        ("status"))
+    issue = IssueClass(db, "issue", topic=Multilink("keyword"),
+        status=Link("status"), assignedto=Link("user"),
+        priority=Link("priority"))
     issue.setkey('title')
 
-XXX security definitions
-
 Classes and Properties - creating a new information store
 ---------------------------------------------------------
 
@@ -434,6 +420,143 @@ See "`adding a new field to the classic schema`_" for an example that requires
 database content changes.
 
 
+Access Controls
+===============
+
+A set of Permissions are built in to the security module by default:
+
+- Edit (everything)
+- View (everything)
+
+The default interfaces define:
+
+- Web Registration
+- Web Access
+- Web Roles
+- Email Registration
+- Email Access
+
+These are hooked into the default Roles:
+
+- Admin (Edit everything, View everything, Web Roles)
+- User (Web Access, Email Access)
+- Anonymous (Web Registration, Email Registration)
+
+And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
+gets the "Anonymous" assigned when the database is initialised on installation.
+The two default schemas then define:
+
+- Edit issue, View issue (both)
+- Edit file, View file (both)
+- Edit msg, View msg (both)
+- Edit support, View support (extended only)
+
+and assign those Permissions to the "User" Role. Put together, these settings
+appear in the ``open()`` function of the tracker ``dbinit.py`` (the following
+is taken from the "minimal" template ``dbinit.py``)::
+
+    #
+    # SECURITY SETTINGS
+    #
+    # new permissions for this schema
+    for cl in ('user', ):
+        db.security.addPermission(name="Edit", klass=cl,
+            description="User is allowed to edit "+cl)
+        db.security.addPermission(name="View", klass=cl,
+            description="User is allowed to access "+cl)
+
+    # and give the regular users access to the web and email interface
+    p = db.security.getPermission('Web Access')
+    db.security.addPermissionToRole('User', p)
+    p = db.security.getPermission('Email Access')
+    db.security.addPermissionToRole('User', p)
+
+    # May users view other user information? Comment these lines out
+    # if you don't want them to
+    p = db.security.getPermission('View', 'user')
+    db.security.addPermissionToRole('User', p)
+
+    # Assign the appropriate permissions to the anonymous user's Anonymous
+    # Role. Choices here are:
+    # - Allow anonymous users to register through the web
+    p = db.security.getPermission('Web Registration')
+    db.security.addPermissionToRole('Anonymous', p)
+    # - Allow anonymous (new) users to register through the email gateway
+    p = db.security.getPermission('Email Registration')
+    db.security.addPermissionToRole('Anonymous', p)
+
+
+New User Roles
+--------------
+
+New users are assigned the Roles defined in the config file as:
+
+- NEW_WEB_USER_ROLES
+- NEW_EMAIL_USER_ROLES
+
+
+Changing Access Controls
+------------------------
+
+You may alter the configuration variables to change the Role that new web or
+email users get, for example to not give them access to the web interface if
+they register through email. 
+
+You may use the ``roundup-admin`` "``security``" command to display the
+current Role and Permission configuration in your tracker.
+
+Adding a new Permission
+~~~~~~~~~~~~~~~~~~~~~~~
+
+When adding a new Permission, you will need to:
+
+1. add it to your tracker's dbinit so it is created
+2. enable it for the Roles that should have it (verify with
+   "``roundup-admin security``")
+3. add it to the relevant HTML interface templates
+4. add it to the appropriate xxxPermission methods on in your tracker
+   interfaces module
+
+Example Scenarios
+~~~~~~~~~~~~~~~~~
+
+**automatic registration of users in the e-mail gateway**
+ By giving the "anonymous" user the "Email Registration" Role, any
+ unidentified user will automatically be registered with the tracker (with
+ no password, so they won't be able to log in through the web until an admin
+ sets them a password). Note: this is the default behaviour in the tracker
+ templates that ship with Roundup.
+
+**anonymous access through the e-mail gateway**
+ Give the "anonymous" user the "Email Access" and ("Edit", "issue") Roles
+ but not giving them the "Email Registration" Role. This means that when an
+ unknown user sends email into the tracker, they're automatically logged in
+ as "anonymous". Since they don't have the "Email Registration" Role, they
+ won't be automatically registered, but since "anonymous" has permission
+ to use the gateway, they'll still be able to submit issues. Note that the
+ Sender information - their email address - will not be available - they're
+ *anonymous*.
+
+**only developers may be assigned issues**
+ Create a new Permission called "Fixer" for the "issue" class. Create a new
+ Role "Developer" which has that Permission, and assign that to the
+ appropriate users. Filter the list of users available in the assignedto
+ list to include only those users. Enforce the Permission with an auditor. See
+ the example `restricting the list of users that are assignable to a task`_.
+
+**only managers may sign off issues as complete**
+ Create a new Permission called "Closer" for the "issue" class. Create a new
+ Role "Manager" which has that Permission, and assign that to the appropriate
+ users. In your web interface, only display the "resolved" issue state option
+ when the user has the "Closer" Permissions. Enforce the Permission with
+ an auditor. This is very similar to the previous example, except that the
+ web interface check would look like::
+
+   <option tal:condition="python:request.user.hasPermission('Closer')"
+           value="resolved">Resolved</option>
+
+
 Web Interface
 =============
 
@@ -777,9 +900,19 @@ forms:
    stringified. Path expressions may have an optional ``path:`` prefix, though
    they are the default expression type, so it's not necessary.
 
-   XXX | components of expressions
+   If an expression evaluates to ``default`` then the expression is
+   "cancelled" - whatever HTML already exists in the template will remain
+   (tag content in the case of tal:content, attributes in the case of
+   tal:attributes).
+
+   If an expression evaluates to ``nothing`` then the target of the expression
+   is removed (tag content in the case of tal:content, attributes in the case
+   of tal:attributes and the tag itself in the case of tal:replace).
 
-   XXX "nothing" and "default"
+   If an element in the path may not exist, then you can use the ``|``
+   operator in the expression to provide an alternative. So, the expression
+   ``request/form/foo/value | default`` would simply leave the current HTML
+   in place if the "foo" form variable doesn't exist.
 
 **String Expressions** - eg. ``string:hello ${user/name}``
    These expressions are simple string interpolations (though they can be just
@@ -1421,91 +1554,70 @@ through the "journal" method of the item*::
 Defining new web actions
 ------------------------
 
-XXX
-
-
-Access Controls
-===============
+You may define new actions to be triggered by the ``:action`` form variable.
+These are added to the tracker ``interfaces.py`` as methods on the ``Client``
+class. 
 
-A set of Permissions are built in to the security module by default:
+Adding action methods takes three steps; first you `define the new action
+method`_, then you `register the action method`_ with the cgi interface so
+it may be triggered by the ``:action`` form variable. Finally you actually
+`use the new action`_ in your HTML form.
 
-- Edit (everything)
-- View (everything)
+See "`setting up a "wizard" (or "druid") for controlled adding of issues`_"
+for an example.
 
-The default interfaces define:
-
-- Web Registration
-- Web Access
-- Web Roles
-- Email Registration
-- Email Access
+Define the new action method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-These are hooked into the default Roles:
+The action methods have the following interface::
 
-- Admin (Edit everything, View everything, Web Roles)
-- User (Web Access, Email Access)
-- Anonymous (Web Registration, Email Registration)
+    def myActionMethod(self):
+        ''' Perform some action. No return value is required.
+        '''
 
-And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
-gets the "Anonymous" assigned when the database is initialised on installation.
-The two default schemas then define:
+The *self* argument is an instance of your tracker ``instance.Client`` class - 
+thus it's mostly implemented by ``roundup.cgi.Client``. See the docstring of
+that class for details of what it can do.
 
-- Edit issue, View issue (both)
-- Edit file, View file (both)
-- Edit msg, View msg (both)
-- Edit support, View support (extended only)
+The method will typically check the ``self.form`` variable's contents. It
+may then:
 
-and assign those Permissions to the "User" Role. New users are assigned the
-Roles defined in the config file as:
+- add information to ``self.ok_message`` or ``self.error_message``
+- change the ``self.template`` variable to alter what the user will see next
+- raise Unauthorised, SendStaticFile, SendFile, NotFound or Redirect
+  exceptions
 
-- NEW_WEB_USER_ROLES
-- NEW_EMAIL_USER_ROLES
 
-You may alter the configuration variables to change the Role that new web or
-email users get, for example to not give them access to the web interface if
-they register through email.
+Register the action method
+~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-You may use the ``roundup-admin`` "``security``" command to display the
-current Role and Permission configuration in your tracker.
+The method is now written, but isn't available to the user until you add it to
+the `instance.Client`` class ``actions`` variable, like so::
 
-Adding a new Permission
------------------------
+    actions = client.Class.actions + (
+        ('myaction', 'myActionMethod'),
+    )
 
-When adding a new Permission, you will need to:
+This maps the action name "myaction" to the action method we defined.
 
-1. add it to your tracker's dbinit so it is created
-2. enable it for the Roles that should have it (verify with
-   "``roundup-admin security``")
-3. add it to the relevant HTML interface templates
-4. add it to the appropriate xxxPermission methods on in your tracker
-   interfaces module
 
-Example Scenarios
------------------
+Use the new action
+~~~~~~~~~~~~~~~~~~
 
-**automatic registration of users in the e-mail gateway**
- By giving the "anonymous" user the "Email Registration" Role, any
- unidentified user will automatically be registered with the tracker (with
- no password, so they won't be able to log in through the web until an admin
- sets them a password). Note: this is the default behaviour in the tracker
- templates that ship with Roundup.
+In your HTML form, add a hidden form element like so::
 
-**anonymous access through the e-mail gateway**
- Give the "anonymous" user the "Email Access" and ("Edit", "issue") Roles
- but not giving them the "Email Registration" Role. This means that when an
- unknown user sends email into the tracker, they're automatically logged in
- as "anonymous". Since they don't have the "Email Registration" Role, they
- won't be automatically registered, but since "anonymous" has permission
- to use the gateway, they'll still be able to submit issues. Note that the
- Sender information - their email address - will not be available - they're
- *anonymous*.
+  <input type="hidden" name=":action" value="myaction">
 
-XXX more examples needed
+where "myaction" is the name you registered in the previous step.
 
 
 Examples
 ========
 
+.. contents::
+   :local:
+   :depth: 1
+
 Adding a new field to the classic schema
 ----------------------------------------
 
@@ -2101,7 +2213,7 @@ Setting up a "wizard" (or "druid") for controlled adding of issues
    hooks to those actions in the "actions" attribute on that class, like so::
 
     actions = client.Class.actions + (
-        ('page1_submit', page1SubmitAction),
+        ('page1_submit', 'page1SubmitAction'),
     )
 
     def page1SubmitAction(self):
@@ -2157,9 +2269,100 @@ matches.
 We also remove the redundant password fields from the ``user.item`` template.
 
 
+Adding a "vacation" flag to users for stopping nosy messages
+------------------------------------------------------------
+
+When users go on vacation and set up vacation email bouncing, you'll start to
+see a lot of messages come back through Roundup "Fred is on vacation". Not
+very useful, and relatively easy to stop.
+
+1. add a "vacation" flag to your users::
+
+         user = Class(db, "user",
+                    username=String(),   password=Password(),
+                    address=String(),    realname=String(),
+                    phone=String(),      organisation=String(),
+                    alternate_addresses=String(),
+                    roles=String(), queries=Multilink("query"),
+                    vacation=Boolean())
+
+2. edit your detector ``nosyreactor.py`` so that the ``nosyreaction()``
+   consists of::
+
+    def nosyreaction(db, cl, nodeid, oldvalues):
+        # send a copy of all new messages to the nosy list
+        for msgid in determineNewMessages(cl, nodeid, oldvalues):
+            try:
+                users = db.user
+                messages = db.msg
+
+                # figure the recipient ids
+                sendto = []
+                r = {}
+                recipients = messages.get(msgid, 'recipients')
+                for recipid in messages.get(msgid, 'recipients'):
+                    r[recipid] = 1
+
+                # figure the author's id, and indicate they've received the
+                # message
+                authid = messages.get(msgid, 'author')
+
+                # possibly send the message to the author, as long as they aren't
+                # anonymous
+                if (db.config.MESSAGES_TO_AUTHOR == 'yes' and
+                        users.get(authid, 'username') != 'anonymous'):
+                    sendto.append(authid)
+                r[authid] = 1
+
+                # now figure the nosy people who weren't recipients
+                nosy = cl.get(nodeid, 'nosy')
+                for nosyid in nosy:
+                    # Don't send nosy mail to the anonymous user (that user
+                    # shouldn't appear in the nosy list, but just in case they
+                    # do...)
+                    if users.get(nosyid, 'username') == 'anonymous':
+                        continue
+                    # make sure they haven't seen the message already
+                    if not r.has_key(nosyid):
+                        # send it to them
+                        sendto.append(nosyid)
+                        recipients.append(nosyid)
+
+                # generate a change note
+                if oldvalues:
+                    note = cl.generateChangeNote(nodeid, oldvalues)
+                else:
+                    note = cl.generateCreateNote(nodeid)
+
+                # we have new recipients
+                if sendto:
+                    # filter out the people on vacation
+                    sendto = [i for i in sendto if not users.get(i, 'vacation', 0)]
+
+                    # map userids to addresses
+                    sendto = [users.get(i, 'address') for i in sendto]
+
+                    # update the message's recipients list
+                    messages.set(msgid, recipients=recipients)
+
+                    # send the message
+                    cl.send_message(nodeid, msgid, note, sendto)
+            except roundupdb.MessageSendError, message:
+                raise roundupdb.DetectorError, message
+
+   Note that this is the standard nosy reaction code, with the small addition
+   of::
+
+    # filter out the people on vacation
+    sendto = [i for i in sendto if not users.get(i, 'vacation', 0)]
+
+   which filters out the users that have the vacation flag set to true.
+
+
 -------------------
 
 Back to `Table of Contents`_
 
 .. _`Table of Contents`: index.html
+.. _`design documentation`: design.html
 
index f5fddbaab9b70ec82521ac6d7b965ad9da3f3a22..5d7ac09cd88a9ad8a203aea4285b8eaa631bd52f 100644 (file)
@@ -1135,34 +1135,29 @@ Displaying Properties
 ~~~~~~~~~~~~~~~~~~~~~
 
 Properties appear in the user interface in three contexts:
-in indices, in editors, and as filters.  For each type of
+in indices, in editors, and as search filters.  For each type of
 property, there are several display possibilities.  For example,
 in an index view, a string property may just be printed as
 a plain string, but in an editor view, that property should
 be displayed in an editable field.
 
 The display of a property is handled by functions in
-a displayers module.  Each function accepts at
-least three standard arguments -- the database, class name,
-and item id -- and returns a chunk of HTML.
+the ``cgi.templating`` module.
 
-Displayer functions are triggered by <display>
-tags in templates.  The call attribute of the tag
-provides a Python expression for calling the displayer
-function.  The three standard arguments are inserted in
-front of the arguments given.  For example, the occurrence of::
+Displayer functions are triggered by ``tal:content`` or ``tal:replace``
+tag attributes in templates.  The value of the attribute
+provides an expression for calling the displayer function.
+For example, the occurrence of::
 
-    <display call="plain('status', max=30)">
+    tal:content="context/status/plain"
 
 in a template triggers a call to::
     
-    plain(db, "issue", 13, "status", max=30)
+    context['status'].plain()
 
-
-when displaying issue 13 in the "issue" class.  The displayer
+where the context would be an item of the "issue" class.  The displayer
 functions can accept extra arguments to further specify
-details about the widgets that should be generated.  By defining new
-displayer functions, the user interface can be highly customized.
+details about the widgets that should be generated.
 
 Some of the standard displayer functions include:
 
@@ -1174,30 +1169,19 @@ plain     display a String property directly;
           to omit the time from the date stamp; for a Link or Multilink
           property, display the key strings of the linked items (or the
           ids if the linked class has no key property)
-field     display a property like the
-          plain displayer above, but in a text field
-          to be edited
-menu      for a Link property, display
-          a menu of the available choices
-link      for a Link or Multilink property,
-          display the names of the linked items, hyperlinked to the
-          issue views on those items
-count     for a Multilink property, display
-          a count of the number of links in the list
-reldate   display a Date property in terms
-          of an interval relative to the current date (e.g. "+ 3w", "- 2d").
-download  show a Link("file") or Multilink("file")
-          property using links that allow you to download files
-checklist for a Link or Multilink property,
-          display checkboxes for the available choices to permit filtering
+field     display a property like the plain displayer above, but in a text
+          field to be edited
+menu      for a Link property, display a menu of the available choices
 ========= ====================================================================
 
-TODO: See the htmltemplate pydoc for a complete list of the functions
+See the `customisation`_ documentation for the complete list.
 
 
 Index Views
 ~~~~~~~~~~~
 
+XXX The following needs to be clearer
+
 An index view contains two sections: a filter section
 and an index section.
 The filter section provides some widgets for selecting
@@ -1210,11 +1194,11 @@ Index View Specifiers
 An index view specifier looks like this (whitespace
 has been added for clarity)::
 
-    /issue?status=unread,in-progress,resolved&amp;
-        topic=security,ui&amp;
-        :group=priority&amp;
-        :sort=-activity&amp;
-        :filters=status,topic&amp;
+    /issue?status=unread,in-progress,resolved&
+        topic=security,ui&
+        :group=priority&
+        :sort=-activity&
+        :filters=status,topic&
         :columns=title,status,fixer
 
 
@@ -1256,29 +1240,6 @@ example is the default layout to be provided with
 the default bug-tracker schema described above in
 section 4.4.
 
-Filter Section
-""""""""""""""
-
-The template for a filter section provides the
-filtering widgets at the top of the index view.
-Fragments enclosed in ``<property>...</property>``
-tags are included or omitted depending on whether the
-view specifier requests a filter for a particular property.
-
-Here's a simple example of a filter template::
-
-    <property name=status>
-        <display call="checklist('status')">
-    </property>
-    <br>
-    <property name=priority>
-        <display call="checklist('priority')">
-    </property>
-    <br>
-    <property name=fixer>
-        <display call="menu('fixer')">
-    </property>
-
 Index Section
 """""""""""""
 
@@ -1293,15 +1254,9 @@ to display the values of the issue's properties.
 Here's a simple example of an index template::
 
     <tr>
-        <property name=title>
-            <td><display call="plain('title', max=50)"></td>
-        </property>
-        <property name=status>
-            <td><display call="plain('status')"></td>
-        </property>
-        <property name=fixer>
-            <td><display call="plain('fixer')"></td>
-        </property>
+      <td tal:condition="request/show/title" tal:content="contex/title"></td>
+      <td tal:condition="request/show/status" tal:content="contex/status"></td>
+      <td tal:condition="request/show/fixer" tal:content="contex/fixer"></td>
     </tr>
 
 Sorting
@@ -1340,36 +1295,25 @@ Here's an example of a basic editor template::
 
     <table>
     <tr>
-        <td colspan=2>
-            <display call="field('title', size=60)">
-        </td>
+        <td colspan=2 tal:content="python:context.title.field(size='60')"></td>
     </tr>
     <tr>
-        <td>
-            <display call="field('fixer', size=30)">
-        </td>
-        <td>
-            <display call="menu('status')>
-        </td>
+        <td tal:content="context/fixer/field"></td>
+        <td tal:content="context/status/menu"></td>
     </tr>
     <tr>
-        <td>
-            <display call="field('nosy', size=30)">
-        </td>
-        <td>
-            <display call="menu('priority')>
-        </td>
+        <td tal:content="context/nosy/field"></td>
+        <td tal:content="context/priority/menu"></td>
     </tr>
     <tr>
         <td colspan=2>
-            <display call="note()">
+          <textarea name=":note" rows=5 cols=60></textarea>
         </td>
     </tr>
     </table>
 
-As shown in the example, the editor template can also
-request the display of a "note" field, which is a
-text area for entering a note to go along with a change.
+As shown in the example, the editor template can also include a ":note" field,
+which is a text area for entering a note to go along with a change.
 
 When a change is submitted, the system automatically
 generates a message describing the changed properties.
@@ -1383,7 +1327,7 @@ An example of such a message might be this::
     fixer: (none)
     keywords: parrot,plumage,perch,nailed,dead
 
-If a note is given in the "note" field, the note is
+If a note is given in the ":note" field, the note is
 appended to the description.  The message is then added
 to the issue's message spool (thus triggering the standard
 detector to react by sending out this message to the nosy list).
@@ -1539,25 +1483,16 @@ to perform some action::
         # all ok
 
 Code in the core will make use of these methods, as should code in auditors in
-custom templates. The htmltemplate will implement a new tag, ``<require>``
-which has the form::
+custom templates. The HTML templating may access the access controls through
+the *user* attribute of the *request* variable. It exposes a ``hasPermission()``
+method::
 
-  <require permission="name,name,name" assignedto="$userid" status="open">
-   HTML to display if the user has the permission.
-  <else>
-   HTML to display if the user does not have the permission.
-  </require>
+  tal:condition="python:request.user.hasPermission('Edit', 'issue')"
 
-where:
+or, if the *context* is *issue*, then the following is the same::
 
-- the permission attribute gives a comma-separated list of permission names.
-  These are checked in turn using ``hasPermission`` and requires one to
-  be OK.
-- the other attributes are lookups on the item using ``hasItemPermission``. If
-  the attribute value is "$userid" then the current user's userid is tested.
+  tal:condition="python:request.user.hasPermission('Edit')"
 
-Any of these tests must pass or the ``<require>`` check will fail. The section
-of html within the side of the ``<else>`` that fails is remove from processing.
 
 Authentication of Users
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -1605,8 +1540,7 @@ system - automated request handlers running various report/escalation scripts
 privacy - issues that are only visible to some users
     A new property is added to the issue which marks the user or group of
     users who are allowed to view and edit the issue. An auditor will check
-    for edit access, and the htmltemplate <require> tag can check for view
-    access.
+    for edit access, and the template user object can check for view access.
 
 
 Deployment Scenarios
@@ -1638,5 +1572,13 @@ Changes to this document
 - Added section Hyperdatabase Implementations
 - "Item" has been renamed to "Issue" to account for the more specific nature
   of the Class.
+- New Templating
+- Access Controls
+
+------------------
+
+Back to `Table of Contents`_
 
+.. _`Table of Contents`: index.html
+.. _customisation: customizing.html
 
index 620d9a6eaa142c4d19c552bf1fb594256d8abf28..24f89594641a04ee350c16e41d0a8088d407b761 100644 (file)
@@ -2,7 +2,7 @@
 Developing Roundup
 ==================
 
-:Version: $Revision: 1.4 $
+:Version: $Revision: 1.5 $
 
 Note: the intended audience of this document is the developers of the core
 Roundup code. If you just wish to alter some behaviour of your Roundup
@@ -35,6 +35,46 @@ CVS Access
 
 To get CVS access, contact richard@users.sourceforge.net.
 
+CVS stuff:
+
+1. to tag a release (eg. the pre-release of 0.5.0)::
+
+    cvs tag release-0-5-0-pr1
+
+1. to make a branch (eg. branching for code freeze/release)::
+
+    cvs co -d maint-0-5 -r release-0-5-0-pr1
+    cd maint-0-5 
+    cvs tag -b maint-0-5
+
+2. to check out a branch (eg. the maintenance branch for 0.5.x)::
+
+    cvs co -d maint-0-5 -r maint-0-5
+
+3. to merge changes from the maintenance branch to the trunk, in the
+   directory containing the HEAD checkout::
+
+    cvs up -j maint-0-5
+
+Standard tag names:
+
+*release-maj-min-patch[-sub]*
+  Release of the major.minor.patch release, possibly a beta or pre-release,
+  in which case *sub* will be one of "b*N*" or "pr*N*".
+*maint-maj-min*
+  Maintenance branch for the major.minor release. Patch releases are tagged in
+  this branch.
+
+Typically, release happen like this:
+
+1. work progresses in the HEAD branch until milestones are met,
+2. a series of beta releases are tagged in the HEAD until the code is
+   stable enough to freeze,
+3. the pre-release is tagged in the HEAD, with the resultant code branched
+   to the maintenance branch for that release,
+4. bugs in the release are patched in the maintenance branch, and the final
+   and patch releases are tagged there, and
+5. further major work happens in the HEAD.
 
 Project Rules
 -------------
diff --git a/doc/getting_started.txt b/doc/getting_started.txt
deleted file mode 100644 (file)
index 689967a..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-===============
-Getting Started
-===============
-
-:Version: $Revision: 1.7 $
-
-.. contents::
-
-
-The following instructions assume that you have installed roundup. If you
-haven't, you may still proceed - just run the commands as
-"``PYTHONPATH=. python roundup/scripts/roundup_admin.py``" for
-``roundup-admin`` and
-"``PYTHONPATH=. python roundup/scripts/roundup_server.py``" for
-``roundup-server``.
-
-The Tracker
------------
-
-We'll be referring to the term tracker a lot from now on. A tracker is a
-directory in your filesystem that is where all the information about a live
-issue
-tracker database is stored. The data that is entered as issues, the users who
-access the database and the definition of the database itself all reside there:
-
-Hyperdatabase
-   This is the lowest component of Roundup and is where all the issues,
-   users, file attachments and messages are stored.
-
-Database schema
-   This describes the content of the hyperdatabase - what fields are stored
-   for issues, what user information, etc. Being stored in the tracker,
-   this allows it to be customised for a particular application. It also
-   means that changes in the Roundup core code do not affect a running
-   tracker.
-
-Web Interface
-   The web interface templates are defined in the tracker too - and the
-   actual CGI interface class is defined (mostly using base classes in the
-   Roundup core code) so it, like the database, may be customised for each
-   tracker in use.
-
-Trackers are created using the ``roundup-admin`` tool.
-
-Command Line Tool
------------------
-
-To set up a new tracker, run "``roundup-admin install``". You will be
-asked a few questions:
-
-1. Tracker home directory
-2. Schema to use
-3. Database back-end to use
-
-Once you've chosen these, roundup will install the tracker for you. It will
-then indicate that you should configure some more information in the
-file "``config.py``" in the tracker home.  It
-should be edited before roundup is initialised, and may have the following
-variable declarations:
-
-MAILHOST
-   The SMTP mail host that roundup will use to send mail
-MAIL_DOMAIN
-   The domain name used for email addresses
-TRACKER_WEB
-   The web address of the issue tracker's web interface
-
-The email addresses used by the system by default are:
-
-TRACKER_EMAIL: ``issue_tracker@MAIL_DOMAIN``
-   submissions of issues
-
-ADMIN_EMAIL: ``roundup-admin@MAIL_DOMAIN``
-   roundup's internal use (problems, etc)
-
-You may also alter the default schema - see the `customisation`_ documentation
-for more info on both configuration variables and schema modifications.
-
-Once you're happy (and note that you can change any of this after the tracker
-is initialised too!) you must run "``roundup-admin initialise``".
-
-You should also think about whether there is going to be controlled access
-to the
-tracker on the machine the tracker is running on. That is, who can
-actually make
-changes to the database using the roundup-admin tool. See the section on
-Users_and_Access_Control for information on how to secure your tracker from the
-start.
-
-E-Mail Interface
-----------------
-
-Setup 1: As a mail alias pipe process 
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Set up a mail alias called "issue_tracker" as (include the quote marks):
-"``|/usr/bin/python /usr/local/bin/roundup-mailgw <tracker_home>``"
-
-In some installations (e.g. RedHat 6.2 I think) you'll need to set up smrsh so
-sendmail will accept the pipe command. In that case, symlink
-``/etc/smrsh/roundup-mailgw`` to "``/usr/local/bin/roundup-mailgw``" and change
-the command to::
-
-    |roundup-mailgw <tracker_home>
-To test the mail gateway on unix systems, try::
-
-    echo test |mail -s '[issue] test' issue_tracker@your.domain
-
-
-Setup 2: As a regular cron job using a mailbox source
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Set ``roundup-mailgw`` up to run every 10 minutes or so. For example::
-
-  10 * * * * /usr/local/bin/roundup-mailgw <tracker_home> mailbox <mail_spool_file>
-
-Where the ``mail_spool_file`` argument is the location of the roundup submission
-user's mail spool. On most systems, the spool for a user "issue_tracker"
-will be "``/var/mail/issue_tracker``".
-
-Setup 3: As a regular cron job using a POP source
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To retrieve from a POP mailbox, use a similar cron entry to the mailbox one::
-
-  10 * * * * /usr/local/bin/roundup-mailgw <tracker_home> pop <pop_spec>
-    
-where pop_spec is "``username:password@server``" that specifies the roundup
-submission user's POP account name, password and server.
-
-
-Web Interface
--------------
-
-This software will work through apache or stand-alone.
-
-Stand-alone:
-  1. Edit roundup-server at the top - ``TRACKER_HOMES`` needs to know
-     about your tracker. You may also specify the values for
-     ``TRACKER_HOMES`` on the command-line using "name=home" pairs.
-    
-  2. "``roundup-server [-p port] (name=tracker_home)*``" (hostname may be "")
-  
-  3. Load up the page "``/<tracker name>/index``" where tracker name is the name
-     you nominated in ``TRACKER_HOMES``.
-
-Apache:
-  1. The CGI script is found in the cgi-bin directory of the roundup
-     distribution.
-     
-  2. Make sure roundup.cgi is executable. Edit it at the top -
-     ``TRACKER_HOMES`` needs to know about your tracker.
-     
-  3. Edit your "``/etc/httpd/conf/httpd.conf``" and make sure that the
-     "``/home/httpd/html/roundup/roundup.cgi``" script will be treated as a CGI script.
-     
-  4. Re-start your apache to re-load the config if necessary.
-  
-  5. Load up the page "``/roundup/roundup.cgi/index/``" where tracker name is the
-     name you nominated in ``TRACKER_HOMES``.
-     
-  6. To use the CGI script unchanged, which allows much easier updates, add
-     these directives to your "httpd.conf"::
-     
-         SetEnv ROUNDUP_LOG "/var/log/roundup.log"
-         SetEnv TRACKER_HOMES "Default=/usr/local/share/roundup/trackers/Default"
-         SetEnv ROUNDUP_DEBUG "0"
-
-  7. On Windows, write a batch file "roundup.bat" similar to the one below and
-     place it into your cgi-bin directory::
-      
-         @echo off
-         set ROUNDUP_LOG=c:\Python21\share\roundup\cgi.log
-         set TRACKER_HOMES=Default=c:\Python21\share\roundup\trackers\Default;
-         set ROUNDUP_DEBUG=0
-         c:\Python21\python.exe c:\Python21\share\roundup\cgi-bin\roundup.cgi
-
-
-Users 
------
-
-Users and permissions
-~~~~~~~~~~~~~~~~~~~~~
-
-By default, roundup automatically creates one user when the tracker database is
-initialised (using roundup-admin init). The user is "admin" and the password is
-the one you supply at that time.
-
-If users attempt to use roundup in any manner and are not identified to roundup,
-they will be using the database in a read-only mode. That is, if roundup doesn't
-know who they are, they can't change anything. This has the following
-repurcussions:
-
-Command-line interface
-    The data modification commands (create, init, retire, set) are performed
-    as the "admin" user. It is therefore important that the database be
-    protected by the filesystem if protection is required. On a Unix system,
-    the easiest and most flexible method of doing so is:
-
-    1. Add a new user and group to your system (e.g. "issue_tracker")
-    
-    2. When creating a new tracker home, use the following commands
-       (substituting tracker_home for the directory you want to use)::
-       
-           mkdir tracker_home
-           chown issue_tracker:issue_tracker tracker_home
-           chmod g+rwxs tracker_home
-           roundup-admin -i tracker_home init
-       
-    3. Now, edit the /etc/group line for the issue_tracker group so it
-       includes the unix logins of all the users who are going to
-       administer your roundup tracker. If you're running the web or mail
-       gateways, then be sure to include those users in the group too (on
-       some Linux systems, these users are "www" or "apache" and "mail".)
-       
-E-Mail interface
-    Users are identified by e-mail address - a new user entry will be created
-    for any e-mail address that is not recognised, so users are always
-    identified by roundup.
-
-Web interface
-    Unidentified users have read-only access. If the users database has an
-    entry with the username "anonymous", then unidentified users are
-    automatically logged in as that user. This gives them write access.
-
-**anonymous access and the ANONYMOUS_* configurations.**
-
-
-Adding users
-~~~~~~~~~~~~
-
-To add users, use one of the following interfaces:
-  
-1. On the web, access the URL .../<tracker name>/newuser to bring up a form
-   which may be used to add a new user.
-    
-2. On the command-line, use::
-
-    roundup-admin -i <tracker home> create user username=bozo password=bozo
-    address=richard@clown.org
-    
-   Supply the admin username and password. roundup-admin will print the id
-   of the new user.
-  
-3. Any e-mail sent to roundup from an address that doesn't match an existing
-   user in the database will result in a new user entry being created for
-   that user.
-    
-
-Issues
-------
-
-To add issues, use one of the following interfaces:
-
-1. On the web, access the URL .../<tracker name>/newissue to bring up a
-   form which may be used to add a new issue.
-  
-2. On the command-line, use::
-
-     roundup-admin -i <tracker home> create issue title="test issue"
-
-   Supply the admin username and password. roundup-admin will print the id
-   of the new issue.
-
-3. Any e-mail sent to roundup with the subject line containing [issue] will
-   automatically created a new issue in the database using the contents of
-   the e-mail.
-
------------------
-
-Back to `Table of Contents`_
-
-Next: `User Guide`_
-
-.. _`table of contents`: index.html
-.. _`user guide`: user_guide.html
-.. _`customisation`: customizing.html
-
index 4bb87a1570f94c55d11ed3631d5bf9f3fdd922a4..a1b5c0980d61310a9c1b462287bc3d969193e50a 100644 (file)
@@ -6,7 +6,7 @@ Contents
 ========
 
 - Overview_ and Features_
-- Installation_ and `Getting Started`_ (and Upgrading_)
+- Installation_ and Upgrading_
 - `User Guide`_
 - `Customising Roundup`_
 - `Maintaining Roundup Trackers`_
index 2f14f1444496eb8dcd75e23fbc54a8a5eb9c1f27..204a3e26c1bec869f9f646e3d549c61a621f5c31 100644 (file)
@@ -2,7 +2,7 @@
 Installing Roundup
 ==================
 
-:Version: $Revision: 1.29 $
+:Version: $Revision: 1.30 $
 
 .. contents::
 
@@ -117,7 +117,14 @@ Basic Installation Steps
           Select backend [anydbm]: anydbm
 
       You will now be directed to edit the tracker configuration and
-      initial schema. See `Customising Roundup`_ for details on configuration
+      initial schema.  At a minimum, you must set ``MAILHOST``,
+      ``TRACKER_WEB``, ``MAIL_DOMAIN`` and ``ADMIN_EMAIL``. If you just want
+      to get set up to test things quickly, you can even just set the
+      TRACKER_WEB variable to::
+
+         TRACKER_WEB = 'http://localhost:8080/support/'
+
+      See `Customising Roundup`_ for details on configuration
       and schema changes. Note that you may change any of the configuration
       after you've initialised the tracker - it's just better to have valid
       values for this stuff now.
@@ -131,9 +138,19 @@ Basic Installation Steps
 
       Once this is done, the tracker has been created.
 
-At this point, your tracker is set up, but doesn't have a nice user interface.
-To set that up, we need to `configure a web interface`_ and optionally
-`configure an email interface`_.
+3. At this point, your tracker is set up, but doesn't have a nice user
+   interface. To set that up, we need to `configure a web interface`_ and
+   optionally `configure an email interface`_. To quickly test the web
+   interface, assuming ``TRACKER_WEB`` is set to
+   ``'http://localhost:8080/support/'``::
+
+     roundup-server -p 8080 support=/opt/roundup/trackers/support
+
+   then direct your web browser at:
+
+     http://locahost:8080/support/
+
+   and you should see the tracker interface.
 
 
 Choosing Your Template
index 7f20653df61db65e66b49a325fba6a30e334458b..5386a397bad8133cdae14ca4e5d585ca7c423f32 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: __init__.py,v 1.13 2002-09-26 04:19:53 richard Exp $
+# $Id: __init__.py,v 1.14 2002-10-03 06:56:28 richard Exp $
+
+''' Roundup - issue tracking for knowledge workers.
 
-__doc__ = '''
 This is a simple-to-use and -install issue-tracking system with
 command-line, web and e-mail interfaces.
 
@@ -28,19 +29,19 @@ other participants. The system will facilitate communication among the
 participants by managing discussions and notifying interested parties when
 issues are edited. 
 
-Roundup's structure is that of a cake:
+Roundup's structure is that of a cake::
 
- _________________________________________________________________________
-|  E-mail Client   |   Web Browser   |   Detector Scripts   |    Shell    |
-|------------------+-----------------+----------------------+-------------|
-|   E-mail User    |    Web User     |      Detector        |   Command   | 
-|-------------------------------------------------------------------------|
-|                         Roundup Database Layer                          |
-|-------------------------------------------------------------------------|
-|                          Hyperdatabase Layer                            |
-|-------------------------------------------------------------------------|
-|                             Storage Layer                               |
- -------------------------------------------------------------------------
 _________________________________________________________________________
+ |  E-mail Client   |   Web Browser   |   Detector Scripts   |    Shell    |
+ |------------------+-----------------+----------------------+-------------|
+ |   E-mail User    |    Web User     |      Detector        |   Command   | 
+ |-------------------------------------------------------------------------|
+ |                         Roundup Database Layer                          |
+ |-------------------------------------------------------------------------|
+ |                          Hyperdatabase Layer                            |
+ |-------------------------------------------------------------------------|
+ |                             Storage Layer                               |
 -------------------------------------------------------------------------
 
 The first layer represents the users (chocolate).
 The second layer is the Roundup interface to the users (vanilla).
@@ -48,10 +49,11 @@ The third and fourth layers are the internal Roundup database storage
   mechanisms (strawberry).
 The final, lowest layer is the underlying database storage (rum).
 
-These are implemented in the code in the following manner:
+These are implemented in the code in the following manner::
+
   E-mail User: roundup-mailgw and roundup.mailgw
      Web User: cgi-bin/roundup.cgi or roundup-server over
-               roundup.cgi_client, roundup.cgitb and roundup.htmltemplate
+               roundup.cgi.client and roundup.cgi.template
      Detector: roundup.roundupdb and templates/<template>/detectors
       Command: roundup-admin
    Roundup DB: roundup.roundupdb
@@ -65,6 +67,6 @@ written by Ka-Ping Yee in the "doc" directory. If nothing else, it has a
 much prettier cake :)
 '''
 
-__version__ = '0.5.0pr1'
+__version__ = '0.5.0'
 
 # vim: set filetype=python ts=4 sw=4 et si
index 45338ffb6016907ccf879e3323984b829f47c047..88cbace2c8e6e3bb253a4d1ec896c38eadd70d07 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: admin.py,v 1.34 2002-09-26 07:41:54 richard Exp $
+# $Id: admin.py,v 1.35 2002-10-03 06:56:28 richard Exp $
+
+'''Administration commands for maintaining Roundup trackers.
+'''
 
 import sys, os, getpass, getopt, re, UserDict, shlex, shutil
 try:
@@ -51,7 +54,17 @@ class UsageError(ValueError):
     pass
 
 class AdminTool:
+    ''' A collection of methods used in maintaining Roundup trackers.
+
+        Typically these methods are accessed through the roundup-admin
+        script. The main() method provided on this class gives the main
+        loop for the roundup-admin script.
+
+        Actions are defined by do_*() methods, with help for the action
+        given in the method docstring.
 
+        Additional help may be supplied by help_*() methods.
+    '''
     def __init__(self):
         self.commands = CommandDict()
         for k in AdminTool.__dict__.keys():
@@ -73,14 +86,20 @@ class AdminTool:
             raise UsageError, _('no such class "%(classname)s"')%locals()
 
     def props_from_args(self, args):
+        ''' Produce a dictionary of prop: value from the args list.
+
+            The args list is specified as ``prop=value prop=value ...``.
+        '''
         props = {}
         for arg in args:
             if arg.find('=') == -1:
-                raise UsageError, _('argument "%(arg)s" not propname=value')%locals()
+                raise UsageError, _('argument "%(arg)s" not propname=value'
+                    )%locals()
             try:
                 key, value = arg.split('=')
             except ValueError:
-                raise UsageError, _('argument "%(arg)s" not propname=value')%locals()
+                raise UsageError, _('argument "%(arg)s" not propname=value'
+                    )%locals()
             if value:
                 props[key] = value
             else:
@@ -88,6 +107,8 @@ class AdminTool:
         return props
 
     def usage(self, message=''):
+        ''' Display a simple usage message.
+        '''
         if message:
             message = _('Problem: %(message)s)\n\n')%locals()
         print _('''%(message)sUsage: roundup-admin [options] <command> <arguments>
@@ -106,6 +127,8 @@ Help:
         self.help_commands()
 
     def help_commands(self):
+        ''' List the commands available with their precis help.
+        '''
         print _('Commands:'),
         commands = ['']
         for command in self.commands.values():
@@ -118,11 +141,13 @@ Help:
         print
 
     def help_commands_html(self, indent_re=re.compile(r'^(\s+)\S+')):
-       commands = self.commands.values()
+        ''' Produce an HTML command list.
+        '''
+        commands = self.commands.values()
         def sortfun(a, b):
             return cmp(a.__name__, b.__name__)
         commands.sort(sortfun)
-       for command in commands:
+        for command in commands:
             h = command.__doc__.split('\n')
             name = command.__name__[3:]
             usage = h[0]
@@ -308,7 +333,8 @@ Command help:
         print _('''
  You should now edit the tracker configuration file:
    %(config_file)s
- ... at a minimum, you must set MAILHOST, MAIL_DOMAIN and ADMIN_EMAIL.
+ ... at a minimum, you must set MAILHOST, TRACKER_WEB, MAIL_DOMAIN and
+ ADMIN_EMAIL.
 
  If you wish to modify the default schema, you should also edit the database
  initialisation file:
index d4efcf73b9ff49e7af9672ea228d80d071a99744..8a6b1079a54dac2899627b997de4fd489481b751 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: __init__.py,v 1.18 2002-09-23 12:02:53 richard Exp $
+# $Id: __init__.py,v 1.19 2002-10-03 06:56:29 richard Exp $
+
+''' Container for the hyperdb storage backend implementations.
+
+The __all__ variable is constructed containing only the backends which are
+available.
+'''
 
 __all__ = []
 
index be4d80d8a13e1d5ab746729f8dd0c2aa1c05f4ba..f894047ea6c2ea9710e19a6d1711495e33f4c6c1 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_bsddb3.py,v 1.17 2002-09-13 08:20:12 richard Exp $
+#$Id: back_bsddb3.py,v 1.18 2002-10-03 06:56:29 richard Exp $
+'''
+This module defines a backend that saves the hyperdatabase in BSDDB3.
+'''
 
 import bsddb3, os, marshal
 from roundup import hyperdb, date
index 1b9b05b38d7a29daf09d53cb71f7bc7f50aa024c..f81dd04826b4e38432da7062747c691dc057f654 100644 (file)
@@ -1,5 +1,6 @@
-# $Id: back_gadfly.py,v 1.27 2002-09-26 03:04:24 richard Exp $
-__doc__ = '''
+# $Id: back_gadfly.py,v 1.28 2002-10-03 06:56:29 richard Exp $
+''' Gadlfy relational database hypderb backend.
+
 About Gadfly
 ============
 
@@ -9,25 +10,6 @@ subset  of  the intergalactic standard RDBMS Structured Query Language
 SQL.
 
 
-Basic Structure
-===============
-
-We map roundup classes to relational tables. Automatically detect schema
-changes and modify the gadfly table schemas appropriately. Multilinks
-(which represent a many-to-many relationship) are handled through
-intermediate tables.
-
-Journals are stored adjunct to the per-class tables.
-
-Table names and columns have "_" prepended so the names can't
-clash with restricted names (like "order"). Retirement is determined by the
-__retired__ column being true.
-
-All columns are defined as VARCHAR, since it really doesn't matter what
-type they're defined as. We stuff all kinds of data in there ;) [as long as
-it's marshallable, gadfly doesn't care]
-
-
 Additional Instance Requirements
 ================================
 
index f9116f0477e34e7f61e91f4c75b17e2049f44739..fc0005bf344235b0ce0550e31314ba9ac3450d1c 100644 (file)
@@ -2,9 +2,9 @@
 #                  Requires python 1.5.2 or better.
 
 # ID line added by richard for Roundup file tracking
-# $Id: portalocker.py,v 1.1 2002-04-15 23:25:15 richard Exp $
+# $Id: portalocker.py,v 1.2 2002-10-03 06:56:29 richard Exp $
 
-"""Cross-platform (posix/nt) API for flock-style file locking.
+""" Cross-platform (posix/nt) API for flock-style file locking.
 
 Synopsis:
 
index 7c48bf563c0a057e2036a64a8ea309f205d91200..ced7ccaf31b56211202d91c24395e6806799052e 100644 (file)
@@ -1,4 +1,25 @@
-# $Id: rdbms_common.py,v 1.19 2002-09-26 03:04:24 richard Exp $
+# $Id: rdbms_common.py,v 1.20 2002-10-03 06:56:29 richard Exp $
+''' Relational database (SQL) backend common code.
+
+Basics:
+
+- map roundup classes to relational tables
+- automatically detect schema changes and modify the table schemas
+  appropriately (we store the "database version" of the schema in the
+  database itself as the only row of the "schema" table)
+- multilinks (which represent a many-to-many relationship) are handled through
+  intermediate tables
+- journals are stored adjunct to the per-class tables
+- table names and columns have "_" prepended so the names can't clash with
+  restricted names (like "order")
+- retirement is determined by the __retired__ column being true
+
+Database-specific changes may generally be pushed out to the overridable
+sql_* methods, since everything else should be fairly generic. There's
+probably a bit of work to be done if a database is used that actually
+honors column typing, since the initial databases don't (sqlite stores
+everything as a string, and gadfly stores anything that's marsallable).
+'''
 
 # standard python modules
 import sys, os, time, re, errno, weakref, copy
index 0bfadea8db55c20373be02215ffef00b5a4db483..53172554eb55383d9ded870b313f411da35074d8 100644 (file)
@@ -237,7 +237,7 @@ class TALGenerator:
             else:
                 self.emit("setGlobal", name, cexpr)
 
-    def emitOnError(self, name, onError):
+    def emitOnError(self, name, onError, TALtag, isend):
         block = self.popProgram()
         key, expr = parseSubstitution(onError)
         cexpr = self.compileExpression(expr)
@@ -246,7 +246,10 @@ class TALGenerator:
         else:
             assert key == "structure"
             self.emit("insertStructure", cexpr, {}, [])
-        self.emitEndTag(name)
+        if TALtag:
+            self.emitOptTag(name, (None, 1), isend)
+        else:
+            self.emitEndTag(name)
         handler = self.popProgram()
         self.emit("onError", block, handler)
 
@@ -471,7 +474,11 @@ class TALGenerator:
             todo["scope"] = 1
         if onError:
             self.pushProgram() # handler
+            if TALtag:
+                self.pushProgram() # start
             self.emitStartTag(name, list(attrlist)) # Must copy attrlist!
+            if TALtag:
+                self.pushProgram() # start
             self.pushProgram() # block
             todo["onError"] = onError
         if define:
@@ -560,7 +567,7 @@ class TALGenerator:
         if condition:
             self.emitCondition(condition)
         if onError:
-            self.emitOnError(name, onError)
+            self.emitOnError(name, onError, optTag and optTag[1], isend)
         if scope:
             self.emit("endScope")
         if defineSlot:
index 7d0e67261dfbf8a91d2c6ce38b654fd37e246475..df85b0c85d596b753b84e39327bce2c1fa79c2c6 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.48 2002-09-27 01:04:38 richard Exp $
+# $Id: client.py,v 1.49 2002-10-03 06:56:29 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -48,23 +48,36 @@ def initialiseSecurity(security):
     security.addPermissionToRole('Admin', p)
 
 class Client:
-    '''
-    A note about login
-    ------------------
-
-    If the user has no login cookie, then they are anonymous. There
-    are two levels of anonymous use. If there is no 'anonymous' user, there
-    is no login at all and the database is opened in read-only mode. If the
-    'anonymous' user exists, the user is logged in using that user (though
-    there is no cookie). This allows them to modify the database, and all
-    modifications are attributed to the 'anonymous' user.
+    ''' Instantiate to handle one CGI request.
 
-    Once a user logs in, they are assigned a session. The Client instance
-    keeps the nodeid of the session as the "session" attribute.
+    See inner_main for request processing.
 
-    Client attributes:
+    Client attributes at instantiation:
         "path" is the PATH_INFO inside the instance (with no leading '/')
         "base" is the base URL for the instance
+        "form" is the cgi form, an instance of FieldStorage from the standard
+               cgi module
+        "additional_headers" is a dictionary of additional HTTP headers that
+               should be sent to the client
+        "response_code" is the HTTP response code to send to the client
+
+    During the processing of a request, the following attributes are used:
+        "error_message" holds a list of error messages
+        "ok_message" holds a list of OK messages
+        "session" is the current user session id
+        "user" is the current user's name
+        "userid" is the current user's id
+        "template" is the current :template context
+        "classname" is the current class context name
+        "nodeid" is the current context item id
+
+    User Identification:
+     If the user has no login cookie, then they are anonymous and are logged
+     in as that user. This typically gives them all Permissions assigned to the
+     Anonymous Role.
+
+     Once a user logs in, they are assigned a session. The Client instance
+     keeps the nodeid of the session as the "session" attribute.
     '''
 
     def __init__(self, instance, request, env, form=None):
@@ -134,7 +147,6 @@ class Client:
             - NotFound       (raised wherever it needs to be)
               percolates up to the CGI interface that called the client
         '''
-        self.content_action = None
         self.ok_message = []
         self.error_message = []
         try:
@@ -167,7 +179,10 @@ class Client:
         except SendStaticFile, file:
             self.serve_static_file(str(file))
         except Unauthorised, message:
-            self.write(self.renderTemplate('page', '', error_message=message))
+            self.classname=None
+            self.template=''
+            self.error_message.append(message)
+            self.write(self.renderContext())
         except NotFound:
             # pass through
             raise
index 63c23b4f5b09ce693c2f4546a814913c5ba118e6..9f24a19fe400831077aebdc1b7f8886d62661b2f 100644 (file)
@@ -646,7 +646,7 @@ class HTMLItem(HTMLPermissions):
         req.updateFromURL(self._klass.get(self._nodeid, 'url'))
 
         # new template, using the specified classname and request
-        pt = getTemplate(self._db.config.TEMPLATES, req.classname, 'search')
+        pt = Templates(self._db.config.TEMPLATES).get(req.classname, 'search')
 
         # use our fabricated request
         return pt.render(self._client, req.classname, req)
index 65d07e9170c74153ab84809f6574bec920c5d604..221377c478b94bdcd8be2de04cd4b34ae1372b5e 100644 (file)
@@ -15,9 +15,9 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: hyperdb.py,v 1.82 2002-09-09 23:55:19 richard Exp $
+# $Id: hyperdb.py,v 1.83 2002-10-03 06:56:29 richard Exp $
 
-__doc__ = """
+"""
 Hyperdatabase implementation, especially field types.
 """
 
index 4cabc099c05c706f8c017eaa7568b3823e86c366..501da003ca21070f568fb94b37c76864d968ff23 100644 (file)
@@ -16,7 +16,7 @@
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
 
-__doc__ = '''
+'''
 An e-mail gateway for Roundup.
 
 Incoming messages are examined for multiple parts:
@@ -73,7 +73,7 @@ are calling the create() method to create a new node). If an auditor raises
 an exception, the original message is bounced back to the sender with the
 explanatory message given in the exception. 
 
-$Id: mailgw.py,v 1.93 2002-09-26 22:15:54 richard Exp $
+$Id: mailgw.py,v 1.94 2002-10-03 06:56:29 richard Exp $
 '''
 
 import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
index ecc05889305633a519f8f9ecaa9e8ca10e69d7d1..1c15ac4346e0cedb9e4e7a353ca6bb1ad212171d 100644 (file)
@@ -29,7 +29,7 @@ You are not allowed to view this page.
        tal:content="python:i[request.group[1]]" class="group">
    </th>
   </tr>
-  <tr tal:attributes="class python:['normal', 'alt'][repeat['i'].even()]">
+  <tr tal:attributes="class python:['normal', 'alt'][repeat['i'].index%6/3]">
    <td tal:condition="request/show/priority" tal:content="i/priority"></td>
    <td tal:condition="request/show/id" tal:content="i/id"></td>
    <td nowrap tal:condition="request/show/activity"
@@ -44,23 +44,19 @@ You are not allowed to view this page.
    <td tal:condition="request/show/assignedto" tal:content="i/assignedto"></td>
   </tr>
  </tal:block>
- <tr>
-  <td style="padding: 0" tal:attributes="colspan python:len(request.columns)">
-   <table class="list">
-    <tr><th style="text-align: left; border: 0">
-     <a tal:define="prev batch/previous" tal:condition="prev"
-        tal:attributes="href python:request.indexargs_href(request.classname,
-        {':startwith':prev.first, ':pagesize':prev.size})">&lt;&lt; previous</a>
-     &nbsp;
-    </th>
-    <th style="text-align: right; border: 0">
-     <a tal:define="next batch/next" tal:condition="next"
-        tal:attributes="href python:request.indexargs_href(request.classname,
-         {':startwith':next.first, ':pagesize':next.size})">next &gt;&gt;</a>
-     &nbsp;
-    </th></tr>
-   </table>
-  </td>
+ <tr class="navigation" tal:define="colspan python:len(request.columns)">
+  <th tal:attributes="colspan python:colspan/2">
+   <a tal:define="prev batch/previous" tal:condition="prev"
+      tal:attributes="href python:request.indexargs_href(request.classname,
+      {':startwith':prev.first, ':pagesize':prev.size})">&lt;&lt; previous</a>
+   &nbsp;
+  </th>
+  <th tal:attributes="colspan python:colspan/2 + colspan%2">
+   <a tal:define="next batch/next" tal:condition="next"
+      tal:attributes="href python:request.indexargs_href(request.classname,
+      {':startwith':next.first, ':pagesize':next.size})">next &gt;&gt;</a>
+   &nbsp;
+  </th>
  </tr>
 </table>
 
index c3b660437d1aef59ab052471ccd47d391cd8db6f..5e969d33bcab3ebab49f4449b88d5407a419a157 100755 (executable)
@@ -1,16 +1,3 @@
-<tal:block metal:use-macro="templates/page/macros/icing">
-<title metal:fill-slot="head_title">Query editing</title>
-<td class="page-header-top" metal:fill-slot="body_title">
- <h2>Query editing</h2>
-</td>
-<td class="content" metal:fill-slot="content">
-<span tal:condition="not:context/is_edit_ok">
-You are not allowed to view this page.
-</span>
+<!-- query.item -->
+<span tal:content="structure context/renderQueryForm" />
 
-<span tal:condition="context/is_edit_ok"
-      tal:content="structure context/renderQueryForm" />
-
-</td>
-
-</tal:block>
index 1a1574e889f26c61cea0e306aa2a404ed37bb739..287b697ce4818ace1280a0e614a905a5b3f23d3b 100644 (file)
@@ -153,6 +153,14 @@ table.list th:first-child {
   empty-cells: show;
 }
 
+table.list tr.navigation th {
+  text-align: right;
+}
+table.list tr.navigation th:first-child {
+  border-right: none;
+  text-align: left;
+}
+
 
 /* style for message displays */
 table.messages {
index 0a71c54d7640a5b6cbcf1643dbf94b35cf3f162a..cb371b5ea332e3f15a9763dcd4aa20acf24e17f3 100644 (file)
@@ -19,7 +19,7 @@ You are not allowed to view this page.
  <th>Phone number</th>
 </tr>
 <tr tal:repeat="user context/list"
-    tal:attributes="class python:['row-normal', 'row-alt'][repeat['user'].even()]">
+    tal:attributes="class python:['normal', 'alt'][repeat['user'].index%6/3]">
  <td>
   <a tal:attributes="href string:user${user/id}"
      tal:content="user/username">username</a>
index 1a1574e889f26c61cea0e306aa2a404ed37bb739..287b697ce4818ace1280a0e614a905a5b3f23d3b 100644 (file)
@@ -153,6 +153,14 @@ table.list th:first-child {
   empty-cells: show;
 }
 
+table.list tr.navigation th {
+  text-align: right;
+}
+table.list tr.navigation th:first-child {
+  border-right: none;
+  text-align: left;
+}
+
 
 /* style for message displays */
 table.messages {
index 1921d39d90340c93d48be24891f49f2b0f7879f1..30312c2f670921f3fe27d43b1f7ecd6bf8b3a2a2 100644 (file)
@@ -16,7 +16,7 @@ You are not allowed to view this page.
  <th>Email address</th>
 </tr>
 <tr tal:repeat="user context/list"
-    tal:attributes="class python:['row-normal', 'row-alt'][repeat['user'].even()]">
+    tal:attributes="class python:['normal', 'alt'][repeat['user'].index%6/3]">
  <td>
   <a tal:attributes="href string:user${user/id}"
      tal:content="user/username">username</a>
index 23a11a8d1c0a854ef9f55ed60d961a1cc94c9c0a..fe4faa681b6d903c5143ea4683174d72a0b3f5a6 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_db.py,v 1.57 2002-10-02 19:15:46 gmcm Exp $ 
+# $Id: test_db.py,v 1.58 2002-10-03 06:56:30 richard Exp $ 
 
 import unittest, os, shutil, time