diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py
index ec45981081b..01441910488 100644
--- a/bin/addons/__init__.py
+++ b/bin/addons/__init__.py
@@ -173,7 +173,10 @@ def create_graph(module_list, force=None):
for module in module_list:
if module[-4:]=='.zip':
module = module[:-4]
- mod_path = get_module_path(module)
+ try:
+ mod_path = get_module_path(module)
+ except IOError:
+ continue
terp_file = get_module_resource(module, '__terp__.py')
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path):
try:
@@ -235,7 +238,11 @@ def load_module_graph(cr, graph, status=None, **kwargs):
sys.stdout.flush()
pool = pooler.get_pool(cr.dbname)
modules = pool.instanciate(m, cr)
- cr.execute('select state, demo from ir_module_module where name=%s', (m,))
+
+ cr.execute('select id from ir_module_module where name=%s', (m,))
+ mid = int(cr.rowcount and cr.fetchone()[0] or 0)
+
+ cr.execute('select state, demo from ir_module_module where id=%d', (mid,))
(package_state, package_demo) = (cr.rowcount and cr.fetchone()) or ('uninstalled', False)
idref = {}
status['progress'] = (float(statusi)+0.4)/len(graph)
@@ -267,31 +274,23 @@ def load_module_graph(cr, graph, status=None, **kwargs):
tools.convert_csv_import(cr, m, os.path.basename(xml), tools.file_open(opj(m, xml)).read(), idref, noupdate=True)
else:
tools.convert_xml_import(cr, m, tools.file_open(opj(m, xml)), idref, noupdate=True, **kwargs)
- cr.execute('update ir_module_module set demo=%s where name=%s', (True, package.name))
+ cr.execute('update ir_module_module set demo=%s where id=%d', (True, mid))
package_todo.append(package.name)
- cr.execute("update ir_module_module set state='installed' where state in ('to upgrade', 'to install') and name=%s", (package.name,))
-
- # check if all model of the module have at least a access rule.
- # TODO: improve this query which is very slow !!!
- cr.execute(""" SELECT name
- FROM ir_model m
- WHERE EXISTS (SELECT 1
- FROM ir_model_data
- WHERE module = %s
- AND model = m.name
- )
- AND NOT EXISTS (SELECT 1
- FROM ir_model_access
- WHERE model_id = m.id
- )
- """, (m,))
-
- for (model,) in cr.fetchall():
- logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon:%s:object %s has no access rules!' % (m,model,))
+ cr.execute("update ir_module_module set state='installed' where state in ('to upgrade', 'to install') and id=%d", (mid,))
cr.commit()
+
+ # Update translations for all installed languages
+ modobj = pool.get('ir.module.module')
+ modobj.update_translations(cr, 1, [mid], None)
+
+ cr.commit()
statusi+=1
+ cr.execute("""select model,name from ir_model where id not in (select model_id from ir_model_access)""")
+ for (model,name) in cr.fetchall():
+ logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon:object %s (%s) has no access rules!' % (model,name))
+
pool = pooler.get_pool(cr.dbname)
cr.execute('select * from ir_model where state=%s', ('manual',))
for model in cr.dictfetchall():
diff --git a/bin/addons/base/__terp__.py b/bin/addons/base/__terp__.py
index e810228ac5f..5ba01da68b6 100644
--- a/bin/addons/base/__terp__.py
+++ b/bin/addons/base/__terp__.py
@@ -2,6 +2,7 @@
##############################################################################
#
# Copyright (c) 2004-2008 Tiny SPRL (http://tiny.be) All Rights Reserved.
+# Copyright (c) 2008 Camptocamp SA
#
# $Id$
#
@@ -37,6 +38,8 @@
"init_xml" : [
"base_data.xml",
"base_menu.xml",
+ "base_security.xml",
+ "res/res_security.xml",
],
"demo_xml" : [
"base_demo.xml",
@@ -44,6 +47,7 @@
"res/partner/crm_demo.xml",
],
"update_xml" : [
+ "ir.model.access.csv",
"base_update.xml",
"ir/wizard/wizard_menu_view.xml",
"ir/ir.xml",
@@ -68,6 +72,3 @@
"active": True,
"installable": True,
}
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/bin/addons/base/base.sql b/bin/addons/base/base.sql
index 044a2d8ebb9..02f6a6836dd 100644
--- a/bin/addons/base/base.sql
+++ b/bin/addons/base/base.sql
@@ -148,9 +148,6 @@ CREATE TABLE res_users (
);
alter table res_users add constraint res_users_login_uniq unique (login);
-insert into res_users (id,login,password,name,action_id,active) values (1,'root',NULL,'Root',NULL,False);
-select setval('res_users_id_seq', 2);
-
CREATE TABLE res_groups (
id serial NOT NULL,
name varchar(32) NOT NULL,
@@ -328,4 +325,14 @@ CREATE TABLE ir_model_data (
res_id integer, primary key(id)
);
+---------------------------------
+-- Users
+---------------------------------
+insert into res_users (id,login,password,name,action_id,active) values (1,'admin',NULL,'Administrator',NULL,True);
+insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_root','base','res.users',True,1);
+
+-- Compatibility purpose, to remove V6.0
+insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_admin','base','res.users',True,1);
+
+select setval('res_users_id_seq', 2);
diff --git a/bin/addons/base/base_data.xml b/bin/addons/base/base_data.xml
index 80bc8953cb5..7ac9b4e0b49 100644
--- a/bin/addons/base/base_data.xml
+++ b/bin/addons/base/base_data.xml
@@ -23,17 +23,6 @@
-
- Administration/Administrator
-
-
-
- Extended View
-
-
- No One
-
-
en_US
English
@@ -1309,7 +1298,7 @@
4
- 1.0
+ 3132.9
@@ -1498,17 +1487,5 @@
Tiny sprl
-
-
- admin
- admin
- Administrator
- Administrator
-
-
-
-
-
-
diff --git a/bin/addons/base/base_demo.xml b/bin/addons/base/base_demo.xml
index fd1cf7c89b0..7c42d89b7f5 100644
--- a/bin/addons/base/base_demo.xml
+++ b/bin/addons/base/base_demo.xml
@@ -10,7 +10,7 @@
+
-
-
\ No newline at end of file
+
diff --git a/bin/addons/base/base_menu.xml b/bin/addons/base/base_menu.xml
index 41ca0d51f6c..24d10e32b7f 100644
--- a/bin/addons/base/base_menu.xml
+++ b/bin/addons/base/base_menu.xml
@@ -1,13 +1,13 @@
-
+
+
+
-
-
-
\ No newline at end of file
+
diff --git a/bin/addons/base/base_security.xml b/bin/addons/base/base_security.xml
index bf529a157d4..7e498a60256 100644
--- a/bin/addons/base/base_security.xml
+++ b/bin/addons/base/base_security.xml
@@ -1,662 +1,160 @@
-
+
-
-
-
-
-
-
- Act window
-
-
-
-
-
-
-
- Act window
-
-
-
-
-
-
-
-
-
- Actions
-
-
-
-
-
-
-
- Actions
-
-
-
-
-
-
-
-
-
- Actions Report Custom
-
-
-
-
-
-
-
- Actions Report Custom
-
-
-
-
-
-
-
-
-
- Actions Report XML
-
-
-
-
-
-
-
- Actions Report XML
-
-
-
-
-
-
-
-
-
- Actions Act Window view
-
-
-
-
-
-
-
- Actions Act Window view
-
-
-
-
-
-
-
-
-
- Actions Wizard
-
-
-
-
-
-
-
- Actions Wizard
-
-
-
-
-
-
-
-
-
- Actions URL
-
-
-
-
-
-
-
- Actions URL
-
-
-
-
-
-
-
-
-
- Groups
-
-
-
-
-
-
-
- Groups
-
-
-
-
-
-
-
-
-
- Roles
-
-
-
-
-
-
-
- Roles
-
-
-
-
-
-
-
-
-
- Lang
-
-
-
-
-
-
-
- Lang
-
-
-
-
-
-
-
-
-
- Country
-
-
-
-
-
-
-
- Country
-
-
-
-
-
-
-
-
-
- Country State
-
-
-
-
-
-
-
- Country State
-
-
-
-
-
-
-
-
-
- Currency
-
-
-
-
-
-
-
- Currency
-
-
-
-
-
-
-
-
-
- Currency Rate
-
-
-
-
-
-
-
- Currency Rate
-
-
-
-
-
-
-
-
-
- Company
-
-
-
-
-
-
-
- Company
-
-
-
-
-
-
-
-
-
- Users
-
-
-
-
-
-
-
- Users
-
-
-
-
-
-
-
-
-
- Cron
-
-
-
-
-
-
-
- Cron
-
-
-
-
-
-
-
-
-
- Model
-
-
-
-
-
-
-
- Model
-
-
-
-
-
-
-
-
-
- Model Fields
-
-
-
-
-
-
-
- Model Fields
-
-
-
-
-
-
-
-
-
- Model Access
-
-
-
-
-
-
-
- Model Access
-
-
-
-
-
-
-
-
-
- Model Data
-
-
-
-
-
-
-
- Model Data
-
-
-
-
-
-
-
-
-
- Report Custom
-
-
-
-
-
-
-
- Report Custom
-
-
-
-
-
-
-
-
-
- Report Custom Fields
-
-
-
-
-
-
-
- Report Custom Fields
-
-
-
-
-
-
-
-
-
- Rule group
-
-
-
-
-
-
-
- Rule group
-
-
-
-
-
-
-
-
-
- Rule
-
-
-
-
-
-
-
- Rule
-
-
-
-
-
-
-
-
-
- Sequence Type
-
-
-
-
-
-
-
- Sequence Type
-
-
-
-
-
-
-
-
-
- Sequence
-
-
-
-
-
-
-
- Sequence
-
-
-
-
-
-
-
-
-
- View
-
-
-
-
-
-
-
- View
-
-
-
-
-
-
-
-
-
- Workflow
-
-
-
-
-
-
-
- Workflow
-
-
-
-
-
-
-
-
-
- Workflow Activity
-
-
-
-
-
-
-
- Workflow Activity
-
-
-
-
-
-
-
-
-
- Workflow Transition
-
-
-
-
-
-
-
- Workflow Transition
-
-
-
-
-
-
-
-
-
- Module Repository
-
-
-
-
-
-
-
- Module Repository
-
-
-
-
-
-
-
-
-
- Module Category
-
-
-
-
-
-
-
- Module Category
-
-
-
-
-
-
-
-
-
- Module
-
-
-
-
-
-
-
- Module
-
-
-
-
-
-
-
-
-
- Module Dependency
-
-
-
-
-
-
-
- Module Dependency
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+ Administrator / Configuration
+
+
+
+ Administrator / Access Rights
+
+
+
+ Employee
+
+
+
+ Account Manager
+
+
+
+ Useability / Extended View
+
+
+
+ Useability / No One
+
+
+
+
+ Administrator
+
+
+
+
+
+
+
+
+
+
+ ir.actions.actions Employee
+
+
+
+
+
+
+
+
+
+ ir.actions.act_window Employee
+
+
+
+
+
+
+
+
+
+ ir.actions.act_window_close System
+
+
+
+
+
+
+
+
+
+ ir.actions.act_window_close System
+
+
+
+
+
+
+
+
+
+ ir.actions.act_window_close System
+
+
+
+
+
+
+
+
+
+ workflow group_system
+
+
+
+
+
+
+
+
+
+ workflow_activity group_system
+
+
+
+
+
+
+
+
+
+ workflow_instance group_system
+
+
+
+
+
+
+
+
+
+ workflow_transition group_system
+
+
+
+
+
+
+
+
+
+ workflow_triggers group_system
+
+
+
+
+
+
+
+
+
+ workflow_workitem group_system
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/addons/base/base_update.xml b/bin/addons/base/base_update.xml
index fbc24121f9c..285e7936bf7 100644
--- a/bin/addons/base/base_update.xml
+++ b/bin/addons/base/base_update.xml
@@ -6,26 +6,7 @@
Languages
======================
-->
-
- Languages
- res.lang
- form
-
-
-
-
-
- Languages
- ir.actions.act_window
- res.lang
- form
-
-
+
@@ -97,7 +97,8 @@
{'active_test': False}
-
+
+
@@ -112,7 +113,7 @@
-
+
Sequence Types
ir.actions.act_window
@@ -157,7 +158,8 @@
form
-
+
+
ir.actions.report.custom
@@ -209,6 +211,7 @@
+
@@ -222,6 +225,8 @@
+
+
@@ -608,8 +613,8 @@
+
+ -->
@@ -634,21 +639,32 @@
@@ -657,7 +673,19 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -686,22 +714,33 @@
form
@@ -727,7 +766,7 @@
-
+
Fields
@@ -776,13 +815,21 @@
form
-
-
+
+
+ Untranslated terms
+ ir.translation
+ form
+
+ [('value','=','')]
+
+
+
+ =============================================================
+ Menu Edition
+ =============================================================
+ -->
ir.ui.view_sc
@@ -831,8 +878,8 @@
-
-
+
+
@@ -845,10 +892,10 @@
+ =============================================================
+ Cron Jobs
+ =============================================================
+ -->
ir.cron.tree
ir.cron
@@ -863,7 +910,7 @@
-
+
ir.cron.form
ir.cron
form
@@ -895,7 +942,8 @@
{'active_test': False}
-
+
+
@@ -903,7 +951,7 @@
ir.model.access
tree
-
+
@@ -919,7 +967,7 @@
ir.model.access
form
-
- Access controls
+ Access Controls List
ir.model.access
form
-
+
+
-
+
Record rules
ir.rule.group
@@ -1039,54 +1088,54 @@
form
@@ -1114,19 +1163,28 @@
{'key':'server_action'}
-
-
+
+
ir.model.fields.tree
ir.model.fields
tree
-
-
+
+
+
+
+ Access Controls Grid
+ ir.model.grid
+ form
+ tree,form
+
+
+
diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py
index 78abfa54ed0..fad22ec5ee1 100755
--- a/bin/addons/base/ir/ir_actions.py
+++ b/bin/addons/base/ir/ir_actions.py
@@ -128,8 +128,8 @@ class report_xml(osv.osv):
('raw', 'raw'),
('sxw', 'sxw'),
], string='Type', required=True),
- 'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups')
-
+ 'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
+ 'attachment': fields.char('Save As Attachment Prefix', size=32, help='This is the prefix of the file name the print will be saved as attachement. Keep empty to not save the printed reports')
}
_defaults = {
'type': lambda *a: 'ir.actions.report.xml',
@@ -138,6 +138,7 @@ class report_xml(osv.osv):
'header': lambda *a: True,
'report_sxw_content': lambda *a: False,
'report_type': lambda *a: 'pdf',
+ 'attachment': lambda *a: False,
}
report_xml()
@@ -154,7 +155,7 @@ class act_window(osv.osv):
if (not act.view_ids):
modes = act.view_mode.split(',')
find = False
- if act.view_id.id:
+ if act.view_id:
res[act.id].append((act.view_id.id, act.view_id.type))
for t in modes:
if act.view_id and (t == act.view_id.type) and not find:
@@ -560,7 +561,3 @@ class act_window_close(osv.osv):
'type': lambda *a: 'ir.actions.act_window_close',
}
act_window_close()
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py
index 3a6765e7f6c..5f31b46c73b 100644
--- a/bin/addons/base/ir/ir_model.py
+++ b/bin/addons/base/ir/ir_model.py
@@ -1,7 +1,7 @@
-# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
+# Copyright (c) 2008 Camptocamp SA
#
# $Id$
#
@@ -50,13 +50,13 @@ class ir_model(osv.osv):
'model': fields.char('Object Name', size=64, required=True, search=1),
'info': fields.text('Information'),
'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
- 'state': fields.selection([('manual','Custom Object'),('base','Base Field')],'Manualy Created',readonly=1),
+ 'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Manualy Created',readonly=1),
+ 'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
}
_defaults = {
'model': lambda *a: 'x_',
'state': lambda self,cr,uid,ctx={}: (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':
@@ -97,6 +97,103 @@ class ir_model(osv.osv):
x_custom_model._rec_name = x_custom_model._columns.keys()[0]
ir_model()
+
+class ir_model_grid(osv.osv):
+ _name = 'ir.model.grid'
+ _table = 'ir_model'
+ _description = "Objects Security Grid"
+ _rec_name = 'name'
+ _columns = {
+ 'name': fields.char('Object', size=64),
+ 'model': fields.char('Object Name', size=64),
+ }
+
+ def create(self, cr, uid, vals, context=None):
+ raise osv.except_osv('Error !', 'You cannot add an entry to this view !')
+
+ def unlink(self, *args, **argv):
+ raise osv.except_osv('Error !', 'You cannot add an entry to this view !')
+
+ def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
+ result = super(osv.osv, self).read(cr, uid, ids, fields, context, load)
+ allgr = self.pool.get('res.groups').search(cr, uid, [], context=context)
+ acc_obj = self.pool.get('ir.model.access')
+ for res in result:
+ rules = acc_obj.search(cr, uid, [('model_id', '=', res['id'])])
+ rules_br = acc_obj.browse(cr, uid, rules, context=context)
+ for g in allgr:
+ res['group_'+str(g)] = ''
+ for rule in rules_br:
+ perm_list = []
+ if rule.perm_read:
+ perm_list.append('r')
+ if rule.perm_write:
+ perm_list.append('w')
+ if rule.perm_create:
+ perm_list.append('c')
+ if rule.perm_unlink:
+ perm_list.append('u')
+ perms = ",".join(perm_list)
+ if rule.group_id:
+ res['group_%d'%rule.group_id.id] = perms
+ else:
+ res['group_0'] = perms
+ return result
+
+ #
+ # This function do not write fields from ir.model because
+ # access rights may be different for managing models and
+ # access rights
+ #
+ def write(self, cr, uid, ids, vals, context=None):
+ vals_new = vals.copy()
+ acc_obj = self.pool.get('ir.model.access')
+ for grid in self.browse(cr, uid, ids, context=context):
+ model_id = grid.id
+ perms_rel = ['read','write','create','unlink']
+ for val in vals:
+ if not val[:6]=='group_':
+ continue
+ group_id = int(val[6:]) or False
+ rules = acc_obj.search(cr, uid, [('model_id', '=', model_id),('group_id', '=', group_id)])
+ if not rules:
+ rules = [acc_obj.create(cr, uid, {
+ 'name': grid.name,
+ 'model_id':model_id,
+ 'group_id':group_id
+ }) ]
+ vals = dict(map(lambda x: ('perm_'+x, x[0] in (vals[val] or '')), perms_rel))
+ acc_obj.write(cr, uid, rules, vals, context=context)
+ return True
+
+ def fields_get(self, cr, uid, fields=None, context=None, read_access=True):
+ result = super(ir_model_grid, self).fields_get(cr, uid, fields, context)
+ groups = self.pool.get('res.groups').search(cr, uid, [])
+ groups_br = self.pool.get('res.groups').browse(cr, uid, groups)
+ result['group_0'] = {'string': 'All Users','type': 'char','size': 7}
+ for group in groups_br:
+ result['group_%d'%group.id] = {'string': '%s'%group.name,'type': 'char','size': 7}
+ return result
+
+ def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={}, toolbar=False):
+ result = super(ir_model_grid, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar)
+ groups = self.pool.get('res.groups').search(cr, uid, [])
+ groups_br = self.pool.get('res.groups').browse(cr, uid, groups)
+ cols = ['model', 'name']
+ xml = '''
+<%s editable="bottom">
+
+
+
+ ''' % (view_type,)
+ for group in groups_br:
+ xml += ''' ''' % (group.id, )
+ xml += '''%s>''' % (view_type,)
+ result['arch'] = xml
+ result['fields'] = self.fields_get(cr, uid, cols, context)
+ return result
+ir_model_grid()
+
class ir_model_fields(osv.osv):
_name = 'ir.model.fields'
_description = "Fields"
@@ -176,7 +273,7 @@ class ir_model_access(osv.osv):
res = False
grouparr = group.split('.')
if grouparr:
- cr.execute("select * from res_groups_users_rel where uid=" + str(uid) + " and gid in(select res_id from ir_model_data where module='%s' and name='%s')" % (grouparr[0], grouparr[1]))
+ cr.execute("select * from res_groups_users_rel where uid=" + str(uid) + " and gid in(select res_id from ir_model_data where module=%s and name=%s)", (grouparr[0], grouparr[1],))
r = cr.fetchall()
if not r:
res = False
@@ -186,30 +283,29 @@ class ir_model_access(osv.osv):
res = False
return res
+ def check_groups_by_id(self, cr, uid, group_id):
+ cr.execute("select * from res_groups_users_rel where uid=%i and gid=%i", (uid, group_id,))
+ r = cr.fetchall()
+ if not r:
+ res = False
+ else:
+ res = True
+ return res
+
def check(self, cr, uid, model_name, mode='read',raise_exception=True):
- assert mode in ['read','write','create','unlink'], 'Invalid access mode for security'
- if uid == 1:
+ # Users root have all access (Todo: exclude xml-rpc requests)
+ if uid==1:
return True
-
+
+ assert mode in ['read','write','create','unlink'], 'Invalid access mode'
+
+ # We check if a specific rule exists
cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) '
- 'FROM ir_model_access a '
- 'JOIN ir_model m '
- 'ON (a.model_id=m.id) '
- 'JOIN res_groups_users_rel gu '
- 'ON (gu.gid = a.group_id) '
- 'WHERE m.model = %s AND gu.uid = %s', (model_name, uid,))
+ 'from ir_model_access a join ir_model m on (m.id=a.model_id) '
+ 'join res_groups_users_rel gu on (gu.gid = a.group_id) '
+ 'where m.model=%s and gu.uid=%s', (model_name, uid,))
r = cr.fetchall()
- if r[0][0] == None:
- cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) '
- 'FROM ir_model_access a '
- 'JOIN ir_model m '
- 'ON (a.model_id = m.id) '
- 'WHERE a.group_id IS NULL AND m.model = %s', (model_name,))
- r= cr.fetchall()
- if r[0][0] == None:
- return True # Changed waiting final rules
- #return False # by default, the user had no access
-
+
if not r[0][0]:
if raise_exception:
msgs = {
@@ -218,14 +314,13 @@ class ir_model_access(osv.osv):
'create': _('You can not create this kind of document! (%s)'),
'unlink': _('You can not delete this document! (%s)'),
}
- # due to the assert at the begin of the function, we will never have a KeyError
raise except_orm(_('AccessError'), msgs[mode] % model_name )
return r[0][0]
check = tools.cache()(check)
#
- # Methods to clean the cache on the Check Method.
+ # Check rights on actions
#
def write(self, cr, uid, *args, **argv):
res = super(ir_model_access, self).write(cr, uid, *args, **argv)
@@ -239,6 +334,10 @@ class ir_model_access(osv.osv):
res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
self.check()
return res
+ def read(self, cr, uid, *args, **argv):
+ res = super(ir_model_access, self).read(cr, uid, *args, **argv)
+ self.check()
+ return res
ir_model_access()
class ir_model_data(osv.osv):
@@ -266,7 +365,7 @@ class ir_model_data(osv.osv):
def _get_id(self,cr, uid, module, xml_id):
ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)])
- assert len(ids)==1, '%d reference(s) to %s. You should have only one !' % (len(ids),xml_id)
+ assert len(ids)==1, '%d reference(s) to %s. You should have one and only one !' % (len(ids),xml_id)
return ids[0]
_get_id = tools.cache()(_get_id)
@@ -430,6 +529,38 @@ class ir_model_data(osv.osv):
return True
ir_model_data()
+class ir_model_config(osv.osv):
+ _name = 'ir.model.config'
+ _columns = {
+ 'password': fields.char('Password', size=64),
+ 'password_check': fields.char('confirmation', size=64),
+ }
+
+ def action_cancel(self, cr, uid, ids, context={}):
+ return {
+ 'view_type': 'form',
+ "view_mode": 'form',
+ 'res_model': 'ir.module.module.configuration.wizard',
+ 'type': 'ir.actions.act_window',
+ 'target':'new',
+ }
+
+ def action_update_pw(self, cr, uid, ids, context={}):
+ res = self.read(cr,uid,ids)[0]
+ root = self.pool.get('res.users').browse(cr, uid, [1])[0]
+ self.unlink(cr, uid, [res['id']])
+ if res['password']!=res['password_check']:
+ raise except_orm(_('Error'), _("Password mismatch !"))
+ elif not res['password']:
+ raise except_orm(_('Error'), _("Password empty !"))
+ self.pool.get('res.users').write(cr, uid, [root.id], {'password':res['password']})
+ return {
+ 'view_type': 'form',
+ "view_mode": 'form',
+ 'res_model': 'ir.module.module.configuration.wizard',
+ 'type': 'ir.actions.act_window',
+ 'target':'new',
+ }
+ir_model_config()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py
index ffd431e8a31..ed524d7654e 100644
--- a/bin/addons/base/ir/ir_sequence.py
+++ b/bin/addons/base/ir/ir_sequence.py
@@ -66,8 +66,7 @@ class ir_sequence(osv.osv):
return (s or '') % {'year':time.strftime('%Y'), 'month': time.strftime('%m'), 'day':time.strftime('%d')}
def get_id(self, cr, uid, sequence_id, test='id=%d'):
- cr.execute('lock table ir_sequence')
- cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True', (sequence_id,))
+ cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True FOR UPDATE', (sequence_id,))
res = cr.dictfetchone()
if res:
cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%d and active=True', (res['id'],))
diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py
index 8b90cfee140..35b594812d7 100644
--- a/bin/addons/base/ir/workflow/workflow.py
+++ b/bin/addons/base/ir/workflow/workflow.py
@@ -54,34 +54,45 @@ class workflow(osv.osv):
return super(workflow, self).write(cr, user, ids, vals, context=context)
#
- # scale = [stepx, stepy, posx, posy ]
+ # scale = (vertical-distance, horizontal-distance, min-node-width(optional), min-node-height(optional), margin(default=20))
#
+
def graph_get(self, cr, uid, id, scale, context={}):
nodes= []
+ nodes_name = []
transitions = []
start = []
tres = {}
+ no_ancester = []
workflow = self.browse(cr, uid, id, context)
for a in workflow.activities:
- nodes.append((a.id,a.name))
+ nodes_name.append((a.id,a.name))
+ nodes.append(a.id)
if a.flow_start:
- start.append((a.id,a.name))
+ start.append(a.id)
+ else:
+ if not a.in_transitions:
+ no_ancester.append(a.id)
+
for t in a.out_transitions:
- transitions.append( ((a.id,a.name), (t.act_to.id,t.act_to.name)) )
- tres[t.id] = (a.id,t.act_to.id)
- g = graph(nodes, transitions)
+ transitions.append((a.id, t.act_to.id))
+ tres[t.id] = (a.id, t.act_to.id)
+
+
+ g = graph(nodes, transitions, no_ancester)
g.process(start)
g.scale(*scale)
result = g.result_get()
results = {}
-
-
- for r in result.items():
- r[1]['name'] = r[0][1]
- results[str(r[0][0])] = r[1]
- return {'node': results, 'transition': tres}
+
+ for node in nodes_name:
+ results[str(node[0])] = result[node[0]]
+ results[str(node[0])]['name'] = node[1]
+
+ return {'nodes': results, 'transitions': tres}
+
def create(self, cr, user, vals, context=None):
if not context:
diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py
index ceb454d40fa..2ff220a4c98 100644
--- a/bin/addons/base/module/module.py
+++ b/bin/addons/base/module/module.py
@@ -40,6 +40,8 @@ import zipimport
import wizard
import addons
+import pooler
+import netsvc
ver_regexp = re.compile("^(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
@@ -270,20 +272,22 @@ class module(osv.osv):
def state_update(self, cr, uid, ids, newstate, states_to_update, context={}, level=50):
if level<1:
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
- demo = True
+ demo = False
for module in self.browse(cr, uid, ids):
- mdemo = True
+ mdemo = False
+ go_deeper = False
for dep in module.dependencies_id:
if dep.state == 'unknown':
- raise orm.except_orm(_('Error'), _('Unmet dependency: %s') % (dep.name,))
+ raise orm.except_orm(_('Error'), _('You try to install a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,))
if dep.state != newstate:
+ go_deeper = True
ids2 = self.search(cr, uid, [('name','=',dep.name)])
- mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) and mdemo
- if not module.dependencies_id:
+ mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) or mdemo
+ if not go_deeper:
mdemo = module.demo
if module.state in states_to_update:
self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo})
- demo = demo and mdemo
+ demo = demo or mdemo
return demo
def button_install(self, cr, uid, ids, context={}):
@@ -317,19 +321,8 @@ class module(osv.osv):
def button_upgrade_cancel(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, {'state': 'installed'})
return True
- def button_update_translations(self, cr, uid, ids, context={}):
- cr.execute('select code from res_lang where translatable=TRUE')
- langs = [l[0] for l in cr.fetchall()]
- modules = self.read(cr, uid, ids, ['name'])
- for module in modules:
- files = self.get_module_info(module['name']).get('translations', {})
- for lang in langs:
- if files.has_key(lang):
- filepath = files[lang]
- # if filepath does not contain :// we prepend the path of the module
- if filepath.find('://') == -1:
- filepath = addons.get_module_resource(module['name'], filepath)
- tools.trans_load(filepath, lang)
+ def button_update_translations(self, cr, uid, ids, context=None):
+ self.update_translations(cr, uid, ids)
return True
# update the list of available packages
@@ -525,6 +518,28 @@ class module(osv.osv):
dep_ids = map(lambda x:x[0],cr.fetchall())
if len(dep_ids):
self.action_install(cr,uid,dep_ids,context=context)
+
+ def update_translations(self, cr, uid, ids, filter_lang=None):
+ logger = netsvc.Logger()
+
+ if not filter_lang:
+ pool = pooler.get_pool(cr.dbname)
+ lang_obj=pool.get('res.lang')
+ lang_ids=lang_obj.search(cr, uid, [('translatable', '=', True)])
+ filter_lang= [lang.code for lang in lang_obj.browse(cr, uid, lang_ids)]
+ elif not isinstance(filter_lang, (list, tuple)):
+ filter_lang = [filter_lang]
+
+ for mod in self.browse(cr, uid, ids):
+ if mod.state != 'installed':
+ continue
+
+ for lang in filter_lang:
+ f = os.path.join(tools.config['addons_path'], mod.name, 'i18n', lang + '.po')
+ if os.path.exists(f):
+ logger.notifyChannel("init", netsvc.LOG_INFO, 'addons %s: loading translation file for language %s' % (mod.name, lang))
+ tools.trans_load(cr.dbname, f, lang, verbose=False)
+
module()
class module_dependency(osv.osv):
@@ -566,7 +581,7 @@ class module_config_wizard_step(osv.osv):
'note':fields.text('Text'),
'action_id':fields.many2one('ir.actions.act_window', 'Action', select=True,required=True, ondelete='cascade'),
'sequence':fields.integer('Sequence'),
- 'state':fields.selection([('open', 'Open'),('done', 'Done'),('skip','Skip')], string='State', required=True)
+ 'state':fields.selection([('open', 'Not Started'),('done', 'Done'),('skip','Skipped')], string='State', required=True)
}
_defaults={
'state': lambda *a: 'open',
@@ -641,6 +656,7 @@ class module_configuration(osv.osv_memory):
'state': 'done',
}, context=context)
return{
+ 'view_mode': item.action_id.view_mode,
'view_type': item.action_id.view_type,
'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False,
'res_model': item.action_id.res_model,
@@ -650,8 +666,3 @@ class module_configuration(osv.osv_memory):
return {'type':'ir.actions.act_window_close' }
module_configuration()
-
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/bin/addons/base/module/module_wizard.xml b/bin/addons/base/module/module_wizard.xml
index 536b2f01648..d789902606c 100644
--- a/bin/addons/base/module/module_wizard.xml
+++ b/bin/addons/base/module/module_wizard.xml
@@ -5,99 +5,104 @@
Import module
base.module.import
-
+
- Download module list
+ Update Modules List
module.module.update
-
+
-
+
- Install new language file
+ Reload an Official Translation
module.lang.install
-
+
-
- Export Language
+
+ Export a Translation File
wizard.module.lang.export
form
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Export language
+
+ Export a Translation File
ir.actions.act_window
wizard.module.lang.export
form
form
new
-
+
- Import language
+ Import a Translation File
module.lang.import
-
-
-
- Import language
- module.lang.import
-
-
-
-
+
+
+
Update Translations
wizard.module.update_translations
form
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Update Translations
+
+ Resynchronise Terms
ir.actions.act_window
wizard.module.update_translations
form
form
new
-
+
diff --git a/bin/addons/base/module/wizard/wizard_export_lang.py b/bin/addons/base/module/wizard/wizard_export_lang.py
index dc288707577..abd82cdef68 100644
--- a/bin/addons/base/module/wizard/wizard_export_lang.py
+++ b/bin/addons/base/module/wizard/wizard_export_lang.py
@@ -44,7 +44,7 @@ class wizard_export_lang(osv.osv_memory):
lang_obj=pooler.get_pool(cr.dbname).get('res.lang')
ids=lang_obj.search(cr, uid, ['&', ('active', '=', True), ('translatable', '=', True),])
langs=lang_obj.browse(cr, uid, ids)
- return [('', _('New language'))] + [(lang.code, lang.name) for lang in langs]
+ return [(lang.code, lang.name) for lang in langs]
def act_cancel(self, cr, uid, ids, context=None):
@@ -56,28 +56,31 @@ class wizard_export_lang(osv.osv_memory):
def act_getfile(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids)[0]
- mods = map(lambda m: m.name, this.modules)
+ mods = map(lambda m: m.name, this.modules) or ['all']
mods.sort()
buf=StringIO.StringIO()
tools.trans_export(this.lang, mods, buf, this.format, dbname=cr.dbname)
-
if this.format == 'csv':
this.advice = _("Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it.")
elif this.format == 'po':
- ext = this.lang and '.po' or '.pot'
- this.advice = _("Save this document to a %s file and edit it with a specific software or a text editor. The file encoding is UTF-8." % (ext,))
+ if not this.lang:
+ this.format = 'pot'
+ this.advice = _("Save this document to a %s file and edit it with a specific software or a text editor. The file encoding is UTF-8.") % ('.'+this.format,)
elif this.format == 'tgz':
ext = this.lang and '.po' or '.pot'
- this.advice = _('Save this document to a .tgz file. This archive containt UTF-8 %s files and may be uploaded to launchpad.' % (ext,))
-
+ this.advice = _('Save this document to a .tgz file. This archive containt UTF-8 %s files and may be uploaded to launchpad.') % (ext,)
+
+ this.name = "%s.%s" % (this.lang or _('new'), this.format)
+
out=base64.encodestring(buf.getvalue())
buf.close()
- return self.write(cr, uid, ids, {'state':'get', 'data':out, 'advice':this.advice}, context=context)
+ return self.write(cr, uid, ids, {'state':'get', 'data':out, 'advice':this.advice, 'name':this.name}, context=context)
_name = "wizard.module.lang.export"
_columns = {
- 'lang': fields.selection(_get_languages, 'Language'), # not required: unset = new language
+ 'name': fields.char('Filename', 16, readonly=True),
+ 'lang': fields.selection(_get_languages, 'Language', help='To export a new language, do not select a language.'), # not required: unset = new language
'format': fields.selection( ( ('csv','CSV File'), ('po','PO File'), ('tgz', 'TGZ Archive')), 'File Format', required=True),
'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Modules', domain=[('state','=','installed')]),
'data': fields.binary('File', readonly=True),
@@ -86,7 +89,8 @@ class wizard_export_lang(osv.osv_memory):
('get','get'), # get the file
) ),
}
- _defaults = { 'state': lambda *a: 'choose', }
+ _defaults = { 'state': lambda *a: 'choose',
+ }
wizard_export_lang()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/bin/addons/base/module/wizard/wizard_module_lang_install.py b/bin/addons/base/module/wizard/wizard_module_lang_install.py
index 5be3bc076cd..0b3c9c7542b 100644
--- a/bin/addons/base/module/wizard/wizard_module_lang_install.py
+++ b/bin/addons/base/module/wizard/wizard_module_lang_install.py
@@ -30,6 +30,7 @@
import wizard
import tools
+import pooler
view_form_end = """
@@ -55,9 +56,11 @@ view_form = """
class wizard_lang_install(wizard.interface):
def _lang_install(self, cr, uid, data, context):
lang = data['form']['lang']
- if lang and lang != 'en_US':
- filename = tools.config["root_path"] + "/i18n/" + lang + ".csv"
- tools.trans_load(cr.dbname, filename, lang)
+ if lang:
+ pool = pooler.get_pool(cr.dbname)
+ modobj = cr.pool.get('ir.module.module')
+ mids = modobj.search(cr, uid, [('state', '=', 'installed')])
+ modobj.update_translations(cr, uid, mids, lang)
return {}
def _get_language(sel, cr, uid, context):
diff --git a/bin/addons/base/module/wizard/wizard_module_upgrade.py b/bin/addons/base/module/wizard/wizard_module_upgrade.py
index 55432bd44f9..c126a70b12a 100644
--- a/bin/addons/base/module/wizard/wizard_module_upgrade.py
+++ b/bin/addons/base/module/wizard/wizard_module_upgrade.py
@@ -101,14 +101,12 @@ class wizard_info_get(wizard.interface):
mod_obj.download(cr, uid, ids, context=context)
cr.commit()
db, pool = pooler.restart_pool(cr.dbname, update_module=True)
-
- lang_obj=pool.get('res.lang')
- lang_ids=lang_obj.search(cr, uid, [])
- langs=lang_obj.browse(cr, uid, lang_ids)
- for lang in langs:
- if lang.code and lang.code != 'en_US':
- filename=os.path.join(tools.config["root_path"], "i18n", lang.code + ".csv")
- tools.trans_load(cr.dbname, filename, lang.code)
+
+ # Update translations for all installed languages
+ cr = db.cursor()
+ modobj = pool.get('ir.module.module')
+ mids = modobj.search(cr, uid, [('state', '=', 'installed')])
+ modobj.update_translations(cr, uid, mids, None)
return {}
def _config(self, cr, uid, data, context=None):
diff --git a/bin/addons/base/res/country_view.xml b/bin/addons/base/res/country_view.xml
index 16a394b9e81..435bdef5e1e 100644
--- a/bin/addons/base/res/country_view.xml
+++ b/bin/addons/base/res/country_view.xml
@@ -38,7 +38,7 @@
-
+