Code

add line to rego email to help URL detection (sf bug 906247)
[roundup.git] / roundup / cgi / templating.py
index aab6a95fb5ac30883d44bb54689a3d0eddeeee42..a403481864364642d7a03072f332ee334593f6d9 100644 (file)
@@ -205,7 +205,10 @@ class RoundupPageTemplate(PageTemplate.PageTemplate):
                 c['context'] = HTMLItem(client, classname, client.nodeid,
                     anonymous=1)
         elif client.db.classes.has_key(classname):
-            c['context'] = HTMLClass(client, classname, anonymous=1)
+            if classname == 'user':
+                c['context'] = HTMLUserClass(client, classname, anonymous=1)
+            else:
+                c['context'] = HTMLClass(client, classname, anonymous=1)
         return c
 
     def render(self, client, classname, request, **options):
@@ -253,6 +256,8 @@ class HTMLDatabase:
             return HTMLItem(self._client, m.group('cl'), m.group('id'))
         else:
             self._client.db.getclass(item)
+            if item == 'user':
+                return HTMLUserClass(self._client, item)
             return HTMLClass(self._client, item)
 
     def __getattr__(self, attr):
@@ -264,9 +269,17 @@ class HTMLDatabase:
     def classes(self):
         l = self._client.db.classes.keys()
         l.sort()
-        return [HTMLClass(self._client, cn) for cn in l]
-
-def lookupIds(db, prop, ids, num_re=re.compile('-?\d+')):
+        r = []
+        for item in l:
+            if item == 'user':
+                m.append(HTMLUserClass(self._client, item))
+            m.append(HTMLClass(self._client, item))
+        return r
+
+def lookupIds(db, prop, ids, fail_ok=0, num_re=re.compile('-?\d+')):
+    ''' "fail_ok" should be specified if we wish to pass through bad values
+        (most likely form values that we wish to represent back to the user)
+    '''
     cl = db.getclass(prop.classname)
     l = []
     for entry in ids:
@@ -275,9 +288,22 @@ def lookupIds(db, prop, ids, num_re=re.compile('-?\d+')):
         else:
             try:
                 l.append(cl.lookup(entry))
-            except KeyError:
-                # ignore invalid keys
-                pass
+            except (TypeError, KeyError):
+                if fail_ok:
+                    # pass through the bad value
+                    l.append(entry)
+    return l
+
+def lookupKeys(linkcl, key, ids, num_re=re.compile('-?\d+')):
+    ''' Look up the "key" values for "ids" list - though some may already
+    be key values, not ids.
+    '''
+    l = []
+    for entry in ids:
+        if num_re.match(entry):
+            l.append(linkcl.get(entry, key))
+        else:
+            l.append(entry)
     return l
 
 class HTMLPermissions:
@@ -362,7 +388,10 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
             return None
 
         # get the property
-        prop = self._props[item]
+        try:
+            prop = self._props[item]
+        except KeyError:
+            raise KeyError, 'No such property "%s" on %s'%(item, self.classname)
 
         # look up the correct HTMLProperty class
         form = self._client.form
@@ -372,11 +401,12 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
             if form.has_key(item):
                 if isinstance(prop, hyperdb.Multilink):
                     value = lookupIds(self._db, prop,
-                        handleListCGIValue(form[item]))
+                        handleListCGIValue(form[item]), fail_ok=1)
                 elif isinstance(prop, hyperdb.Link):
                     value = form[item].value.strip()
                     if value:
-                        value = lookupIds(self._db, prop, [value])[0]
+                        value = lookupIds(self._db, prop, [value],
+                            fail_ok=1)[0]
                     else:
                         value = None
                 else:
@@ -407,7 +437,7 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
         ''' Get an item of this class by its item id.
         '''
         # make sure we're looking at an itemid
-        if not num_re.match(itemid):
+        if not isinstance(itemid, type(1)) and not num_re.match(itemid):
             itemid = self._klass.lookup(itemid)
 
         if self.classname == 'user':
@@ -612,14 +642,17 @@ class HTMLItem(HTMLInputMixin, HTMLPermissions):
             raise AttributeError, attr
 
     def designator(self):
-        ''' Return this item's designator (classname + id) '''
+        """Return this item's designator (classname + id)."""
         return '%s%s'%(self._classname, self._nodeid)
     
     def submit(self, label="Submit Changes"):
-        ''' Generate a submit button (and action hidden element)
-        '''
-        return self.input(type="hidden",name="@action",value="edit") + '\n' + \
-               self.input(type="submit",name="submit",value=label)
+        """Generate a submit button.
+
+        Also sneak in the lastactivity and action hidden elements.
+        """
+        return self.input(type="hidden", name="@lastactivity", value=date.Date('.')) + '\n' + \
+               self.input(type="hidden", name="@action", value="edit") + '\n' + \
+               self.input(type="submit", name="submit", value=label)
 
     def journal(self, direction='descending'):
         ''' Return a list of HTMLJournalEntry instances.
@@ -847,7 +880,44 @@ class HTMLItem(HTMLInputMixin, HTMLPermissions):
         # use our fabricated request
         return pt.render(self._client, req.classname, req)
 
-class HTMLUser(HTMLItem):
+class HTMLUserPermission:
+
+    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._user_perm_check('Edit')
+
+    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._user_perm_check('View')
+
+    def _user_perm_check(self, type):
+        # some users may view / edit all users
+        s = self._db.security
+        userid = self._client.userid
+        if s.hasPermission(type, userid, self._classname):
+            return 1
+
+        # users may view their own info
+        is_anonymous = self._db.user.get(userid, 'username') == 'anonymous'
+        if getattr(self, '_nodeid', None) == userid and not is_anonymous:
+            return 1
+
+        # may anonymous users register?
+        if (is_anonymous and s.hasPermission('Web Registration', userid,
+                self._classname)):
+            return 1
+
+        # nope, no access here
+        return 0
+
+class HTMLUserClass(HTMLUserPermission, HTMLClass):
+    pass
+
+class HTMLUser(HTMLUserPermission, HTMLItem):
     ''' Accesses through the *user* (a special case of item)
     '''
     def __init__(self, client, classname, nodeid, anonymous=0):
@@ -868,22 +938,6 @@ class HTMLUser(HTMLItem):
             classname = self._default_classname
         return self._security.hasPermission(permission, 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 and
-            self._db.user.get(self._client.userid, 'username') != 'anonymous')
-
-    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('View', self._client.userid,
-            self._classname) or (self._nodeid == self._client.userid and
-            self._db.user.get(self._client.userid, 'username') != 'anonymous')
-
 class HTMLProperty(HTMLInputMixin, HTMLPermissions):
     ''' String, Number, Date, Interval HTMLProperty
 
@@ -958,8 +1012,10 @@ class StringHTMLProperty(HTMLProperty):
             s2 = match.group('id')
             try:
                 # make sure s1 is a valid tracker classname
-                self._db.getclass(s1)
-                return '<a href="%s">%s %s</a>'%(s, s1, s2)
+                cl = self._db.getclass(s1)
+                if not cl.hasnode(s2):
+                    raise KeyError, 'oops'
+                return '<a href="%s">%s%s</a>'%(s, s1, s2)
             except KeyError:
                 return '%s%s'%(s1, s2)
 
@@ -1185,8 +1241,8 @@ class DateHTMLProperty(HTMLProperty):
         '''
         self.view_check()
 
-        return DateHTMLProperty(self._client, self._nodeid, self._prop,
-            self._formname, date.Date('.'))
+        return DateHTMLProperty(self._client, self._classname, self._nodeid,
+            self._prop, self._formname, date.Date('.'))
 
     def field(self, size = 30):
         ''' Render a form edit field for the property
@@ -1244,8 +1300,8 @@ class DateHTMLProperty(HTMLProperty):
         '''
         self.view_check()
 
-        return DateHTMLProperty(self._client, self._nodeid, self._prop,
-            self._formname, self._value.local(offset))
+        return DateHTMLProperty(self._client, self._classname, self._nodeid,
+            self._prop, self._formname, self._value.local(offset))
 
 class IntervalHTMLProperty(HTMLProperty):
     def plain(self):
@@ -1494,7 +1550,7 @@ class MultilinkHTMLProperty(HTMLProperty):
             showid=1
         if not showid:
             k = linkcl.labelprop(1)
-            value = [linkcl.get(v, k) for v in value]
+            value = lookupKeys(linkcl, k, value)
         value = cgi.escape(','.join(value))
         return self.input(name=self._formname,size=size,value=value)