[IMP] Improve view validation to based on fields_view_get rendering, not just raw...
authorOlivier Dony <odo@openerp.com>
Thu, 14 Jun 2012 14:46:33 +0000 (16:46 +0200)
committerOlivier Dony <odo@openerp.com>
Thu, 14 Jun 2012 14:46:33 +0000 (16:46 +0200)
This will allow improved validation of inherited
views, which is not possible when only the raw
arch is validated on its own - without context
many things cannot be verified.
Calling fields_view_get() also catches early all
mistakes that require dynamic validation, like
wrong XPath expressions (parent view contains
no match).
In order to have current addons pass the improved
validation the RNG had to be fixed to support
the new @modifiers attribute added by fields_view_get()
itself on many view elements, and a few missing
valid attributes, like @invisible on <filter>
and <group>. The latter had never been used
as part of the view architecture but appear
as a result of the handling of @groups
restrictions on view elements, and must
be allowed by the RNG schema.

bzr revid: odo@openerp.com-20120614144633-31c642s7q7f28o6b

openerp/addons/base/ir/ir_ui_view.py
openerp/addons/base/rng/view.rng

index 4d7b0f4..ac3644a 100644 (file)
@@ -94,21 +94,26 @@ class view(osv.osv):
            its inherited views, by rendering it using ``fields_view_get()``.
            
            @param browse_record view: view to validate
-           @return: True if the view hierarchy was rendered without any error, False if an error occurred.  
+           @return: the rendered definition (arch) of the view, always utf-8 bytestring (legacy convention)
+               if no error occurred, else False.  
         """
         try:
-            self.pool.get(view.model).fields_view_get(cr, uid, view_id=view.id, view_type=view.type, context=context)
-            return True
+            fvg = self.pool.get(view.model).fields_view_get(cr, uid, view_id=view.id, view_type=view.type, context=context)
+            return fvg['arch']
         except:
             _logger.exception("Can't render view %s for model: %s", view.xml_id, view.model)
             return False
 
     def _check_xml(self, cr, uid, ids, context=None):
         for view in self.browse(cr, uid, ids, context):
+            # Sanity check: the view should not break anything upon rendering!
+            view_arch_utf8 = self._check_render_view(cr, uid, view, context=context)
+            # always utf-8 bytestring - legacy convention
+            if not view_arch_utf8: return False
+
             # RNG-based validation is not possible anymore with 7.0 forms
-            # TODO 7.0: provide alternative assertion-based validation!
-            # TODO 7.0: and do the tests on the result of fields_view_get instead of each arch.
-            view_docs = [etree.fromstring(view.arch.encode('utf8'))]
+            # TODO 7.0: provide alternative assertion-based validation of view_arch_utf8
+            view_docs = [etree.fromstring(view_arch_utf8)]
             if view_docs[0].tag == 'data':
                 # A <data> element is a wrapper for multiple root nodes
                 view_docs = view_docs[0]
@@ -118,10 +123,6 @@ class view(osv.osv):
                     for error in validator.error_log:
                         _logger.error(tools.ustr(error))
                     return False
-
-            # Second sanity check: the view should not break anything upon rendering!
-            if not self._check_render_view(cr, uid, view, context=context):
-                return False
         return True
 
     _constraints = [
index 54d08ed..22d6ee1 100644 (file)
         </rng:optional>
     </rng:define>
 
+    <rng:define name="modifiable">
+        <rng:optional>
+            <!-- @modifiers contains a JSON map unifying the various
+                 modifier attributes: @readonly, @required, @invisible.
+                 Each attribute is a key, mapped to a JSON list representing
+                 a condition expressed as an OpenERP `domain` filter
+                 Only some of the modifier keys make sense on some
+                 elements, for example <filter> and <group> only support
+                 `invisible`. -->
+            <rng:attribute name="modifiers"/>
+        </rng:optional>
+    </rng:define>
+
     <rng:define name="access_rights">
         <rng:optional>
             <rng:attribute name="groups"/>
         <rng:element name="label">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
+            <rng:optional><rng:attribute name="invisible"/></rng:optional>
             <rng:optional><rng:attribute name="align"/></rng:optional>
             <rng:optional><rng:attribute name="nolabel"/></rng:optional>
             <rng:optional><rng:attribute name="colspan"/></rng:optional>
         <rng:element name="page">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
             <rng:optional><rng:attribute name="string"/></rng:optional>
             <rng:optional><rng:attribute name="name"/></rng:optional>
             <rng:optional><rng:attribute name="attrs"/></rng:optional>
         <rng:element name="separator">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
+            <rng:optional><rng:attribute name="invisible"/></rng:optional>
             <rng:optional><rng:attribute name="name"/></rng:optional>
             <rng:optional><rng:attribute name="colspan"/></rng:optional>
             <rng:optional><rng:attribute name="rowspan"/></rng:optional>
             <rng:attribute name="name" />
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
             <rng:optional><rng:attribute name="domain_filter"/></rng:optional>
             <rng:optional><rng:attribute name="attrs"/></rng:optional>
             <rng:optional><rng:attribute name="string"/></rng:optional>
         <rng:element name="group">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
             <rng:optional><rng:attribute name="attrs"/></rng:optional>
             <rng:optional><rng:attribute name="colspan"/></rng:optional>
             <rng:optional><rng:attribute name="rowspan"/></rng:optional>
             <rng:optional><rng:attribute name="width"/></rng:optional>
             <rng:optional><rng:attribute name="name"/></rng:optional>
             <rng:optional><rng:attribute name="color" /></rng:optional>
+            <rng:optional><rng:attribute name="invisible"/></rng:optional>
             <rng:ref name="container"/>
         </rng:element>
     </rng:define>
         <rng:element name="button">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
             <rng:optional><rng:attribute name="attrs"/></rng:optional>
             <rng:optional><rng:attribute name="invisible"/></rng:optional>
             <rng:optional><rng:attribute name="name" /></rng:optional>
         <rng:element name="filter">
             <rng:ref name="overload"/>
             <rng:ref name="access_rights"/>
+            <rng:ref name="modifiable"/>
             <rng:optional><rng:attribute name="attrs"/></rng:optional>
+            <rng:optional><rng:attribute name="invisible"/></rng:optional>
             <rng:optional><rng:attribute name="name" /></rng:optional>
             <rng:optional><rng:attribute name="separator" /></rng:optional>
             <rng:optional><rng:attribute name="icon" /></rng:optional>