Code

fixed ZRoundup - mostly changes to classic template
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 12 Nov 2003 01:00:59 +0000 (01:00 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 12 Nov 2003 01:00:59 +0000 (01:00 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1979 57a73879-2fb5-44c3-a270-3262357dd7e2

27 files changed:
CHANGES.txt
doc/upgrading.txt
frontends/ZRoundup/ZRoundup.py
roundup/backends/back_mysql.py
roundup/backends/back_postgresql.py
roundup/backends/back_sqlite.py
roundup/backends/rdbms_common.py
roundup/cgi/client.py
roundup/cgi/templating.py
templates/classic/html/_generic.help.html
templates/classic/html/_generic.index.html
templates/classic/html/_generic.item.html
templates/classic/html/file.item.html
templates/classic/html/issue.index.html
templates/classic/html/issue.item.html
templates/classic/html/issue.search.html
templates/classic/html/keyword.item.html
templates/classic/html/page.html
templates/classic/html/user.index.html
templates/classic/html/user.item.html
templates/classic/html/user.register.html
templates/classic/html/user.rego_progress.html
templates/minimal/html/_generic.help.html
templates/minimal/html/_generic.index.html
templates/minimal/html/_generic.item.html
templates/minimal/html/page.html
templates/minimal/html/user.item.html

index a8fbbef5e649a1ce84c95daf1d4a84bfeab239b2..361eddd7b7a4633e191fda63fabf828293d4fdd5 100644 (file)
@@ -7,7 +7,8 @@ Feature:
 - support setgid and running on port < 1024 (sf patch 777528)
 - using Zope3's test runner now, allowing GC checks, nicer controls and
   coverage analysis
-- added postgresql backend (originally from patch #761740, many changes since)
+- added postgresql backend (originally from sf patch 761740, many changes
+  since)
 - all RDBMS backends now have indexes on several columns
 - Change nosymessage and send_message to accept msgid=None (RFE #707235).
 
@@ -17,18 +18,19 @@ Fixed:
   couple of cases
 - HTML 4.01 validation on the 'classic' backend
 - Messages to the mailgw can be about classes other than issues now.
-- Signature matching is more precise (bug #827775).
-- Anonymous user can no longer edit or view itself (bug #828901).
-- Corrected typo in installation.html (bug #822967).
+- Signature matching is more precise (sf bug 827775).
+- Anonymous user can no longer edit or view itself (sf bug 828901).
+- Corrected typo in installation.html (sf bug 822967).
 - Clarified listTemplates docstring.
 - Print a nicer error message when the address is already in use 
-  (bug #798659).
+  (sf bug 798659).
 - Remove empty lines before sending strings off to the csv parser 
-  (bug #821364).
-- Centralised conversion of user-input data to hyperdb values (bug #802405,
-  bug #817217, rfe #816994)
+  (sf bug 821364).
+- Centralised conversion of user-input data to hyperdb values (sf bug 802405,
+  sf bug 817217, sf rfe 816994)
 - recalculate SHA on template files when installed tracker used as
   template (sf bug 827510)
+- fixed ZRoundup (sf bug 624380)
 
 Cleanup:
 - Replace curuserid attribute on Database with the extended getuid() method.
index 148aa09764a25e6f5e77299b4c0fb1933362950c..bc1c9700673068bebdd93cab34c4d1a5d65cccc5 100644 (file)
@@ -21,6 +21,29 @@ be replaced with a call to Database.getuid().
 might still need to create an index "create index ids_name_idx on 
 ids(name)".
 
+0.7.0 ZRoundup changes
+----------------------
+
+The templates in your tracker's html directory will need updating if you
+wish to use ZRoundup. If you've not modified those files (or some of them),
+you may just copy the new versions from the Roundup source in the
+templates/classic/html directory.
+
+If you have modified the html files, then you'll need to manually edit them
+to change all occurances of special form variables from using the colon ":"
+special character to the at "@" special character. That is, variables such
+as::
+
+  :action :required :template :remove:messages ...
+
+should become:
+
+  @action @required @template @remove@messages ...
+
+Note that ``tal:`` statements are unaffected. So are TAL expression type
+prefixes such as ``python:`` and ``string:``. Please ask on the
+roundup-users mailing list for help if you're unsure.
+
 
 Migrating from 0.6.x to 0.6.3
 =============================
index bcde6b1b017b2e2fb859ed6a25967806376c3757..5b4077203cf5cdadaeaa910b26d467981000ab3d 100644 (file)
@@ -14,7 +14,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: ZRoundup.py,v 1.16 2002-10-18 03:34:58 richard Exp $
+# $Id: ZRoundup.py,v 1.17 2003-11-12 01:00:58 richard Exp $
 #
 ''' ZRoundup module - exposes the roundup web interface to Zope
 
@@ -26,11 +26,6 @@ This means that the regular CGI interface does all authentication quite
 independently of Zope. The roundup code is kept in memory though, and it
 runs in the same server as all your other Zope stuff, so it does have _some_
 advantages over regular CGI :)
-
-It also means that any requests which specify :filter, :columns or :sort
-_must_ be done using a GET, so that this interface can re-parse the
-QUERY_STRING. Zope interprets the ':' as a special character, and the special
-args are lost to it.
 '''
 
 import urlparse
@@ -142,16 +137,7 @@ class ZRoundup(Item, PropertyManager, Implicit, Persistent):
             # the last element is the name
             env['TRACKER_NAME'] = path_components[-1]
 
-        if env['REQUEST_METHOD'] == 'GET':
-            # force roundup to re-parse the request because Zope fiddles
-            # with it and we lose all the :filter, :columns, etc goodness
-            form = None
-        else:
-            # For some reason, CRs are embeded in multiline notes.
-            # It doesn't occur with apache/roundup.cgi, though.
-            form = FormWrapper(self.REQUEST.form)
-
-        print (env['SCRIPT_NAME'], env['PATH_INFO'])
+        form = FormWrapper(self.REQUEST.form)
         return instance.Client(instance, request, env, form)
 
     security.declareProtected('View', 'index_html')
@@ -160,7 +146,7 @@ class ZRoundup(Item, PropertyManager, Implicit, Persistent):
         '''
         # Redirect misdirected requests -- bugs 558867 , 565992
         # PATH_INFO, as defined by the CGI spec, has the *real* request path
-        orig_path = self.REQUEST.environ[ 'PATH_INFO' ]
+        orig_path = self.REQUEST.environ['PATH_INFO']
         if orig_path[-1] != '/' : 
             url = urlparse.urlparse( self.absolute_url() )
             url = list( url ) # make mutable
@@ -179,31 +165,30 @@ class ZRoundup(Item, PropertyManager, Implicit, Persistent):
     def __getitem__(self, item):
         '''All other URL accesses are passed throuh to roundup
         '''
-        return PathElement(self, item)
+        return PathElement(self, item).__of__(self)
 
-class PathElement(Item, Implicit, Persistent):
-    def __init__(self, parent, path):
-        self.parent = parent
+class PathElement(Item, Implicit):
+    def __init__(self, zr, path):
+        self.zr = zr
         self.path = path
 
     def __getitem__(self, item):
         ''' Get a subitem.
         '''
-        return PathElement(self.path + '/' + item)
+        return PathElement(self.zr, self.path + '/' + item).__of__(self)
 
-    def __call__(self, *args, **kw):
+    def index_html(self, REQUEST=None):
         ''' Actually call through to roundup to handle the request.
         '''
-        print '*****', self.path
         try:
-            client = self.parent.roundup_opendb()
+            client = self.zr.roundup_opendb()
             # fake the path that roundup should use
             client.path = self.path
             # and call roundup to do something 
             client.main()
             return ''
         except NotFound:
-            raise 'NotFound', self.REQUEST.URL
+            raise 'NotFound', REQUEST.URL
             pass
         except:
             import traceback
index bac7d7a60df507cf2755703a8e1076e38e83608f..2993b4ba07a1394713e3b9414a457042820ecf49 100644 (file)
@@ -53,7 +53,7 @@ class Database(Database):
     mysql_backend = 'InnoDB'
     #mysql_backend = 'BDB'    # much slower, only use if you have no choice
     
-    def open_connection(self):
+    def sql_open_connection(self):
         db = getattr(self.config, 'MYSQL_DATABASE')
         try:
             self.conn = MySQLdb.connect(*db)
@@ -81,9 +81,6 @@ class Database(Database):
                 self.mysql_backend)
             self.sql("CREATE INDEX ids_name_idx on ids(name)")
 
-    def close(self):
-        self.conn.close()
-
     def __repr__(self):
         return '<myroundsql 0x%x>'%id(self)
 
index 762f7f3438777be0590810ecdd02c0e847fbf831..9559ee4eac4e02f0187549d28f6f406eec4d9f04 100644 (file)
@@ -8,15 +8,15 @@
 # psycopg backend for roundup
 #
 
-from roundup.backends.rdbms_common import *
+from roundup import hyperdb, date
 from roundup.backends import rdbms_common
 import psycopg
 import os, shutil, popen2
 
-class Database(Database):
+class Database(rdbms_common.Database):
     arg = '%s'
 
-    def open_connection(self):
+    def sql_open_connection(self):
         db = getattr(self.config, 'POSTGRESQL_DATABASE')
         try:
             self.conn = psycopg.connect(**db)
@@ -33,18 +33,9 @@ class Database(Database):
             self.sql("CREATE TABLE schema (schema TEXT)")
             self.sql("CREATE TABLE ids (name VARCHAR(255), num INT4)")
 
-    def close(self):
-        self.conn.close()
-
     def __repr__(self):
         return '<roundpsycopgsql 0x%x>' % id(self)
 
-    def sql_fetchone(self):
-        return self.cursor.fetchone()
-
-    def sql_fetchall(self):
-        return self.cursor.fetchall()
-
     def sql_stringquote(self, value):
         ''' psycopg.QuotedString returns a "buffer" object with the
             single-quotes around it... '''
@@ -56,44 +47,6 @@ class Database(Database):
         self.cursor.execute(sql, (table_name, index_name))
         return self.cursor.fetchone()[0]
 
-    def save_dbschema(self, schema):
-        s = repr(self.database_schema)
-        self.sql('INSERT INTO schema VALUES (%s)', (s,))
-    
-    def load_dbschema(self):
-        self.cursor.execute('SELECT schema FROM schema')
-        schema = self.cursor.fetchone()
-        if schema:
-            return eval(schema[0])
-
-    def save_journal(self, classname, cols, nodeid, journaldate,
-                     journaltag, action, params):
-        params = repr(params)
-        entry = (nodeid, journaldate, journaltag, action, params)
-
-        a = self.arg
-        sql = 'INSERT INTO %s__journal (%s) values (%s, %s, %s, %s, %s)'%(
-            classname, cols, a, a, a, a, a)
-
-        if __debug__:
-          print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
-
-        self.cursor.execute(sql, entry)
-
-    def load_journal(self, classname, cols, nodeid):
-        sql = 'SELECT %s FROM %s__journal WHERE nodeid = %s' % (
-            cols, classname, self.arg)
-        
-        if __debug__:
-            print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid)
-
-        self.cursor.execute(sql, (nodeid,))
-        res = []
-        for nodeid, date_stamp, user, action, params in self.cursor.fetchall():
-            params = eval(params)
-            res.append((nodeid, date.Date(date_stamp), user, action, params))
-        return res
-
     def create_class_table(self, spec):
         cols, mls = self.determine_columns(spec.properties.items())
         cols.append('id')
@@ -126,79 +79,10 @@ class Database(Database):
 
         self.cursor.execute(sql)
 
-class PsycopgClass:
-    def find(self, **propspec):
-        """Get the ids of nodes in this class which link to the given nodes."""
-        
-        if __debug__:
-            print >>hyperdb.DEBUG, 'find', (self, propspec)
-
-        # shortcut
-        if not propspec:
-            return []
-
-        # validate the args
-        props = self.getprops()
-        propspec = propspec.items()
-        for propname, nodeids in propspec:
-            # check the prop is OK
-            prop = props[propname]
-            if not isinstance(prop, Link) and not isinstance(prop, Multilink):
-                raise TypeError, "'%s' not a Link/Multilink property"%propname
-
-        # first, links
-        l = []
-        where = []
-        allvalues = ()
-        a = self.db.arg
-        for prop, values in propspec:
-            if not isinstance(props[prop], hyperdb.Link):
-                continue
-            if type(values) is type(''):
-                allvalues += (values,)
-                where.append('_%s = %s' % (prop, a))
-            elif values is None:
-                where.append('_%s is NULL'%prop)
-            else:
-                allvalues += tuple(values.keys())
-                where.append('_%s in (%s)' % (prop, ','.join([a]*len(values))))
-        tables = []
-        if where:
-            self.db.sql('SELECT id AS nodeid FROM _%s WHERE %s' % (
-                self.classname, ' and '.join(where)), allvalues)
-            l += [x[0] for x in self.db.sql_fetchall()]
-
-        # now multilinks
-        for prop, values in propspec:
-            vals = ()
-            if not isinstance(props[prop], hyperdb.Multilink):
-                continue
-            if type(values) is type(''):
-                vals = (values,)
-                s = a
-            else:
-                vals = tuple(values.keys())
-                s = ','.join([a]*len(values))
-            query = 'SELECT nodeid FROM %s_%s WHERE linkid IN (%s)'%(
-                self.classname, prop, s)
-            self.db.sql(query, vals)
-            l += [x[0] for x in self.db.sql_fetchall()]
-            
-        if __debug__:
-            print >>hyperdb.DEBUG, 'find ... ', l
-
-        # Remove duplicated ids
-        d = {}
-        for k in l:
-            d[k] = 1
-        return d.keys()
-
-        return l
-
-class Class(PsycopgClass, rdbms_common.Class):
+class Class(rdbms_common.Class):
     pass
-class IssueClass(PsycopgClass, rdbms_common.IssueClass):
+class IssueClass(rdbms_common.IssueClass):
     pass
-class FileClass(PsycopgClass, rdbms_common.FileClass):
+class FileClass(rdbms_common.FileClass):
     pass
 
index 25c69b9296ae1ea6b02501974c67f91e43889d09..6315fc86ffb7934533a7bd5cef3c0b29a4bf1106 100644 (file)
@@ -1,17 +1,19 @@
-# $Id: back_sqlite.py,v 1.11 2003-11-11 11:19:18 richard Exp $
+# $Id: back_sqlite.py,v 1.12 2003-11-12 01:00:58 richard Exp $
 __doc__ = '''
 See https://pysqlite.sourceforge.net/ for pysqlite info
 '''
-import base64, marshal
-from roundup.backends.rdbms_common import *
+import os, base64, marshal
+
+from roundup import hyperdb
+from roundup.backends import rdbms_common
 from roundup.backends import locking
 import sqlite
 
-class Database(Database):
+class Database(rdbms_common.Database):
     # char to use for positional arguments
     arg = '%s'
 
-    def open_connection(self):
+    def sql_open_connection(self):
         # ensure files are group readable and writable
         os.umask(0002)
         db = os.path.join(self.config.DATABASE, 'db')
@@ -34,10 +36,8 @@ class Database(Database):
             self.cursor.execute('create table ids (name varchar, num integer)')
             self.cursor.execute('create index ids_name_idx on ids(name)')
 
-    def close(self):
-        ''' Close off the connection.
-
-            Squash any error caused by us already having closed the
+    def sql_close(self):
+        ''' Squash any error caused by us already having closed the
             connection.
         '''
         try:
@@ -46,55 +46,19 @@ class Database(Database):
             if str(value) != 'close failed - Connection is closed.':
                 raise
 
-        # release the lock too
-        if self.lockfile is not None:
-            locking.release_lock(self.lockfile)
-        if self.lockfile is not None:
-            self.lockfile.close()
-            self.lockfile = None
-
-    def rollback(self):
-        ''' Reverse all actions from the current transaction.
-
-            Undo all the changes made since the database was opened or the
-            last commit() or rollback() was performed.
-
-            Squash any error caused by us having closed the connection (and
+    def sql_rollback(self):
+        ''' Squash any error caused by us having closed the connection (and
             therefore not having anything to roll back)
         '''
-        if __debug__:
-            print >>hyperdb.DEBUG, 'rollback', (self,)
-
-        # roll back
         try:
             self.conn.rollback()
         except sqlite.ProgrammingError, value:
             if str(value) != 'rollback failed - Connection is closed.':
                 raise
 
-        # roll back "other" transaction stuff
-        for method, args in self.transactions:
-            # delete temporary files
-            if method == self.doStoreFile:
-                self.rollbackStoreFile(*args)
-        self.transactions = []
-
-        # clear the cache
-        self.clearCache()
-
     def __repr__(self):
         return '<roundlite 0x%x>'%id(self)
 
-    def sql_fetchone(self):
-        ''' Fetch a single row. If there's nothing to fetch, return None.
-        '''
-        return self.cursor.fetchone()
-
-    def sql_fetchall(self):
-        ''' Fetch a single row. If there's nothing to fetch, return [].
-        '''
-        return self.cursor.fetchall()
-
     def sql_commit(self):
         ''' Actually commit to the database.
 
@@ -113,86 +77,22 @@ class Database(Database):
                 return 1
         return 0
 
-    def save_dbschema(self, schema):
-        ''' Save the schema definition that the database currently implements
+class sqliteClass:
+    def filter(self, search_matches, filterspec, sort=(None,None),
+            group=(None,None)):
+        ''' If there's NO matches to a fetch, sqlite returns NULL
+            instead of nothing
         '''
-        s = repr(self.database_schema)
-        self.sql('insert into schema values (%s)', (s,))
+        return filter(None, rdbms_common.Class.filter(self, search_matches,
+            filterspec, sort=sort, group=group))
 
-    def load_dbschema(self):
-        ''' Load the schema definition that the database currently implements
-        '''
-        self.cursor.execute('select schema from schema')
-        return eval(self.cursor.fetchone()[0])
+class Class(sqliteClass, rdbms_common.Class):
+    pass
+
+class IssueClass(sqliteClass, rdbms_common.IssueClass):
+    pass
+
+class FileClass(sqliteClass, rdbms_common.FileClass):
+    pass
 
-    def save_journal(self, classname, cols, nodeid, journaldate,
-            journaltag, action, params):
-        ''' Save the journal entry to the database
-        '''
-        # make the params db-friendly
-        params = repr(params)
-        entry = (nodeid, journaldate, journaltag, action, params)
-
-        # do the insert
-        a = self.arg
-        sql = 'insert into %s__journal (%s) values (%s,%s,%s,%s,%s)'%(classname,
-            cols, a, a, a, a, a)
-        if __debug__:
-            print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
-        self.cursor.execute(sql, entry)
-
-    def load_journal(self, classname, cols, nodeid):
-        ''' Load the journal from the database
-        '''
-        # now get the journal entries
-        sql = 'select %s from %s__journal where nodeid=%s'%(cols, classname,
-            self.arg)
-        if __debug__:
-            print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid)
-        self.cursor.execute(sql, (nodeid,))
-        res = []
-        for nodeid, date_stamp, user, action, params in self.cursor.fetchall():
-            params = eval(params)
-            res.append((nodeid, date.Date(date_stamp), user, action, params))
-        return res
-
-    def unserialise(self, classname, node):
-        ''' Decode the marshalled node data
-
-            SQLite stringifies _everything_... so we need to re-numberificate
-            Booleans and Numbers.
-        '''
-        if __debug__:
-            print >>hyperdb.DEBUG, 'unserialise', classname, node
-        properties = self.getclass(classname).getprops()
-        d = {}
-        for k, v in node.items():
-            # if the property doesn't exist, or is the "retired" flag then
-            # it won't be in the properties dict
-            if not properties.has_key(k):
-                d[k] = v
-                continue
-
-            # get the property spec
-            prop = properties[k]
-
-            if isinstance(prop, Date) and v is not None:
-                d[k] = date.Date(v)
-            elif isinstance(prop, Interval) and v is not None:
-                d[k] = date.Interval(v)
-            elif isinstance(prop, Password) and v is not None:
-                p = password.Password()
-                p.unpack(v)
-                d[k] = p
-            elif isinstance(prop, Boolean) and v is not None:
-                d[k] = int(v)
-            elif isinstance(prop, Number) and v is not None:
-                # try int first, then assume it's a float
-                try:
-                    d[k] = int(v)
-                except ValueError:
-                    d[k] = float(v)
-            else:
-                d[k] = v
-        return d
 
index 36eb94bf195bc4fec40d0cecc3f2154451b59427..f5cb69b7e4a09e8a3c9a915129a117184f01a921 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: rdbms_common.py,v 1.67 2003-11-11 11:19:18 richard Exp $
+# $Id: rdbms_common.py,v 1.68 2003-11-12 01:00:58 richard Exp $
 ''' Relational database (SQL) backend common code.
 
 Basics:
@@ -69,13 +69,13 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         self.lockfile = None
 
         # open a connection to the database, creating the "conn" attribute
-        self.open_connection()
+        self.sql_open_connection()
 
     def clearCache(self):
         self.cache = {}
         self.cache_lru = []
 
-    def open_connection(self):
+    def sql_open_connection(self):
         ''' Open a connection to the database, creating it if necessary
         '''
         raise NotImplemented
@@ -93,7 +93,12 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
     def sql_fetchone(self):
         ''' Fetch a single row. If there's nothing to fetch, return None.
         '''
-        raise NotImplemented
+        return self.cursor.fetchone()
+
+    def sql_fetchall(self):
+        ''' Fetch all rows. If there's nothing to fetch, return [].
+        '''
+        return self.cursor.fetchall()
 
     def sql_stringquote(self, value):
         ''' Quote the string so it's safe to put in the 'sql quotes'
@@ -103,12 +108,14 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
     def save_dbschema(self, schema):
         ''' Save the schema definition that the database currently implements
         '''
-        raise NotImplemented
+        s = repr(self.database_schema)
+        self.sql('insert into schema values (%s)', (s,))
 
     def load_dbschema(self):
         ''' Load the schema definition that the database currently implements
         '''
-        raise NotImplemented
+        self.cursor.execute('select schema from schema')
+        return eval(self.cursor.fetchone()[0])
 
     def post_init(self):
         ''' Called once the schema initialisation has finished.
@@ -806,8 +813,14 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
                 p = password.Password()
                 p.unpack(v)
                 d[k] = p
-            elif (isinstance(prop, Boolean) or isinstance(prop, Number)) and v is not None:
-                d[k]=float(v)
+            elif isinstance(prop, Boolean) and v is not None:
+                d[k] = int(v)
+            elif isinstance(prop, Number) and v is not None:
+                # try int first, then assume it's a float
+                try:
+                    d[k] = int(v)
+                except ValueError:
+                    d[k] = float(v)
             else:
                 d[k] = v
         return d
@@ -865,12 +878,6 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         self.save_journal(classname, cols, nodeid, journaldate,
             journaltag, action, params)
 
-    def save_journal(self, classname, cols, nodeid, journaldate,
-            journaltag, action, params):
-        ''' Save the journal entry to the database
-        '''
-        raise NotImplemented
-
     def getjournal(self, classname, nodeid):
         ''' get the journal for id
         '''
@@ -881,10 +888,36 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         cols = ','.join('nodeid date tag action params'.split())
         return self.load_journal(classname, cols, nodeid)
 
+    def save_journal(self, classname, cols, nodeid, journaldate,
+            journaltag, action, params):
+        ''' Save the journal entry to the database
+        '''
+        # make the params db-friendly
+        params = repr(params)
+        entry = (nodeid, journaldate, journaltag, action, params)
+
+        # do the insert
+        a = self.arg
+        sql = 'insert into %s__journal (%s) values (%s,%s,%s,%s,%s)'%(classname,
+            cols, a, a, a, a, a)
+        if __debug__:
+            print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
+        self.cursor.execute(sql, entry)
+
     def load_journal(self, classname, cols, nodeid):
         ''' Load the journal from the database
         '''
-        raise NotImplemented
+        # now get the journal entries
+        sql = 'select %s from %s__journal where nodeid=%s'%(cols, classname,
+            self.arg)
+        if __debug__:
+            print >>hyperdb.DEBUG, 'load_journal', (self, sql, nodeid)
+        self.cursor.execute(sql, (nodeid,))
+        res = []
+        for nodeid, date_stamp, user, action, params in self.cursor.fetchall():
+            params = eval(params)
+            res.append((nodeid, date.Date(date_stamp), user, action, params))
+        return res
 
     def pack(self, pack_before):
         ''' Delete all journal entries except "create" before 'pack_before'.
@@ -933,6 +966,9 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         # clear out the transactions
         self.transactions = []
 
+    def sql_rollback(self):
+        self.conn.rollback()
+
     def rollback(self):
         ''' Reverse all actions from the current transaction.
 
@@ -942,8 +978,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         if __debug__:
             print >>hyperdb.DEBUG, 'rollback', (self,)
 
-        # roll back
-        self.conn.rollback()
+        self.sql_rollback()
 
         # roll back "other" transaction stuff
         for method, args in self.transactions:
@@ -961,10 +996,13 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         # return the classname, nodeid so we reindex this content
         return (classname, nodeid)
 
+    def sql_close(self):
+        self.conn.close()
+
     def close(self):
         ''' Close off the connection.
         '''
-        self.conn.close()
+        self.sql_close()
         if self.lockfile is not None:
             locking.release_lock(self.lockfile)
         if self.lockfile is not None:
@@ -2061,12 +2099,10 @@ class Class(hyperdb.Class):
         else:
             # psycopg doesn't like empty args
             self.db.cursor.execute(sql)
-        l = self.db.cursor.fetchall()
+        l = self.db.sql_fetchall()
 
         # return the IDs (the first column)
-        # XXX The filter(None, l) bit is sqlite-specific... if there's _NO_
-        # XXX matches to a fetch, it returns NULL instead of nothing!?!
-        return filter(None, [row[0] for row in l])
+        return [row[0] for row in l]
 
     def count(self):
         '''Get the number of nodes in this class.
index 5e581b08757c40e28ea987c74384e9522f49df62..29622035708ec0ecccb6ab9f9f5ee7b8589540f9 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.144 2003-11-11 00:35:14 richard Exp $
+# $Id: client.py,v 1.145 2003-11-12 01:00:59 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -422,7 +422,7 @@ class Client:
             else:
                 self.template = ''
             return
-        elif path[0] == '_file':
+        elif path[0] in ('_file', '@@file'):
             raise SendStaticFile, os.path.join(*path[1:])
         else:
             self.classname = path[0]
index 9600f15c1c3fb6918cdc72048acd6bafdf24f38c..4a90637d6168124a680655a7d58e41a887270c78 100644 (file)
@@ -469,14 +469,14 @@ class HTMLClass(HTMLPermissions):
         if property:
             property = '&amp;property=%s'%property
         return '<a class="classhelp" href="javascript:help_window(\'%s?'\
-            ':startwith=0&amp;:template=help&amp;properties=%s%s\', \'%s\', \
+            '@startwith=0&amp;@template=help&amp;properties=%s%s\', \'%s\', \
             \'%s\')">%s</a>'%(self.classname, properties, property, width,
             height, label)
 
     def submit(self, label="Submit New Entry"):
         ''' Generate a submit button (and action hidden element)
         '''
-        return '  <input type="hidden" name=":action" value="new">\n'\
+        return '  <input type="hidden" name="@action" value="new">\n'\
         '  <input type="submit" name="submit" value="%s">'%label
 
     def history(self):
@@ -554,7 +554,7 @@ class HTMLItem(HTMLPermissions):
     def submit(self, label="Submit Changes"):
         ''' Generate a submit button (and action hidden element)
         '''
-        return '  <input type="hidden" name=":action" value="edit">\n'\
+        return '  <input type="hidden" name="@action" value="edit">\n'\
         '  <input type="submit" name="submit" value="%s">'%label
 
     def journal(self, direction='descending'):
@@ -773,7 +773,7 @@ class HTMLItem(HTMLPermissions):
         req.classname = self._klass.get(self._nodeid, 'klass')
         name = self._klass.get(self._nodeid, 'name')
         req.updateFromURL(self._klass.get(self._nodeid, 'url') +
-            '&:queryname=%s'%urllib.quote(name))
+            '&@queryname=%s'%urllib.quote(name))
 
         # new template, using the specified classname and request
         pt = Templates(self._db.config.TEMPLATES).get(req.classname, 'search')
@@ -961,9 +961,9 @@ class PasswordHTMLProperty(HTMLProperty):
     def confirm(self, size = 30):
         ''' Render a second form edit field for the property, used for 
             confirmation that the user typed the password correctly. Generates
-            a field with name ":confirm:name".
+            a field with name "@confirm@name".
         '''
-        return '<input type="password" name=":confirm:%s" size="%s">'%(
+        return '<input type="password" name="@confirm@%s" size="%s">'%(
             self._formname, size)
 
 class NumberHTMLProperty(HTMLProperty):
index cf97953a54f8a3b3837eaef898589f2ee1022f1b..78879381f015597c920838a579f89becca3dcabe 100644 (file)
@@ -1,7 +1,7 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html>
   <head>
-      <link rel="stylesheet" type="text/css" href="_file/style.css" />
+      <link rel="stylesheet" type="text/css" href="@@file/style.css" />
       <meta http-equiv="Content-Type" content="text/html; charset=utf-8;" />
       <tal:block tal:condition="python:request.form.has_key('property')">
       <title tal:content="string:${request/form/property/value} help">Property</title>
           // this is the name of the field in the original form that we're working on
           field = '${request/form/property/value}';" >
       </script>
-      <script src="_file/help_controls.js" type="text/javascript"><!-- 
+      <script src="@@file/help_controls.js" type="text/javascript"><!-- 
       //--></script>
       </tal:block>
   </head>
  <body class="body" onload="resetList();">
  <form name="frm_help" tal:attributes="action request/base"
-       tal:define="start python:int(request.form[':startwith'].value);
+       tal:define="start python:int(request.form['@startwith'].value);
                    batch python:utils.Batch(context.list(), 500, start);
                    props python:request.form['properties'].value.split(',')">
      
       <tr class="navigation">
        <th>
         <a tal:define="prev batch/previous" tal:condition="prev"
-           tal:attributes="href string:${request/classname}?:template=help&:startwith=${prev/first}&properties=${request/form/properties/value}">&lt;&lt; previous</a>
+           tal:attributes="href string:${request/classname}?@template=help&@startwith=${prev/first}&properties=${request/form/properties/value}">&lt;&lt; previous</a>
         &nbsp;
        </th>
        <th tal:content="python: '%d...%d out of %d'%(batch.start,
                batch.start+batch.length-1, batch.sequence_length)">current</th>
        <th>
         <a tal:define="next batch/next" tal:condition="next"
-           tal:attributes="href string:${request/classname}?:template=help&:startwith=${next/first}&properties=${request/form/properties/value}">next &gt;&gt;</a>
+           tal:attributes="href string:${request/classname}?@template=help&@startwith=${next/first}&properties=${request/form/properties/value}">next &gt;&gt;</a>
         &nbsp;
        </th>
       </tr>
index abf8b162e6220123cd227ddb4c9686df896b1a8a..e39c77421cd03e05ca7a1517addd683b678dea72 100644 (file)
@@ -35,7 +35,7 @@ You are not allowed to view this page.
       tal:attributes="action context/designator">
 <textarea rows="15" cols="60" name="rows" tal:content="context/csv"></textarea>
 <br>
-<input type="hidden" name=":action" value="editCSV">
+<input type="hidden" name="@action" value="editCSV">
 <input type="submit" value="Edit Items">
 </form>
 </tal:block>
index 7d331273a3fa2799538aaca7337ebb635c576d62..fc35866791008530f4124ed36543358303b6d400 100644 (file)
@@ -14,8 +14,7 @@ You are not allowed to view this page.
       enctype="multipart/form-data" tal:condition="context/is_edit_ok"
       tal:attributes="action context/designator">
 
-<input type="hidden" name=":template" value="item">
-<input type="hidden" name=":required" value="title">
+<input type="hidden" name="@template" value="item">
 
 <table class="form">
 
index 8361524c8aeede327ca7117783f2a4764ea682df..6f4542d9a3ae8dc0aa9c65e3503701d397830c0a 100644 (file)
@@ -27,11 +27,11 @@ You are not allowed to view this page.
  <tr>
   <td>
    &nbsp;
-   <input type="hidden" name=":template" value="item">
-   <input type="hidden" name=":required" value="name,type">
-   <input type="hidden" name=":multilink"
-          tal:condition="python:request.form.has_key(':multilink')"
-          tal:attributes="value request/form/:multilink/value">
+   <input type="hidden" name="@template" value="item">
+   <input type="hidden" name="@required" value="name,type">
+   <input type="hidden" name="@multilink"
+          tal:condition="python:request.form.has_key('@multilink')"
+          tal:attributes="value request/form/@multilink/value">
   </td>
   <td tal:content="structure context/submit">submit button here</td>
  </tr>
index 0a26b59d66f0fc799980d977ad96f53365c0971c..df9f61f73acfcba2ab4e208f8defb871983c287d 100644 (file)
@@ -64,7 +64,7 @@ You are not allowed to view this page.
      <th>
       <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>
+         {'@startwith':prev.first, '@pagesize':prev.size})">&lt;&lt; previous</a>
       &nbsp;
      </th>
      <th tal:content="python: '%d...%d out of %d'%(batch.start,
@@ -72,7 +72,7 @@ You are not allowed to view this page.
      <th>
       <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>
+         {'@startwith':next.first, '@pagesize':next.size})">next &gt;&gt;</a>
       &nbsp;
      </th>
     </tr>
@@ -87,7 +87,7 @@ You are not allowed to view this page.
   <tr tal:condition="batch">
    <th>Sort on:</th>
    <td>
-    <select name=":sort">
+    <select name="@sort">
      <option value="">- nothing -</option>
      <option tal:repeat="col context/properties"
              tal:attributes="value col/_name;
@@ -96,14 +96,14 @@ You are not allowed to view this page.
     </select>
    </td>
    <th>Descending:</th>
-   <td><input type="checkbox" name=":sortdir"
+   <td><input type="checkbox" name="@sortdir"
               tal:attributes="checked python:request.sort[0] == '-'"> 
    </td>
   </tr>
   <tr>
    <th>Group on:</th>
    <td>
-    <select name=":group">
+    <select name="@group">
      <option value="">- nothing -</option>
      <option tal:repeat="col context/properties"
              tal:attributes="value col/_name;
@@ -112,7 +112,7 @@ You are not allowed to view this page.
     </select>
    </td>
    <th>Descending:</th>
-   <td><input type="checkbox" name=":groupdir"
+   <td><input type="checkbox" name="@groupdir"
               tal:attributes="checked python:request.group[0] == '-'"> 
    </td>
   </tr>
@@ -128,4 +128,3 @@ You are not allowed to view this page.
 
 </td>
 </tal:block>
-
index 1ac2325d30f0ca6055edd987a280e52a6152c842..c7f8b64de18c81b7ae1323d69bc1e29142467c85 100644 (file)
@@ -67,21 +67,21 @@ python:db.user.classhelp('username,realname,address', property='nosy', width='60
 <tr>
  <th>Change Note</th>
  <td colspan=3>
-  <textarea tal:content="request/form/:note/value | default"
-            name=":note" wrap="hard" rows="5" cols="80"></textarea>
+  <textarea tal:content="request/form/@note/value | default"
+            name="@note" wrap="hard" rows="5" cols="80"></textarea>
  </td>
 </tr>
 
 <tr>
  <th>File</th>
- <td colspan=3><input type="file" name=":file" size="40"></td>
+ <td colspan=3><input type="file" name="@file" size="40"></td>
 </tr>
 
 <tr>
  <td>
   &nbsp;
-  <input type="hidden" name=":template" value="item">
-  <input type="hidden" name=":required" value="title,priority">
+  <input type="hidden" name="@template" value="item">
+  <input type="hidden" name="@required" value="title,priority">
  </td>
  <td colspan=3 tal:content="structure context/submit">
   submit button will go here
@@ -142,7 +142,7 @@ python:db.user.classhelp('username,realname,address', property='nosy', width='60
     <th tal:content="string:Date: ${msg/date}">date</th>
     <th>
      <a tal:condition="context/is_edit_ok"
-        tal:attributes="href string:issue${context/id}?:remove:messages=${msg/id}&:action=edit">remove</a>
+        tal:attributes="href string:issue${context/id}?@remove@messages=${msg/id}&@action=edit">remove</a>
     </th>
    </tr>
    <tr>
index a46334f8e84e2844666d66b06395eb65eeb2badd..91f738bea96d262b01d5aa38f0c23b60ca9c7980 100644 (file)
@@ -26,7 +26,7 @@
  <th class="header">Group on</th>
 </tr>
 
-<tr tal:define="name string::search_text">
+<tr tal:define="name string:@search_text">
   <th>All text*:</th>
   <td metal:use-macro="search_input"></td>
   <td>&nbsp;</td>
 
 <tr>
 <th>Pagesize:</th>
-<td><input name=":pagesize" size="3" value="50"
-           tal:attributes="value request/form/:pagesize/value | default"></td>
+<td><input name="@pagesize" size="3" value="50"
+           tal:attributes="value request/form/@pagesize/value | default"></td>
 </tr>
 
 <tr>
 <th>Start With:</th>
-<td><input name=":startwith" size="3" value="0"
-           tal:attributes="value request/form/:startwith/value | default"></td>
+<td><input name="@startwith" size="3" value="0"
+           tal:attributes="value request/form/@startwith/value | default"></td>
 </tr>
 
 <tr>
 <th>Sort Descending:</th>
-<td><input type="checkbox" name=":sortdir"
+<td><input type="checkbox" name="@sortdir"
            tal:attributes="checked python:request.sort[0] == '-' or request.sort[0] is None">
 </td>
 </tr>
 
 <tr>
 <th>Group Descending:</th>
-<td><input type="checkbox" name=":groupdir"
+<td><input type="checkbox" name="@groupdir"
            tal:attributes="checked python:request.group[0] == '-'">
 </td>
 </tr>
 
 <tr>
 <th>Query name**:</th>
-<td><input name=":queryname"
-           tal:attributes="value request/form/:queryname/value | default"></td>
+<td><input name="@queryname"
+           tal:attributes="value request/form/@queryname/value | default"></td>
 </tr>
 
 <tr>
   <td>
    &nbsp;
-   <input type="hidden" name=":action" value="search">
+   <input type="hidden" name="@action" value="search">
   </td>
   <td><input type="submit" value="Search"></td>
 </tr>
index 42917bdf6ac7a61731c3d6688504c10d087a41a7..a73c9d212c4db1edd0d61702f8d2fcc38819277e 100644 (file)
@@ -41,8 +41,8 @@
   <tr>
    <td>
     &nbsp;
-    <input type="hidden" name=":required" value="name">
-    <input type="hidden" name=":template" value="item">
+    <input type="hidden" name="@required" value="name">
+    <input type="hidden" name="@template" value="item">
    </td>
    <td colspan=3 tal:content="structure context/submit">
     submit button will go here
index f5e7068ce0649903a4d801035efef53e99dd5fa0..d2a877873b073aa9917f56f80fa984120f99c0ed 100644 (file)
@@ -6,7 +6,7 @@
 <title metal:define-slot="head_title">title goes here</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8;">
 
-<link rel="stylesheet" type="text/css" href="_file/style.css">
+<link rel="stylesheet" type="text/css" href="@@file/style.css">
 
 <script tal:replace="structure request/base_javascript">
 </script>
        tal:condition="python:request.user.hasPermission('View', 'issue')">
     <b>Issues</b><br>
     <a tal:condition="python:request.user.hasPermission('Edit', 'issue')"
-      href="issue?:template=item">Create New<br></a>
-    <a href="issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=-1">Show Unassigned</a><br>
-    <a href="issue?:sort=-activity&:group=priority&:filter=status&:columns=id,activity,title,creator,assignedto,status&status=-1,1,2,3,4,5,6,7">Show All</a><br>
-    <a href="issue?:template=search">Search</a><br>
-    <input type="submit" style="padding: 0" value="Show issue:"><input size="4" type="text" name=":number">
-    <input type="hidden" name=":type" value="issue">
-    <input type="hidden" name=":action" value="show">
+      href="issue?@template=item">Create New<br></a>
+    <a href="issue?@sort=-activity&@group=priority&@filter=status,assignedto&@columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=-1">Show Unassigned</a><br>
+    <a href="issue?@sort=-activity&@group=priority&@filter=status&@columns=id,activity,title,creator,assignedto,status&status=-1,1,2,3,4,5,6,7">Show All</a><br>
+    <a href="issue?@template=search">Search</a><br>
+    <input type="submit" style="padding: 0" value="Show issue:"><input size="4" type="text" name="@number">
+    <input type="hidden" name="@type" value="issue">
+    <input type="hidden" name="@action" value="show">
    </p>
   </form>
 
      tal:condition="python:request.user.hasPermission('View', 'keyword')">
    <b>Keywords</b><br>
    <a tal:condition="python:request.user.hasPermission('Edit', 'keyword')"
-      href="keyword?:template=item">Create New<br></a>
+      href="keyword?@template=item">Create New<br></a>
    <a tal:condition="python:request.user.hasPermission('Edit', 'keyword') and
                             len(db.keyword.list())"
-      href="keyword?:template=item">Edit Existing<br></a>
+      href="keyword?@template=item">Edit Existing<br></a>
   </p>
 
   <p class="classblock"
        tal:condition="python:request.user.username != 'anonymous'">
    <b>Administration</b><br>
    <tal:block tal:condition="python:request.user.hasPermission('Edit', None)">
-    <a href="home?:template=classlist">Class List</a><br>
+    <a href="home?@template=classlist">Class List</a><br>
    </tal:block>
    <a tal:condition="python:request.user.hasPermission('View', 'user')
                             or request.user.hasPermission('Edit', 'user')"
       href="user" >User List</a><br>
    <a tal:condition="python:request.user.hasPermission('Edit', 'user')"
-      href="user?:template=item">Add User</a>
+      href="user?@template=item">Add User</a>
   </p>
 
   <form method="POST" tal:condition="python:request.user.username=='anonymous'"
     <b>Login</b><br>
     <input size="10" name="__login_name"><br>
     <input size="10" type="password" name="__login_password"><br>
-    <input type="submit" name=":action" value="Login"><br>
+    <input type="submit" name="@action" value="Login"><br>
     <span tal:replace="structure request/indexargs_form" />
-    <a href="user?:template=register"
+    <a href="user?@template=register"
        tal:condition="python:request.user.hasPermission('Web Registration')">Register<br></a>
-    <a href="user?:template=forgotten">Lost&nbsp;your&nbsp;login?</a><br>
+    <a href="user?@template=forgotten">Lost&nbsp;your&nbsp;login?</a><br>
    </p>
   </form>
    
   <p class="userblock" tal:condition="python:request.user.username != 'anonymous'">
    <b>Hello,</b> <b tal:content="request/user/username">username</b><br>
-   <a tal:attributes="href string:issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=${request/user/id}">My Issues</a><br>
+   <a tal:attributes="href string:issue?@sort=-activity&@group=priority&@filter=status,assignedto&@columns=id,activity,title,creator,status&status=-1,1,2,3,4,5,6,7&assignedto=${request/user/id}">My Issues</a><br>
    <a tal:attributes="href string:user${request/user/id}">My Details</a><br>
    <a tal:attributes="href python:request.indexargs_href('',
-       {':action':'logout'})">Logout</a>
+       {'@action':'logout'})">Logout</a>
   </p>
   <p class="userblock">
    <b>Help</b><br>
 </td>
 
 <td metal:define-macro="column_input">
-  <input type="checkbox" name=":columns"
+  <input type="checkbox" name="@columns"
          tal:attributes="value name;
                          checked python:name in cols">
 </td>
 
 <td metal:define-macro="sort_input">
-  <input type="radio" name=":sort"
+  <input type="radio" name="@sort"
          tal:attributes="value name;
                          checked python:name == sort_on">
 </td>
 
 <td metal:define-macro="group_input">
-  <input type="radio" name=":group"
+  <input type="radio" name="@group"
          tal:attributes="value name;
                          checked python:name == group_on">
 </td>
index 136e5441deeca03b34bc54de00425a7ce8137b20..d9d3c805ac3450586f2868b8b6884fe2b240635b 100644 (file)
@@ -30,7 +30,7 @@ You are not allowed to view this page.
  <td tal:content="python:user.address.email() or default">&nbsp;</td>
  <td tal:content="python:user.phone.plain() or default">&nbsp;</td>
  <td tal:condition="context/is_edit_ok">
-  <a tal:attributes="href string:user${user/id}?:action=retire&:template=index">
+  <a tal:attributes="href string:user${user/id}?@action=retire&@template=index">
    retire</a>
  </td>
 </tr>
index dc78107ea49416f1deb61fcbfe5c3e10591e638f..e0cb51a13d521c0aeebf63d4a3cefbe1377d5c06 100644 (file)
@@ -72,8 +72,8 @@ You are not allowed to view this page.
  <tr>
   <td>
    &nbsp;
-   <input type="hidden" name=":template" value="item">
-   <input type="hidden" name=":required" value="username,address">
+   <input type="hidden" name="@template" value="item">
+   <input type="hidden" name="@required" value="username,address">
   </td>
   <td tal:content="structure context/submit">submit button here</td>
  </tr>
@@ -90,7 +90,7 @@ You are not allowed to view this page.
    <a tal:attributes="href string:${query/klass}?${query/url}">display</a>   
   </td>
   <td>
-   <a tal:attributes="href string:?:remove:queries=${query/id}&:action=edit">remove</a>
+   <a tal:attributes="href string:?@remove@queries=${query/id}&@action=edit">remove</a>
   </td>
  </tr>
 </table>
index 930616dd0b20f37cd12dc442761977097e60c1d5..7340887902d6082e34763b78f7c67c7b2596d46b 100644 (file)
@@ -64,9 +64,9 @@ You are not allowed to view this page.
  <tr>
   <td>&nbsp;</td>
   <td>
-   <input type="hidden" name=":template" value="register">
-   <input type="hidden" name=":required" value="username,password,address">
-   <input type="hidden" name=":action" value="register">
+   <input type="hidden" name="@template" value="register">
+   <input type="hidden" name="@required" value="username,password,address">
+   <input type="hidden" name="@action" value="register">
    <input type="submit" name="submit" value="Register">
   </td>
  </tr>
index fe78cd7141cc9179e439fdc09538764eb2aaf589..9df2de0e8de14b001dfdf9b79a6c6c6760ce1bcf 100644 (file)
@@ -13,4 +13,3 @@ email.
 
 </td>
 </tal:block>
-
index 8b035b69d44a75d81cd0ed05e6fd7084bdbbe8fc..9eee58fa3c50d2220652e6d7018f4de00981e20f 100644 (file)
@@ -16,7 +16,7 @@
   </head>
  <body class="body" marginwidth="0" marginheight="0" onload="resetList();">
  <form name="frm_help" action=""
-       tal:define="start python:int(request.form[':startwith'].value);
+       tal:define="start python:int(request.form['@startwith'].value);
                    batch python:utils.Batch(context.list(), 500, start);
                    props python:request.form['properties'].value.split(',')">
      
       <tr class="navigation">
        <th>
         <a tal:define="prev batch/previous" tal:condition="prev"
-           tal:attributes="href string:${request/classname}?:template=help&:startwith=${prev/first}&properties=${request/form/properties/value}">&lt;&lt; previous</a>
+           tal:attributes="href string:${request/classname}?@template=help&@startwith=${prev/first}&properties=${request/form/properties/value}">&lt;&lt; previous</a>
         &nbsp;
        </th>
        <th tal:content="python: '%d...%d out of %d'%(batch.start,
                batch.start+batch.length-1, batch.sequence_length)">current</th>
        <th>
         <a tal:define="next batch/next" tal:condition="next"
-           tal:attributes="href string:${request/classname}?:template=help&:startwith=${next/first}&properties=${request/form/properties/value}">next &gt;&gt;</a>
+           tal:attributes="href string:${request/classname}?@template=help&@startwith=${next/first}&properties=${request/form/properties/value}">next &gt;&gt;</a>
         &nbsp;
        </th>
       </tr>
index f21b5d2b700fc6ec0dda2d93e3b22db232c1f60b..ac4f069e5d15023a500da05adc0ba563e3778cd0 100644 (file)
@@ -34,7 +34,7 @@ You are not allowed to view this page.
 <form onSubmit="return submit_once()" method="POST">
 <textarea rows="15" cols="60" name="rows" tal:content="context/csv"></textarea>
 <br>
-<input type="hidden" name=":action" value="editCSV">
+<input type="hidden" name="@action" value="editCSV">
 <input type="submit" value="Edit Items">
 </form>
 </tal:block>
index a9d2e859f4a6aa7bc905c91f97b925493560e5d9..0090c62d0a544974859fae2b09edd305298e67f3 100644 (file)
@@ -13,8 +13,7 @@ You are not allowed to view this page.
 <form method="POST" onSubmit="return submit_once()"
       enctype="multipart/form-data" tal:condition="context/is_edit_ok">
 
-<input type="hidden" name=":template" value="item">
-<input type="hidden" name=":required" value="title">
+<input type="hidden" name="@template" value="item">
 
 <table class="form">
 
index 87517c223c3b2d5649b106a46d396a99d8ea4bfb..b563d766b714698970996b39727eadf5b695071f 100644 (file)
@@ -6,7 +6,7 @@
 <title metal:define-slot="head_title">title goes here</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8;">
 
-<link rel="stylesheet" type="text/css" href="_file/style.css">
+<link rel="stylesheet" type="text/css" href="@@file/style.css">
 
 <script tal:replace="structure request/base_javascript">
 </script>
    <form method="POST" action="">
     <input size="10" name="__login_name"><br>
     <input size="10" type="password" name="__login_password"><br>
-    <input type="submit" name=":action" value="login">
+    <input type="submit" name="@action" value="login">
     <span tal:replace="structure request/indexargs_form" />
    </form>
    <a tal:condition="python:request.user.hasPermission('Web Registration')"
-      href="user?:template=register">Register</a>
+      href="user?@template=register">Register</a>
   </p>
 
   <p class="userblock" tal:condition="python:request.user.username != 'anonymous'">
    <b>Hello,</b><br><b tal:content="request/user/username">username</b><br>
    <a tal:attributes="href string:user${request/user/id}">My Details</a><br>
    <a tal:attributes="href python:request.indexargs_href('',
-       {':action':'logout'})">Logout</a>
+       {'@action':'logout'})">Logout</a>
   </p>
 
   <p class="classblock"
        tal:condition="python:request.user.username != 'anonymous'">
    <b>Administration</b><br>
    <a tal:condition="python:request.user.hasPermission('Edit', None)"
-      href="home?:template=classlist">Class List</a><br>
+      href="home?@template=classlist">Class List</a><br>
    <a tal:condition="python:request.user.hasPermission('View', 'user')
                             or request.user.hasPermission('Edit', 'user')"
       href="user" >User List</a><br>
    <a tal:condition="python:request.user.hasPermission('Edit', 'user')"
-      href="user?:template=item">Add User</a>
+      href="user?@template=item">Add User</a>
   </p>
  </td>
  <td>
index 642b7764343539ae62b5c4b90e8ef0b1890217c5..41b1a02bb6e99c3cb6e97dccfb07369dc03b6eb1 100644 (file)
@@ -12,7 +12,6 @@ You are not allowed to view this page.
 <form method="POST" onSubmit="return submit_once()"
       enctype="multipart/form-data" tal:condition="context/is_edit_ok">
 
-<input type="hidden" name=":required" value="username,address">
 
 <table class="form">
  <tr>
@@ -45,7 +44,10 @@ You are not allowed to view this page.
  </tr>
 
  <tr>
-  <td>&nbsp;</td>
+  <td>&nbsp;
+   <input type="hidden" name="@required" value="username,address">
+   <input type="hidden" name="@template" value="item">
+  </td>
   <td tal:content="structure context/submit">submit button here</td>
  </tr>
 </table>