[IMP] tools.safe_eval_qweb: methods intended to provide more restricted alternatives...
[odoo/odoo.git] / openerp / tools / convert.py
index e3c5311..b91343d 100644 (file)
@@ -33,6 +33,7 @@ import time
 import openerp
 import openerp.release
 import openerp.workflow
+from yaml_import import convert_yaml_import
 
 import assertion_report
 
@@ -49,7 +50,7 @@ except:
 
 from datetime import datetime, timedelta
 from dateutil.relativedelta import relativedelta
-from lxml import etree
+from lxml import etree, builder
 import misc
 from config import config
 from translate import _
@@ -151,11 +152,22 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
                     'Could not eval(%s) for %s in %s', a_eval, node.get('name'), context)
                 raise
         def _process(s, idref):
-            m = re.findall('[^%]%\((.*?)\)[ds]', s)
-            for id in m:
+            matches = re.finditer('[^%]%\((.*?)\)[ds]', s)
+            done = []
+            for m in matches:
+                found = m.group()[1:]
+                if found in done:
+                    continue
+                done.append(found)
+                id = m.groups()[0]
                 if not id in idref:
-                    idref[id]=self.id_get(cr, id)
-            return s % idref
+                    idref[id] = self.id_get(cr, id)
+                s = s.replace(found, str(idref[id]))
+
+            s = s.replace('%%', '%') # Quite wierd but it's for (somewhat) backward compatibility sake
+
+            return s
+
         if t == 'xml':
             _fix_multiple_roots(node)
             return '<?xml version="1.0"?>\n'\
@@ -164,27 +176,38 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
         if t == 'html':
             return _process("".join([etree.tostring(n, encoding='utf-8')
                                    for n in node]), idref)
+
+        data = node.text
+        if node.get('file'):
+            with openerp.tools.file_open(node.get('file'), 'rb') as f:
+                data = f.read()
+
         if t == 'file':
             from ..modules import module
-            path = node.text.strip()
+            path = data.strip()
             if not module.get_module_resource(self.module, path):
                 raise IOError("No such file or directory: '%s' in %s" % (
                     path, self.module))
             return '%s,%s' % (self.module, path)
-        if t in ('char', 'int', 'float'):
-            d = node.text
-            if t == 'int':
-                d = d.strip()
-                if d == 'None':
-                    return None
-                else:
-                    return int(d.strip())
-            elif t == 'float':
-                return float(d.strip())
-            return d
-        elif t in ('list','tuple'):
+
+        if t == 'char':
+            return data
+
+        if t == 'base64':
+            return data.encode('base64')
+
+        if t == 'int':
+            d = data.strip()
+            if d == 'None':
+                return None
+            return int(d)
+
+        if t == 'float':
+            return float(data.strip())
+
+        if t in ('list','tuple'):
             res=[]
-            for n in node.findall('./value'):
+            for n in node.iterchildren(tag='value'):
                 res.append(_eval_xml(self,n,pool,cr,uid,idref))
             if t=='tuple':
                 return tuple(res)
@@ -615,9 +638,8 @@ form: module.record_id""" % (xml_id,)
                     "Verify that this is a window action or add a type argument." % (a_action,)
                 action_type,action_mode,action_name,view_id,target = rrres
                 if view_id:
-                    cr.execute('SELECT arch FROM ir_ui_view WHERE id=%s', (int(view_id),))
-                    arch, = cr.fetchone()
-                    action_mode = etree.fromstring(arch.encode('utf8')).tag
+                    view_arch = self.pool['ir.ui.view'].read(cr, 1, [view_id], ['arch'])
+                    action_mode = etree.fromstring(view_arch[0]['arch'].encode('utf8')).tag
                 cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%s ORDER BY sequence LIMIT 1', (int(a_id),))
                 if cr.rowcount:
                     action_mode, = cr.fetchone()
@@ -829,6 +851,43 @@ form: module.record_id""" % (xml_id,)
             cr.commit()
         return rec_model, id
 
+    def _tag_template(self, cr, el, data_node=None):
+        # This helper transforms a <template> element into a <record> and forwards it
+        tpl_id = el.get('id', el.get('t-name', '')).encode('ascii')
+        module = self.module
+        if '.' in tpl_id:
+            module, tpl_id = tpl_id.split('.', 1)
+        # set the full template name for qweb <module>.<id>
+        if not (el.get('inherit_id') or el.get('inherit_option_id')):
+            el.set('t-name', '%s.%s' % (module, tpl_id))
+            el.tag = 't'
+        else:
+            el.tag = 'data'
+        el.attrib.pop('id', None)
+
+        record_attrs = {
+            'id': tpl_id,
+            'model': 'ir.ui.view',
+        }
+        for att in ['forcecreate', 'context', 'priority']:
+            if att in el.keys():
+                record_attrs[att] = el.attrib.pop(att)
+
+        Field = builder.E.field
+        name = el.get('name', tpl_id)
+
+        record = etree.Element('record', attrib=record_attrs)
+        record.append(Field(name, name='name'))
+        record.append(Field("qweb", name='type'))
+        record.append(Field(el, name="arch", type="xml"))
+        for field_name in ('inherit_id','inherit_option_id'):
+            value = el.attrib.pop(field_name, None)
+            if value: record.append(Field(name=field_name, ref=value))
+        if el.attrib.pop('page', None) == 'True':
+            record.append(Field(name="page", eval="True"))
+
+        return self._tag_record(cr, record, data_node)
+
     def id_get(self, cr, id_str):
         if id_str in self.idref:
             return self.idref[id_str]
@@ -873,6 +932,7 @@ form: module.record_id""" % (xml_id,)
         self._tags = {
             'menuitem': self._tag_menuitem,
             'record': self._tag_record,
+            'template': self._tag_template,
             'assert': self._tag_assert,
             'report': self._tag_report,
             'wizard': self._tag_wizard,
@@ -884,6 +944,33 @@ form: module.record_id""" % (xml_id,)
             'url': self._tag_url
         }
 
+def convert_file(cr, module, filename, idref, mode='update', noupdate=False, kind=None, report=None):
+    pathname = os.path.join(module, filename)
+    fp = misc.file_open(pathname)
+    ext = os.path.splitext(filename)[1].lower()
+    try:
+        if ext == '.csv':
+            convert_csv_import(cr, module, pathname, fp.read(), idref, mode, noupdate)
+        elif ext == '.sql':
+            convert_sql_import(cr, fp)
+        elif ext == '.yml':
+            convert_yaml_import(cr, module, fp, kind, idref, mode, noupdate, report)
+        elif ext == '.xml':
+            convert_xml_import(cr, module, fp, idref, mode, noupdate, report)
+        elif ext == '.js':
+            pass # .js files are valid but ignored here.
+        else:
+            _logger.warning("Can't load unknown file type %s.", filename)
+    finally:
+        fp.close()
+
+def convert_sql_import(cr, fp):
+    queries = fp.read().split(';')
+    for query in queries:
+        new_query = ' '.join(query.split())
+        if new_query:
+            cr.execute(new_query)
+
 def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
         noupdate=False):
     '''Import csv file :