diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index d7e433a6b47..4994ba9c99b 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -21,6 +21,7 @@
import logging
from lxml import etree
+from operator import itemgetter
import os
from openerp import tools
@@ -280,6 +281,22 @@ class view(osv.osv):
'blank_nodes': blank_nodes,
'node_parent_field': _Model_Field,}
+ def _validate_custom_views(self, cr, uid, model):
+ """Validate architecture of custom views (= without xml id) for a given model.
+ This method is called at the end of registry update.
+ """
+ cr.execute("""SELECT max(v.id)
+ FROM ir_ui_view v
+ LEFT JOIN ir_model_data md ON (md.model = 'ir.ui.view' AND md.res_id = v.id)
+ WHERE md.module IS NULL
+ AND v.model = %s
+ GROUP BY coalesce(v.inherit_id, v.id)
+ """, (model,))
+
+ ids = map(itemgetter(0), cr.fetchall())
+ return self._check_xml(cr, uid, ids)
+
+
class view_sc(osv.osv):
_name = 'ir.ui.view_sc'
_columns = {
diff --git a/openerp/addons/base/tests/test_views.py b/openerp/addons/base/tests/test_views.py
index 74f6f847dba..992ef0886da 100644
--- a/openerp/addons/base/tests/test_views.py
+++ b/openerp/addons/base/tests/test_views.py
@@ -1,3 +1,4 @@
+from functools import partial
import unittest2
import openerp.tests.common as common
@@ -39,6 +40,66 @@ class test_views(common.TransactionCase):
""",
})
+ def _insert_view(self, **kw):
+ """Insert view into database via a query to passtrough validation"""
+ kw.pop('id', None)
+
+ keys = sorted(kw.keys())
+ fields = ','.join('"%s"' % (k.replace('"', r'\"'),) for k in keys)
+ params = ','.join('%%(%s)s' % (k,) for k in keys)
+
+ query = 'INSERT INTO ir_ui_view(%s) VALUES(%s) RETURNING id' % (fields, params)
+ self.cr.execute(query, kw)
+ return self.cr.fetchone()[0]
+
+ def test_10_validate_custom_views(self):
+ Views = self.registry('ir.ui.view')
+ model = 'ir.actions.act_url'
+
+ validate = partial(Views._validate_custom_views, self.cr, self.uid, model)
+
+ # validation of a single view
+ vid = self._insert_view(**{
+ 'name': 'base view',
+ 'model': model,
+ 'priority': 1,
+ 'arch': """
+
+
+
+ """,
+ })
+ self.assertTrue(validate()) # single view
+
+ # validation of a inherited view
+ self._insert_view(**{
+ 'name': 'inherited view',
+ 'model': model,
+ 'priority': 1,
+ 'inherit_id': vid,
+ 'arch': """
+
+
+
+ """,
+ })
+ self.assertTrue(validate()) # inherited view
+
+ # validation of a bad inherited view
+ self._insert_view(**{
+ 'name': 'bad inherited view',
+ 'model': model,
+ 'priority': 2,
+ 'inherit_id': vid,
+ 'arch': """
+
+
+
+ """,
+ })
+ with mute_logger('openerp.osv.orm', 'openerp.addons.base.ir.ir_ui_view'):
+ self.assertFalse(validate()) # bad inherited view
+
if __name__ == '__main__':
unittest2.main()
diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py
index f669d038b20..ed2119e7e40 100644
--- a/openerp/modules/loading.py
+++ b/openerp/modules/loading.py
@@ -414,12 +414,22 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
_logger.info('Reloading registry once more after uninstalling modules')
return openerp.modules.registry.RegistryManager.new(cr.dbname, force_demo, status, update_module)
+ # STEP 7: verify custom views on every model
+ if update_module:
+ Views = registry['ir.ui.view']
+ custom_view_test = True
+ for model in registry.models.keys():
+ if not Views._validate_custom_views(cr, SUPERUSER_ID, model):
+ custom_view_test = False
+ _logger.error('invalid custom view(s) for model %s', model)
+ report.record_result(custom_view_test)
+
if report.failures:
_logger.error('At least one test failed when loading the modules.')
else:
_logger.info('Modules loaded.')
- # STEP 7: call _register_hook on every model
+ # STEP 8: call _register_hook on every model
for model in registry.models.values():
model._register_hook(cr)