model = pool.get(model_str)
return lambda x: model.browse(cr, uid, x, context=context)
+def _fix_multiple_roots(node):
+ """
+ Surround the children of the ``node`` element of an XML field with a
+ single root "data" element, to prevent having a document with multiple
+ roots once parsed separately.
+
+ XML nodes should have one root only, but we'd like to support
+ direct multiple roots in our partial documents (like inherited view architectures).
+ As a convention we'll surround multiple root with a container "data" element, to be
+ ignored later when parsing.
+ """
+
+ if len(node) > 1:
+ data_node = etree.Element("data")
+ for child in node:
+ data_node.append(child)
+ node.append(data_node)
+
def _eval_xml(self, node, pool, cr, uid, idref, context=None):
if context is None:
context = {}
if not id in idref:
idref[id]=self.id_get(cr, False, id)
return s % idref
+ _fix_multiple_roots(node)
return '<?xml version="1.0"?>\n'\
+_process("".join([etree.tostring(n, encoding='utf-8')
for n in node]),
return res
class xml_import(object):
+ __logger = logging.getLogger('tools.convert.xml_import')
@staticmethod
def nodeattr2bool(node, attr, default=False):
if not node.get(attr):
assert modcnt == 1, """The ID "%s" refers to an uninstalled module""" % (xml_id,)
if len(id) > 64:
- self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'% (id,))
+ self.logger.error('id: %s is to long (max: 64)', id)
def _tag_delete(self, cr, rec, data_node=None):
d_model = rec.get("model",'')
for dest,f in (('name','string'),('model','model'),('report_name','name')):
res[dest] = rec.get(f,'').encode('utf8')
assert res[dest], "Attribute %s of report is empty !" % (f,)
- for field,dest in (('rml','report_rml'),('xml','report_xml'),('xsl','report_xsl'),('attachment','attachment'),('attachment_use','attachment_use')):
+ for field,dest in (('rml','report_rml'),('file','report_rml'),('xml','report_xml'),('xsl','report_xsl'),('attachment','attachment'),('attachment_use','attachment_use')):
if rec.get(field):
res[dest] = rec.get(field).encode('utf8')
if rec.get('auto'):
if rec.get('target'):
res['target'] = rec.get('target','')
+ if rec.get('multi'):
+ res['multi'] = rec.get('multi', False)
id = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.actions.act_window', self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
self.idref[xml_id] = int(id)
m_l = map(escape, escape_re.split(rec.get("name",'').encode('utf8')))
values = {'parent_id': False}
- if rec.get('parent', False) is False:
+ if rec.get('parent', False) is False and len(m_l) > 1:
+ # No parent attribute specified and the menu name has several menu components,
+ # try to determine the ID of the parent according to menu path
pid = False
res = None
values['name'] = m_l[-1]
pid = res[0]
else:
# the menuitem does't exist but we are in branch (not a leaf)
- self.logger.notifyChannel("init", netsvc.LOG_WARNING, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l)))
+ self.logger.warning('Warning no ID for submenu %s of menu %s !', menu_elem, str(m_l))
pid = self.pool.get('ir.ui.menu').create(cr, self.uid, {'parent_id' : pid, 'name' : menu_elem})
values['parent_id'] = pid
else:
+ # The parent attribute was specified, if non-empty determine its ID, otherwise
+ # explicitly make a top-level menu
if rec.get('parent'):
menu_parent_id = self.id_get(cr, 'ir.ui.menu', rec.get('parent',''))
- else: # we get here with <menuitem parent="">, explicit clear of parent
+ else:
+ # we get here with <menuitem parent="">, explicit clear of parent, or
+ # if no parent attribute at all but menu name is not a menu path
menu_parent_id = False
values = {'parent_id': menu_parent_id}
if rec.get('name'):
self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(pid))], action, True, True, xml_id=rec_id)
return ('ir.ui.menu', pid)
- def _assert_equals(self, f1, f2, prec = 4):
+ def _assert_equals(self, f1, f2, prec=4):
return not round(f1 - f2, prec)
def _tag_assert(self, cr, rec, data_node=None):
' expected count: %d\n' \
' obtained count: %d\n' \
% (rec_string, count, len(ids))
- self.logger.notifyChannel('init', severity, msg)
+ self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
' expected value: %r\n' \
' obtained value: %r\n' \
% (rec_string, etree.tostring(test), expected_value, expression_value)
- self.logger.notifyChannel('init', severity, msg)
+ self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
def id_get(self, cr, model, id_str):
if id_str in self.idref:
return self.idref[id_str]
- return self.model_id_get(cr, model, id_str)[1]
+ res = self.model_id_get(cr, model, id_str)
+ if res and len(res)>1: res = res[1]
+ return res
def model_id_get(self, cr, model, id_str):
model_data_obj = self.pool.get('ir.model.data')
mod = self.module
if '.' in id_str:
mod,id_str = id_str.split('.')
- result = model_data_obj._get_id(cr, self.uid, mod, id_str)
- res = model_data_obj.read(cr, self.uid, [result], ['model', 'res_id'])
- if res and res[0] and res[0]['res_id']:
- return res[0]['model'], int(res[0]['res_id'])
- return False
+ return model_data_obj.get_object_reference(cr, self.uid, mod, id_str)
def parse(self, de):
if not de.tag in ['terp', 'openerp']:
- self.logger.notifyChannel("init", netsvc.LOG_ERROR, "Mismatch xml format" )
+ self.logger.error("Mismatch xml format")
raise Exception( "Mismatch xml format: only terp or openerp as root tag" )
if de.tag == 'terp':
- self.logger.notifyChannel("init", netsvc.LOG_WARNING, "The tag <terp/> is deprecated, use <openerp/>")
+ self.logger.warning("The tag <terp/> is deprecated, use <openerp/>")
for n in de.findall('./data'):
for rec in n:
try:
self._tags[rec.tag](self.cr, rec, n)
except:
- self.logger.notifyChannel("init", netsvc.LOG_ERROR, '\n'+etree.tostring(rec))
+ self.__logger.error('Parse error in %s:%d: \n%s',
+ rec.getroottree().docinfo.URL,
+ rec.sourceline,
+ etree.tostring(rec).strip())
self.cr.rollback()
raise
return True
def __init__(self, cr, module, idref, mode, report=None, noupdate=False):
- self.logger = netsvc.Logger()
+ self.logger = logging.getLogger('init')
self.mode = mode
self.module = module
self.cr = cr
encoding: utf-8'''
if not idref:
idref={}
+ logger = logging.getLogger('init')
model = ('.'.join(fname.split('.')[:-1]).split('-'))[0]
#remove folder path from model
head, model = os.path.split(model)
pool = pooler.get_pool(cr.dbname)
- input = cStringIO.StringIO(csvcontent)
+ input = cStringIO.StringIO(csvcontent) #FIXME
reader = csv.reader(input, quotechar='"', delimiter=',')
fields = reader.next()
fname_partial = ""
reader.next()
if not (mode == 'init' or 'id' in fields):
- logger = netsvc.Logger()
- logger.notifyChannel("init", netsvc.LOG_ERROR,
- "Import specification does not contain 'id' and we are in init mode, Cannot continue.")
+ logger.error("Import specification does not contain 'id' and we are in init mode, Cannot continue.")
return
uid = 1
try:
datas.append(map(lambda x: misc.ustr(x), line))
except:
- logger = netsvc.Logger()
- logger.notifyChannel("init", netsvc.LOG_ERROR, "Cannot import the line: %s" % line)
+ logger.error("Cannot import the line: %s", line)
pool.get(model).import_data(cr, uid, fields, datas,mode, module, noupdate, filename=fname_partial)
if config.get('import_partial'):
data = pickle.load(file(config.get('import_partial')))