Code

Allow to turn off translation of generated html options in menu method
[roundup.git] / roundup / cgi / templating.py
index 9176420f55abedd00debfb8553b1880df259f26b..3cb9780c909f94bb833018cce663673f09ec2971 100644 (file)
@@ -27,6 +27,8 @@ from roundup import hyperdb, date, support
 from roundup import i18n
 from roundup.i18n import _
 
+from KeywordsExpr import render_keywords_expression_editor
+
 try:
     import cPickle as pickle
 except ImportError:
@@ -565,10 +567,7 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
         for klass, htmlklass in propclasses:
             if not isinstance(prop, klass):
                 continue
-            if isinstance(prop, hyperdb.Multilink):
-                value = []
-            else:
-                value = None
+            value = prop.get_default_value()
             return htmlklass(self._client, self._classname, None, prop, item,
                 value, self._anonymous)
 
@@ -601,13 +600,10 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
         l = []
         for name, prop in self._props.items():
             for klass, htmlklass in propclasses:
-                if isinstance(prop, hyperdb.Multilink):
-                    value = []
-                else:
-                    value = None
                 if isinstance(prop, klass):
+                    value = prop.get_default_value()
                     l.append(htmlklass(self._client, self._classname, '',
-                        prop, name, value, self._anonymous))
+                                       prop, name, value, self._anonymous))
         if sort:
             l.sort(lambda a,b:cmp(a._name, b._name))
         return l
@@ -673,13 +669,21 @@ class HTMLClass(HTMLInputMixin, HTMLPermissions):
 
             "request" takes precedence over the other three arguments.
         """
+        security = self._db.security
+        userid = self._client.userid
         if request is not None:
+            # for a request we asume it has already been
+            # security-filtered
             filterspec = request.filterspec
             sort = request.sort
             group = request.group
+        else:
+            cn = self.classname
+            filterspec = security.filterFilterspec(userid, cn, filterspec)
+            sort = security.filterSortspec(userid, cn, sort)
+            group = security.filterSortspec(userid, cn, group)
 
-        check = self._db.security.hasPermission
-        userid = self._client.userid
+        check = security.hasPermission
         if not check('Web Access', userid):
             return []
 
@@ -1104,6 +1108,13 @@ class _HTMLItem(HTMLInputMixin, HTMLPermissions):
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = val
 
+                    elif isinstance(prop, hyperdb.Password) and args[k] is not None:
+                        val = args[k].dummystr()
+                        cell.append('%s: %s'%(self._(k), val))
+                        if current.has_key(k):
+                            cell[-1] += ' -> %s'%current[k]
+                            current[k] = val
+
                     elif not args[k]:
                         if current.has_key(k):
                             cell.append('%s: %s'%(self._(k), current[k]))
@@ -1281,8 +1292,9 @@ class HTMLProperty(HTMLInputMixin, HTMLPermissions):
         HTMLInputMixin.__init__(self)
 
     def __repr__(self):
-        return '<HTMLProperty(0x%x) %s %r %r>'%(id(self), self._formname,
-            self._prop, self._value)
+        classname = self.__class__.__name__
+        return '<%s(0x%x) %s %r %r>'%(classname, id(self), self._formname,
+                                      self._prop, self._value)
     def __str__(self):
         return self.plain()
     def __cmp__(self, other):
@@ -1361,18 +1373,18 @@ class StringHTMLProperty(HTMLProperty):
         u = s = match.group('url')
         if not self.protocol_re.search(s):
             u = 'http://' + s
-        if s.endswith('&gt;'):
-            # catch an escaped ">" at the end of the URL
-            u = s = s[:-4]
-            e = '&gt;'
-        elif s.count('(') != s.count(')'):
+        end = ''
+        if '&gt;' in s:
+            # catch an escaped ">" in the URL
+            pos = s.find('&gt;')
+            end = s[pos:]
+            u = s = s[:pos]
+        if ')' in s and s.count('(') != s.count(')'):
             # don't include extraneous ')' in the link
             pos = s.rfind(')')
-            e = s[pos:]
+            end = s[pos:] + end
             u = s = s[:pos]
-        else:
-            e = ''
-        return replacement % (u, s, e)
+        return replacement % (u, s, end)
 
     def _hyper_repl_email(self, match, replacement):
         s = match.group('email')
@@ -1556,7 +1568,10 @@ class PasswordHTMLProperty(HTMLProperty):
 
         if self._value is None:
             return ''
-        return self._('*encrypted*')
+        value = self._value.dummystr()
+        if escape:
+            value = cgi.escape(value)
+        return value
 
     def field(self, size=30, **kwargs):
         """ Render a form edit field for the property.
@@ -1958,7 +1973,7 @@ class LinkHTMLProperty(HTMLProperty):
                           **kwargs)
 
     def menu(self, size=None, height=None, showid=0, additional=[], value=None,
-             sort_on=None, html_kwargs = {}, **conditions):
+             sort_on=None, html_kwargs={}, translate=True, **conditions):
         """ Render a form select list for this property
 
             "size" is used to limit the length of the list labels
@@ -1971,6 +1986,11 @@ class LinkHTMLProperty(HTMLProperty):
                 (direction, property) where direction is '+' or '-'. A
                 single string with the direction prepended may be used.
                 For example: ('-', 'order'), '+name'.
+            "html_kwargs" specified additional html args for the
+            generated html <select>
+            "translate" indicates if we should do translation of labels
+            using gettext -- this is often desired (e.g. for status
+            labels) but sometimes not.
 
             The remaining keyword arguments are used as conditions for
             filtering the items in the list - they're passed as the
@@ -2059,7 +2079,10 @@ class LinkHTMLProperty(HTMLProperty):
                 lab = lab + ' (%s)'%', '.join(m)
 
             # and generate
-            lab = cgi.escape(self._(lab))
+            tr = str
+            if translate:
+                tr = self._
+            lab = cgi.escape(tr(lab))
             l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
         l.append('</select>')
         return '\n'.join(l)
@@ -2183,7 +2206,8 @@ class MultilinkHTMLProperty(HTMLProperty):
         return self.input(name=self._formname, size=size, **kwargs)
 
     def menu(self, size=None, height=None, showid=0, additional=[],
-             value=None, sort_on=None, html_kwargs = {}, **conditions):
+             value=None, sort_on=None, html_kwargs={}, translate=True,
+             **conditions):
         """ Render a form <select> list for this property.
 
             "size" is used to limit the length of the list labels
@@ -2284,7 +2308,10 @@ class MultilinkHTMLProperty(HTMLProperty):
                 lab = lab + ' (%s)'%', '.join(m)
 
             # and generate
-            lab = cgi.escape(self._(lab))
+            tr = str
+            if translate:
+                tr = self._
+            lab = cgi.escape(tr(lab))
             l.append('<option %svalue="%s">%s</option>'%(s, optionid,
                 lab))
         l.append('</select>')
@@ -2314,13 +2341,19 @@ def register_propclass(prop, cls):
 
 
 def make_sort_function(db, classname, sort_on=None):
-    """Make a sort function for a given class
+    """Make a sort function for a given class.
+
+    The list being sorted may contain mixed ids and labels.
     """
     linkcl = db.getclass(classname)
     if sort_on is None:
         sort_on = linkcl.orderprop()
     def sortfunc(a, b):
-        return cmp(linkcl.get(a, sort_on), linkcl.get(b, sort_on))
+        if num_re.match(a):
+            a = linkcl.get(a, sort_on)
+        if num_re.match(b):
+            b = linkcl.get(b, sort_on)
+        return cmp(a, b)
     return sortfunc
 
 def handleListCGIValue(value):
@@ -2440,12 +2473,16 @@ class HTMLRequest(HTMLInputMixin):
                 self.columns = handleListCGIValue(self.form[name])
                 break
         self.show = support.TruthDict(self.columns)
+        security = self._client.db.security
+        userid = self._client.userid
 
         # sorting and grouping
         self.sort = []
         self.group = []
         self._parse_sort(self.sort, 'sort')
         self._parse_sort(self.group, 'group')
+        self.sort = security.filterSortspec(userid, self.classname, self.sort)
+        self.group = security.filterSortspec(userid, self.classname, self.group)
 
         # filtering
         self.filter = []
@@ -2475,6 +2512,8 @@ class HTMLRequest(HTMLInputMixin):
                         self.filterspec[name] = handleListCGIValue(fv)
                     else:
                         self.filterspec[name] = fv.value
+        self.filterspec = security.filterFilterspec(userid, self.classname,
+            self.filterspec)
 
         # full-text search argument
         self.search_text = None
@@ -2691,7 +2730,7 @@ function help_window(helpurl, width, height) {
 </script>
 """%self.base
 
-    def batch(self):
+    def batch(self, permission='View'):
         """ Return a batch object for results from the "current search"
         """
         check = self._client.db.security.hasPermission
@@ -2717,7 +2756,7 @@ function help_window(helpurl, width, height) {
 
         # filter for visibility
         l = [id for id in klass.filter(matches, filterspec, sort, group)
-            if check('View', userid, self.classname, itemid=id)]
+            if check(permission, userid, self.classname, itemid=id)]
 
         # return the batch object, using IDs only
         return Batch(self.client, l, self.pagesize, self.startwith,
@@ -2842,6 +2881,9 @@ class TemplatingUtils:
             raise AttributeError, name
         return self.client.instance.templating_utils[name]
 
+    def keywords_expressions(self, request):
+        return render_keywords_expression_editor(request)
+
     def html_calendar(self, request):
         """Generate a HTML calendar.