[MERGE] forward port of branch 7.0 up to revid 5288 mat@openerp.com-20140423150215...
authorChristophe Simonis <chs@openerp.com>
Thu, 24 Apr 2014 12:53:38 +0000 (14:53 +0200)
committerChristophe Simonis <chs@openerp.com>
Thu, 24 Apr 2014 12:53:38 +0000 (14:53 +0200)
bzr revid: chs@openerp.com-20140424125338-26k5atvtqwmmfv4e

1  2 
openerp/addons/base/res/res_partner_view.xml
openerp/addons/base/res/res_users.py
openerp/addons/base/test/base_test.yml
openerp/tools/convert.py

@@@ -10,7 -10,7 +10,7 @@@
  
              <menuitem id="base.menu_sales" parent="base.menu_base_partner" name="Sales" sequence="1" />
              <menuitem id="menu_base_config" parent="menu_base_partner" name="Configuration" sequence="30" groups="group_system"/>
 -                <menuitem id="menu_config_address_book" parent="menu_base_config" name="Address Book" sequence="40" groups="group_system"/>
 +            <menuitem id="menu_config_address_book" parent="menu_base_config" name="Address Book" sequence="40" groups="group_system"/>
  
          <!-- Partner Titles -->
          <record id="view_partner_title_tree" model="ir.ui.view">
@@@ -77,7 -77,7 +77,7 @@@
              <field eval="8" name="priority"/>
              <field name="arch" type="xml">
                  <tree string="Contacts">
 -                    <field name="name"/>
 +                    <field name="display_name"/>
                      <field name="function" invisible="1"/>
                      <field name="phone"/>
                      <field name="email"/>
                          <group>
                              <field name="category_id" widget="many2many_tags" placeholder="Tags..."/>
                              <field name="function" placeholder="e.g. Sales Director"/>
 -                            <field name="email" required="context.get('force_email', False)"/>
 +                            <field name="user_ids" invisible="1"/>
 +                            <field name="email" widget="email" required="context.get('force_email', False)" attrs="{'required': [('user_ids','!=', [])]}"/>
                              <field name="phone"/>
                              <field name="mobile"/>
                          </group>
                  <form string="Partners" version="7.0">
                  <sheet>
                      <field name="image" widget='image' class="oe_left oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/>
 -                    <div class="oe_title">
 +                    <div class="oe_title oe_left">
                          <div class="oe_edit_only">
                              <label for="name"/> (
                              <field name="is_company" on_change="onchange_type(is_company)" class="oe_inline"/> <label for="is_company" string="Is a Company?"/>)
                              <field name="phone" placeholder="e.g. +32.81.81.37.00"/>
                              <field name="mobile"/>
                              <field name="fax"/>
 -                            <field name="email" widget="email"/>
 +                            <field name="user_ids" invisible="1"/>
 +                            <field name="email" widget="email" attrs="{'required': [('user_ids','!=', [])]}"/>
                              <field name="title" domain="[('domain', '=', 'contact')]"
                                  options='{"no_open": True}' attrs="{'invisible': [('is_company','=', True)]}" />
                          </group>
                                  </form>
                              </field>
                          </page>
 -                        <page string="Internal Notes">
 +                        <page name='internal_notes' string="Internal Notes">
                              <field name="comment" placeholder="Put an internal note..."/>
                          </page>
 -                        <page string="Sales &amp; Purchases">
 +                        <page name='sales_purchases' string="Sales &amp; Purchases">
                              <group>
                                  <group>
 -                                    <field name="user_id"/>
 +                                    <field name="user_id" 
 +                                        context="{'default_groups_ref': ['base.group_partner_manager']}"/>
                                      <field name="company_id" groups="base.group_multi_company" widget="selection"/>
                                  </group>
                                  <group>
                                  </group>
                              </group>
                          </page>
 -                        <!-- The History page becomes visible as soon as there is something to display inside -->
 -                        <page string="History" name="page_history" invisible="True">
 -                        </page>
                      </notebook>
                  </sheet>
                  </form>
              <field name="arch" type="xml">
                  <search string="Search Partner">
                     <field name="name"
 -                       filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"/>
 +                       filter_domain="['|','|',('display_name','ilike',self),('ref','=',self),('email','ilike',self)]"/>
                     <filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
                     <separator/>
                     <filter string="Persons" name="type_person" domain="[('is_company','=',0)]"/>
                     <separator/>
                     <field name="category_id" string="Tag" filter_domain="[('category_id','ilike', self)]"/>
                     <field name="user_id"/>
-                    <field name="parent_id" domain="[('is_company','=',1)]" filter_domain="[('parent_id','child_of',[self])]"/>
+                    <field name="parent_id" domain="[('is_company','=',1)]" operator="child_of"/>
                     <group expand="0" string="Group By...">
                         <filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" />
                         <filter string="Company" context="{'group_by': 'parent_id'}"/>
              <field name="arch" type="xml">
                  <kanban>
                      <field name="color"/>
 -                    <field name="name"/>
 +                    <field name="display_name"/>
                      <field name="title"/>
                      <field name="email"/>
                      <field name="parent_id"/>
                                      </t>
                                  </a>
                                  <div class="oe_kanban_details">
 -                                    <h4 class="oe_partner_heading"><a type="open"><field name="name"/></a></h4>
 +                                    <h4 class="oe_partner_heading"><a type="open"><field name="display_name"/></a></h4>
                                      <div class="oe_kanban_partner_categories"/>
                                      <div class="oe_kanban_partner_links"/>
                                      <ul>
              <field name="view_id" ref="view_partner_tree"/>
              <field name="act_window_id" ref="action_partner_form"/>
          </record>
 -        <menuitem id="menu_partner_form" parent="base.menu_sales" action="action_partner_form" sequence="1"/>
 +        <menuitem id="menu_partner_form" parent="base.menu_sales" action="action_partner_form" sequence="2"/>
  
          <record id="action_partner_customer_form" model="ir.actions.act_window">
              <field name="name">Customers</field>
  
          <!-- Categories -->
          <record id="view_partner_category_form" model="ir.ui.view">
 -            <field name="name">Partner Categories</field>
 +            <field name="name">Partner Tags</field>
              <field name="model">res.partner.category</field>
              <field name="arch" type="xml">
 -                <form string="Partner Category" version="7.0">
 +                <form string="Partner Tag" version="7.0">
                      <group col="4">
                          <field name="name"/>
                          <field name="active"/>
              </field>
          </record>
          <record id="view_partner_category_list" model="ir.ui.view">
 -            <field name="name">Partner Categories</field>
 +            <field name="name">Partner Tags</field>
              <field name="model">res.partner.category</field>
              <field eval="6" name="priority"/>
              <field name="arch" type="xml">
 -                <tree string="Partner Categories">
 +                <tree string="Partner Tags">
                      <field name="complete_name"/>
                  </tree>
              </field>
              <field name="model">res.partner.category</field>
              <field name="field_parent">child_ids</field>
              <field name="arch" type="xml">
 -                <tree toolbar="1" string="Partner Categories">
 +                <tree toolbar="1" string="Partner Tags">
                      <field name="name"/>
                  </tree>
              </field>
          -->
  
          <record id="action_partner_by_category" model="ir.actions.act_window">
 -            <field name="name">Partner Categories</field>
 +            <field name="name">Partner Tags</field>
              <field name="res_model">res.partner</field>
              <field name="view_type">form</field>
              <field name="view_mode">tree,form</field>
          </record>
  
          <record id="action_partner_category_form" model="ir.actions.act_window">
 -            <field name="name">Partner Categories</field>
 +            <field name="name">Partner Tags</field>
              <field name="type">ir.actions.act_window</field>
              <field name="res_model">res.partner.category</field>
              <field name="view_type">form</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
 -                Click to create a new partner category.
 +                Click to create a new partner tags.
                </p><p>
 -                Manage the partner categories in order to better classify them for tracking and analysis purposes.
 -                A partner may belong to several categories and categories have a hierarchy structure: a partner belonging to a category also belong to his parent category.
 +                Manage the partner tags to better classify them for tracking and analysis purposes.
 +                A partner may have several tags and tags have a hierarchical structure: a partner with a tag has also the parent tags.
                </p>
              </field>
          </record>
@@@ -3,7 -3,7 +3,7 @@@
  #
  #    OpenERP, Open Source Management Solution
  #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
 -#    Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
 +#    Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
  #
  #    This program is free software: you can redistribute it and/or modify
  #    it under the terms of the GNU Affero General Public License as
@@@ -26,23 -26,18 +26,23 @@@ from lxml.builder import 
  
  import openerp
  from openerp import SUPERUSER_ID
 -from openerp import pooler, tools
 +from openerp import tools
  import openerp.exceptions
- from openerp.osv import fields,osv
+ from openerp.osv import fields,osv, expression
  from openerp.osv.orm import browse_record
  from openerp.tools.translate import _
  
  _logger = logging.getLogger(__name__)
  
 -class groups(osv.osv):
 +#----------------------------------------------------------
 +# Basic res.groups and res.users
 +#----------------------------------------------------------
 +
 +class res_groups(osv.osv):
      _name = "res.groups"
      _description = "Access Groups"
      _rec_name = 'full_name'
 +    _order = 'name'
  
      def _get_full_name(self, cr, uid, ids, field, arg, context=None):
          res = {}
      def _search_group(self, cr, uid, obj, name, args, context=None):
          operand = args[0][2]
          operator = args[0][1]
-         values = operand.split('/')
-         group_name = values[0]
-         where = [('name', operator, group_name)]
-         if len(values) > 1:
-             application_name = values[0]
-             group_name = values[1]
-             where = ['|',('category_id.name', operator, application_name)] + where
+         lst = True
+         if isinstance(operand, bool):
+             domains = [[('name', operator, operand)], [('category_id.name', operator, operand)]]
+             if operator in expression.NEGATIVE_TERM_OPERATORS == (not operand):
+                 return expression.AND(domains)
+             else:
+                 return expression.OR(domains)
+         if isinstance(operand, basestring):
+             lst = False
+             operand = [operand]
+         where = []
+         for group in operand:
+             values = filter(bool, group.split('/'))
+             group_name = values.pop().strip()
+             category_name = values and '/'.join(values).strip() or group_name
+             group_domain = [('name', operator, lst and [group_name] or group_name)]
+             category_domain = [('category_id.name', operator, lst and [category_name] or category_name)]
+             if operator in expression.NEGATIVE_TERM_OPERATORS and not values:
+                 category_domain = expression.OR([category_domain, [('category_id', '=', False)]])
+             if (operator in expression.NEGATIVE_TERM_OPERATORS) == (not values):
+                 sub_where = expression.AND([group_domain, category_domain])
+             else:
+                 sub_where = expression.OR([group_domain, category_domain])
+             if operator in expression.NEGATIVE_TERM_OPERATORS:
+                 where = expression.AND([where, sub_where])
+             else:
+                 where = expression.OR([where, sub_where])
          return where
  
      _columns = {
      def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
          # add explicit ordering if search is sorted on full_name
          if order and order.startswith('full_name'):
 -            ids = super(groups, self).search(cr, uid, args, context=context)
 +            ids = super(res_groups, self).search(cr, uid, args, context=context)
              gs = self.browse(cr, uid, ids, context)
              gs.sort(key=lambda g: g.full_name, reverse=order.endswith('DESC'))
              gs = gs[offset:offset+limit] if limit else gs[offset:]
              return map(int, gs)
 -        return super(groups, self).search(cr, uid, args, offset, limit, order, context, count)
 +        return super(res_groups, self).search(cr, uid, args, offset, limit, order, context, count)
  
      def copy(self, cr, uid, id, default=None, context=None):
          group_name = self.read(cr, uid, [id], ['name'])[0]['name']
          default.update({'name': _('%s (copy)')%group_name})
 -        return super(groups, self).copy(cr, uid, id, default, context)
 +        return super(res_groups, self).copy(cr, uid, id, default, context)
  
      def write(self, cr, uid, ids, vals, context=None):
          if 'name' in vals:
              if vals['name'].startswith('-'):
                  raise osv.except_osv(_('Error'),
                          _('The name of the group can not start with "-"'))
 -        res = super(groups, self).write(cr, uid, ids, vals, context=context)
 -        self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
 +        res = super(res_groups, self).write(cr, uid, ids, vals, context=context)
 +        self.pool['ir.model.access'].call_cache_clearing_methods(cr)
          return res
  
 -groups()
 -
  class res_users(osv.osv):
      """ User class. A res.users record models an OpenERP user and is different
          from an employee.
              deprecated='Use the email field instead of user_email. This field will be removed with OpenERP 7.1.'),
      }
  
 -    def on_change_company_id(self, cr, uid, ids, company_id):
 -        return {'warning' : {
 -                    'title': _("Company Switch Warning"),
 -                    'message': _("Please keep in mind that documents currently displayed may not be relevant after switching to another company. If you have unsaved changes, please make sure to save and close all forms before switching to a different company. (You can click on Cancel in the User Preferences now)"),
 -                }
 -        }
 +    def on_change_login(self, cr, uid, ids, login, context=None):
 +        if login and tools.single_email_re.match(login):
 +            return {'value': {'email': login}}
 +        return {}
  
      def onchange_state(self, cr, uid, ids, state_id, context=None):
          partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]
              partner.onchange_type method, but applied to the user object.
          """
          partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]
 -        return self.pool.get('res.partner').onchange_type(cr, uid, partner_ids, is_company, context=context)
 +        return self.pool['res.partner'].onchange_type(cr, uid, partner_ids, is_company, context=context)
  
      def onchange_address(self, cr, uid, ids, use_parent_address, parent_id, context=None):
          """ Wrapper on the user.partner onchange_address, because some calls to the
              partner.onchange_type method, but applied to the user object.
          """
          partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]
 -        return self.pool.get('res.partner').onchange_address(cr, uid, partner_ids, use_parent_address, parent_id, context=context)
 +        return self.pool['res.partner'].onchange_address(cr, uid, partner_ids, use_parent_address, parent_id, context=context)
  
      def _check_company(self, cr, uid, ids, context=None):
          return all(((this.company_id in this.company_ids) or not this.company_ids) for this in self.browse(cr, uid, ids, context))
      def _get_company(self,cr, uid, context=None, uid2=False):
          if not uid2:
              uid2 = uid
 -        user = self.pool.get('res.users').read(cr, uid, uid2, ['company_id'], context)
 +        user = self.pool['res.users'].read(cr, uid, uid2, ['company_id'], context)
          company_id = user.get('company_id', False)
          return company_id and company_id[0] or False
  
          'company_id': _get_company,
          'company_ids': _get_companies,
          'groups_id': _get_group,
 -        'image': lambda self, cr, uid, ctx={}: self.pool.get('res.partner')._get_default_image(cr, uid, False, ctx, colorize=True),
 +        'image': lambda self, cr, uid, ctx={}: self.pool['res.partner']._get_default_image(cr, uid, False, ctx, colorize=True),
      }
  
      # User can write on a few of his own fields (but not his groups for example)
                  uid = SUPERUSER_ID
  
          result = super(res_users, self).read(cr, uid, ids, fields=fields, context=context, load=load)
 -        canwrite = self.pool.get('ir.model.access').check(cr, uid, 'res.users', 'write', False)
 +        canwrite = self.pool['ir.model.access'].check(cr, uid, 'res.users', 'write', False)
          if not canwrite:
              if isinstance(ids, (int, long)):
                  result = override_password(result)
                  if user.partner_id.company_id and user.partner_id.company_id.id != values['company_id']: 
                      user.partner_id.write({'company_id': user.company_id.id})
          # clear caches linked to the users
 -        self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
 -        clear = partial(self.pool.get('ir.rule').clear_cache, cr)
 +        self.pool['ir.model.access'].call_cache_clearing_methods(cr)
 +        clear = partial(self.pool['ir.rule'].clear_cache, cr)
          map(clear, ids)
          db = cr.dbname
          if db in self._uid_cache:
          return result
  
      def action_get(self, cr, uid, context=None):
 -        dataobj = self.pool.get('ir.model.data')
 +        dataobj = self.pool['ir.model.data']
          data_id = dataobj._get_id(cr, SUPERUSER_ID, 'base', 'action_res_users_my')
          return dataobj.browse(cr, uid, data_id, context=context).res_id
  
          if not password:
              return False
          user_id = False
 -        cr = pooler.get_db(db).cursor()
 +        cr = self.pool.db.cursor()
          try:
              # autocommit: our single update request will be performed atomically.
              # (In this way, there is no opportunity to have two transactions
              # Successfully logged in as admin!
              # Attempt to guess the web base url...
              if user_agent_env and user_agent_env.get('base_location'):
 -                cr = pooler.get_db(db).cursor()
 +                cr = self.pool.db.cursor()
                  try:
                      base = user_agent_env['base_location']
 -                    ICP = self.pool.get('ir.config_parameter')
 +                    ICP = self.pool['ir.config_parameter']
                      if not ICP.get_param(cr, uid, 'web.base.url.freeze'):
                          ICP.set_param(cr, uid, 'web.base.url', base)
                      cr.commit()
              raise openerp.exceptions.AccessDenied()
          if self._uid_cache.get(db, {}).get(uid) == passwd:
              return
 -        cr = pooler.get_db(db).cursor()
 +        cr = self.pool.db.cursor()
          try:
              self.check_credentials(cr, uid, passwd)
              if self._uid_cache.has_key(db):
                     (uid, module, ext_id))
          return bool(cr.fetchone())
  
 -
 -#
 -# Extension of res.groups and res.users with a relation for "implied" or
 -# "inherited" groups.  Once a user belongs to a group, it automatically belongs
 -# to the implied groups (transitively).
 +#----------------------------------------------------------
 +# Implied groups
  #
 +# Extension of res.groups and res.users with a relation for "implied"
 +# or "inherited" groups.  Once a user belongs to a group, it
 +# automatically belongs to the implied groups (transitively).
 +#----------------------------------------------------------
  
  class cset(object):
      """ A cset (constrained set) is a set of elements that may be constrained to
@@@ -549,6 -567,7 +569,6 @@@ def concat(ls)
      return res
  
  
 -
  class groups_implied(osv.osv):
      _inherit = 'res.groups'
  
@@@ -601,7 -620,6 +621,7 @@@ class users_implied(osv.osv)
          if groups:
              # delegate addition of groups to add implied groups
              self.write(cr, uid, [user_id], {'groups_id': groups}, context)
 +            self.pool['ir.ui.view'].clear_cache()
          return user_id
  
      def write(self, cr, uid, ids, values, context=None):
                  gs = set(concat([g.trans_implied_ids for g in user.groups_id]))
                  vals = {'groups_id': [(4, g.id) for g in gs]}
                  super(users_implied, self).write(cr, uid, [user.id], vals, context)
 +            self.pool['ir.ui.view'].clear_cache()
          return res
  
 +#----------------------------------------------------------
 +# Vitrual checkbox and selection for res.user form view
  #
  # Extension of res.groups and res.users for the special groups view in the users
  # form.  This extension presents groups with selection and boolean widgets:
  #       any of ID1, ..., IDk is in 'groups_id'
  # - selection field 'sel_groups_ID1_..._IDk' is ID iff
  #       ID is in 'groups_id' and ID is maximal in the set {ID1, ..., IDk}
 +#----------------------------------------------------------
  
  def name_boolean_group(id): return 'in_group_' + str(id)
  def name_boolean_groups(ids): return 'in_groups_' + '_'.join(map(str, ids))
@@@ -663,6 -677,7 +683,6 @@@ def partition(f, xs)
      return yes, nos
  
  
 -
  class groups_view(osv.osv):
      _inherit = 'res.groups'
  
      def update_user_groups_view(self, cr, uid, context=None):
          # the view with id 'base.user_groups_view' inherits the user form view,
          # and introduces the reified group fields
 -        view = self.get_user_groups_view(cr, uid, context)
 -        if view:
 +        # we have to try-catch this, because at first init the view does not exist
 +        # but we are already creating some basic groups
 +        view = self.pool['ir.model.data'].xmlid_to_object(cr, SUPERUSER_ID, 'base.user_groups_view', context=context)
 +        if view and view.exists() and view._table_name == 'ir.ui.view':
              xml1, xml2 = [], []
              xml1.append(E.separator(string=_('Application'), colspan="4"))
              for app, kind, gs in self.get_groups_by_application(cr, uid, context):
              view.write({'arch': xml_content})
          return True
  
 -    def get_user_groups_view(self, cr, uid, context=None):
 -        try:
 -            view = self.pool.get('ir.model.data').get_object(cr, SUPERUSER_ID, 'base', 'user_groups_view', context)
 -            assert view and view._table_name == 'ir.ui.view'
 -        except Exception:
 -            view = False
 -        return view
 -
      def get_application_groups(self, cr, uid, domain=None, context=None):
          return self.search(cr, uid, domain or [])
  
@@@ -798,37 -819,18 +818,37 @@@ class users_view(osv.osv)
          fields1 = (fields + ['groups_id']) if group_fields else fields
          values = super(users_view, self).default_get(cr, uid, fields1, context)
          self._get_reified_groups(group_fields, values)
 +
 +        # add "default_groups_ref" inside the context to set default value for group_id with xml values
 +        if 'groups_id' in fields and isinstance(context.get("default_groups_ref"), list):
 +            groups = []
 +            ir_model_data = self.pool.get('ir.model.data')
 +            for group_xml_id in context["default_groups_ref"]:
 +                group_split = group_xml_id.split('.')
 +                if len(group_split) != 2:
 +                    raise osv.except_osv(_('Invalid context value'), _('Invalid context default_groups_ref value (model.name_id) : "%s"') % group_xml_id)
 +                try:
 +                    temp, group_id = ir_model_data.get_object_reference(cr, uid, group_split[0], group_split[1])
 +                except ValueError:
 +                    group_id = False
 +                groups += [group_id]
 +            values['groups_id'] = groups
          return values
  
      def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
 -        if not fields:
 -            fields = self.fields_get(cr, uid, context=context).keys()
 -        group_fields, fields = partition(is_reified_group, fields)
 -        if not 'groups_id' in fields:
 +        fields_get = fields if fields is not None else self.fields_get(cr, uid, context=context).keys()
 +        group_fields, _ = partition(is_reified_group, fields_get)
 +
 +        inject_groups_id = group_fields and fields and 'groups_id' not in fields
 +        if inject_groups_id:
              fields.append('groups_id')
          res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
 -        if res:
 +
 +        if res and group_fields:
              for values in (res if isinstance(res, list) else [res]):
                  self._get_reified_groups(group_fields, values)
 +                if inject_groups_id:
 +                    values.pop('groups_id', None)
          return res
  
      def _get_reified_groups(self, fields, values):
      def fields_get(self, cr, uid, allfields=None, context=None, write_access=True):
          res = super(users_view, self).fields_get(cr, uid, allfields, context, write_access)
          # add reified groups fields
 -        for app, kind, gs in self.pool.get('res.groups').get_groups_by_application(cr, uid, context):
 +        for app, kind, gs in self.pool['res.groups'].get_groups_by_application(cr, uid, context):
              if kind == 'selection':
                  # selection group field
                  tips = ['%s: %s' % (g.name, g.comment) for g in gs if g.comment]
                      }
          return res
  
 +#----------------------------------------------------------
 +# change password wizard
 +#----------------------------------------------------------
 +
 +class change_password_wizard(osv.TransientModel):
 +    """
 +        A wizard to manage the change of users' passwords
 +    """
 +
 +    _name = "change.password.wizard"
 +    _description = "Change Password Wizard"
 +    _columns = {
 +        'user_ids': fields.one2many('change.password.user', 'wizard_id', string='Users'),
 +    }
 +
 +    def default_get(self, cr, uid, fields, context=None):
 +        if context == None:
 +            context = {}
 +        user_ids = context.get('active_ids', [])
 +        wiz_id = context.get('active_id', None)
 +        res = []
 +        users = self.pool.get('res.users').browse(cr, uid, user_ids, context=context)
 +        for user in users:
 +            res.append((0, 0, {
 +                'wizard_id': wiz_id,
 +                'user_id': user.id,
 +                'user_login': user.login,
 +            }))
 +        return {'user_ids': res}
 +
 +    def change_password_button(self, cr, uid, id, context=None):
 +        wizard = self.browse(cr, uid, id, context=context)[0]
 +        need_reload = any(uid == user.user_id.id for user in wizard.user_ids)
 +        line_ids = [user.id for user in wizard.user_ids]
 +
 +        self.pool.get('change.password.user').change_password_button(cr, uid, line_ids, context=context)
 +        # don't keep temporary password copies in the database longer than necessary
 +        self.pool.get('change.password.user').write(cr, uid, line_ids, {'new_passwd': False}, context=context)
 +
 +        if need_reload:
 +            return {
 +                'type': 'ir.actions.client',
 +                'tag': 'reload'
 +            }
 +
 +        return {'type': 'ir.actions.act_window_close'}
 +
 +class change_password_user(osv.TransientModel):
 +    """
 +        A model to configure users in the change password wizard
 +    """
 +
 +    _name = 'change.password.user'
 +    _description = 'Change Password Wizard User'
 +    _columns = {
 +        'wizard_id': fields.many2one('change.password.wizard', string='Wizard', required=True),
 +        'user_id': fields.many2one('res.users', string='User', required=True),
 +        'user_login': fields.char('User Login', readonly=True),
 +        'new_passwd': fields.char('New Password'),
 +    }
 +    _defaults = {
 +        'new_passwd': '',
 +    }
 +
 +    def change_password_button(self, cr, uid, ids, context=None):
 +        for user in self.browse(cr, uid, ids, context=context):
 +            self.pool.get('res.users').write(cr, uid, user.user_id.id, {'password': user.new_passwd})
 +
 +
  # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -5,7 -5,7 +5,7 @@@
      1. Try a few common expressions to verify they work with safe_eval
  -
      !python {model: ir.model}: |
 -        from tools.safe_eval import safe_eval
 +        from openerp.tools.safe_eval import safe_eval
          expected = (1, {"a": 9 * 2}, (True, False, None))
          actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
          assert actual == expected, "Simple python expressions are not working with safe_eval"
      5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
  -
      !python {model: ir.model}: |
 -        from tools.safe_eval import safe_eval
 -        from tools.misc import mute_logger
 +        from openerp.tools.safe_eval import safe_eval
 +        from openerp.tools.misc import mute_logger
          try:
              with mute_logger('openerp.tools.safe_eval'):
                  safe_eval('open("/etc/passwd","r")')
              assert False, "safe_eval should not allow calling open() builtin"
 -        except NameError:
 +        except ValueError:
              pass
  
  -
      "Float precision tests: verify that float rounding methods are working correctly via res.currency"
  -
      !python {model: res.currency}: |
 -        from tools import float_repr
 +        from openerp.tools import float_repr
          from math import log10
          currency = self.browse(cr, uid, ref('base.EUR'))
          def try_round(amount, expected, self=self, cr=cr, currency=currency, float_repr=float_repr,
      "Float precision tests: verify that float rounding methods are working correctly via tools"
  -
      !python {model: res.currency}: |
 -        from tools import float_compare, float_is_zero, float_round, float_repr
 +        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
          def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr):
              result = float_repr(float_round(amount, precision_digits=precision_digits),
                                  precision_digits=precision_digits)
      !python {model: res.currency}: |
          currency = self.browse(cr, uid, ref('base.EUR'))
          res_currency_rate = self.pool.get('res.currency.rate')
 -        from tools import float_compare, float_is_zero, float_round, float_repr
 +        from openerp.tools import float_compare, float_is_zero, float_round, float_repr
          def try_roundtrip(value, expected, self=self, cr=cr, currency=currency,
                            res_currency_rate=res_currency_rate):
              rate_id = res_currency_rate.create(cr, 1, {'name':'2000-01-01',
      "Float precision tests: verify that invalid parameters are forbidden"
  -
      !python {model: res.currency}: |
 -        from tools import float_compare, float_is_zero, float_round
 +        from openerp.tools import float_compare, float_is_zero, float_round
          try:
              float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)
          except AssertionError:
              float_round(0.01, precision_digits=3, precision_rounding=0.01)
          except AssertionError:
              pass
+ -
+    Test res.groups name search
+ -
+      !python {model: res.groups}: |
+          all_groups = self.search(cr, uid, [])
+          full_names = [(group.id, group.full_name) for group in self.browse(cr, uid, all_groups)]
+          group_ids = self.search(cr, uid, [('full_name', 'like', '%Sale%')])
+          assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sale' in full_name]), "did not match search for 'Sale'"
+          group_ids = self.search(cr, uid, [('full_name', 'like', '%Technical%')])
+          assert set(group_ids) == set([id for (id, full_name) in full_names if 'Technical' in full_name]), "did not match search for 'Technical'"
+          group_ids = self.search(cr, uid, [('full_name', 'like', '%Sales /%')])
+          assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sales /' in full_name]), "did not match search for 'Sales /'"
+          group_ids = self.search(cr, uid, [('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
+          assert group_ids, "did not match search for 'Administration / Access Rights' and 'Contact Creation'"
diff --combined openerp/tools/convert.py
@@@ -25,15 -25,10 +25,15 @@@ import loggin
  import os.path
  import pickle
  import re
 +import sys
  
  # for eval context:
  import time
 -import openerp.release as release
 +
 +import openerp
 +import openerp.release
 +import openerp.workflow
 +from yaml_import import convert_yaml_import
  
  import assertion_report
  
@@@ -49,9 -44,9 +49,9 @@@ except
  
  
  from datetime import datetime, timedelta
 -from lxml import etree
 +from dateutil.relativedelta import relativedelta
 +from lxml import etree, builder
  import misc
 -import openerp.pooler as pooler
  from config import config
  from translate import _
  
@@@ -66,32 -61,27 +66,32 @@@ from misc import unquot
  unsafe_eval = eval
  from safe_eval import safe_eval as eval
  
 -class ConvertError(Exception):
 -    def __init__(self, doc, orig_excpt):
 -        self.d = doc
 -        self.orig = orig_excpt
 +class ParseError(Exception):
 +    def __init__(self, msg, text, filename, lineno):
 +        self.msg = msg
 +        self.text = text
 +        self.filename = filename
 +        self.lineno = lineno
  
      def __str__(self):
 -        return 'Exception:\n\t%s\nUsing file:\n%s' % (self.orig, self.d)
 +        return '"%s" while parsing %s:%s, near\n%s' \
 +            % (self.msg, self.filename, self.lineno, self.text)
  
  def _ref(self, cr):
      return lambda x: self.id_get(cr, x)
  
  def _obj(pool, cr, uid, model_str, context=None):
 -    model = pool.get(model_str)
 +    model = pool[model_str]
      return lambda x: model.browse(cr, uid, x, context=context)
  
  def _get_idref(self, cr, uid, model_str, context, idref):
      idref2 = dict(idref,
                    time=time,
                    DateTime=datetime,
 +                  datetime=datetime,
                    timedelta=timedelta,
 -                  version=release.major_version,
 +                  relativedelta=relativedelta,
 +                  version=openerp.release.major_version,
                    ref=_ref(self, cr),
                    pytz=pytz)
      if len(model_str):
@@@ -130,10 -120,10 +130,10 @@@ def _eval_xml(self, node, pool, cr, uid
              if f_search:
                  idref2 = _get_idref(self, cr, uid, f_model, context, idref)
              q = unsafe_eval(f_search, idref2)
 -            ids = pool.get(f_model).search(cr, uid, q)
 +            ids = pool[f_model].search(cr, uid, q)
              if f_use != 'id':
 -                ids = map(lambda x: x[f_use], pool.get(f_model).read(cr, uid, ids, [f_use]))
 -            _cols = pool.get(f_model)._columns
 +                ids = map(lambda x: x[f_use], pool[f_model].read(cr, uid, ids, [f_use]))
 +            _cols = pool[f_model]._columns
              if (f_name in _cols) and _cols[f_name]._type=='many2many':
                  return ids
              f_val = False
                      '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'\
          if t == 'html':
              return _process("".join([etree.tostring(n, encoding='utf-8')
                                     for n in node]), idref)
 -        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'):
 +
 +        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 = 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 == '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)
              return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
              if return_val is not None:
                  args.append(return_val)
 -        model = pool.get(node.get('model',''))
 +        model = pool[node.get('model','')]
          method = node.get('name','')
          res = getattr(model, method)(cr, uid, *args)
          return res
@@@ -291,7 -252,7 +291,7 @@@ class xml_import(object)
  maximum one dot. They are used to refer to other modules ID, in the
  form: module.record_id""" % (xml_id,)
              if module != self.module:
 -                modcnt = self.pool.get('ir.module.module').search_count(self.cr, self.uid, ['&', ('name', '=', module), ('state', 'in', ['installed'])])
 +                modcnt = self.pool['ir.module.module'].search_count(self.cr, self.uid, ['&', ('name', '=', module), ('state', 'in', ['installed'])])
                  assert modcnt == 1, """The ID "%s" refers to an uninstalled module""" % (xml_id,)
  
          if len(id) > 64:
  
          if d_search:
              idref = _get_idref(self, cr, self.uid, d_model, context={}, idref={})
 -            ids = self.pool.get(d_model).search(cr, self.uid, unsafe_eval(d_search, idref))
 +            ids = self.pool[d_model].search(cr, self.uid, unsafe_eval(d_search, idref))
          if d_id:
              try:
                  ids.append(self.id_get(cr, d_id))
                  # d_id cannot be found. doesn't matter in this case
                  pass
          if ids:
 -            self.pool.get(d_model).unlink(cr, self.uid, ids)
 +            self.pool[d_model].unlink(cr, self.uid, ids)
  
      def _remove_ir_values(self, cr, name, value, model):
 -        ir_values_obj = self.pool.get('ir.values')
 +        ir_values_obj = self.pool['ir.values']
          ir_value_ids = ir_values_obj.search(cr, self.uid, [('name','=',name),('value','=',value),('model','=',model)])
          if ir_value_ids:
              ir_values_obj.unlink(cr, self.uid, ir_value_ids)
              res[dest] = rec.get(f,'').encode('utf8')
              assert res[dest], "Attribute %s of report is empty !" % (f,)
          for field,dest in (('rml','report_rml'),('file','report_rml'),('xml','report_xml'),('xsl','report_xsl'),
 -                           ('attachment','attachment'),('attachment_use','attachment_use'), ('usage','usage')):
 +                           ('attachment','attachment'),('attachment_use','attachment_use'), ('usage','usage'),
 +                           ('report_type', 'report_type'), ('parser', 'parser')):
              if rec.get(field):
                  res[dest] = rec.get(field).encode('utf8')
          if rec.get('auto'):
              res['report_sxw_content'] = sxw_content
          if rec.get('header'):
              res['header'] = eval(rec.get('header','False'))
 -        if rec.get('report_type'):
 -            res['report_type'] = rec.get('report_type')
  
          res['multi'] = rec.get('multi') and eval(rec.get('multi','False'))
  
                      groups_value.append((4, group_id))
              res['groups_id'] = groups_value
  
 -        id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
 +        id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
          self.idref[xml_id] = int(id)
  
          if not rec.get('menu') or eval(rec.get('menu','False')):
              keyword = str(rec.get('keyword', 'client_print_multi'))
              value = 'ir.actions.report.xml,'+str(id)
              replace = rec.get('replace', True)
 -            self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id)
 +            self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id)
          elif self.mode=='update' and eval(rec.get('menu','False'))==False:
              # Special check for report having attribute menu=False on update
              value = 'ir.actions.report.xml,'+str(id)
                      groups_value.append((4, group_id))
              res['groups_id'] = groups_value
  
 -        id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
 +        id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
          self.idref[xml_id] = int(id)
          # ir_set
          if (not rec.get('menu') or eval(rec.get('menu','False'))) and id:
              keyword = str(rec.get('keyword','') or 'client_action_multi')
              value = 'ir.actions.wizard,'+str(id)
              replace = rec.get("replace",'') or True
 -            self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id)
 +            self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id)
          elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False):
              # Special check for wizard having attribute menu=False on update
              value = 'ir.actions.wizard,'+str(id)
  
          res = {'name': name, 'url': url, 'target':target}
  
 -        id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.act_url", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
 +        id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.act_url", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
          self.idref[xml_id] = int(id)
  
      def _tag_act_window(self, cr, rec, data_node=None):
          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)
 +            res['multi'] = eval(rec.get('multi', 'False'))
 +        id = self.pool['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)
  
          if src_model:
              keyword = rec.get('key2','').encode('utf-8') or 'client_action_relate'
              value = 'ir.actions.act_window,'+str(id)
              replace = rec.get('replace','') or True
 -            self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, xml_id, [src_model], value, replace=replace, isobject=True, xml_id=xml_id)
 +            self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, xml_id, [src_model], value, replace=replace, isobject=True, xml_id=xml_id)
          # TODO add remove ir.model.data
  
      def _tag_ir_set(self, cr, rec, data_node=None):
              f_name = field.get("name",'').encode('utf-8')
              f_val = _eval_xml(self,field,self.pool, cr, self.uid, self.idref)
              res[f_name] = f_val
 -        self.pool.get('ir.model.data').ir_set(cr, self.uid, res['key'], res['key2'], res['name'], res['models'], res['value'], replace=res.get('replace',True), isobject=res.get('isobject', False), meta=res.get('meta',None))
 +        self.pool['ir.model.data'].ir_set(cr, self.uid, res['key'], res['key2'], res['name'], res['models'], res['value'], replace=res.get('replace',True), isobject=res.get('isobject', False), meta=res.get('meta',None))
  
      def _tag_workflow(self, cr, rec, data_node=None):
          if self.isnoupdate(data_node) and self.mode != 'init':
              id = _eval_xml(self, rec[0], self.pool, cr, self.uid, self.idref)
  
          uid = self.get_uid(cr, self.uid, data_node, rec)
 -        import openerp.netsvc as netsvc
 -        wf_service = netsvc.LocalService("workflow")
 -        wf_service.trg_validate(uid, model,
 +        openerp.workflow.trg_validate(uid, model,
              id,
              str(rec.get('action','')), cr)
  
                  else:
                      # the menuitem does't exist but we are in branch (not a leaf)
                      _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})
 +                    pid = self.pool['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
                      "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()
                      groups_value.append((4, group_id))
              values['groups_id'] = groups_value
  
 -        pid = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.ui.menu', self.module, values, rec_id, noupdate=self.isnoupdate(data_node), mode=self.mode, res_id=res and res[0] or False)
 +        pid = self.pool['ir.model.data']._update(cr, self.uid, 'ir.ui.menu', self.module, values, rec_id, noupdate=self.isnoupdate(data_node), mode=self.mode, res_id=res and res[0] or False)
  
          if rec_id and pid:
              self.idref[rec_id] = int(pid)
  
          if rec.get('action') and pid:
              action = "ir.actions.%s,%d" % (a_type, a_id)
 -            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)
 +            self.pool['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):
              return
  
          rec_model = rec.get("model",'').encode('ascii')
 -        model = self.pool.get(rec_model)
 -        assert model, "The model %s does not exist !" % (rec_model,)
 +        model = self.pool[rec_model]
          rec_id = rec.get("id",'').encode('ascii')
          self._test_xml_id(rec_id)
          rec_src = rec.get("search",'').encode('utf8')
              ids = [self.id_get(cr, rec_id)]
          elif rec_src:
              q = unsafe_eval(rec_src, eval_dict)
 -            ids = self.pool.get(rec_model).search(cr, uid, q, context=context)
 +            ids = self.pool[rec_model].search(cr, uid, q, context=context)
              if rec_src_count:
                  count = int(rec_src_count)
                  if len(ids) != count:
  
      def _tag_record(self, cr, rec, data_node=None):
          rec_model = rec.get("model").encode('ascii')
 -        model = self.pool.get(rec_model)
 -        assert model, "The model %s does not exist !" % (rec_model,)
 +        model = self.pool[rec_model]
          rec_id = rec.get("id",'').encode('ascii')
          rec_context = rec.get("context", None)
          if rec_context:
              rec_context = unsafe_eval(rec_context)
          self._test_xml_id(rec_id)
 +        # in update mode, the record won't be updated if the data node explicitely
 +        # opt-out using @noupdate="1". A second check will be performed in
 +        # ir.model.data#_update() using the record's ir.model.data `noupdate` field.
          if self.isnoupdate(data_node) and self.mode != 'init':
              # check if the xml record has an id string
              if rec_id:
                  else:
                      module = self.module
                      rec_id2 = rec_id
 -                id = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, rec_model, module, rec_id2)
 +                id = self.pool['ir.model.data']._update_dummy(cr, self.uid, rec_model, module, rec_id2)
                  # check if the resource already existed at the last update
                  if id:
                      # if it existed, we don't update the data, but we need to
              f_ref = field.get("ref",'').encode('utf-8')
              f_search = field.get("search",'').encode('utf-8')
              f_model = field.get("model",'').encode('utf-8')
-             if not f_model and model._columns.get(f_name,False):
-                 f_model = model._columns[f_name]._obj
+             if not f_model and model._all_columns.get(f_name,False):
+                 f_model = model._all_columns[f_name].column._obj
              f_use = field.get("use",'').encode('utf-8') or 'id'
              f_val = False
  
                  q = unsafe_eval(f_search, self.idref)
                  field = []
                  assert f_model, 'Define an attribute model="..." in your .XML file !'
 -                f_obj = self.pool.get(f_model)
 +                f_obj = self.pool[f_model]
                  # browse the objects searched
                  s = f_obj.browse(cr, self.uid, f_obj.search(cr, self.uid, q))
                  # column definitions of the "local" object
-                 _cols = self.pool[rec_model]._columns
 -                _cols = self.pool.get(rec_model)._all_columns
++                _cols = self.pool[rec_model]._all_columns
                  # if the current field is many2many
-                 if (f_name in _cols) and _cols[f_name]._type=='many2many':
+                 if (f_name in _cols) and _cols[f_name].column._type=='many2many':
                      f_val = [(6, 0, map(lambda x: x[f_use], s))]
                  elif len(s):
                      # otherwise (we are probably in a many2one field),
                  if f_ref=="null":
                      f_val = False
                  else:
-                     if f_name in model._columns \
-                               and model._columns[f_name]._type == 'reference':
+                     if f_name in model._all_columns \
+                               and model._all_columns[f_name].column._type == 'reference':
                          val = self.model_id_get(cr, f_ref)
                          f_val = val[0] + ',' + str(val[1])
                      else:
                          f_val = self.id_get(cr, f_ref)
              else:
                  f_val = _eval_xml(self,field, self.pool, cr, self.uid, self.idref)
-                 if model._columns.has_key(f_name):
+                 if f_name in model._all_columns:
                      import openerp.osv as osv
-                     if isinstance(model._columns[f_name], osv.fields.integer):
+                     if isinstance(model._all_columns[f_name].column, osv.fields.integer):
                          f_val = int(f_val)
              res[f_name] = f_val
  
 -        id = self.pool.get('ir.model.data')._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode, context=rec_context )
 +        id = self.pool['ir.model.data']._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode, context=rec_context )
          if rec_id:
              self.idref[rec_id] = int(id)
          if config.get('import_partial', False):
              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']:
 +            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.get('priority', "16"), name='priority'))
 +        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))
 +        groups = el.attrib.pop('groups', None)
 +        if groups:
 +            grp_lst = map(lambda x: "ref('%s')" % x, groups.split(','))
 +            record.append(Field(name="groups_id", eval="[(6, 0, ["+', '.join(grp_lst)+"])]"))
 +        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]
          return res
  
      def model_id_get(self, cr, id_str):
 -        model_data_obj = self.pool.get('ir.model.data')
 +        model_data_obj = self.pool['ir.model.data']
          mod = self.module
          if '.' in id_str:
              mod,id_str = id_str.split('.')
          return model_data_obj.get_object_reference(cr, self.uid, mod, id_str)
  
      def parse(self, de):
 -        if not de.tag in ['terp', 'openerp']:
 -            _logger.error("Mismatch xml format")
 -            raise Exception( "Mismatch xml format: only terp or openerp as root tag" )
 -
 -        if de.tag == 'terp':
 -            _logger.warning("The tag <terp/> is deprecated, use <openerp/>")
 +        if de.tag != 'openerp':
 +            raise Exception("Mismatch xml format: root tag must be `openerp`.")
  
          for n in de.findall('./data'):
              for rec in n:
                  if rec.tag in self._tags:
                      try:
                          self._tags[rec.tag](self.cr, rec, n)
 -                    except:
 -                        _logger.error('Parse error in %s:%d: \n%s',
 -                                      rec.getroottree().docinfo.URL,
 -                                      rec.sourceline,
 -                                      etree.tostring(rec).strip(), exc_info=True)
 +                    except Exception, e:
                          self.cr.rollback()
 -                        raise
 +                        exc_info = sys.exc_info()
 +                        raise ParseError, (misc.ustr(e), etree.tostring(rec).rstrip(), rec.getroottree().docinfo.URL, rec.sourceline), exc_info[2]
          return True
  
      def __init__(self, cr, module, idref, mode, report=None, noupdate=False):
          self.module = module
          self.cr = cr
          self.idref = idref
 -        self.pool = pooler.get_pool(cr.dbname)
 +        self.pool = openerp.registry(cr.dbname)
          self.uid = 1
          if report is None:
              report = assertion_report.assertion_report()
          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,
              '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 :
      #remove folder path from model
      head, model = os.path.split(model)
  
 -    pool = pooler.get_pool(cr.dbname)
 -
      input = cStringIO.StringIO(csvcontent) #FIXME
      reader = csv.reader(input, quotechar='"', delimiter=',')
      fields = reader.next()
      uid = 1
      datas = []
      for line in reader:
 -        if (not line) or not reduce(lambda x,y: x or y, line) :
 +        if not (line and any(line)):
              continue
          try:
 -            datas.append(map(lambda x: misc.ustr(x), line))
 +            datas.append(map(misc.ustr, line))
          except:
              _logger.error("Cannot import the line: %s", line)
 -    result, rows, warning_msg, dummy = pool.get(model).import_data(cr, uid, fields, datas,mode, module, noupdate, filename=fname_partial)
 +
 +    registry = openerp.registry(cr.dbname)
 +    result, rows, warning_msg, dummy = registry[model].import_data(cr, uid, fields, datas,mode, module, noupdate, filename=fname_partial)
      if result < 0:
          # Report failed import and abort module install
          raise Exception(_('Module loading %s failed: file %s could not be processed:\n %s') % (module, fname, warning_msg))