diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py
index 0b5c6dc09c5..2413b21ce4e 100644
--- a/bin/addons/__init__.py
+++ b/bin/addons/__init__.py
@@ -30,7 +30,7 @@ import tools
import tools.osutil
from tools.safe_eval import safe_eval as eval
import pooler
-
+from tools.translate import _
import netsvc
@@ -649,8 +649,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
try:
_load_data(cr, module_name, id_map, mode, 'test')
except Exception, e:
- logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
- pass
+ logging.getLogger('test').exception('Tests failed to execute in %s module %s', module_name)
finally:
if tools.config.options['test_commit']:
cr.commit()
@@ -806,7 +805,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
graph = create_graph(cr, ['base'], force)
if not graph:
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
- raise osv.osv.except_osv('Could not load base module', 'module base cannot be loaded! (hint: verify addons-path)')
+ raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
has_updates = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
if update_module:
@@ -857,7 +856,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
for (model, name) in cr.fetchall():
model_obj = pool.get(model)
- if not isinstance(model_obj, osv.osv.osv_memory):
+ if model_obj and not isinstance(model_obj, osv.osv.osv_memory):
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
# Temporary warning while we remove access rights on osv_memory objects, as they have
@@ -873,6 +872,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
obj = pool.get(model)
if obj:
obj._check_removed_columns(cr, log=True)
+ else:
+ logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model)
if report.get_report():
logger.notifyChannel('init', netsvc.LOG_INFO, report)
diff --git a/bin/addons/base/__openerp__.py b/bin/addons/base/__openerp__.py
index 492c397754d..d57a8878199 100644
--- a/bin/addons/base/__openerp__.py
+++ b/bin/addons/base/__openerp__.py
@@ -23,7 +23,7 @@
{
'name': 'Base',
- 'version': '1.2',
+ 'version': '1.3',
'category': 'Generic Modules/Base',
'description': """The kernel of OpenERP, needed for all installation.""",
'author': 'OpenERP SA',
@@ -81,10 +81,11 @@
'base_demo.xml',
'res/partner/partner_demo.xml',
'res/partner/crm_demo.xml',
+ 'res/res_widget_demo.xml',
],
'test': [
'test/base_test.xml',
- #'test/base_test.yml'
+ 'test/base_test.yml',
'test/test_context.xml',
'test/bug_lp541545.xml',
],
diff --git a/bin/addons/base/base.sql b/bin/addons/base/base.sql
index 71bfda541c4..90da118f475 100644
--- a/bin/addons/base/base.sql
+++ b/bin/addons/base/base.sql
@@ -288,6 +288,7 @@ CREATE TABLE ir_module_module (
description text,
demo boolean default False,
web boolean DEFAULT FALSE,
+ license character varying(32),
primary key(id)
);
ALTER TABLE ir_module_module add constraint name_uniq unique (name);
diff --git a/bin/addons/base/base_data.xml b/bin/addons/base/base_data.xml
index b4340e3faf0..143d2e26b07 100644
--- a/bin/addons/base/base_data.xml
+++ b/bin/addons/base/base_data.xml
@@ -915,7 +915,7 @@
United Kingdom
- uk
+ gb
USA Minor Outlying Islands
@@ -989,7 +989,7 @@
Zambia
zm
-
+
Zaire
zr
@@ -1044,7 +1044,7 @@
-
+
OpenERP S.A.
@@ -1170,7 +1170,7 @@
-
+
Kč
CZK
@@ -1213,7 +1213,7 @@
-
+
Rs
IDR
@@ -1290,7 +1290,7 @@
-
+
kr
SEK
@@ -1304,7 +1304,7 @@
-
+
GBP
GBP
@@ -1326,7 +1326,7 @@
0.01
4
-
+
5.0881
@@ -1378,5 +1378,19 @@
Reserve
RSV
+
+
+ CRC
+ CRC
+ 0.01
+ 4
+ ¢
+
+
+ 691.3153
+
+
+
+
diff --git a/bin/addons/base/base_menu.xml b/bin/addons/base/base_menu.xml
index 676fd2ff015..0e37c783cb3 100644
--- a/bin/addons/base/base_menu.xml
+++ b/bin/addons/base/base_menu.xml
@@ -1,7 +1,10 @@
-
+
diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py
index 84419d25b51..18dc7f514d4 100644
--- a/bin/addons/base/ir/ir_actions.py
+++ b/bin/addons/base/ir/ir_actions.py
@@ -26,6 +26,7 @@ import time
from tools.config import config
from tools.translate import _
import netsvc
+import logging
import re
import copy
import os
@@ -144,18 +145,22 @@ class act_window(osv.osv):
_table = 'ir_act_window'
_sequence = 'ir_actions_id_seq'
- def _check_model(self, cr, uid, ids, context={}):
+ def _check_model(self, cr, uid, ids, context=None):
for action in self.browse(cr, uid, ids, context):
if not self.pool.get(action.res_model):
return False
if action.src_model and not self.pool.get(action.src_model):
return False
return True
+
+ def _invalid_model_msg(self, cr, uid, ids, context=None):
+ return _('Invalid model name in the action definition.')
+
_constraints = [
- (_check_model, 'Invalid model name in the action definition.', ['res_model','src_model'])
+ (_check_model, _invalid_model_msg, ['res_model','src_model'])
]
- def _views_get_fnc(self, cr, uid, ids, name, arg, context={}):
+ def _views_get_fnc(self, cr, uid, ids, name, arg, context=None):
res={}
for act in self.browse(cr, uid, ids):
res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids]
@@ -171,30 +176,34 @@ class act_window(osv.osv):
res[act.id].append((False, t))
return res
- def _search_view(self, cr, uid, ids, name, arg, context={}):
+ def _search_view(self, cr, uid, ids, name, arg, context=None):
res = {}
def encode(s):
if isinstance(s, unicode):
return s.encode('utf8')
return s
- for act in self.browse(cr, uid, ids):
+ for act in self.browse(cr, uid, ids, context=context):
fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid, context=context)
search_view_id = False
if act.search_view_id:
search_view_id = act.search_view_id.id
else:
- res_view = self.pool.get('ir.ui.view').search(cr, uid, [('model','=',act.res_model),('type','=','search'),('inherit_id','=',False)])
+ res_view = self.pool.get('ir.ui.view').search(cr, uid,
+ [('model','=',act.res_model),('type','=','search'),
+ ('inherit_id','=',False)], context=context)
if res_view:
search_view_id = res_view[0]
if search_view_id:
- field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, search_view_id, 'search', context)
+ field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, search_view_id,
+ 'search', context)
fields_from_fields_get.update(field_get['fields'])
field_get['fields'] = fields_from_fields_get
res[act.id] = str(field_get)
else:
def process_child(node, new_node, doc):
for child in node.childNodes:
- if child.localName=='field' and child.hasAttribute('select') and child.getAttribute('select')=='1':
+ if child.localName=='field' and child.hasAttribute('select') \
+ and child.getAttribute('select')=='1':
if child.childNodes:
fld = doc.createElement('field')
for attr in child.attributes.keys():
@@ -218,7 +227,7 @@ class act_window(osv.osv):
res[act.id] = str(form_arch)
return res
- def _get_help_status(self, cr, uid, ids, name, arg, context={}):
+ def _get_help_status(self, cr, uid, ids, name, arg, context=None):
activate_tips = self.pool.get('res.users').browse(cr, uid, uid).menu_tips
return dict([(id, activate_tips) for id in ids])
@@ -272,6 +281,19 @@ class act_window(osv.osv):
'multi': False,
}
+ def for_xml_id(self, cr, uid, module, xml_id, context=None):
+ """ Returns the act_window object created for the provided xml_id
+
+ :param module: the module the act_window originates in
+ :param xml_id: the namespace-less id of the action (the @id
+ attribute from the XML file)
+ :return: A read() view of the ir.actions.act_window
+ """
+ dataobj = self.pool.get('ir.model.data')
+ data_id = dataobj._get_id (cr, 1, module, xml_id)
+ res_id = dataobj.browse(cr, uid, data_id, context).res_id
+ return self.read(cr, uid, res_id, [], context)
+
act_window()
class act_window_view(osv.osv):
@@ -336,7 +358,7 @@ class act_url(osv.osv):
}
act_url()
-def model_get(self, cr, uid, context={}):
+def model_get(self, cr, uid, context=None):
wkf_pool = self.pool.get('workflow')
ids = wkf_pool.search(cr, uid, [])
osvs = wkf_pool.read(cr, uid, ids, ['osv'])
@@ -381,7 +403,7 @@ server_object_lines()
#
class actions_server(osv.osv):
- def _select_signals(self, cr, uid, context={}):
+ def _select_signals(self, cr, uid, context=None):
cr.execute("SELECT distinct w.osv, t.signal FROM wkf w, wkf_activity a, wkf_transition t \
WHERE w.id = a.wkf_id AND t.act_from = a.id OR t.act_to = a.id AND t.signal!='' \
AND t.signal NOT IN (null, NULL)")
@@ -393,13 +415,13 @@ class actions_server(osv.osv):
res.append(line)
return res
- def _select_objects(self, cr, uid, context={}):
+ def _select_objects(self, cr, uid, context=None):
model_pool = self.pool.get('ir.model')
ids = model_pool.search(cr, uid, [('name','not ilike','.')])
res = model_pool.read(cr, uid, ids, ['model', 'name'])
return [(r['model'], r['name']) for r in res] + [('','')]
- def change_object(self, cr, uid, ids, copy_object, state, context={}):
+ def change_object(self, cr, uid, ids, copy_object, state, context=None):
if state == 'object_copy':
model_pool = self.pool.get('ir.model')
model = copy_object.split(',')[0]
@@ -470,7 +492,7 @@ class actions_server(osv.osv):
}
def get_email(self, cr, uid, action, context):
- logger = netsvc.Logger()
+ logger = logging.getLogger('Workflow')
obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id')
obj = obj_pool.browse(cr, uid, id)
@@ -485,13 +507,13 @@ class actions_server(osv.osv):
for field in fields:
try:
obj = getattr(obj, field)
- except Exception,e :
- logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
+ except Exception:
+ logger.exception('Failed to parse: %s', field)
return obj
def get_mobile(self, cr, uid, action, context):
- logger = netsvc.Logger()
+ logger = logging.getLogger('Workflow')
obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id')
obj = obj_pool.browse(cr, uid, id)
@@ -506,15 +528,14 @@ class actions_server(osv.osv):
for field in fields:
try:
obj = getattr(obj, field)
- except Exception,e :
- logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
+ except Exception:
+ logger.exception('Failed to parse: %s', field)
return obj
def merge_message(self, cr, uid, keystr, action, context=None):
if context is None:
context = {}
- logger = netsvc.Logger()
def merge(match):
obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id')
@@ -544,7 +565,7 @@ class actions_server(osv.osv):
# FIXME: refactor all the eval() calls in run()!
def run(self, cr, uid, ids, context=None):
- logger = netsvc.Logger()
+ logger = logging.getLogger(self._name)
if context is None:
context = {}
for action in self.browse(cr, uid, ids, context):
@@ -592,18 +613,19 @@ class actions_server(osv.osv):
pass
if not address:
- logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!')
+ logger.info('Partner Email address not Specified!')
continue
if not user:
+ logger.info('Email-From address not Specified at server!')
raise osv.except_osv(_('Error'), _("Please specify server option --email-from !"))
subject = self.merge_message(cr, uid, action.subject, action, context)
body = self.merge_message(cr, uid, action.message, action, context)
if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
- logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
+ logger.info('Email successfully sent to: %s', address)
else:
- logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
+ logger.warning('Failed to send email to: %s', address)
if action.state == 'trigger':
wf_service = netsvc.LocalService("workflow")
@@ -617,7 +639,7 @@ class actions_server(osv.osv):
#TODO: set the user and password from the system
# for the sms gateway user / password
# USE smsclient module from extra-addons
- logger.notifyChannel('sms', netsvc.LOG_ERROR, 'SMS Facility has not been implemented yet. Use smsclient module!')
+ logger.warning('SMS Facility has not been implemented yet. Use smsclient module!')
if action.state == 'other':
res = []
diff --git a/bin/addons/base/ir/ir_attachment.py b/bin/addons/base/ir/ir_attachment.py
index fb621ea58fc..0e2ae2ad13d 100644
--- a/bin/addons/base/ir/ir_attachment.py
+++ b/bin/addons/base/ir/ir_attachment.py
@@ -24,16 +24,29 @@ from osv.orm import except_orm
import tools
class ir_attachment(osv.osv):
- def check(self, cr, uid, ids, mode, context=None):
+ def check(self, cr, uid, ids, mode, context=None, values=None):
+ """Restricts the access to an ir.attachment, according to referred model
+ In the 'document' module, it is overriden to relax this hard rule, since
+ more complex ones apply there.
+ """
if not ids:
return
ima = self.pool.get('ir.model.access')
- if isinstance(ids, (int, long)):
- ids = [ids]
- cr.execute('select distinct res_model from ir_attachment where id IN %s', (tuple(ids),))
- for obj in cr.fetchall():
- if obj[0]:
- ima.check(cr, uid, obj[0], mode, context=context)
+ res_ids = {}
+ if ids:
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ cr.execute('SELECT DISTINCT res_model, res_id FROM ir_attachment WHERE id = ANY (%s)', (ids,))
+ for rmod, rid in cr.fetchall():
+ if not (rmod and rid):
+ continue
+ res_ids.setdefault(rmod,[]).append(rid)
+ if values:
+ if 'res_model' in values and 'res_id' in values:
+ res_ids.setdefault(values['res_model'],[]).append(values['res_id'])
+
+ for model, mids in res_ids.items():
+ self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context)
def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False):
@@ -64,7 +77,7 @@ class ir_attachment(osv.osv):
return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
def write(self, cr, uid, ids, vals, context=None):
- self.check(cr, uid, ids, 'write', context=context)
+ self.check(cr, uid, ids, 'write', context=context, values=vals)
return super(ir_attachment, self).write(cr, uid, ids, vals, context)
def copy(self, cr, uid, id, default=None, context=None):
@@ -76,15 +89,12 @@ class ir_attachment(osv.osv):
return super(ir_attachment, self).unlink(cr, uid, ids, context)
def create(self, cr, uid, values, context=None):
- if 'res_model' in values and values['res_model'] != '':
- self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create', context=context)
+ self.check(cr, uid, [], mode='create', context=context, values=values)
return super(ir_attachment, self).create(cr, uid, values, context)
def action_get(self, cr, uid, context=None):
- dataobj = self.pool.get('ir.model.data')
- data_id = dataobj._get_id(cr, 1, 'base', 'action_attachment')
- res_id = dataobj.browse(cr, uid, data_id, context).res_id
- return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context)
+ return self.pool.get('ir.actions.act_window').for_xml_id(
+ cr, uid, 'base', 'action_attachment', context=context)
def _name_get_resname(self, cr, uid, ids, object,method, context):
data = {}
@@ -114,11 +124,11 @@ class ir_attachment(osv.osv):
'url': fields.char('Url', size=512, oldname="link"),
'type': fields.selection(
[ ('url','URL'), ('binary','Binary'), ],
- 'Type', help="Binary File or external URL", required=True),
+ 'Type', help="Binary File or external URL", required=True, change_default=True),
'create_date': fields.datetime('Date Created', readonly=True),
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
- 'company_id': fields.many2one('res.company', 'Company'),
+ 'company_id': fields.many2one('res.company', 'Company', change_default=True),
}
_defaults = {
diff --git a/bin/addons/base/ir/ir_exports.py b/bin/addons/base/ir/ir_exports.py
index 48f4141d0b5..39477f51fbe 100644
--- a/bin/addons/base/ir/ir_exports.py
+++ b/bin/addons/base/ir/ir_exports.py
@@ -26,7 +26,7 @@ class ir_exports(osv.osv):
_name = "ir.exports"
_columns = {
'name': fields.char('Export Name', size=128),
- 'resource': fields.char('Resource', size=128),
+ 'resource': fields.char('Resource', size=128, select=True),
'export_fields': fields.one2many('ir.exports.line', 'export_id',
'Export ID'),
}
diff --git a/bin/addons/base/ir/ir_filters.py b/bin/addons/base/ir/ir_filters.py
index 541dbdb60c6..0146b8dd85b 100644
--- a/bin/addons/base/ir/ir_filters.py
+++ b/bin/addons/base/ir/ir_filters.py
@@ -60,7 +60,7 @@ class ir_filters(osv.osv):
'user_id':fields.many2one('res.users', 'User', help='False means for every user'),
'domain': fields.text('Domain Value', required=True),
'context': fields.text('Context Value', required=True),
- 'model_id': fields.selection(_list_all_models, 'Object', required=True),
+ 'model_id': fields.selection(_list_all_models, 'Object', size=64, required=True),
}
ir_filters()
diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py
index 3f3700b0e45..583985b5930 100644
--- a/bin/addons/base/ir/ir_model.py
+++ b/bin/addons/base/ir/ir_model.py
@@ -57,7 +57,7 @@ class ir_model(osv.osv):
return []
field, operator, value = domain[0]
if operator not in ['=', '!=']:
- raise osv.except_osv('Invalid search criterions','The osv_memory field can only be compared with = and != operator.')
+ raise osv.except_osv(_('Invalid search criterions'), _('The osv_memory field can only be compared with = and != operator.'))
value = bool(value) if operator == '=' else not bool(value)
all_model_ids = self.search(cr, uid, [], context=context)
is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context)
@@ -75,10 +75,12 @@ class ir_model(osv.osv):
fnct_search=_search_osv_memory,
help="Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory)")
}
+
_defaults = {
'model': lambda *a: 'x_',
- 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
+ 'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
}
+
def _check_model_name(self, cr, uid, ids):
for model in self.browse(cr, uid, ids):
if model.state=='manual':
@@ -88,8 +90,10 @@ class ir_model(osv.osv):
return False
return True
+ def _model_name_msg(self, cr, uid, ids, context=None):
+ return _('The Object name must start with x_ and not contain any special character !')
_constraints = [
- (_check_model_name, 'The Object name must start with x_ and not contain any special character !', ['model']),
+ (_check_model_name, _model_name_msg, ['model']),
]
# overridden to allow searching both on model name (model field)
@@ -181,8 +185,11 @@ class ir_model_fields(osv.osv):
'selectable': lambda *a: 1,
}
_order = "id"
+ def _size_gt_zero_msg(self, cr, user, ids, context=None):
+ return _('Size of the field can never be less than 1 !')
+
_sql_constraints = [
- ('size_gt_zero', 'CHECK (size>0)', 'Size of the field can never be less than 1 !'),
+ ('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
]
def unlink(self, cr, user, ids, context=None):
for field in self.browse(cr, user, ids, context):
@@ -381,8 +388,8 @@ class ir_model_data(osv.osv):
_defaults = {
'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'noupdate': lambda *a: False,
- 'module': lambda *a: ''
+ 'noupdate': False,
+ 'module': ''
}
_sql_constraints = [
('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same id for the same module !'),
@@ -394,6 +401,12 @@ class ir_model_data(osv.osv):
self.doinit = True
self.unlink_mark = {}
+ def _auto_init(self, cr, context=None):
+ super(ir_model_data, self)._auto_init(cr, context)
+ cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_model_data_module_name_index\'')
+ if not cr.fetchone():
+ cr.execute('CREATE INDEX ir_model_data_module_name_index ON ir_model_data (module, name)')
+
@tools.cache()
def _get_id(self, cr, uid, module, xml_id):
"""Returns the id of the ir.model.data record corresponding to a given module and xml_id (cached) or raise a ValueError if not found"""
@@ -429,6 +442,10 @@ class ir_model_data(osv.osv):
model_obj = self.pool.get(model)
if not context:
context = {}
+
+ # records created during module install should result in res.log entries that are already read!
+ context = dict(context, res_log_read=True)
+
if xml_id and ('.' in xml_id):
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
module, xml_id = xml_id.split('.')
@@ -437,18 +454,19 @@ class ir_model_data(osv.osv):
action_id = False
if xml_id:
- cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id))
+ cr.execute('''SELECT imd.id, imd.res_id, md.id
+ FROM ir_model_data imd LEFT JOIN %s md ON (imd.res_id = md.id)
+ WHERE imd.module=%%s AND imd.name=%%s''' % model_obj._table,
+ (module, xml_id))
results = cr.fetchall()
- for action_id2,res_id2 in results:
- cr.execute('select id from '+model_obj._table+' where id=%s', (res_id2,))
- result3 = cr.fetchone()
- if not result3:
+ for imd_id2,res_id2,real_id2 in results:
+ if not real_id2:
self._get_id.clear_cache(cr.dbname, uid, module, xml_id)
self.get_object_reference.clear_cache(cr.dbname, uid, module, xml_id)
- cr.execute('delete from ir_model_data where id=%s', (action_id2,))
+ cr.execute('delete from ir_model_data where id=%s', (imd_id2,))
res_id = False
else:
- res_id,action_id = res_id2,action_id2
+ res_id,action_id = res_id2,imd_id2
if action_id and res_id:
model_obj.write(cr, uid, [res_id], values, context=context)
diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py
index 83ab79f20ac..641dd333b81 100644
--- a/bin/addons/base/ir/ir_sequence.py
+++ b/bin/addons/base/ir/ir_sequence.py
@@ -74,10 +74,18 @@ class ir_sequence(osv.osv):
def get_id(self, cr, uid, sequence_id, test='id', context=None):
assert test in ('code','id')
- cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE NOWAIT', (sequence_id, True))
+ company_id = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)['company_id'][0] or None
+ cr.execute('''SELECT id, number_next, prefix, suffix, padding
+ FROM ir_sequence
+ WHERE %s=%%s
+ AND active=true
+ AND (company_id = %%s or company_id is NULL)
+ ORDER BY company_id, id
+ FOR UPDATE NOWAIT''' % test,
+ (sequence_id, company_id))
res = cr.dictfetchone()
if res:
- cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
+ cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=true', (res['id'],))
if res['number_next']:
return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'])
else:
diff --git a/bin/addons/base/ir/ir_translation.py b/bin/addons/base/ir/ir_translation.py
index 6f8a4affbe9..8efeac8a0f8 100644
--- a/bin/addons/base/ir/ir_translation.py
+++ b/bin/addons/base/ir/ir_translation.py
@@ -57,7 +57,7 @@ class ir_translation(osv.osv):
_columns = {
'name': fields.char('Field Name', size=128, required=True),
'res_id': fields.integer('Resource ID', select=True),
- 'lang': fields.selection(_get_language, string='Language', size=5),
+ 'lang': fields.selection(_get_language, string='Language', size=16),
'type': fields.selection(TRANSLATION_TYPE, string='Type', size=16, select=True),
'src': fields.text('Source'),
'value': fields.text('Translation Value'),
diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py
index f79493b4813..43a5cb5bf76 100644
--- a/bin/addons/base/ir/ir_ui_menu.py
+++ b/bin/addons/base/ir/ir_ui_menu.py
@@ -19,9 +19,13 @@
#
##############################################################################
-from osv import fields, osv
+import base64
import re
+
import tools
+import addons
+from osv import fields, osv
+from tools.translate import _
def one_in(setA, setB):
"""Check the presence of an element of setA in setB
@@ -67,8 +71,10 @@ class ir_ui_menu(osv.osv):
# radical but this doesn't frequently happen
self._cache = {}
- def create_shortcut(self, cr, uid, values, context={}):
+ def create_shortcut(self, cr, uid, values, context=None):
dataobj = self.pool.get('ir.model.data')
+ if context is None:
+ context = {}
new_context = context.copy()
for key in context:
if key.startswith('default_'):
@@ -260,8 +266,26 @@ class ir_ui_menu(osv.osv):
return False
level -= 1
return True
-
-
+
+ def read_image(self, path):
+ path_info = path.split(',')
+ icon_path = addons.get_module_resource(path_info[0],path_info[1])
+ icon = tools.file_open(icon_path,'rb').read()
+ return base64.encodestring(icon)
+
+ def _get_image_icon(self, cr, uid, ids, name, args, context=None):
+ res = {}
+ for menu in self.browse(cr, uid, ids, context=context):
+ res[menu.id] = {
+ 'web_icon_data': False,
+ 'web_icon_hover_data': False,
+ }
+ if not menu.parent_id:
+ if menu.web_icon_hover:
+ res[menu.id]['web_icon_hover_data'] = self.read_image(menu.web_icon_hover)
+ if menu.web_icon:
+ res[menu.id]['web_icon_data'] = self.read_image(menu.web_icon)
+ return res
_columns = {
'name': fields.char('Menu', size=64, required=True, translate=True),
@@ -275,6 +299,10 @@ class ir_ui_menu(osv.osv):
string='Complete Name', type='char', size=128),
'icon': fields.selection(tools.icons, 'Icon', size=64),
'icon_pict': fields.function(_get_icon_pict, method=True, type='char', size=32),
+ 'web_icon': fields.char('Icon File', size=128),
+ 'web_icon_hover':fields.char('Icon hover File', size=128),
+ 'web_icon_data': fields.function(_get_image_icon, string='Web Icons', type='binary', method=True, readonly=True, store=True, multi='icon'),
+ 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icons Hover', type='binary', method=True, readonly=True, store=True,multi='icon'),
'action': fields.function(_action, fnct_inv=_action_inv,
method=True, type='reference', string='Action',
selection=[
@@ -285,13 +313,17 @@ class ir_ui_menu(osv.osv):
('ir.actions.server', 'ir.actions.server'),
]),
}
+
+ def _rec_message(self, cr, uid, ids, context=None):
+ return _('Error ! You can not create recursive Menu.')
+
_constraints = [
- (_check_recursion, 'Error ! You can not create recursive Menu.', ['parent_id'])
+ (_check_recursion, _rec_message , ['parent_id'])
]
_defaults = {
- 'icon' : lambda *a: 'STOCK_OPEN',
- 'icon_pict': lambda *a: ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
- 'sequence' : lambda *a: 10,
+ 'icon' : 'STOCK_OPEN',
+ 'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
+ 'sequence' : 10,
}
_order = "sequence,id"
ir_ui_menu()
diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py
index 310f3552403..602e2a10a80 100644
--- a/bin/addons/base/ir/ir_values.py
+++ b/bin/addons/base/ir/ir_values.py
@@ -24,6 +24,11 @@ from osv.orm import except_orm
import pickle
from tools.translate import _
+EXCLUDED_FIELDS = set((
+ 'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
+ 'report_sxw_content_data', 'report_rml_content_data', 'search_view',
+ 'search_view_id'))
+
class ir_values(osv.osv):
_name = 'ir.values'
@@ -67,21 +72,21 @@ class ir_values(osv.osv):
'name': fields.char('Name', size=128),
'model_id': fields.many2one('ir.model', 'Object', size=128,
help="This field is not used, it only helps you to select a good model."),
- 'model': fields.char('Object Name', size=128),
+ 'model': fields.char('Object Name', size=128, select=True),
'action_id': fields.many2one('ir.actions.actions', 'Action',
help="This field is not used, it only helps you to select the right action."),
'value': fields.text('Value'),
'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
method=True, type='text', string='Value'),
'object': fields.boolean('Is Object'),
- 'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128),
- 'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128),
+ 'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128, select=True),
+ 'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128, select=True),
'meta': fields.text('Meta Datas'),
'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
method=True, type='text', string='Metadata'),
- 'res_id': fields.integer('Object ID', help="Keep 0 if the action must appear on all resources."),
- 'user_id': fields.many2one('res.users', 'User', ondelete='cascade'),
- 'company_id': fields.many2one('res.company', 'Company')
+ 'res_id': fields.integer('Object ID', help="Keep 0 if the action must appear on all resources.", select=True),
+ 'user_id': fields.many2one('res.users', 'User', ondelete='cascade', select=True),
+ 'company_id': fields.many2one('res.company', 'Company', select=True)
}
_defaults = {
'key': lambda *a: 'action',
@@ -89,14 +94,14 @@ class ir_values(osv.osv):
'company_id': lambda *a: False
}
- def _auto_init(self, cr, context={}):
+ def _auto_init(self, cr, context=None):
super(ir_values, self)._auto_init(cr, context)
- cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_index\'')
+ cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_res_id_user_id_idx\'')
if not cr.fetchone():
- cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)')
+ cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
- if type(value)==type(u''):
+ if isinstance(value, unicode):
value = value.encode('utf8')
if not isobject:
value = pickle.dumps(value)
@@ -104,30 +109,24 @@ class ir_values(osv.osv):
meta = pickle.dumps(meta)
ids_res = []
for model in models:
- if type(model)==type([]) or type(model)==type(()):
+ if isinstance(model, (list, tuple)):
model,res_id = model
else:
res_id=False
if replace:
+ search_criteria = [
+ ('key', '=', key),
+ ('key2', '=', key2),
+ ('model', '=', model),
+ ('res_id', '=', res_id),
+ ('user_id', '=', preserve_user and uid)
+ ]
if key in ('meta', 'default'):
- ids = self.search(cr, uid, [
- ('key', '=', key),
- ('key2', '=', key2),
- ('name', '=', name),
- ('model', '=', model),
- ('res_id', '=', res_id),
- ('user_id', '=', preserve_user and uid)
- ])
+ search_criteria.append(('name', '=', name))
else:
- ids = self.search(cr, uid, [
- ('key', '=', key),
- ('key2', '=', key2),
- ('value', '=', value),
- ('model', '=', model),
- ('res_id', '=', res_id),
- ('user_id', '=', preserve_user and uid)
- ])
- self.unlink(cr, uid, ids)
+ search_criteria.append(('value', '=', value))
+
+ self.unlink(cr, uid, self.search(cr, uid, search_criteria))
vals = {
'name': name,
'value': value,
@@ -149,8 +148,8 @@ class ir_values(osv.osv):
def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True):
result = []
for m in models:
- if type(m)==type([]) or type(m)==type(()):
- m,res_id = m
+ if isinstance(m, (list, tuple)):
+ m, res_id = m
else:
res_id=False
@@ -159,9 +158,8 @@ class ir_values(osv.osv):
if key2:
where.append('key2=%s')
params.append(key2[:200])
- else:
- if key2_req and not meta:
- where.append('key2 is null')
+ elif key2_req and not meta:
+ where.append('key2 is null')
if res_id_req and (models[-1][0]==m):
if res_id:
where.append('res_id=%s')
@@ -193,48 +191,37 @@ class ir_values(osv.osv):
keys.append(x[1])
if x[3]:
model,id = x[2].split(',')
- id = int(id)
- fields = self.pool.get(model).fields_get_keys(cr, uid)
- pos = 0
- while pos).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from osv import osv
+from osv.orm import orm_memory
+
+class osv_memory_autovacuum(osv.osv_memory):
+ _name = 'osv_memory.autovacuum'
+
+ def power_on(self, cr, uid, context=None):
+ for model in self.pool.obj_list():
+ obj = self.pool.get(model)
+ if isinstance(obj, orm_memory):
+ obj.vaccum(cr, uid)
+ return True
+
+osv_memory_autovacuum()
+
diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py
index a117688399d..dda5dd46276 100644
--- a/bin/addons/base/ir/workflow/workflow.py
+++ b/bin/addons/base/ir/workflow/workflow.py
@@ -175,7 +175,7 @@ class wkf_instance(osv.osv):
'res_type': fields.char('Resource Object', size=64, select=True),
'state': fields.char('State', size=32, select=True),
}
- def _auto_init(self, cr, context={}):
+ def _auto_init(self, cr, context=None):
super(wkf_instance, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_res_type_state_index\'')
if not cr.fetchone():
diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py
index f552a24de64..1bdf2a7aebb 100644
--- a/bin/addons/base/module/module.py
+++ b/bin/addons/base/module/module.py
@@ -27,7 +27,6 @@ import urllib
import zipimport
import addons
-import netsvc
import pooler
import release
import tools
@@ -41,7 +40,7 @@ class module_category(osv.osv):
_name = "ir.module.category"
_description = "Module Category"
- def _module_nbr(self,cr,uid, ids, prop, unknow_none,context):
+ def _module_nbr(self,cr,uid, ids, prop, unknow_none, context):
cr.execute('SELECT category_id, COUNT(*) \
FROM ir_module_module \
WHERE category_id IN %(ids)s \
@@ -53,12 +52,12 @@ class module_category(osv.osv):
result = dict(cr.fetchall())
for id in ids:
cr.execute('select id from ir_module_category where parent_id=%s', (id,))
- childs = [c for c, in cr.fetchall()]
- result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0))
+ result[id] = sum([result.get(c, 0) for (c,) in cr.fetchall()],
+ result.get(id, 0))
return result
_columns = {
- 'name': fields.char("Name", size=128, required=True),
+ 'name': fields.char("Name", size=128, required=True, select=True),
'parent_id': fields.many2one('ir.module.category', 'Parent Category', select=True),
'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Categories'),
'module_nr': fields.function(_module_nbr, method=True, string='Number of Modules', type='integer')
@@ -71,24 +70,25 @@ class module(osv.osv):
_description = "Module"
__logger = logging.getLogger('base.' + _name)
- def get_module_info(self, name):
+ @classmethod
+ def get_module_info(cls, name):
info = {}
try:
info = addons.load_information_from_description_file(name)
if 'version' in info:
info['version'] = release.major_version + '.' + info['version']
except Exception:
- self.__logger.debug('Error when trying to fetch informations for '
+ cls.__logger.debug('Error when trying to fetch informations for '
'module %s', name, exc_info=True)
return info
- def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context={}):
+ def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = dict.fromkeys(ids, '')
for m in self.browse(cr, uid, ids):
res[m.id] = self.get_module_info(m.name).get('version', '')
return res
- def _get_views(self, cr, uid, ids, field_name=None, arg=None, context={}):
+ def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = {}
model_data_obj = self.pool.get('ir.model.data')
view_obj = self.pool.get('ir.ui.view')
@@ -97,39 +97,46 @@ class module(osv.osv):
mlist = self.browse(cr, uid, ids, context=context)
mnames = {}
for m in mlist:
- mnames[m.name] = m.id
+ # skip uninstalled modules below,
+ # no data to find anyway
+ if m.state in ('installed', 'to upgrade', 'to remove'):
+ mnames[m.name] = m.id
res[m.id] = {
- 'menus_by_module':'',
- 'reports_by_module':'',
- 'views_by_module': ''
+ 'menus_by_module':[],
+ 'reports_by_module':[],
+ 'views_by_module': []
}
+
+ if not mnames:
+ return res
+
view_id = model_data_obj.search(cr,uid,[('module','in', mnames.keys()),
('model','in',('ir.ui.view','ir.actions.report.xml','ir.ui.menu'))])
for data_id in model_data_obj.browse(cr,uid,view_id,context):
# We use try except, because views or menus may not exist
try:
- key = data_id['model']
+ key = data_id.model
+ res_mod_dic = res[mnames[data_id.module]]
if key=='ir.ui.view':
- try:
- v = view_obj.browse(cr,uid,data_id.res_id)
- aa = v.inherit_id and '* INHERIT ' or ''
- res[mnames[data_id.module]]['views_by_module'] += aa + v.name + ' ('+v.type+')\n'
- except Exception:
- self.__logger.debug(
- 'Unknown error while browsing ir.ui.view[%s]',
- data_id.res_id, exc_info=True)
+ v = view_obj.browse(cr,uid,data_id.res_id)
+ aa = v.inherit_id and '* INHERIT ' or ''
+ res_mod_dic['views_by_module'].append(aa + v.name + '('+v.type+')')
elif key=='ir.actions.report.xml':
- res[mnames[data_id.module]]['reports_by_module'] += report_obj.browse(cr,uid,data_id.res_id).name + '\n'
+ res_mod_dic['reports_by_module'].append(report_obj.browse(cr,uid,data_id.res_id).name)
elif key=='ir.ui.menu':
- try:
- m = menu_obj.browse(cr,uid,data_id.res_id)
- res[mnames[data_id.module]]['menus_by_module'] += m.complete_name + '\n'
- except Exception:
- self.__logger.debug(
- 'Unknown error while browsing ir.ui.menu[%s]',
- data_id.res_id, exc_info=True)
- except KeyError:
+ res_mod_dic['menus_by_module'].append(menu_obj.browse(cr,uid,data_id.res_id).complete_name)
+ except KeyError, e:
+ self.__logger.warning(
+ 'Data not found for reference %s[%s:%s.%s]', data_id.model,
+ data_id.res_id, data_id.model, data_id.name, exc_info=True)
pass
+ except Exception, e:
+ self.__logger.warning('Unknown error while browsing %s[%s]',
+ data_id.model, data_id.res_id, exc_info=True)
+ pass
+ for key, value in res.iteritems():
+ for k, v in res[key].iteritems() :
+ res[key][k] = "\n".join(sorted(v))
return res
_columns = {
@@ -180,16 +187,21 @@ class module(osv.osv):
}
_defaults = {
- 'state': lambda *a: 'uninstalled',
- 'demo': lambda *a: False,
- 'license': lambda *a: 'AGPL-3',
+ 'state': 'uninstalled',
+ 'demo': False,
+ 'license': 'AGPL-3',
'web': False,
}
_order = 'name'
+ def _name_uniq_msg(self, cr, uid, ids, context=None):
+ return _('The name of the module must be unique !')
+ def _certificate_uniq_msg(self, cr, uid, ids, context=None):
+ return _('The certificate ID of the module must be unique !')
+
_sql_constraints = [
- ('name_uniq', 'unique (name)', 'The name of the module must be unique !'),
- ('certificate_uniq', 'unique (certificate)', 'The certificate ID of the module must be unique !')
+ ('name_uniq', 'UNIQUE (name)',_name_uniq_msg ),
+ ('certificate_uniq', 'UNIQUE (certificate)',_certificate_uniq_msg )
]
def unlink(self, cr, uid, ids, context=None):
@@ -231,6 +243,19 @@ class module(osv.osv):
if tools.find_in_path(binary) is None:
raise Exception('Unable to find %r in path' % (binary,))
+ @classmethod
+ def check_external_dependencies(cls, module_name, newstate='to install'):
+ terp = cls.get_module_info(module_name)
+ try:
+ cls._check_external_dependencies(terp)
+ except Exception, e:
+ if newstate == 'to install':
+ msg = _('Unable to install module "%s" because an external dependency is not met: %s')
+ elif newstate == 'to upgrade':
+ msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s')
+ else:
+ msg = _('Unable to process module "%s" because an external dependency is not met: %s')
+ raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
if level<1:
@@ -248,17 +273,7 @@ class module(osv.osv):
od = self.browse(cr, uid, ids2)[0]
mdemo = od.demo or mdemo
- terp = self.get_module_info(module.name)
- try:
- self._check_external_dependencies(terp)
- except Exception, e:
- if newstate == 'to install':
- msg = _('Unable to install module "%s" because an external dependency is not met: %s')
- elif newstate == 'to upgrade':
- msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s')
- else:
- msg = _('Unable to process module "%s" because an external dependency is not met: %s')
- raise orm.except_orm(_('Error'), msg % (module.name, e.args[0]))
+ self.check_external_dependencies(module.name, newstate)
if not module.dependencies_id:
mdemo = module.demo
if module.state in states_to_update:
@@ -266,14 +281,14 @@ class module(osv.osv):
demo = demo or mdemo
return demo
- def button_install(self, cr, uid, ids, context={}):
+ def button_install(self, cr, uid, ids, context=None):
return self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
- def button_install_cancel(self, cr, uid, ids, context={}):
+ def button_install_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
return True
- def button_uninstall(self, cr, uid, ids, context={}):
+ def button_uninstall(self, cr, uid, ids, context=None):
for module in self.browse(cr, uid, ids):
cr.execute('''select m.state,m.name
from
@@ -289,7 +304,7 @@ class module(osv.osv):
self.write(cr, uid, ids, {'state': 'to remove'})
return True
- def button_uninstall_cancel(self, cr, uid, ids, context={}):
+ def button_uninstall_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'installed'})
return True
@@ -305,6 +320,7 @@ class module(osv.osv):
if mod.state not in ('installed','to upgrade'):
raise orm.except_orm(_('Error'),
_("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
+ self.check_external_dependencies(mod.name, 'to upgrade')
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
for dep in depobj.browse(cr, uid, iids, context=context):
if dep.module_id.state=='installed' and dep.module_id not in todo:
@@ -325,7 +341,7 @@ class module(osv.osv):
self.button_install(cr, uid, to_install, context=context)
return True
- def button_upgrade_cancel(self, cr, uid, ids, context={}):
+ def button_upgrade_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'installed'})
return True
def button_update_translations(self, cr, uid, ids, context=None):
@@ -341,8 +357,8 @@ class module(osv.osv):
'maintainer': terp.get('maintainer', False),
'contributors': ', '.join(terp.get('contributors', [])) or False,
'website': terp.get('website', ''),
- 'license': terp.get('license', 'GPL-2'),
- 'certificate': terp.get('certificate') or None,
+ 'license': terp.get('license', 'AGPL-3'),
+ 'certificate': terp.get('certificate') or False,
'web': terp.get('web') or False,
}
@@ -350,34 +366,40 @@ class module(osv.osv):
def update_list(self, cr, uid, context={}):
res = [0, 0] # [update, add]
- # iterate through installed modules and mark them as being so
+ known_mods = self.browse(cr, uid, self.search(cr, uid, []))
+ known_mods_names = dict([(m.name, m) for m in known_mods])
+
+ # iterate through detected modules and update/create them in db
for mod_name in addons.get_modules():
- ids = self.search(cr, uid, [('name','=',mod_name)])
+ mod = known_mods_names.get(mod_name)
terp = self.get_module_info(mod_name)
values = self.get_values_from_terp(terp)
- if ids:
- id = ids[0]
- mod = self.browse(cr, uid, id)
+ if mod:
+ updated_values = {}
+ for key in values:
+ old = getattr(mod, key)
+ updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
+ if not old == updated:
+ updated_values[key] = values[key]
if terp.get('installable', True) and mod.state == 'uninstallable':
- self.write(cr, uid, id, {'state': 'uninstalled'})
+ updated_values['state'] = 'uninstalled'
if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''):
- self.write(cr, uid, id, {'url': ''})
res[0] += 1
- self.write(cr, uid, id, values)
- cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (id,))
+ if updated_values:
+ self.write(cr, uid, mod.id, updated_values)
else:
mod_path = addons.get_module_path(mod_name)
if not mod_path:
continue
if not terp or not terp.get('installable', True):
continue
-
- ids = self.search(cr, uid, [('name','=',mod_name)])
id = self.create(cr, uid, dict(name=mod_name, state='uninstalled', **values))
+ mod = self.browse(cr, uid, id)
res[1] += 1
- self._update_dependencies(cr, uid, id, terp.get('depends', []))
- self._update_category(cr, uid, id, terp.get('category', 'Uncategorized'))
+
+ self._update_dependencies(cr, uid, mod, terp.get('depends', []))
+ self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
return res
@@ -409,40 +431,52 @@ class module(osv.osv):
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
cr.execute('DELETE FROM ir_module_module_dependency ' \
'WHERE module_id = %s', (mod.id,))
- self._update_dependencies(cr, uid, mod.id, terp.get('depends',
+ self._update_dependencies(cr, uid, mod, terp.get('depends',
[]))
- self._update_category(cr, uid, mod.id, terp.get('category',
+ self._update_category(cr, uid, mod, terp.get('category',
'Uncategorized'))
# Import module
zimp = zipimport.zipimporter(fname)
zimp.load_module(mod.name)
return res
- def _update_dependencies(self, cr, uid, id, depends=[]):
- for d in depends:
- cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (id, d))
+ def _update_dependencies(self, cr, uid, mod_browse, depends=None):
+ if depends is None:
+ depends = []
+ existing = set(x.name for x in mod_browse.dependencies_id)
+ needed = set(depends)
+ for dep in (needed - existing):
+ cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep))
+ for dep in (existing - needed):
+ cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep))
+
+ def _update_category(self, cr, uid, mod_browse, category='Uncategorized'):
+ current_category = mod_browse.category_id
+ current_category_path = []
+ while current_category:
+ current_category_path.insert(0, current_category.name)
+ current_category = current_category.parent_id
- def _update_category(self, cr, uid, id, category='Uncategorized'):
categs = category.split('/')
- p_id = None
- while categs:
- if p_id is not None:
- cr.execute('select id from ir_module_category where name=%s and parent_id=%s', (categs[0], p_id))
- else:
- cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],))
- c_id = cr.fetchone()
- if not c_id:
- cr.execute('select nextval(\'ir_module_category_id_seq\')')
- c_id = cr.fetchone()[0]
- cr.execute('insert into ir_module_category (id, name, parent_id) values (%s, %s, %s)', (c_id, categs[0], p_id))
- else:
- c_id = c_id[0]
- p_id = c_id
- categs = categs[1:]
- self.write(cr, uid, [id], {'category_id': p_id})
+ if categs != current_category_path:
+ p_id = None
+ while categs:
+ if p_id is not None:
+ cr.execute('SELECT id FROM ir_module_category WHERE name=%s AND parent_id=%s', (categs[0], p_id))
+ else:
+ cr.execute('SELECT id FROM ir_module_category WHERE name=%s AND parent_id is NULL', (categs[0],))
+ c_id = cr.fetchone()
+ if not c_id:
+ cr.execute('INSERT INTO ir_module_category (name, parent_id) VALUES (%s, %s) RETURNING id', (categs[0], p_id))
+ c_id = cr.fetchone()[0]
+ else:
+ c_id = c_id[0]
+ p_id = c_id
+ categs = categs[1:]
+ self.write(cr, uid, [mod_browse.id], {'category_id': p_id})
- def update_translations(self, cr, uid, ids, filter_lang=None, context={}):
- logger = netsvc.Logger()
+ def update_translations(self, cr, uid, ids, filter_lang=None, context=None):
+ logger = logging.getLogger('i18n')
if not filter_lang:
pool = pooler.get_pool(cr.dbname)
lang_obj = pool.get('res.lang')
@@ -459,16 +493,19 @@ class module(osv.osv):
# unable to find the module. we skip
continue
for lang in filter_lang:
- if len(lang) > 5:
- raise osv.except_osv(_('Error'), _('You Can Not Load Translation For language Due To Invalid Language/Country Code'))
iso_lang = tools.get_iso_codes(lang)
- f = os.path.join(modpath, 'i18n', iso_lang + '.po')
- if not os.path.exists(f) and iso_lang.find('_') != -1:
- f = os.path.join(modpath, 'i18n', iso_lang.split('_')[0] + '.po')
+ f = addons.get_module_resource(mod.name, 'i18n', iso_lang + '.po')
+ # Implementation notice: we must first search for the full name of
+ # the language derivative, like "en_UK", and then the generic,
+ # like "en".
+ if (not f) and '_' in iso_lang:
+ f = addons.get_module_resource(mod.name, 'i18n', iso_lang.split('_')[0] + '.po')
iso_lang = iso_lang.split('_')[0]
- if os.path.exists(f):
- logger.notifyChannel("i18n", netsvc.LOG_INFO, 'module %s: loading translation file for language %s' % (mod.name, iso_lang))
+ if f:
+ logger.info('module %s: loading translation file for language %s', mod.name, iso_lang)
tools.trans_load(cr.dbname, f, lang, verbose=False, context=context)
+ elif iso_lang != 'en':
+ logger.warning('module %s: no translation for language %s', mod.name, iso_lang)
def check(self, cr, uid, ids, context=None):
logger = logging.getLogger('init')
@@ -537,7 +574,7 @@ class module_dependency(osv.osv):
_name = "ir.module.module.dependency"
_description = "Module dependency"
- def _state(self, cr, uid, ids, name, args, context={}):
+ def _state(self, cr, uid, ids, name, args, context=None):
result = {}
mod_obj = self.pool.get('ir.module.module')
for md in self.browse(cr, uid, ids):
diff --git a/bin/addons/base/module/module_view.xml b/bin/addons/base/module/module_view.xml
index 7a303b9a9ae..2d391cdd8e6 100644
--- a/bin/addons/base/module/module_view.xml
+++ b/bin/addons/base/module/module_view.xml
@@ -68,7 +68,6 @@
form
tree,form
[('category_id','=',active_id)]
-
@@ -169,7 +168,8 @@
form
tree,form
-
+
+ List all certified modules available to configure your OpenERP. Modules that are installed are flagged as such. You can search for a specific module using the name or the description of the module. You do not need to install modules one by one, you can install many at the same time by clicking on the schedule button in this list. Then, apply the schedule upgrade from Action menu once for all the ones you have scheduled for installation.
diff --git a/bin/addons/base/module/wizard/base_import_language_view.xml b/bin/addons/base/module/wizard/base_import_language_view.xml
index c7cdac663b5..d313afc4080 100644
--- a/bin/addons/base/module/wizard/base_import_language_view.xml
+++ b/bin/addons/base/module/wizard/base_import_language_view.xml
@@ -22,6 +22,8 @@
+
+
diff --git a/bin/addons/base/module/wizard/base_language_install.py b/bin/addons/base/module/wizard/base_language_install.py
index 9cdae41ad2b..f7b469f85ed 100644
--- a/bin/addons/base/module/wizard/base_language_install.py
+++ b/bin/addons/base/module/wizard/base_language_install.py
@@ -38,7 +38,9 @@ class base_language_install(osv.osv_memory):
'state': 'init',
'overwrite': False
}
- def lang_install(self, cr, uid, ids, context):
+ def lang_install(self, cr, uid, ids, context=None):
+ if context is None:
+ context = {}
language_obj = self.browse(cr, uid, ids)[0]
lang = language_obj.lang
if lang:
diff --git a/bin/addons/base/module/wizard/base_module_import.py b/bin/addons/base/module/wizard/base_module_import.py
index 68822690f45..05c3b18198d 100644
--- a/bin/addons/base/module/wizard/base_module_import.py
+++ b/bin/addons/base/module/wizard/base_module_import.py
@@ -56,7 +56,7 @@ class base_module_import(osv.osv_memory):
fname = fdata.namelist()[0]
module_name = os.path.split(fname)[0]
- ad = tools.config['addons_path']
+ ad = tools.config['addons_path'].split(",")[-1]
fname = os.path.join(ad, module_name+'.zip')
try:
diff --git a/bin/addons/base/module/wizard/base_module_import_view.xml b/bin/addons/base/module/wizard/base_module_import_view.xml
index b04bc050e97..8e43789b057 100644
--- a/bin/addons/base/module/wizard/base_module_import_view.xml
+++ b/bin/addons/base/module/wizard/base_module_import_view.xml
@@ -9,22 +9,22 @@