changeset 2633:a9e1fff1e793

I thought I committed this last night. Ho hum. - This implements most of the rest of the new tracker config layout: - dbinit.py split between schema.py and initial_data.py - interfaces.py gone - tracker and detectors __init__.py gone - Added some missing functionality to backends: db_exists test and db_nuke. - Implemented configuration file options in postgresql backend. - Cleaned up tracker initialisation a lot.
author Richard Jones <richard@users.sourceforge.net>
date Tue, 27 Jul 2004 00:57:19 +0000
parents 9c55f2bc5961
children f47ca4541770
files cgi-bin/roundup.cgi demo.py frontends/ZRoundup/ZRoundup.py roundup/admin.py roundup/backends/back_anydbm.py roundup/backends/back_bsddb.py roundup/backends/back_bsddb3.py roundup/backends/back_metakit.py roundup/backends/back_postgresql.py roundup/backends/back_sqlite.py roundup/init.py roundup/instance.py roundup/scripts/roundup_mailgw.py roundup/scripts/roundup_server.py templates/classic/__init__.py templates/classic/config.ini templates/classic/config.py templates/classic/dbinit.py templates/classic/detectors/__init__.py templates/classic/initial_data.py templates/classic/interfaces.py templates/classic/schema.py
diffstat 22 files changed, 369 insertions(+), 538 deletions(-) [+]
line wrap: on
line diff
--- a/cgi-bin/roundup.cgi	Tue Jul 27 00:45:49 2004 +0000
+++ b/cgi-bin/roundup.cgi	Tue Jul 27 00:57:19 2004 +0000
@@ -16,7 +16,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: roundup.cgi,v 1.39 2004-06-29 08:59:08 richard Exp $
+# $Id: roundup.cgi,v 1.40 2004-07-27 00:57:17 richard Exp $
 
 # python version check
 from roundup import version_check
@@ -158,16 +158,19 @@
         else:
             tracker_home = TRACKER_HOMES[tracker]
             tracker = roundup.instance.open(tracker_home)
-            from roundup.cgi.client import Unauthorised, NotFound
-            client = tracker.Client(tracker, request, os.environ)
+            from roundup.cgi import client
+            if hasattr(tracker, 'Client'):
+                client = tracker.Client(tracker, request, os.environ)
+            else:
+                client = client.Client(tracker, request, os.environ)
             try:
                 client.main()
-            except Unauthorised:
+            except client.Unauthorised:
                 request.send_response(403)
                 request.send_header('Content-Type', 'text/html')
                 request.end_headers()
                 out.write('Unauthorised')
-            except NotFound:
+            except client.NotFound:
                 request.send_response(404)
                 request.send_header('Content-Type', 'text/html')
                 request.end_headers()
--- a/demo.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/demo.py	Tue Jul 27 00:57:19 2004 +0000
@@ -2,7 +2,7 @@
 #
 # Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
 # 
-# $Id: demo.py,v 1.11 2004-05-28 01:09:10 richard Exp $
+# $Id: demo.py,v 1.12 2004-07-27 00:57:17 richard Exp $
 
 import sys, os, string, re, urlparse
 import shutil, socket, errno, BaseHTTPServer
@@ -27,7 +27,7 @@
             module.db_nuke(config)
     elif backend == 'postgresql':
         class config:
-            POSTGRESQL_DATABASE = {'database': 'rounduptest'}
+            POSTGRESQL_DATABASE = 'rounduptest'
             DATABASE = 'home'
         if module.db_exists(config):
             module.db_nuke(config)
--- a/frontends/ZRoundup/ZRoundup.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/frontends/ZRoundup/ZRoundup.py	Tue Jul 27 00:57:19 2004 +0000
@@ -14,7 +14,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: ZRoundup.py,v 1.18 2004-07-21 04:50:40 richard Exp $
+# $Id: ZRoundup.py,v 1.19 2004-07-27 00:57:17 richard Exp $
 #
 ''' ZRoundup module - exposes the roundup web interface to Zope
 
@@ -40,7 +40,7 @@
 modulesecurity = ModuleSecurityInfo()
 
 import roundup.instance
-from roundup.cgi.client import NotFound
+from roundup.cgi import client
 
 modulesecurity.declareProtected('View management screens',
     'manage_addZRoundupForm')
@@ -139,7 +139,7 @@
     def roundup_opendb(self):
         '''Open the roundup instance database for a transaction.
         '''
-        instance = roundup.instance.open(self.instance_home)
+        tracker = roundup.instance.open(self.instance_home)
         request = RequestWrapper(self.REQUEST['RESPONSE'])
         env = self.REQUEST.environ
 
@@ -159,7 +159,9 @@
             env['TRACKER_NAME'] = path_components[-1]
 
         form = FormWrapper(self.REQUEST.form)
-        return instance.Client(instance, request, env, form)
+        if hasattr(tracker, 'Client'):
+            return tracker.Client(tracker, request, env, form)
+        return client.Client(tracker, request, env, form)
 
     security.declareProtected('View', 'index_html')
     def index_html(self):
@@ -208,7 +210,7 @@
             # and call roundup to do something 
             client.main()
             return ''
-        except NotFound:
+        except client.NotFound:
             raise 'NotFound', REQUEST.URL
             pass
         except:
--- a/roundup/admin.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/admin.py	Tue Jul 27 00:57:19 2004 +0000
@@ -16,7 +16,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
-# $Id: admin.py,v 1.76 2004-07-13 10:21:32 a1s Exp $
+# $Id: admin.py,v 1.77 2004-07-27 00:57:17 richard Exp $
 
 '''Administration commands for maintaining Roundup trackers.
 '''
@@ -445,12 +445,7 @@
             raise UsageError, _('Instance has not been installed')%locals()
 
         # is there already a database?
-        try:
-            db_exists = tracker.select_db.Database.exists(tracker.config)
-        except AttributeError:
-            # TODO: move this code to exists() static method in every backend
-            db_exists = os.path.exists(os.path.join(tracker_home, 'db'))
-        if db_exists:
+        if tracker.exists():
             ok = raw_input(_(
 """WARNING: The database is already initialised!
 If you re-initialise it, you will lose all the data!
@@ -458,16 +453,11 @@
             if ok.strip().lower() != 'y':
                 return 0
 
-            # Get a database backend in use by tracker
-            try:
-                # nuke it
-                tracker.select_db.Database.nuke(tracker.config)
-            except AttributeError:
-                # TODO: move this code to nuke() static method in every backend
-                shutil.rmtree(os.path.join(tracker_home, 'db'))
+            # nuke it
+            tracker.nuke()
 
         # GO
-        init.initialise(tracker_home, adminpw)
+        tracker.init(password.Password(adminpw))
 
         return 0
 
--- a/roundup/backends/back_anydbm.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_anydbm.py	Tue Jul 27 00:57:19 2004 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_anydbm.py,v 1.166 2004-07-22 04:47:35 richard Exp $
+#$Id: back_anydbm.py,v 1.167 2004-07-27 00:57:18 richard Exp $
 '''This module defines a backend that saves the hyperdatabase in a
 database chosen by anydbm. It is guaranteed to always be available in python
 versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
@@ -43,6 +43,16 @@
     Multilink, DatabaseError, Boolean, Number, Node
 from roundup.date import Range
 
+def db_exists(config):
+    # check for the user db
+    for db in 'user user.db'.split():
+        if os.path.exists(os.path.join(config.TRACKER_HOME, 'db', db)):
+            return 1
+    return 0
+
+def db_nuke(config):
+    shutil.rmtree(os.path.join(config.TRACKER_HOME, 'db'))
+
 #
 # Now the database
 #
--- a/roundup/backends/back_bsddb.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_bsddb.py	Tue Jul 27 00:57:19 2004 +0000
@@ -15,17 +15,23 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_bsddb.py,v 1.30 2004-07-02 05:22:09 richard Exp $
+#$Id: back_bsddb.py,v 1.31 2004-07-27 00:57:18 richard Exp $
 '''This module defines a backend that saves the hyperdatabase in BSDDB.
 '''
 __docformat__ = 'restructuredtext'
 
-import bsddb, os, marshal
+import bsddb, os, marshal, shutil
 from roundup import hyperdb, date
 
 # these classes are so similar, we just use the anydbm methods
 from back_anydbm import Database, Class, FileClass, IssueClass
 
+def db_exists(config):
+    return os.path.exists(os.path.join(config.TRACKER_HOME, 'db', 'user'))
+
+def db_nuke(config):
+    shutil.rmtree(os.path.join(config.TRACKER_HOME, 'db'))
+
 #
 # Now the database
 #
--- a/roundup/backends/back_bsddb3.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_bsddb3.py	Tue Jul 27 00:57:19 2004 +0000
@@ -15,17 +15,23 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_bsddb3.py,v 1.24 2004-07-02 05:22:09 richard Exp $
+#$Id: back_bsddb3.py,v 1.25 2004-07-27 00:57:18 richard Exp $
 '''This module defines a backend that saves the hyperdatabase in BSDDB3.
 '''
 __docformat__ = 'restructuredtext'
 
-import bsddb3, os, marshal, errno
+import bsddb3, os, marshal, errno, shutil
 from roundup import hyperdb, date
 
 # these classes are so similar, we just use the anydbm methods
 from back_anydbm import Database, Class, FileClass, IssueClass
 
+def db_exists(config):
+    return os.path.exists(os.path.join(config.TRACKER_HOME, 'db', 'user'))
+
+def db_nuke(config):
+    shutil.rmtree(os.path.join(config.TRACKER_HOME, 'db'))
+
 #
 # Now the database
 #
--- a/roundup/backends/back_metakit.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_metakit.py	Tue Jul 27 00:57:19 2004 +0000
@@ -1,4 +1,4 @@
-# $Id: back_metakit.py,v 1.81 2004-07-20 23:24:26 richard Exp $
+# $Id: back_metakit.py,v 1.82 2004-07-27 00:57:18 richard Exp $
 '''Metakit backend for Roundup, originally by Gordon McMillan.
 
 Known Current Bugs:
@@ -56,6 +56,13 @@
 READ = 0
 READWRITE = 1
 
+def db_exists(config):
+    return os.path.exists(os.path.join(config.TRACKER_HOME, 'db',
+        'tracker.mk4'))
+
+def db_nuke(config):
+    shutil.rmtree(os.path.join(config.TRACKER_HOME, 'db'))
+
 # general metakit error
 class MKBackendError(Exception):
     pass
--- a/roundup/backends/back_postgresql.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_postgresql.py	Tue Jul 27 00:57:19 2004 +0000
@@ -14,15 +14,37 @@
 from roundup import hyperdb, date
 from roundup.backends import rdbms_common
 
+from roundup import configuration
+
+configuration.SETTINGS += (
+    ("postgresql", (
+        (configuration.Option, 'database', 'roundup'),
+        (configuration.NullableOption, 'host', 'localhost'),
+        (configuration.NullableOption, 'port', '5432'),
+        (configuration.NullableOption, 'user', 'roundup'),
+        (configuration.NullableOption, 'password', 'roundup'),
+    )),
+)
+
+def connection_dict(config):
+    d = {
+        'database': config.POSTGRESQL_DATABASE,
+    }
+    for name in 'host', 'port', 'password', 'user':
+        cvar = 'POSTGRESQL_'+name.upper()
+        if config[cvar] is not None:
+            d[name] = config[cvar]
+    return d
+
 def db_create(config):
     """Clear all database contents and drop database itself"""
-    command = 'CREATE DATABASE %s'%config.POSTGRESQL_DATABASE['database']
+    command = 'CREATE DATABASE %s'%config.POSTGRESQL_DATABASE
     config.logging.getLogger('hyperdb').info(command)
     db_command(config, command)
 
 def db_nuke(config, fail_ok=0):
     """Clear all database contents and drop database itself"""
-    command = 'DROP DATABASE %s'% config.POSTGRESQL_DATABASE['database']
+    command = 'DROP DATABASE %s'% config.POSTGRESQL_DATABASE
     config.logging.getLogger('hyperdb').info(command)
     db_command(config, command)
 
@@ -33,7 +55,7 @@
     '''Perform some sort of database-level command. Retry 10 times if we
     fail by conflicting with another user.
     '''
-    template1 = config.POSTGRESQL_DATABASE.copy()
+    template1 = connection_dict(config)
     template1['database'] = 'template1'
     
     try:
@@ -70,7 +92,7 @@
 
 def db_exists(config):
     """Check if database already exists"""
-    db = getattr(config, 'POSTGRESQL_DATABASE')
+    db = connection_dict(config)
     try:
         conn = psycopg.connect(**db)
         conn.close()
@@ -82,7 +104,7 @@
     arg = '%s'
 
     def sql_open_connection(self):
-        db = getattr(self.config, 'POSTGRESQL_DATABASE')
+        db = connection_dict(config)
         self.config.logging.getLogger('hyperdb').info('open database %r'%db)
         try:
             conn = psycopg.connect(**db)
--- a/roundup/backends/back_sqlite.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/backends/back_sqlite.py	Tue Jul 27 00:57:19 2004 +0000
@@ -1,4 +1,4 @@
-# $Id: back_sqlite.py,v 1.30 2004-07-02 05:22:09 richard Exp $
+# $Id: back_sqlite.py,v 1.31 2004-07-27 00:57:18 richard Exp $
 '''Implements a backend for SQLite.
 
 See https://pysqlite.sourceforge.net/ for pysqlite info
@@ -9,13 +9,19 @@
 '''
 __docformat__ = 'restructuredtext'
 
-import os, base64, marshal
+import os, base64, marshal, shutil
 
 from roundup import hyperdb, date, password
 from roundup.backends import locking
 from roundup.backends import rdbms_common
 import sqlite
 
+def db_exists(config):
+    return os.path.exists(os.path.join(config.TRACKER_HOME, 'db', 'db'))
+
+def db_nuke(config):
+    shutil.rmtree(os.path.join(config.TRACKER_HOME, 'db'))
+
 class Database(rdbms_common.Database):
     # char to use for positional arguments
     arg = '%s'
--- a/roundup/init.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/init.py	Tue Jul 27 00:57:19 2004 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: init.py,v 1.29 2004-02-11 23:55:08 richard Exp $
+# $Id: init.py,v 1.30 2004-07-27 00:57:18 richard Exp $
 
 """Init (create) a roundup instance.
 """
@@ -159,20 +159,13 @@
 def write_select_db(instance_home, backend):
     ''' Write the file that selects the backend for the tracker
     '''
-    # now select database
-    db = '''# WARNING: DO NOT EDIT THIS FILE!!!
-from roundup.backends.back_%s import Database, Class, FileClass, IssueClass
-'''%backend
-    open(os.path.join(instance_home, 'select_db.py'), 'w').write(db)
+    dbdir = os.path.join(instance_home, 'db')
+    if not os.path.exists(dbdir):
+        os.makedirs(dbdir)
+    f = open(os.path.join(dbdir, 'backend_name'), 'w')
+    f.write(backend+'\n')
+    f.close()
 
 
-def initialise(instance_home, adminpw):
-    '''Initialise an instance's database
-
-    adminpw    - the password for the "admin" user
-    '''
-    # now import the instance and call its init
-    instance = roundup.instance.open(instance_home)
-    instance.init(password.Password(adminpw))
 
 # vim: set filetype=python ts=4 sw=4 et si
--- a/roundup/instance.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/instance.py	Tue Jul 27 00:57:19 2004 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
-# $Id: instance.py,v 1.16 2004-07-25 13:14:57 a1s Exp $
+# $Id: instance.py,v 1.17 2004-07-27 00:57:18 richard Exp $
 
 '''Tracker handling (open tracker).
 
@@ -25,31 +25,80 @@
 
 import os
 from roundup import configuration, rlog
+from roundup import hyperdb, backends
 
 class Vars:
-    ''' I'm just a container '''
+    def __init__(self, vars):
+        self.__dict__.update(vars)
 
 class Tracker:
     def __init__(self, tracker_home):
         self.tracker_home = tracker_home
-        self.select_db = self._load_python('select_db.py')
-        self.config = self._load_config('config.py')
-        raise NotImplemented, 'this is *so* not finished'
-        self.init =  XXX
-        self.Client = XXX
-        self.MailGW = XXX
+        self.config = configuration.Config(tracker_home)
+        self.cgi_actions = {}
+        self.templating_utils = {}
+
+    def get_backend(self):
+        o = __builtins__['open']
+        f = o(os.path.join(self.tracker_home, 'db', 'backend_name'))
+        name = f.readline().strip()
+        f.close()
+        return getattr(backends, name)
 
-    def open(self):
-        return self._load_config('schema.py').db
-        self._load_config('security.py', db=db)
-
+    def open(self, name):
+        backend = self.get_backend()
+        vars = {
+            'Class': backend.Class,
+            'FileClass': backend.FileClass,
+            'IssueClass': backend.IssueClass,
+            'String': hyperdb.String,
+            'Password': hyperdb.Password,
+            'Date': hyperdb.Date,
+            'Link': hyperdb.Link,
+            'Multilink': hyperdb.Multilink,
+            'Interval': hyperdb.Interval,
+            'Boolean': hyperdb.Boolean,
+            'Number': hyperdb.Number,
+            'db': backend.Database(self.config, name)
+        }
+        self._load_python('schema.py', vars)
+        db = vars['db']
 
-    def _load_python(self, file):
+        detectors_dir = os.path.join(self.tracker_home, 'detectors')
+        for name in os.listdir(detectors_dir):
+            if not name.endswith('.py'):
+                continue
+            self._load_python(os.path.join('detectors', name), vars)
+            vars['init'](db)
+
+        db.post_init()
+        return db
+
+    def init(self, adminpw):
+        db = self.open('admin')
+        self._load_python('initial_data.py', {'db': db, 'adminpw': adminpw,
+            'admin_email': self.config['ADMIN_EMAIL']})
+        db.commit()
+        db.close()
+
+    def exists(self):
+        backend = self.get_backend()
+        return backend.db_exists(self.config)
+
+    def nuke(self):
+        backend = self.get_backend()
+        backend.db_nuke(self.config)
+
+    def _load_python(self, file, vars):
         file = os.path.join(self.tracker_home, file)
-        vars = Vars()
-        execfile(file, vars.__dict__)
+        execfile(file, vars)
         return vars
 
+    def registerAction(self, name, action):
+        self.cgi_actions[name] = action
+
+    def registerUtil(self, name, function):
+        self.templating_utils[name] = function
 
 class TrackerError(Exception):
     pass
--- a/roundup/scripts/roundup_mailgw.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/scripts/roundup_mailgw.py	Tue Jul 27 00:57:19 2004 +0000
@@ -14,7 +14,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
-# $Id: roundup_mailgw.py,v 1.18 2004-05-28 00:56:50 richard Exp $
+# $Id: roundup_mailgw.py,v 1.19 2004-07-27 00:57:18 richard Exp $
 
 """Command-line script stub that calls the roundup.mailgw.
 """
@@ -26,7 +26,7 @@
 
 import sys, os, re, cStringIO, getopt
 
-from roundup.mailgw import Message
+from roundup import mailgw
 from roundup.i18n import _
 
 def usage(args, message=None):
@@ -132,7 +132,10 @@
 
     # now wrap in try/finally so we always close the database
     try:
-        handler = instance.MailGW(instance, db, optionsList)
+        if hasattr(instance, 'MailGW'):
+            handler = instance.MailGW(instance, db, optionsList)
+        else:
+            handler = mailgw.MailGW(instance, db, optionsList)
 
         # if there's no more arguments, read a single message from stdin
         if len(args) == 1:
--- a/roundup/scripts/roundup_server.py	Tue Jul 27 00:45:49 2004 +0000
+++ b/roundup/scripts/roundup_server.py	Tue Jul 27 00:57:19 2004 +0000
@@ -17,7 +17,7 @@
 
 """Command-line script that runs a server over roundup.cgi.client.
 
-$Id: roundup_server.py,v 1.57 2004-07-27 00:45:49 richard Exp $
+$Id: roundup_server.py,v 1.58 2004-07-27 00:57:18 richard Exp $
 """
 __docformat__ = 'restructuredtext'
 
@@ -187,7 +187,10 @@
         decoded_query = query.replace('+', ' ')
 
         # do the roundup thang
-        c = tracker.Client(tracker, self, env)
+        if hasattr(tracker, 'Client'):
+            c = tracker.Client(tracker, self, env)
+        else:
+            c = client.Client(tracker, self, env)
         c.main()
 
     def address_string(self):
--- a/templates/classic/__init__.py	Tue Jul 27 00:45:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#
-# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
-# This module is free software, and you may redistribute it and/or modify
-# under the same terms as Python, so long as this copyright message and
-# disclaimer are retained in their original form.
-#
-# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
-# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
-# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-# 
-# $Id: __init__.py,v 1.1 2003-04-17 03:26:03 richard Exp $
-
-import config
-from dbinit import open, init
-from interfaces import Client, MailGW
-
-# vim: set filetype=python ts=4 sw=4 et si
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/classic/config.ini	Tue Jul 27 00:57:19 2004 +0000
@@ -0,0 +1,34 @@
+
+[main]
+database = %(TRACKER_HOME)s/db
+templates = %(TRACKER_HOME)s/html
+admin_email = roundup-admin
+new_web_user_roles = User
+new_email_user_roles = User
+error_messages_to = user
+html_version = html4
+default_timezone = 0
+
+[tracker]
+name = Roundup issue tracker
+web = http://tracker.example/cgi-bin/roundup.cgi/bugs/
+emaiL = issue_tracker
+
+[logging]
+level = ERROR
+
+[mail]
+domain = your.tracker.email.domain.example
+host = localhost
+charset = utf-8
+
+[mailgw]
+email_keep_quoted_text = yes
+email_leave_body_unchanged = no
+mail_default_class = issue
+
+[nosy]
+messages_to_author = no
+add_author_to_nosy = new
+add_recipients_to_nosy = new
+email_signature_position = bottom
--- a/templates/classic/config.py	Tue Jul 27 00:45:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-#
-# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
-# This module is free software, and you may redistribute it and/or modify
-# under the same terms as Python, so long as this copyright message and
-# disclaimer are retained in their original form.
-#
-# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
-# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
-# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-# 
-# $Id: config.py,v 1.10 2004-07-20 00:15:23 richard Exp $
-
-import os
-
-# roundup home is this package's directory
-TRACKER_HOME=os.path.split(__file__)[0]
-
-# The SMTP mail host that roundup will use to send mail
-MAILHOST = 'localhost'
-
-# If your SMTP mail host requires a username and password for access, then
-# specify them here.
-# eg. MAILUSER = ('username', 'password')
-MAILUSER = ()
-
-# If your SMTP mail host provides or requires TLS (Transport Layer
-# Security) then set MAILHOST_TLS = 'yes'
-# Optionallly, you may also set MAILHOST_TLS_KEYFILE to the name of a PEM
-# formatted file that contains your private key, and MAILHOST_TLS_CERTFILE
-# to the name of a PEM formatted certificate chain file.
-MAILHOST_TLS = 'no'
-MAILHOST_TLS_KEYFILE = ''
-MAILHOST_TLS_CERTFILE = ''
-
-# The domain name used for email addresses.
-MAIL_DOMAIN = 'your.tracker.email.domain.example'
-
-# This is the directory that the database is going to be stored in
-DATABASE = os.path.join(TRACKER_HOME, 'db')
-
-# This is the directory that the HTML templates reside in
-TEMPLATES = os.path.join(TRACKER_HOME, 'html')
-
-# Optional: the directory that static files are served from (files with the
-# URL /@@file/<filename>). If this is not defined, then static files are
-# served from the TEMPLATES directory.
-# STATIC_FILES = os.path.join(TRACKER_HOME, 'files')
-
-# A descriptive name for your roundup instance
-TRACKER_NAME = 'Roundup issue tracker'
-
-# The email address that mail to roundup should go to
-TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
-
-# The web address that the tracker is viewable at. This will be included in
-# information sent to users of the tracker. The URL MUST include the cgi-bin
-# part or anything else that is required to get to the home page of the
-# tracker. You MUST include a trailing '/' in the URL.
-TRACKER_WEB = 'http://tracker.example/cgi-bin/roundup.cgi/bugs/'
-
-# The email address that roundup will complain to if it runs into trouble
-ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
-
-# These variables define where to log Roundup's internal messages to.
-# You have two choices - either using the standard Python logging module
-# or a minimal logging facility built into Roundup. The former is activated
-# when you provide a LOGGING_CONFIG variable below which contains the
-# configuration of the logging module. The latter is activated when you
-# provide the LOGGING_FILENAME and optionally LOGGING_LEVEL variables. If
-# none of these variables are defined then only errors will be logged, and
-# they will go to stderr.
-# LOGGGING_CONFIG = os.path.join(TRACKER_HOME, 'logging.ini')
-# or,
-# LOGGING_FILENAME = '/path/to/log file'
-# LOGGING_LEVEL = 'INFO'   # one of 'DEBUG', 'INFO', 'WARNING', 'ERROR'
-
-# The 'dispatcher' is a role that can get notified of new items to the
-# database. It is used by the ERROR_MESSAGES_TO config setting.
-DISPATCHER_EMAIL = ADMIN_EMAIL
-
-# Additional text to include in the "name" part of the From: address used
-# in nosy messages. If the sending user is "Foo Bar", the From: line is
-# usually: "Foo Bar" <issue_tracker@tracker.example>
-# the EMAIL_FROM_TAG goes inside the "Foo Bar" quotes like so:
-#    "Foo Bar EMAIL_FROM_TAG" <issue_tracker@tracker.example>
-EMAIL_FROM_TAG = ""
-
-# 
-# SECURITY DEFINITIONS
-#
-# define the Roles that a user gets when they register with the tracker
-# these are a comma-separated string of role names (e.g. 'Admin,User')
-NEW_WEB_USER_ROLES = 'User'
-NEW_EMAIL_USER_ROLES = 'User'
-
-# Send error message emails to the dispatcher, user, or both?
-# If 'dispatcher', error messages will only be sent to the dispatcher.
-# If 'user',       error messages will only be sent to the user.
-# If 'both',       error messages will be sent to both individuals.
-# The dispatcher is configured using the DISPATCHER_EMAIL setting, which
-# defaults to ADMIN_EMAIL.
-ERROR_MESSAGES_TO = 'user'
-
-# Send nosy messages to the author of the message
-MESSAGES_TO_AUTHOR = 'no'           # either 'yes' or 'no'
-
-# Does the author of a message get placed on the nosy list automatically?
-# If 'new' is used, then the author will only be added when a message
-# creates a new issue. If 'yes', then the author will be added on followups
-# too. If 'no', they're never added to the nosy.
-ADD_AUTHOR_TO_NOSY = 'new'          # one of 'yes', 'no', 'new'
-
-# Do the recipients (To:, Cc:) of a message get placed on the nosy list?
-# If 'new' is used, then the recipients will only be added when a message
-# creates a new issue. If 'yes', then the recipients will be added on followups
-# too. If 'no', they're never added to the nosy.
-ADD_RECIPIENTS_TO_NOSY = 'new'      # either 'yes', 'no', 'new'
-
-# Where to place the email signature
-EMAIL_SIGNATURE_POSITION = 'bottom' # one of 'top', 'bottom', 'none'
-
-# Keep email citations when accepting messages. Setting this to "no" strips
-# out "quoted" text from the message. Signatures are also stripped.
-EMAIL_KEEP_QUOTED_TEXT = 'yes'      # either 'yes' or 'no'
-
-# Preserve the email body as is - that is, keep the citations _and_
-# signatures.
-EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
-
-# Default class to use in the mailgw if one isn't supplied in email
-# subjects. To disable, comment out the variable below or leave it blank.
-# Examples:
-MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
-#MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
-
-# HTML version to generate. The templates are html4 by default. If you
-# wish to make them xhtml, then you'll need to change this var to 'xhtml'
-# too so all auto-generated HTML is compliant.
-HTML_VERSION = 'html4'         # either 'html4' or 'xhtml'
-
-# Character set to encode email headers with. We use utf-8 by default, as
-# it's the most flexible. Some mail readers (eg. Eudora) can't cope with
-# that, so you might need to specify a more limited character set (eg.
-# 'iso-8859-1'.
-EMAIL_CHARSET = 'utf-8'
-#EMAIL_CHARSET = 'iso-8859-1'   # use this instead for Eudora users
-
-# You may specify a different default timezone, for use when users do not
-# choose their own in their settings.
-DEFAULT_TIMEZONE = 0            # specify as numeric hour offest
-
-# vim: set filetype=python ts=4 sw=4 et si
--- a/templates/classic/dbinit.py	Tue Jul 27 00:45:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,200 +0,0 @@
-#
-# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
-# This module is free software, and you may redistribute it and/or modify
-# under the same terms as Python, so long as this copyright message and
-# disclaimer are retained in their original form.
-#
-# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
-# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
-# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-# 
-# $Id: dbinit.py,v 1.8 2004-05-02 23:16:05 richard Exp $
-
-import os
-
-import config
-from select_db import Database, Class, FileClass, IssueClass
-
-def open(name=None):
-    ''' as from the roundupdb method openDB 
-    ''' 
-    from roundup.hyperdb import String, Password, Date, Link, Multilink
-    from roundup.hyperdb import Interval, Boolean, Number
-
-    # open the database
-    db = Database(config, name)
-
-    #
-    # Now initialise the schema. Must do this each time the database is
-    # opened.
-    #
-
-    # Class automatically gets these properties:
-    #   creation = Date()
-    #   activity = Date()
-    #   creator = Link('user')
-    pri = Class(db, "priority", 
-                    name=String(), order=String())
-    pri.setkey("name")
-
-    stat = Class(db, "status", 
-                    name=String(), order=String())
-    stat.setkey("name")
-
-    keyword = Class(db, "keyword", 
-                    name=String())
-    keyword.setkey("name")
-    
-    query = Class(db, "query",
-                    klass=String(),     name=String(),
-                    url=String(),       private_for=Link('user'))
-
-    # add any additional database schema configuration here
-    
-    # Note: roles is a comma-separated string of Role names
-    user = Class(db, "user", 
-                    username=String(),   password=Password(),
-                    address=String(),    realname=String(), 
-                    phone=String(),      organisation=String(),
-                    alternate_addresses=String(),
-                    queries=Multilink('query'), roles=String(),
-                    timezone=String())
-    user.setkey("username")
-
-    # FileClass automatically gets these properties:
-    #   content = String()    [saved to disk in <tracker home>/db/files/]
-    #   (it also gets the Class properties creation, activity and creator)
-    msg = FileClass(db, "msg", 
-                    author=Link("user", do_journal='no'),
-                    recipients=Multilink("user", do_journal='no'), 
-                    date=Date(),         summary=String(), 
-                    files=Multilink("file"),
-                    messageid=String(),  inreplyto=String())
-
-    file = FileClass(db, "file", 
-                    name=String(),       type=String())
-
-    # IssueClass automatically gets these properties:
-    #   title = String()
-    #   messages = Multilink("msg")
-    #   files = Multilink("file")
-    #   nosy = Multilink("user")
-    #   superseder = Multilink("issue")
-    #   (it also gets the Class properties creation, activity and creator)
-    issue = IssueClass(db, "issue", 
-                    assignedto=Link("user"), topic=Multilink("keyword"),
-                    priority=Link("priority"), status=Link("status"))
-
-    #
-    # SECURITY SETTINGS
-    #
-    # See the configuration and customisation document for information
-    # about security setup.
-    # Assign the access and edit Permissions for issue, file and message
-    # to regular users now
-    for cl in 'issue', 'file', 'msg', 'query', 'keyword':
-        p = db.security.getPermission('View', cl)
-        db.security.addPermissionToRole('User', p)
-        p = db.security.getPermission('Edit', cl)
-        db.security.addPermissionToRole('User', p)
-    for cl in 'priority', 'status':
-        p = db.security.getPermission('View', cl)
-        db.security.addPermissionToRole('User', p)
-
-    # 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)
-    # - Allow anonymous users access to view issues (which implies being
-    #   able to view all linked information too
-    for cl in 'issue', 'file', 'msg', 'keyword', 'priority', 'status':
-        p = db.security.getPermission('View', cl)
-        db.security.addPermissionToRole('Anonymous', p)
-    # - Allow anonymous users access to edit the "issue" class of data
-    #   Note: this also grants access to create related information like
-    #         files and messages etc that are linked to issues
-    #p = db.security.getPermission('Edit', 'issue')
-    #db.security.addPermissionToRole('Anonymous', p)
-
-    # oh, g'wan, let anonymous access the web interface too
-    p = db.security.getPermission('Web Access')
-    db.security.addPermissionToRole('Anonymous', p)
-
-    import detectors
-    detectors.init(db)
-
-    # schema is set up - run any post-initialisation
-    db.post_init()
-    return db
- 
-def init(adminpw): 
-    '''Invoked by the "roundup-admin initialise" command to set up the
-    initial state of the hyperdb.
- 
-    If you wish to change the hyperdb *after* running that command, see
-    the customisation doc "Database Content" section.
-    ''' 
-    dbdir = os.path.join(config.DATABASE, 'files')
-    if not os.path.isdir(dbdir):
-        os.makedirs(dbdir)
-
-    db = open("admin")
-    db.clear()
-
-    #
-    # INITIAL PRIORITY AND STATUS VALUES
-    #
-    pri = db.getclass('priority')
-    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 = db.getclass('status')
-    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")
-
-    # create the two default users
-    user = db.getclass('user')
-    user.create(username="admin", password=adminpw,
-        address=config.ADMIN_EMAIL, roles='Admin')
-    user.create(username="anonymous", roles='Anonymous')
-
-    # add any additional database create steps here - but only if you
-    # haven't initialised the database with the admin "initialise" command
-
-    db.commit()
-    db.close()
-
-# vim: set filetype=python ts=4 sw=4 et si
-
-#SHA: 92c54c05ba9f59453dc74fa9fdbbae34f7a9c077
--- a/templates/classic/detectors/__init__.py	Tue Jul 27 00:45:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#
-# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
-# This module is free software, and you may redistribute it and/or modify
-# under the same terms as Python, so long as this copyright message and
-# disclaimer are retained in their original form.
-#
-# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
-# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
-# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-# 
-#$Id: __init__.py,v 1.4 2003-10-07 06:18:45 richard Exp $
-
-import sys, os, imp
-
-def init(db):
-    ''' execute the init functions of all the modules in this directory
-    '''
-    this_dir = os.path.split(__file__)[0]
-    for filename in os.listdir(this_dir):
-        name, ext = os.path.splitext(filename)
-        if name == '__init__':
-            continue
-        if ext == '.py':
-            path = os.path.abspath(os.path.join(this_dir, filename))
-            fp = open(path)
-            try:
-                module = imp.load_module(name, fp, path,
-                    ('.py', 'r', imp.PY_SOURCE))
-            finally:
-                fp.close()
-            module.init(db)
-
-# vim: set filetype=python ts=4 sw=4 et si
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/classic/initial_data.py	Tue Jul 27 00:57:19 2004 +0000
@@ -0,0 +1,32 @@
+#
+# TRACKER INITIAL PRIORITY AND STATUS VALUES
+#
+pri = db.getclass('priority')
+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 = db.getclass('status')
+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")
+
+# create the two default users
+user = db.getclass('user')
+user.create(username="admin", password=adminpw,
+    address=admin_email, roles='Admin')
+user.create(username="anonymous", roles='Anonymous')
+
+# add any additional database create steps here - but only if you
+# haven't initialised the database with the admin "initialise" command
+
+
+# vim: set filetype=python sts=4 sw=4 et si
+#SHA: b1da2e72a7fe9f26086f243eb744135b085101d9
--- a/templates/classic/interfaces.py	Tue Jul 27 00:45:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-#
-# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
-# This module is free software, and you may redistribute it and/or modify
-# under the same terms as Python, so long as this copyright message and
-# disclaimer are retained in their original form.
-#
-# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
-# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
-# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-# 
-# $Id: interfaces.py,v 1.1 2003-04-17 03:26:03 richard Exp $
-
-from roundup import mailgw 
-from roundup.cgi import client
-
-class Client(client.Client): 
-    ''' derives basic CGI implementation from the standard module, 
-        with any specific extensions 
-    ''' 
-    pass
-
-class TemplatingUtils:
-    ''' Methods implemented on this class will be available to HTML templates
-        through the 'utils' variable.
-    '''
-    pass
-
-class MailGW(mailgw.MailGW): 
-    ''' derives basic mail gateway implementation from the standard module, 
-        with any specific extensions 
-    ''' 
-    pass
-
-# vim: set filetype=python ts=4 sw=4 et si
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/classic/schema.py	Tue Jul 27 00:57:19 2004 +0000
@@ -0,0 +1,129 @@
+
+#
+# TRACKER SCHEMA
+#
+
+# Class automatically gets these properties:
+#   creation = Date()
+#   activity = Date()
+#   creator = Link('user')
+#   actor = Link('user')
+
+# Priorities
+pri = Class(db, "priority", 
+                name=String(),
+                order=Number())
+pri.setkey("name")
+
+# Statuses
+stat = Class(db, "status", 
+                name=String(),
+                order=Number())
+stat.setkey("name")
+
+# Keywords
+keyword = Class(db, "keyword", 
+                name=String())
+keyword.setkey("name")
+
+# User-defined saved searches
+query = Class(db, "query",
+                klass=String(),
+                name=String(),
+                url=String(),
+                private_for=Link('user'))
+
+# add any additional database schema configuration here
+
+user = Class(db, "user", 
+                username=String(),
+                password=Password(),
+                address=String(),
+                realname=String(), 
+                phone=String(),
+                organisation=String(),
+                alternate_addresses=String(),
+                queries=Multilink('query'),
+                roles=String(),     # comma-separated string of Role names
+                timezone=String())
+user.setkey("username")
+
+# FileClass automatically gets this property in addition to the Class ones:
+#   content = String()    [saved to disk in <tracker home>/db/files/]
+msg = FileClass(db, "msg", 
+                author=Link("user", do_journal='no'),
+                recipients=Multilink("user", do_journal='no'), 
+                date=Date(),
+                summary=String(), 
+                files=Multilink("file"),
+                messageid=String(),
+                inreplyto=String())
+
+file = FileClass(db, "file", 
+                name=String(),
+                type=String())
+
+# IssueClass automatically gets these properties in addition to the Class ones:
+#   title = String()
+#   messages = Multilink("msg")
+#   files = Multilink("file")
+#   nosy = Multilink("user")
+#   superseder = Multilink("issue")
+issue = IssueClass(db, "issue", 
+                assignedto=Link("user"),
+                topic=Multilink("keyword"),
+                priority=Link("priority"),
+                status=Link("status"))
+
+#
+# TRACKER SECURITY SETTINGS
+#
+# See the configuration and customisation document for information
+# about security setup.
+# Assign the access and edit Permissions for issue, file and message
+# to regular users now
+for cl in 'issue', 'file', 'msg', 'query', 'keyword':
+    p = db.security.getPermission('View', cl)
+    db.security.addPermissionToRole('User', p)
+    p = db.security.getPermission('Edit', cl)
+    db.security.addPermissionToRole('User', p)
+for cl in 'priority', 'status':
+    p = db.security.getPermission('View', cl)
+    db.security.addPermissionToRole('User', p)
+
+# 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)
+# - Allow anonymous users access to view issues (which implies being
+#   able to view all linked information too
+for cl in 'issue', 'file', 'msg', 'keyword', 'priority', 'status':
+    p = db.security.getPermission('View', cl)
+    db.security.addPermissionToRole('Anonymous', p)
+# - Allow anonymous users access to edit the "issue" class of data
+#   Note: this also grants access to create related information like
+#         files and messages etc that are linked to issues
+#p = db.security.getPermission('Edit', 'issue')
+#db.security.addPermissionToRole('Anonymous', p)
+
+# oh, g'wan, let anonymous access the web interface too
+p = db.security.getPermission('Web Access')
+db.security.addPermissionToRole('Anonymous', p)
+
+
+# vim: set filetype=python sts=4 sw=4 et si

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