From 696b92ea373706dc392aea89a27bef7e54c6b34d Mon Sep 17 00:00:00 2001 From: richard Date: Wed, 12 Nov 2003 01:00:59 +0000 Subject: [PATCH] fixed ZRoundup - mostly changes to classic template git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1979 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 18 ++- doc/upgrading.txt | 23 +++ frontends/ZRoundup/ZRoundup.py | 37 ++--- roundup/backends/back_mysql.py | 5 +- roundup/backends/back_postgresql.py | 128 +-------------- roundup/backends/back_sqlite.py | 152 +++--------------- roundup/backends/rdbms_common.py | 80 ++++++--- roundup/cgi/client.py | 4 +- roundup/cgi/templating.py | 12 +- templates/classic/html/_generic.help.html | 10 +- templates/classic/html/_generic.index.html | 2 +- templates/classic/html/_generic.item.html | 3 +- templates/classic/html/file.item.html | 10 +- templates/classic/html/issue.index.html | 13 +- templates/classic/html/issue.item.html | 12 +- templates/classic/html/issue.search.html | 20 +-- templates/classic/html/keyword.item.html | 4 +- templates/classic/html/page.html | 40 ++--- templates/classic/html/user.index.html | 2 +- templates/classic/html/user.item.html | 6 +- templates/classic/html/user.register.html | 6 +- .../classic/html/user.rego_progress.html | 1 - templates/minimal/html/_generic.help.html | 6 +- templates/minimal/html/_generic.index.html | 2 +- templates/minimal/html/_generic.item.html | 3 +- templates/minimal/html/page.html | 12 +- templates/minimal/html/user.item.html | 6 +- 27 files changed, 221 insertions(+), 396 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a8fbbef..361eddd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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. diff --git a/doc/upgrading.txt b/doc/upgrading.txt index 148aa09..bc1c970 100644 --- a/doc/upgrading.txt +++ b/doc/upgrading.txt @@ -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 ============================= diff --git a/frontends/ZRoundup/ZRoundup.py b/frontends/ZRoundup/ZRoundup.py index bcde6b1..5b40772 100644 --- a/frontends/ZRoundup/ZRoundup.py +++ b/frontends/ZRoundup/ZRoundup.py @@ -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 diff --git a/roundup/backends/back_mysql.py b/roundup/backends/back_mysql.py index bac7d7a..2993b4b 100644 --- a/roundup/backends/back_mysql.py +++ b/roundup/backends/back_mysql.py @@ -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 ''%id(self) diff --git a/roundup/backends/back_postgresql.py b/roundup/backends/back_postgresql.py index 762f7f3..9559ee4 100644 --- a/roundup/backends/back_postgresql.py +++ b/roundup/backends/back_postgresql.py @@ -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 '' % 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 diff --git a/roundup/backends/back_sqlite.py b/roundup/backends/back_sqlite.py index 25c69b9..6315fc8 100644 --- a/roundup/backends/back_sqlite.py +++ b/roundup/backends/back_sqlite.py @@ -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 ''%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 diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index 36eb94b..f5cb69b 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -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. diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py index 5e581b0..2962203 100644 --- a/roundup/cgi/client.py +++ b/roundup/cgi/client.py @@ -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] diff --git a/roundup/cgi/templating.py b/roundup/cgi/templating.py index 9600f15..4a90637 100644 --- a/roundup/cgi/templating.py +++ b/roundup/cgi/templating.py @@ -469,14 +469,14 @@ class HTMLClass(HTMLPermissions): if property: property = '&property=%s'%property return '%s'%(self.classname, properties, property, width, height, label) def submit(self, label="Submit New Entry"): ''' Generate a submit button (and action hidden element) ''' - return ' \n'\ + return ' \n'\ ' '%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 ' \n'\ + return ' \n'\ ' '%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 ''%( + return ''%( self._formname, size) class NumberHTMLProperty(HTMLProperty): diff --git a/templates/classic/html/_generic.help.html b/templates/classic/html/_generic.help.html index cf97953..7887938 100644 --- a/templates/classic/html/_generic.help.html +++ b/templates/classic/html/_generic.help.html @@ -1,7 +1,7 @@ - + Property @@ -10,13 +10,13 @@ // this is the name of the field in the original form that we're working on field = '${request/form/property/value}';" > -
@@ -60,14 +60,14 @@ << previous + tal:attributes="href string:${request/classname}?@template=help&@startwith=${prev/first}&properties=${request/form/properties/value}"><< previous   current next >> + tal:attributes="href string:${request/classname}?@template=help&@startwith=${next/first}&properties=${request/form/properties/value}">next >>   diff --git a/templates/classic/html/_generic.index.html b/templates/classic/html/_generic.index.html index abf8b16..e39c774 100644 --- a/templates/classic/html/_generic.index.html +++ b/templates/classic/html/_generic.index.html @@ -35,7 +35,7 @@ You are not allowed to view this page. tal:attributes="action context/designator">
- +
diff --git a/templates/classic/html/_generic.item.html b/templates/classic/html/_generic.item.html index 7d33127..fc35866 100644 --- a/templates/classic/html/_generic.item.html +++ b/templates/classic/html/_generic.item.html @@ -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"> - - + diff --git a/templates/classic/html/file.item.html b/templates/classic/html/file.item.html index 8361524..6f4542d 100644 --- a/templates/classic/html/file.item.html +++ b/templates/classic/html/file.item.html @@ -27,11 +27,11 @@ You are not allowed to view this page. diff --git a/templates/classic/html/issue.index.html b/templates/classic/html/issue.index.html index 0a26b59..df9f61f 100644 --- a/templates/classic/html/issue.index.html +++ b/templates/classic/html/issue.index.html @@ -64,7 +64,7 @@ You are not allowed to view this page. @@ -87,7 +87,7 @@ You are not allowed to view this page. @@ -128,4 +128,3 @@ You are not allowed to view this page. - diff --git a/templates/classic/html/issue.item.html b/templates/classic/html/issue.item.html index 1ac2325..c7f8b64 100644 --- a/templates/classic/html/issue.item.html +++ b/templates/classic/html/issue.item.html @@ -67,21 +67,21 @@ python:db.user.classhelp('username,realname,address', property='nosy', width='60 - + diff --git a/templates/classic/html/issue.search.html b/templates/classic/html/issue.search.html index a46334f..91f738b 100644 --- a/templates/classic/html/issue.search.html +++ b/templates/classic/html/issue.search.html @@ -26,7 +26,7 @@ - + @@ -136,40 +136,40 @@ - + - + - - - + diff --git a/templates/classic/html/keyword.item.html b/templates/classic/html/keyword.item.html index 42917bd..a73c9d2 100644 --- a/templates/classic/html/keyword.item.html +++ b/templates/classic/html/keyword.item.html @@ -41,8 +41,8 @@ diff --git a/templates/classic/html/user.index.html b/templates/classic/html/user.index.html index 136e544..d9d3c80 100644 --- a/templates/classic/html/user.index.html +++ b/templates/classic/html/user.index.html @@ -30,7 +30,7 @@ You are not allowed to view this page. diff --git a/templates/classic/html/user.item.html b/templates/classic/html/user.item.html index dc78107..e0cb51a 100644 --- a/templates/classic/html/user.item.html +++ b/templates/classic/html/user.item.html @@ -72,8 +72,8 @@ You are not allowed to view this page. @@ -90,7 +90,7 @@ You are not allowed to view this page. display
  - - - + + + submit button here
<< previous + {'@startwith':prev.first, '@pagesize':prev.size})"><< previous   next >> + {'@startwith':next.first, '@pagesize':next.size})">next >>  
Sort on: -
Group on: -
Change Note - +
File
  - - + + submit button will go here @@ -142,7 +142,7 @@ python:db.user.classhelp('username,realname,address', property='nosy', width='60 date remove + tal:attributes="href string:issue${context/id}?@remove@messages=${msg/id}&@action=edit">remove
Group on
All text*:  
Pagesize:
Start With:
Sort Descending:
Group Descending:
Query name**:
  - +
  - - + + submit button will go here diff --git a/templates/classic/html/page.html b/templates/classic/html/page.html index f5e7068..d2a8778 100644 --- a/templates/classic/html/page.html +++ b/templates/classic/html/page.html @@ -6,7 +6,7 @@ title goes here - + @@ -36,13 +36,13 @@ tal:condition="python:request.user.hasPermission('View', 'issue')"> Issues
Create New
- Show Unassigned
- Show All
- Search
- - - + href="issue?@template=item">Create New
+ Show Unassigned
+ Show All
+ Search
+ + +

@@ -50,23 +50,23 @@ tal:condition="python:request.user.hasPermission('View', 'keyword')"> Keywords
Create New
+ href="keyword?@template=item">Create New
Edit Existing
+ href="keyword?@template=item">Edit Existing

Administration
- Class List
+ Class List
User List
Add User + href="user?@template=item">Add User

Login


-
+
- Register
- Lost your login?
+ Lost your login?

Hello, username
- My Issues
+ My Issues
My Details
Logout + {'@action':'logout'})">Logout

Help
@@ -133,19 +133,19 @@

- - -     - + retire
  - - + + submit button here
- remove + remove
diff --git a/templates/classic/html/user.register.html b/templates/classic/html/user.register.html index 930616d..7340887 100644 --- a/templates/classic/html/user.register.html +++ b/templates/classic/html/user.register.html @@ -64,9 +64,9 @@ You are not allowed to view this page.   - - - + + + diff --git a/templates/classic/html/user.rego_progress.html b/templates/classic/html/user.rego_progress.html index fe78cd7..9df2de0 100644 --- a/templates/classic/html/user.rego_progress.html +++ b/templates/classic/html/user.rego_progress.html @@ -13,4 +13,3 @@ email. - diff --git a/templates/minimal/html/_generic.help.html b/templates/minimal/html/_generic.help.html index 8b035b6..9eee58f 100644 --- a/templates/minimal/html/_generic.help.html +++ b/templates/minimal/html/_generic.help.html @@ -16,7 +16,7 @@
@@ -59,14 +59,14 @@ << previous + tal:attributes="href string:${request/classname}?@template=help&@startwith=${prev/first}&properties=${request/form/properties/value}"><< previous   current next >> + tal:attributes="href string:${request/classname}?@template=help&@startwith=${next/first}&properties=${request/form/properties/value}">next >>   diff --git a/templates/minimal/html/_generic.index.html b/templates/minimal/html/_generic.index.html index f21b5d2..ac4f069 100644 --- a/templates/minimal/html/_generic.index.html +++ b/templates/minimal/html/_generic.index.html @@ -34,7 +34,7 @@ You are not allowed to view this page.
- +
diff --git a/templates/minimal/html/_generic.item.html b/templates/minimal/html/_generic.item.html index a9d2e85..0090c62 100644 --- a/templates/minimal/html/_generic.item.html +++ b/templates/minimal/html/_generic.item.html @@ -13,8 +13,7 @@ You are not allowed to view this page.
- - + diff --git a/templates/minimal/html/page.html b/templates/minimal/html/page.html index 87517c2..b563d76 100644 --- a/templates/minimal/html/page.html +++ b/templates/minimal/html/page.html @@ -6,7 +6,7 @@ title goes here - + @@ -27,30 +27,30 @@

- + Register + href="user?@template=register">Register

Hello,
username
My Details
Logout + {'@action':'logout'})">Logout

Administration
Class List
+ href="home?@template=classlist">Class List
User List
Add User + href="user?@template=item">Add User

diff --git a/templates/minimal/html/user.item.html b/templates/minimal/html/user.item.html index 642b776..41b1a02 100644 --- a/templates/minimal/html/user.item.html +++ b/templates/minimal/html/user.item.html @@ -12,7 +12,6 @@ You are not allowed to view this page.
- @@ -45,7 +44,10 @@ You are not allowed to view this page. - +
   + + + submit button here
-- 2.30.2