Code

Fixed instance installation ... moved the htmlbase module into templates
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 5 Sep 2002 23:39:14 +0000 (23:39 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 5 Sep 2002 23:39:14 +0000 (23:39 +0000)
and call it <template>_htmlbase.py ... no more try/except in instance __init__!
Added :required to form handling.
Handle multiple values for single form items with decent error report.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1074 57a73879-2fb5-44c3-a270-3262357dd7e2

TODO.txt
roundup/cgi/client.py
roundup/init.py
roundup/templates/.cvsignore
roundup/templates/builder.py
roundup/templates/classic/__init__.py

index 4407f61f7782b958b3a6edb356b82f42907b841a..b7b3abb0ec6b90298264a61cd41a7a2b1d85eb7b 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -47,19 +47,14 @@ pending web: search "refinement"
                query values
 pending web: have roundup.cgi pick up instance config from the environment 
 pending web: UNIX init.d script for roundup-server
+pending web: rewritten documentation (can come after the beta though so stuff
+             is settled)
+pending web: modify cgitb to handle PageTemplate errors better (see how 
+             Zope handles __traceback_supplement__ and __traceback_info__)
+pending web: title is stoopid
 
-New templating TODO:
-. rewritten documentation (can come after the beta though so stuff is settled)
-. modify cgitb to handle PageTemplate errors better
-. add :required to edit action
-active web: title is stoopid
-active hyperdb: full-text searching doesn't appear to match stuff in titles,
-                even though they're supposed to be indexed...
-
-ongoing: any bugs
 bug: request.url is incorrect in cgi-bin environments
 
-rejected instance: the use of non-Python configuration files (ConfigParser)
 done web: Re-enable link backrefs from messages (feature request #568714) (RJ)
 done web: have the page layout (header/footer) be templatable (RJ)
 done web: fixing the templating so it works (RJ)
@@ -79,7 +74,10 @@ done mailgw: better help message (feature request #558562) (RJ)
 done security: add info from doc/security.txt to design doc (RJ)
 done security: switch to sessions for web authentication (RJ)
 done security: implement and use the new logical control mechanisms
-done web: saving of named queries (GM)
+done web: saving of named queries (GM, RJ)
 done web: handle "not found", access and item page render errors better (RJ)
 done web: fix double-submit by having new-item-submit redirect at end (RJ)
-done web: daemonify roundup-server (fork, logfile, pidfile)
+done web: daemonify roundup-server (fork, logfile, pidfile) (RJ)
+
+rejected instance: the use of non-Python configuration files (ConfigParser)
+
index 22555bbaba852736b6f7bae4b90e22575a9d9b63..663f0c764d429ce0dd22d043b182907768dbbc3a 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.14 2002-09-05 05:25:23 richard Exp $
+# $Id: client.py,v 1.15 2002-09-05 23:39:12 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -114,9 +114,15 @@ class Client:
             In some situations, exceptions occur:
             - HTTP Redirect  (generally raised by an action)
             - SendFile       (generally raised by determine_context)
+              serve up a FileClass "content" property
             - SendStaticFile (generally raised by determine_context)
-            - Unauthorised   (raised pretty much anywhere it needs to be)
-            - NotFound       (see above... percolates up to the CGI interface)
+              serve up a file from the tracker "html" directory
+            - Unauthorised   (generally raised by an action)
+              the action is cancelled, the request is rendered and an error
+              message is displayed indicating that permission was not
+              granted for the action to take place
+            - NotFound       (raised wherever it needs to be)
+              percolates up to the CGI interface that called the client
         '''
         self.content_action = None
         self.ok_message = []
@@ -581,6 +587,10 @@ class Client:
              Create a file and attach it to the current node's
              "files" property. Attach the file to the message created from
              the __note if it's supplied.
+
+            :required=property,property,...
+             The named properties are required to be filled in the form.
+
         '''
         cl = self.db.classes[self.classname]
 
@@ -653,7 +663,8 @@ class Client:
     def newItemAction(self):
         ''' Add a new item to the database.
 
-            This follows the same form as the editItemAction
+            This follows the same form as the editItemAction, with the same
+            special form values.
         '''
         cl = self.db.classes[self.classname]
 
@@ -1033,36 +1044,58 @@ class Client:
 
 
 def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
-    '''Pull properties for the given class out of the form.
+    ''' Pull properties for the given class out of the form.
+
+        If a ":required" parameter is supplied, then the names property values
+        must be supplied or a ValueError will be raised.
     '''
+    required = []
+    if form.has_key(':required'):
+        value = form[':required']
+        if isinstance(value, type([])):
+            required = [i.value.strip() for i in value]
+        else:
+            required = [i.strip() for i in value.value.split(',')]
+
     props = {}
     keys = form.keys()
     for key in keys:
         if not cl.properties.has_key(key):
             continue
         proptype = cl.properties[key]
+
+        # Get the form value. This value may be a MiniFieldStorage or a list
+        # of MiniFieldStorages.
+        value = form[key]
+
+        # make sure non-multilinks only get one value
+        if not isinstance(proptype, hyperdb.Multilink):
+            if isinstance(value, type([])):
+                raise ValueError, 'You have submitted more than one value'\
+                    ' for the %s property'%key
+            # we've got a MiniFieldStorage, so pull out the value and strip
+            # surrounding whitespace
+            value = value.value.strip()
+
         if isinstance(proptype, hyperdb.String):
+            pass
             value = form[key].value.strip()
         elif isinstance(proptype, hyperdb.Password):
-            value = form[key].value.strip()
             if not value:
                 # ignore empty password values
                 continue
             value = password.Password(value)
         elif isinstance(proptype, hyperdb.Date):
-            value = form[key].value.strip()
             if value:
                 value = date.Date(form[key].value.strip())
             else:
                 value = None
         elif isinstance(proptype, hyperdb.Interval):
-            value = form[key].value.strip()
             if value:
                 value = date.Interval(form[key].value.strip())
             else:
                 value = None
         elif isinstance(proptype, hyperdb.Link):
-            value = form[key].value.strip()
             # see if it's the "no selection" choice
             if value == '-1':
                 value = None
@@ -1077,11 +1110,13 @@ def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
                             '%(value)s not a %(classname)s')%{'propname':key, 
                             'value': value, 'classname': link}
         elif isinstance(proptype, hyperdb.Multilink):
-            value = form[key]
-            if not isinstance(value, type([])):
-                value = [i.strip() for i in value.value.split(',')]
-            else:
+            if isinstance(value, type([])):
+                # it's a list of MiniFieldStorages
                 value = [i.value.strip() for i in value]
+            else:
+                # it's a MiniFieldStorage, but may be a comma-separated list
+                # of values
+                value = [i.strip() for i in value.value.split(',')]
             link = cl.properties[key].classname
             l = []
             for entry in map(str, value):
@@ -1097,10 +1132,8 @@ def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
             l.sort()
             value = l
         elif isinstance(proptype, hyperdb.Boolean):
-            value = form[key].value.strip()
             props[key] = value = value.lower() in ('yes', 'true', 'on', '1')
         elif isinstance(proptype, hyperdb.Number):
-            value = form[key].value.strip()
             props[key] = value = int(value)
 
         # get the old value
@@ -1117,6 +1150,15 @@ def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
                 props[key] = value
         else:
             props[key] = value
+
+    # see if all the required properties have been supplied
+    l = []
+    for property in required:
+        if not props.has_key(property):
+            l.append(property)
+    if l:
+        raise ValueError, 'Required properties %s not supplied'%(', '.join(l))
+
     return props
 
 
index 7723aba1088499ded89fef1d7c99ca8a4b4fe0c0..0ede7b06f0c697539bb75a2bd9d651e838605628 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: init.py,v 1.21 2002-08-16 04:25:03 richard Exp $
+# $Id: init.py,v 1.22 2002-09-05 23:39:12 richard Exp $
 
 __doc__ = """
 Init (create) a roundup instance.
@@ -89,6 +89,7 @@ def install(instance_home, template, backend):
     # first, copy the template dir over
     from roundup.templates import builder
 
+    # copy the roundup.templates.<template> package contents to the instance dir
     template_dir = os.path.split(__file__)[0]
     template_name = template
     template = os.path.join(template_dir, 'templates', template)
@@ -114,6 +115,9 @@ def initialise(instance_home, adminpw):
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.21  2002/08/16 04:25:03  richard
+# cleanup: moved templatebuilder into templates.builder
+#
 # Revision 1.20  2002/07/14 02:05:53  richard
 # . all storage-specific code (ie. backend) is now implemented by the backends
 #
index 4162d5ec670fe99e1cc8e163ad6a0fe34e9baa42..8d65e4170ddc7fe4a52b96672263b58c812de428 100644 (file)
@@ -1,3 +1,5 @@
 *.pyc
 *.pyo
 *.cover
+*_htmlbase.py
+*_htmlbase.pyc
index 40e6eec848716b21b5155fe7e7faea97b4eb3ae1..11ea2104188564fbfe628b9acfba5926da15c7f7 100644 (file)
@@ -15,8 +15,8 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: builder.py,v 1.1 2002-08-16 04:25:03 richard Exp $
-import errno, re
+# $Id: builder.py,v 1.2 2002-09-05 23:39:13 richard Exp $
+import os, sys, glob, errno, re
 
 __doc__ = """
 Collect template parts and create instance template files.
@@ -29,14 +29,19 @@ preamble = """
 """
 
 def makeHtmlBase(templateDir):
-    """ make a htmlbase.py file in the given templateDir, from the
-        contents of templateDir/html """
-    import os, glob, re
+    ''' make a <template>_htmlbase.py file in rondup.tempaltes, from the
+        contents of templateDir/html
+    '''
     print "packing up templates in", templateDir
+
     filelist = glob.glob(os.path.join(templateDir, 'html', '*'))
     filelist = filter(os.path.isfile, filelist) # only want files
     filelist.sort()
-    fd = open(os.path.join(templateDir, 'htmlbase.py'), 'w')
+
+    # ok, figure the template name and templates dir
+    dir, name = os.path.split(templateDir)
+
+    fd = open(os.path.join(dir, '%s_htmlbase.py'%name), 'w')
     fd.write(preamble)
     for file in filelist:
         # skip the backup files created by richard's vim
@@ -49,16 +54,16 @@ def makeHtmlBase(templateDir):
     fd.close()
 
 def installHtmlBase(template, installDir):
-    """ passed a template package and an installDir, unpacks the html files into
-      the installdir """
-    import os,sys,re
-
-    tdir = __import__('roundup.templates.%s.htmlbase'%template).templates
-    if hasattr(tdir, template):
-        tmod = getattr(tdir, template)
+    ''' passed a template name and an installDir, unpacks the html files into
+        the installdir
+    '''
+    tmod = '%s_htmlbase'%template
+    tdir = __import__('roundup.templates.'+tmod).templates
+    if hasattr(tdir, tmod):
+        htmlbase = getattr(tdir, tmod)
     else:
-        raise "TemplateError", "couldn't find roundup.template.%s.htmlbase"%template
-    htmlbase = tmod.htmlbase
+        raise "TemplateError", \
+            "couldn't find roundup.templates.%s_htmlbase"%template
     installDir = os.path.join(installDir, 'html')
     try:
         os.makedirs(installDir)
@@ -76,10 +81,7 @@ def installHtmlBase(template, installDir):
         data = getattr(htmlbase, mangledfile)
         outfd.write(data)
     
-
-
 if __name__ == "__main__":
-    import sys
     if len(sys.argv) == 2:
         makeHtmlBase(sys.argv[1])
     elif len(sys.argv) == 3:
@@ -89,6 +91,9 @@ if __name__ == "__main__":
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.1  2002/08/16 04:25:03  richard
+# cleanup: moved templatebuilder into templates.builder
+#
 # Revision 1.14  2002/02/05 09:59:05  grubert
 #  . makeHtmlBase: re.sub under python 2.2 did not replace '.', string.replace does it.
 #
index 86d1da08a50da9b2d1f46b8403b3f39f856debc3..de8c855886db7cc95569da7234832ef77bc7402e 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: __init__.py,v 1.5 2001-08-07 00:24:43 richard Exp $
+# $Id: __init__.py,v 1.6 2002-09-05 23:39:14 richard Exp $
 
 import sys
 from instance_config import *
-try:
-    from dbinit import *
-except:
-    pass # in install dir (probably :)
+from dbinit import *
 from interfaces import *
 
 # 
 # $Log: not supported by cvs2svn $
+# Revision 1.5  2001/08/07 00:24:43  richard
+# stupid typo
+#
 # Revision 1.4  2001/08/07 00:15:51  richard
 # Added the copyright/license notice to (nearly) all files at request of
 # Bizar Software.