From: richard Date: Wed, 20 Feb 2002 05:05:29 +0000 (+0000) Subject: . Added simple editing for classes that don't define a templated interface. X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=cd355af47a60fa0ea49b4a42a376066dae1c7716;p=roundup.git . Added simple editing for classes that don't define a templated interface. - access using the admin "class list" interface - limited to admin-only - requires the csv module from object-craft (url given if it's missing) git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@639 57a73879-2fb5-44c3-a270-3262357dd7e2 --- diff --git a/CHANGES.txt b/CHANGES.txt index f20f74d..646fa55 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,7 +8,10 @@ Feature: better configuration system. . Alternate email addresses are now available for users. See the MIGRATION file for info on how to activate the feature. - + . Added simple editing for classes that don't define a templated interface. + - access using the admin "class list" interface + - limited to admin-only + - requires the csv module from object-craft (url given if it's missing) Fixed: . Clean up mail handling, multipart handling. @@ -25,6 +28,7 @@ Fixed: . #516854 ] "My Issues" and redisplay . #517906 ] Attribute order in "View customisation" . #514854 ] History: "User" is always ticket creator + . wasn't handling cvs parser feeding correctly 2002-01-24 - 0.4.0 diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py index 661dbb0..0e446d4 100644 --- a/roundup/cgi_client.py +++ b/roundup/cgi_client.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: cgi_client.py,v 1.102 2002-02-15 07:08:44 richard Exp $ +# $Id: cgi_client.py,v 1.103 2002-02-20 05:05:28 richard Exp $ __doc__ = """ WWW request handler (also used in the stand-alone server). @@ -298,10 +298,88 @@ function submit_once() { show_customization = self.customization_widget() index = htmltemplate.IndexTemplate(self, self.instance.TEMPLATES, cn) - index.render(filterspec, filter, columns, sort, group, - show_customization=show_customization) + try: + index.render(filterspec, filter, columns, sort, group, + show_customization=show_customization) + except htmltemplate.MissingTemplateError: + self.basicClassEditPage() self.pagefoot() + def basicClassEditPage(self): + '''Display a basic edit page that allows simple editing of the + nodes of the current class + ''' + if self.user != 'admin': + raise Unauthorised + w = self.write + cn = self.classname + cl = self.db.classes[cn] + props = ['id'] + cl.getprops(protected=0).keys() + + # get the CSV module + try: + import csv + except ImportError: + w(_('Sorry, you need the csv module to use this function.
\n' + 'Get it from: http://www.object-craft.com.au/projects/csv/')) + return + + # do the edit + if self.form.has_key('rows'): + rows = self.form['rows'].value.splitlines() + p = csv.parser() + idlessprops = props[1:] + found = {} + for row in rows: + values = p.parse(row) + # not a complete row, keep going + if not values: continue + + # extract the nodeid + nodeid, values = values[0], values[1:] + found[nodeid] = 1 + + # extract the new values + d = {} + for name, value in zip(idlessprops, values): + d[name] = value.strip() + + # perform the edit + if cl.hasnode(nodeid): + # edit existing + cl.set(nodeid, **d) + else: + # new node + found[cl.create(**d)] = 1 + + # retire the removed entries + for nodeid in cl.list(): + if not found.has_key(nodeid): + cl.retire(nodeid) + + w(_('''

You may edit the contents of the + "%(classname)s" class using this form.

+

Remove entries by deleting their line. Add + new entries by appending + them to the table - put an X in the id column.

''')%{'classname':cn}) + + l = [] + for name in props: + l.append(name) + w('') + w(', '.join(l) + '\n') + w('') + + w('
') + w('
')) + def shownode(self, message=None): ''' display an item ''' @@ -744,7 +822,8 @@ function submit_once() { self.write('\n') for cn in classnames: cl = self.db.getclass(cn) - self.write(''%cn.capitalize()) + self.write(''%(cn, cn.capitalize())) for key, value in cl.properties.items(): if value is None: value = '' else: value = str(value) @@ -1012,6 +1091,8 @@ function submit_once() { if action == 'logout': self.logout() return + + # see if we're to display an existing node m = dre.match(action) if m: self.classname = m.group(1) @@ -1030,6 +1111,8 @@ function submit_once() { raise NotFound func() return + + # see if we're to put up the new node page m = nre.match(action) if m: self.classname = m.group(1) @@ -1039,6 +1122,8 @@ function submit_once() { raise NotFound func() return + + # otherwise, display the named class self.classname = action try: self.db.getclass(self.classname) @@ -1202,6 +1287,10 @@ def parsePropsFromForm(db, cl, form, nodeid=0): # # $Log: not supported by cvs2svn $ +# Revision 1.102 2002/02/15 07:08:44 richard +# . Alternate email addresses are now available for users. See the MIGRATION +# file for info on how to activate the feature. +# # Revision 1.101 2002/02/14 23:39:18 richard # . All forms now have "double-submit" protection when Javascript is enabled # on the client-side. diff --git a/roundup/htmltemplate.py b/roundup/htmltemplate.py index ad7d8de..92c0937 100644 --- a/roundup/htmltemplate.py +++ b/roundup/htmltemplate.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: htmltemplate.py,v 1.76 2002-02-16 09:10:52 richard Exp $ +# $Id: htmltemplate.py,v 1.77 2002-02-20 05:05:29 richard Exp $ __doc__ = """ Template engine. @@ -33,6 +33,9 @@ try: except ImportError: StructuredText = None +class MissingTemplateError(ValueError): + pass + class TemplateFunctions: def __init__(self): self.form = None @@ -714,8 +717,12 @@ class IndexTemplate(TemplateFunctions): # XXX deviate from spec here ... # load the index section template and figure the default columns from it - template = open(os.path.join(self.templates, - self.classname+'.index')).read() + try: + template = open(os.path.join(self.templates, + self.classname+'.index')).read() + except IOError, error: + if error.errno not in (errno.ENOENT, errno.ESRCH): raise + raise MissingTemplateError, self.classname+'.index' all_columns = self.col_re.findall(template) if not columns: columns = [] @@ -1070,6 +1077,9 @@ class NewItemTemplate(TemplateFunctions): # # $Log: not supported by cvs2svn $ +# Revision 1.76 2002/02/16 09:10:52 richard +# oops +# # Revision 1.75 2002/02/16 08:43:23 richard # . #517906 ] Attribute order in "View customisation" # diff --git a/roundup/hyperdb.py b/roundup/hyperdb.py index f0aaf92..0386a73 100644 --- a/roundup/hyperdb.py +++ b/roundup/hyperdb.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: hyperdb.py,v 1.55 2002-02-15 07:27:12 richard Exp $ +# $Id: hyperdb.py,v 1.56 2002-02-20 05:05:28 richard Exp $ __doc__ = """ Hyperdatabase implementation, especially field types. @@ -490,6 +490,7 @@ class Class: if node.has_key(self.db.RETIRED_FLAG): raise IndexError num_re = re.compile('^\d+$') + set = {} for key, value in propvalues.items(): # check to make sure we're not duplicating an existing key if key == self.key and node[key] != value: @@ -505,6 +506,13 @@ class Class: # the writeable properties. prop = self.properties[key] + # if the value's the same as the existing value, no sense in + # doing anything + if value == node[key]: + del propvalues[key] + continue + + # do stuff based on the prop type if isinstance(prop, Link): link_class = self.properties[key].classname # if it isn't a number, it's a key @@ -598,6 +606,11 @@ class Class: node[key] = value + # nothing to do? + if not propvalues: + return + + # do the set, and journal it self.db.setnode(self.classname, nodeid, node) self.db.addjournal(self.classname, nodeid, 'set', propvalues) @@ -633,6 +646,10 @@ class Class: return self.db.getjournal(self.classname, nodeid) # Locating nodes: + def hasnode(self, nodeid): + '''Determine if the given nodeid actually exists + ''' + return self.db.hasnode(self.classname, nodeid) def setkey(self, propname): """Select a String property of this class to be the key property. @@ -1066,6 +1083,9 @@ def Choice(name, *options): # # $Log: not supported by cvs2svn $ +# Revision 1.55 2002/02/15 07:27:12 richard +# Oops, precedences around the way w0rng. +# # Revision 1.54 2002/02/15 07:08:44 richard # . Alternate email addresses are now available for users. See the MIGRATION # file for info on how to activate the feature.
%s
' + '%s