Code

removed debugging
[roundup.git] / roundup / cgi / templating.py
index 81d10ac83241ffca52207c118d09dd2c57195395..1de56e1ce1d42b1503959af6015915b1961f6203 100644 (file)
@@ -155,7 +155,10 @@ class RoundupPageTemplate(PageTemplate.PageTemplate):
         }
         # add in the item if there is one
         if client.nodeid:
-            c['context'] = HTMLItem(client, classname, client.nodeid)
+            if classname == 'user':
+                c['context'] = HTMLUser(client, classname, client.nodeid)
+            else:
+                c['context'] = HTMLItem(client, classname, client.nodeid)
         else:
             c['context'] = HTMLClass(client, classname)
         return c
@@ -170,7 +173,7 @@ class RoundupPageTemplate(PageTemplate.PageTemplate):
 
         if self._v_errors:
             raise PageTemplate.PTRuntimeError, \
-                'Page Template %s has errors.' % self.id
+                'Page Template %s has errors.'%self.id
 
         # figure the context
         classname = classname or client.classname
@@ -193,12 +196,16 @@ class HTMLDatabase:
         # we want config to be exposed
         self.config = client.db.config
 
+    def __getitem__(self, item):
+        self._client.db.getclass(item)
+        return HTMLClass(self._client, item)
+
     def __getattr__(self, attr):
         try:
-            self._client.db.getclass(attr)
+            return self[attr]
         except KeyError:
             raise AttributeError, attr
-        return HTMLClass(self._client, attr)
+
     def classes(self):
         l = self._client.db.classes.keys()
         l.sort()
@@ -214,15 +221,34 @@ def lookupIds(db, prop, ids, num_re=re.compile('-?\d+')):
             l.append(cl.lookup(entry))
     return l
 
-class HTMLClass:
+class HTMLPermissions:
+    ''' Helpers that provide answers to commonly asked Permission questions.
+    '''
+    def is_edit_ok(self):
+        ''' Is the user allowed to Edit the current class?
+        '''
+        return self._db.security.hasPermission('Edit', self._client.userid,
+            self._classname)
+    def is_view_ok(self):
+        ''' Is the user allowed to View the current class?
+        '''
+        return self._db.security.hasPermission('View', self._client.userid,
+            self._classname)
+    def is_only_view_ok(self):
+        ''' Is the user only allowed to View (ie. not Edit) the current class?
+        '''
+        return self.is_view_ok() and not self.is_edit_ok()
+
+class HTMLClass(HTMLPermissions):
     ''' Accesses through a class (either through *class* or *db.<classname>*)
     '''
     def __init__(self, client, classname):
         self._client = client
         self._db = client.db
 
-        # we want classname to be exposed
-        self.classname = classname
+        # we want classname to be exposed, but _classname gives a
+        # consistent API for extending Class/Item
+        self._classname = self.classname = classname
         if classname is not None:
             self._klass = self._db.getclass(self.classname)
             self._props = self._klass.getprops()
@@ -297,7 +323,13 @@ class HTMLClass:
             klass = HTMLUser
         else:
             klass = HTMLItem
-        l = [klass(self._client, self.classname, x) for x in self._klass.list()]
+
+        # get the list and sort it nicely
+        l = self._klass.list()
+        sortfunc = make_sort_function(self._db, self.classname)
+        l.sort(sortfunc)
+
+        l = [klass(self._client, self.classname, x) for x in l]
         return l
 
     def csv(self):
@@ -395,7 +427,7 @@ class HTMLClass:
         # use our fabricated request
         return pt.render(self._client, self.classname, req)
 
-class HTMLItem:
+class HTMLItem(HTMLPermissions):
     ''' Accesses through an *item*
     '''
     def __init__(self, client, classname, nodeid):
@@ -413,7 +445,7 @@ class HTMLItem:
     def __getitem__(self, item):
         ''' return an HTMLProperty instance
         '''
-       #print 'HTMLItem.getitem', (self, item)
+        #print 'HTMLItem.getitem', (self, item)
         if item == 'id':
             return self._nodeid
 
@@ -623,6 +655,7 @@ class HTMLUser(HTMLItem):
 
         # used for security checks
         self._security = client.db.security
+
     _marker = []
     def hasPermission(self, role, classname=_marker):
         ''' Determine if the user has the Role.
@@ -634,6 +667,20 @@ class HTMLUser(HTMLItem):
             classname = self._default_classname
         return self._security.hasPermission(role, self._nodeid, classname)
 
+    def is_edit_ok(self):
+        ''' Is the user allowed to Edit the current class?
+            Also check whether this is the current user's info.
+        '''
+        return self._db.security.hasPermission('Edit', self._client.userid,
+            self._classname) or self._nodeid == self._client.userid
+
+    def is_view_ok(self):
+        ''' Is the user allowed to View the current class?
+            Also check whether this is the current user's info.
+        '''
+        return self._db.security.hasPermission('Edit', self._client.userid,
+            self._classname) or self._nodeid == self._client.userid
+
 class HTMLProperty:
     ''' String, Number, Date, Interval HTMLProperty
 
@@ -726,10 +773,18 @@ class PasswordHTMLProperty(HTMLProperty):
         return _('*encrypted*')
 
     def field(self, size = 30):
-        ''' Render a form edit field for the property
+        ''' Render a form edit field for the property.
         '''
         return '<input type="password" name="%s" size="%s">'%(self._name, size)
 
+    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 "name:confirm".
+        '''
+        return '<input type="password" name="%s:confirm" size="%s">'%(
+            self._name, size)
+
 class NumberHTMLProperty(HTMLProperty):
     def plain(self):
         ''' Render a "plain" representation of the property
@@ -857,7 +912,7 @@ class LinkHTMLProperty(HTMLProperty):
             value = cgi.escape(value)
         return value
 
-    def field(self):
+    def field(self, showid=0, size=None):
         ''' Render a form edit field for the property
         '''
         linkcl = self._db.getclass(self._prop.classname)
@@ -865,26 +920,35 @@ class LinkHTMLProperty(HTMLProperty):
             sort_on = 'order'  
         else:  
             sort_on = linkcl.labelprop()  
-        options = linkcl.filter(None, {}, [sort_on], []) 
+        options = linkcl.filter(None, {}, ('+', sort_on), (None, None))
         # TODO: make this a field display, not a menu one!
-        l = ['<select name="%s">'%property]
+        l = ['<select name="%s">'%self._name]
         k = linkcl.labelprop(1)
-        if value is None:
+        if self._value is None:
             s = 'selected '
         else:
             s = ''
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
         for optionid in options:
-            option = linkcl.get(optionid, k)
+            # get the option value, and if it's None use an empty string
+            option = linkcl.get(optionid, k) or ''
+
+            # figure if this option is selected
             s = ''
-            if optionid == value:
+            if optionid == self._value:
                 s = 'selected '
+
+            # figure the label
             if showid:
                 lab = '%s%s: %s'%(self._prop.classname, optionid, option)
             else:
                 lab = option
+
+            # truncate if it's too long
             if size is not None and len(lab) > size:
                 lab = lab[:size-3] + '...'
+
+            # and generate
             lab = cgi.escape(lab)
             l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
         l.append('</select>')
@@ -899,9 +963,6 @@ class LinkHTMLProperty(HTMLProperty):
         # sort function
         sortfunc = make_sort_function(self._db, self._prop.classname)
 
-        # force the value to be a single choice
-        if isinstance(value, type('')):
-            value = value[0]
         linkcl = self._db.getclass(self._prop.classname)
         l = ['<select name="%s">'%self._name]
         k = linkcl.labelprop(1)
@@ -915,14 +976,21 @@ class LinkHTMLProperty(HTMLProperty):
             sort_on = ('+', linkcl.labelprop())
         options = linkcl.filter(None, conditions, sort_on, (None, None))
         for optionid in options:
-            option = linkcl.get(optionid, k)
+            # get the option value, and if it's None use an empty string
+            option = linkcl.get(optionid, k) or ''
+
+            # figure if this option is selected
             s = ''
             if value in [optionid, option]:
                 s = 'selected '
+
+            # figure the label
             if showid:
                 lab = '%s%s: %s'%(self._prop.classname, optionid, option)
             else:
                 lab = option
+
+            # truncate if it's too long
             if size is not None and len(lab) > size:
                 lab = lab[:size-3] + '...'
             if additional:
@@ -930,6 +998,8 @@ class LinkHTMLProperty(HTMLProperty):
                 for propname in additional:
                     m.append(linkcl.get(optionid, propname))
                 lab = lab + ' (%s)'%', '.join(map(str, m))
+
+            # and generate
             lab = cgi.escape(lab)
             l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
         l.append('</select>')
@@ -999,6 +1069,8 @@ class MultilinkHTMLProperty(HTMLProperty):
         if value:
             value.sort(sortfunc)
         # map the id to the label property
+        if not linkcl.getkey():
+            showid=1
         if not showid:
             k = linkcl.labelprop(1)
             value = [linkcl.get(v, k) for v in value]
@@ -1024,14 +1096,20 @@ class MultilinkHTMLProperty(HTMLProperty):
         l = ['<select multiple name="%s" size="%s">'%(self._name, height)]
         k = linkcl.labelprop(1)
         for optionid in options:
-            option = linkcl.get(optionid, k)
+            # get the option value, and if it's None use an empty string
+            option = linkcl.get(optionid, k) or ''
+
+            # figure if this option is selected
             s = ''
             if optionid in value or option in value:
                 s = 'selected '
+
+            # figure the label
             if showid:
                 lab = '%s%s: %s'%(self._prop.classname, optionid, option)
             else:
                 lab = option
+            # truncate if it's too long
             if size is not None and len(lab) > size:
                 lab = lab[:size-3] + '...'
             if additional:
@@ -1039,6 +1117,8 @@ class MultilinkHTMLProperty(HTMLProperty):
                 for propname in additional:
                     m.append(linkcl.get(optionid, propname))
                 lab = lab + ' (%s)'%', '.join(m)
+
+            # and generate
             lab = cgi.escape(lab)
             l.append('<option %svalue="%s">%s</option>'%(s, optionid,
                 lab))
@@ -1096,7 +1176,6 @@ class HTMLRequest:
 
         "form" the CGI form as a cgi.FieldStorage
         "env" the CGI environment variables
-        "url" the current URL path for this request
         "base" the base URL for this instance
         "user" a HTMLUser instance for this user
         "classname" the current classname (possibly None)
@@ -1120,7 +1199,6 @@ class HTMLRequest:
         self.form = client.form
         self.env = client.env
         self.base = client.base
-        self.url = client.url
         self.user = HTMLUser(client, 'user', client.userid)
 
         # store the current class name and action
@@ -1247,7 +1325,6 @@ class HTMLRequest:
         d['env'] = e
         return '''
 form: %(form)s
-url: %(url)r
 base: %(base)r
 classname: %(classname)r
 template: %(template)r