bzr revid: hmo@tinyerp.com-20101206121927-hikb1xzabqs46uzh
This commit is contained in:
Harry (OpenERP)
2010-12-06 17:49:27 +05:30
116 changed files with 74632 additions and 43478 deletions
+6 -5
View File
@@ -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)
+3 -2
View File
@@ -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',
],
+1
View File
@@ -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);
+22 -8
View File
@@ -915,7 +915,7 @@
</record>
<record id="uk" model="res.country">
<field name="name">United Kingdom</field>
<field name="code">uk</field>
<field name="code">gb</field>
</record>
<record id="um" model="res.country">
<field name="name">USA Minor Outlying Islands</field>
@@ -989,7 +989,7 @@
<field name="name">Zambia</field>
<field name="code">zm</field>
</record>
<!-- DEPRECATED, News name of Zaire is Democratic Republic of the Congo ! -->
<!-- DEPRECATED, New name of Zaire is Democratic Republic of the Congo ! -->
<record id="zr" model="res.country">
<field name="name">Zaire</field>
<field name="code">zr</field>
@@ -1044,7 +1044,7 @@
</record>
<assert id="main_company" model="res.company">
<test expr="currency_id.code == 'eur'.upper()"/>
<test expr="currency_id.name == 'eur'.upper()"/>
<test expr="name">OpenERP S.A.</test>
</assert>
@@ -1170,7 +1170,7 @@
<field name="currency_id" ref="COP"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<record id="CZK" model="res.currency">
<field name="name">Kč</field>
<field name="code">CZK</field>
@@ -1213,7 +1213,7 @@
<field name="currency_id" ref="HUF"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<record id="IDR" model="res.currency">
<field name="name">Rs</field>
<field name="code">IDR</field>
@@ -1290,7 +1290,7 @@
<field name="currency_id" ref="PLN"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<record id="SEK" model="res.currency">
<field name="name">kr</field>
<field name="code">SEK</field>
@@ -1304,7 +1304,7 @@
<field name="currency_id" ref="SEK"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<record id="GBP" model="res.currency">
<field name="name">GBP</field>
<field name="code">GBP</field>
@@ -1326,7 +1326,7 @@
<field name="rounding">0.01</field>
<field name="accuracy">4</field>
<field name="company_id" ref="main_company"/>
</record>
</record>
<record id="rateARS" model="res.currency.rate">
<field name="rate">5.0881</field>
<field name="currency_id" ref="ARS"/>
@@ -1378,5 +1378,19 @@
<field name="name">Reserve</field>
<field name="code">RSV</field>
</record>
<record id="CRC" model="res.currency">
<field name="name">CRC</field>
<field name="code">CRC</field>
<field name="rounding">0.01</field>
<field name="accuracy">4</field>
<field name="symbol">¢</field>
</record>
<record id="rateCRC" model="res.currency.rate">
<field name="rate">691.3153</field>
<field name="currency_id" ref="CRC"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
</data>
</openerp>
+4 -1
View File
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem icon="terp-administration" id="menu_administration" name="Administration" sequence="50"/>
<menuitem icon="terp-administration" id="menu_administration"
name="Administration" sequence="50"
web_icon="data/administration.png"
web_icon_hover="data/administration-hover.png"/>
<menuitem icon="terp-administration" id="menu_administration_shortcut" parent="menu_administration" name="Custom Shortcuts" sequence="50"/>
<menuitem id="next_id_4" name="Low Level Objects"
+2 -2
View File
@@ -79,7 +79,8 @@
<notebook colspan="4">
<page string="Current Activity">
<field name="company_id" widget="selection" readonly="0"
context="{'user_id': self, 'user_preference': 1}" groups="base.group_multi_company"/>
context="{'user_id': self, 'user_preference': 1}" groups="base.group_multi_company"
on_change="on_change_company_id(company_id)" />
<field name="view" readonly="0"/>
<label string="" colspan="2"/>
<separator string="Default Filters" colspan="4"/>
@@ -118,7 +119,6 @@
<separator string="Contact" colspan="2"/>
<field name="company_id" required="1"
context="{'user_id': self, 'user_preference': 1}"
on_change="on_change_company_id(company_id)"
groups="base.group_multi_company"
/>
<field name="address_id"/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1621 -1345
View File
File diff suppressed because it is too large Load Diff
+4303 -2462
View File
File diff suppressed because it is too large Load Diff
+1647 -1261
View File
File diff suppressed because it is too large Load Diff
+3139 -2076
View File
File diff suppressed because it is too large Load Diff
+4047 -2396
View File
File diff suppressed because it is too large Load Diff
+1640 -1451
View File
File diff suppressed because it is too large Load Diff
+4309 -2921
View File
File diff suppressed because it is too large Load Diff
+1838 -1499
View File
File diff suppressed because it is too large Load Diff
+4059 -2427
View File
File diff suppressed because it is too large Load Diff
+1695 -1588
View File
File diff suppressed because it is too large Load Diff
+4791 -2443
View File
File diff suppressed because it is too large Load Diff
+3956 -1965
View File
File diff suppressed because it is too large Load Diff
+4189 -2433
View File
File diff suppressed because it is too large Load Diff
+1324 -1244
View File
File diff suppressed because it is too large Load Diff
+3333 -2561
View File
File diff suppressed because it is too large Load Diff
+1307 -1218
View File
File diff suppressed because it is too large Load Diff
+3822 -2334
View File
File diff suppressed because it is too large Load Diff
+3891 -2013
View File
File diff suppressed because it is too large Load Diff
+3448 -2336
View File
File diff suppressed because it is too large Load Diff
+2957 -2268
View File
File diff suppressed because it is too large Load Diff
+1310 -1270
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -34,6 +34,7 @@ import ir_exports
import workflow
import ir_rule
import wizard
import osv_memory_autovacuum
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+26
View File
@@ -705,6 +705,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.company</field>
<field name="view_type">form</field>
<field name="help">Create and manage the companies that will be managed by OpenERP from here. Shops or subsidiaries can be created and maintained from here.</field>
</record>
<menuitem action="action_res_company_form" id="menu_action_res_company_form" parent="base.menu_res_company_global"/>
@@ -716,6 +717,7 @@
<field name="view_type">form</field>
<field name="view_id" ref="view_users_tree"/>
<field name="search_view_id" ref="view_users_search"/>
<field name="help">Create and manage users that will connect to the system. Users can be deactivated should there be a period of time during which they will/should not connect to the system. You can assign them groups in order to give them specific access to the applications they need to use in the system.</field>
</record>
<record id="action_res_users_view1" model="ir.actions.act_window.view">
<field eval="10" name="sequence"/>
@@ -757,6 +759,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.groups</field>
<field name="view_type">form</field>
<field name="help">A group is a set of functional areas that will be assigned to the user in order to give them access and rights to specific applications and tasks in the system. You can create custom groups or edit the ones existing by default in order to customize the view of the menu that users will be able to see. Whether they can have a read, write, create and delete access right can be managed from here.</field>
</record>
<menuitem action="action_res_groups" id="menu_action_res_groups" parent="base.menu_users"
groups="base.group_extended"/>
@@ -844,6 +847,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.ui.view</field>
<field name="view_id" ref="view_view_tree"/>
<field name="help">Views allows you to personalize each view of OpenERP. You can add new fields, move fields, rename them or delete the ones that you do not need.</field>
</record>
<menuitem action="action_ui_view" id="menu_action_ui_view" parent="base.next_id_2"/>
@@ -1375,6 +1379,12 @@
<field name="action" colspan="4" />
<field name="icon" on_change="onchange_icon(icon)" colspan="2"/>
<field name="icon_pict" widget="picture" nolabel="1" colspan="2"/>
<group col="4" colspan="8" groups="base.group_extended">
<field name="web_icon" groups="base.group_extended" />
<field name="web_icon_hover" groups="base.group_extended" />
<field name="web_icon_data" widget="image" groups="base.group_extended"/>
<field name="web_icon_hover_data" widget="image" groups="base.group_extended"/>
</group>
</group>
<notebook colspan="4">
<page string="Groups">
@@ -1403,6 +1413,7 @@
<field name="view_type">form</field>
<field name="view_id" ref="edit_menu"/>
<field name="search_view_id" ref="edit_menu_access_search"/>
<field name="help">Manage and customize the items available and displayed in your OpenERP system menu. You can delete an item by clicking on the box at the beginning of each line and then delete it through the button that appeared. Items can be assigned to specific groups in order to make them accessible to some users within the system.</field>
</record>
<menuitem action="grant_menu_access" id="menu_grant_menu_access" parent="base.next_id_2" sequence="1"/>
@@ -1868,5 +1879,20 @@
<menuitem action="act_ir_actions_todo_form" id="menu_ir_actions_todo_form"
parent="next_id_11" groups="base.group_extended" sequence="20"/>
<record model="ir.cron" id="cronjob_osv_memory_autovacuum">
<field name='name'>AutoVacuum osv_memory objects</field>
<field name='interval_number'>30</field>
<field name='interval_type'>minutes</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
<field name="doall" eval="False" />
<field name="model">osv_memory.autovacuum</field>
<field name="function">power_on</field>
<field name="args">()</field>
</record>
</data>
</openerp>
+47 -25
View File
@@ -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 = []
+26 -16
View File
@@ -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 = {
+1 -1
View File
@@ -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'),
}
+1 -1
View File
@@ -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()
+31 -13
View File
@@ -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)
+10 -2
View File
@@ -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:
+1 -1
View File
@@ -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'),
+40 -8
View File
@@ -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()
+53 -66
View File
@@ -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<len(fields):
if fields[pos] in ('report_sxw_content', 'report_rml_content',
'report_sxw', 'report_rml', 'report_sxw_content_data',
'report_rml_content_data'):
del fields[pos]
else:
pos+=1
# FIXME: It might be a good idea to opt-in that kind of stuff
# FIXME: instead of arbitrarily removing random fields
fields = [
field
for field in self.pool.get(model).fields_get_keys(cr, uid)
if field not in EXCLUDED_FIELDS]
try:
datas = self.pool.get(model).read(cr, uid, [id], fields, context)
datas = self.pool.get(model).read(cr, uid, [int(id)], fields, context)
except except_orm, e:
return False
datas= datas and datas[0] or None
datas = datas and datas[0]
if not datas:
#ir_del(cr, uid, x[0])
return False
else:
datas = pickle.loads(str(x[2].encode('utf-8')))
datas = pickle.loads(x[2].encode('utf-8'))
if meta:
meta2 = pickle.loads(x[4])
return (x[0],x[1],datas,meta2)
return (x[0],x[1],datas)
return (x[0], x[1], datas, pickle.loads(x[4]))
return (x[0], x[1], datas)
keys = []
res = filter(bool, map(lambda x: _result_get(x, keys), list(result)))
res = filter(None, map(lambda x: _result_get(x, keys), result))
res2 = res[:]
for r in res:
if type(r[2])==type({}) and 'type' in r[2]:
if r[2]['type'] in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'):
groups = r[2].get('groups_id')
if groups:
cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid))
cnt = cr.fetchone()[0]
if not cnt:
res2.remove(r)
if r[1] == 'Menuitem' and not res2:
raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!')
if isinstance(r[2], dict) and r[2].get('type') in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'):
groups = r[2].get('groups_id')
if groups:
cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid))
cnt = cr.fetchone()[0]
if not cnt:
res2.remove(r)
if r[1] == 'Menuitem' and not res2:
raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!')
return res2
ir_values()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010 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
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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()
+1 -1
View File
@@ -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():
+136 -99
View File
@@ -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):
+2 -2
View File
@@ -68,7 +68,6 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('category_id','=',active_id)]</field>
</record>
<record id="ir_action_module_category" model="ir.values">
<field eval="'tree_but_open'" name="key2"/>
@@ -169,7 +168,8 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain"/>
<field name="search_view_id" ref="view_module_filter"/>
<field name="search_view_id" ref="view_module_filter"/>
<field name="help">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.</field>
</record>
<menuitem action="open_module_tree" id="menu_module_tree" parent="base.menu_management"/>
@@ -22,6 +22,8 @@
<separator orientation="vertical" rowspan="15"/>
<group colspan="4" col="4">
<separator string="Import Translation" colspan="4"/>
<label colspan="4" nolabel="1" string="If you need another language than the official ones available, you can import a language pack from here. Other OpenERP languages than the official ones can be found on launchpad."/>
<newline/>
<field name="name" width="200"/>
<field name="code"/>
<field name="data" colspan="4"/>
@@ -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:
@@ -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:
@@ -9,22 +9,22 @@
<field name="arch" type="xml">
<form string="Import module">
<group col="8">
<group colspan="3">
<group colspan="3" col="1">
<field name="config_logo" widget="image" width="220" height="130" nolabel="1" colspan="1"/>
<newline/>
<label colspan="4" width="220" string="This wizard helps you add a new language to you OpenERP system. After loading a new language it becomes available as default interface language for users and partners."/>
<label colspan="4" width="220"/>
<label colspan="4" width="220" string="Please be patient, this operation may take a few minutes (depending on the number of modules currently installed)..."/>
<label width="220" string="This wizard helps you add a new language to you OpenERP system. After loading a new language it becomes available as default interface language for users and partners."/>
<label width="220"/>
<label width="220" string="Please be patient, this operation may take a few minutes (depending on the number of modules currently installed)..."/>
<field name="state" invisible="1"/>
</group>
<separator orientation="vertical" rowspan="15"/>
<separator orientation="vertical" rowspan="5"/>
<group colspan="4">
<separator string="Module Import" colspan="4"/>
<group states="init" colspan="4" col="4">
<group states="init" col="4">
<label string="Select module package to import (.zip file):" colspan="4"/>
<field name="module_file" colspan="4"/>
</group>
<group states="done" colspan="4" col="4">
<group states="done" col="4">
<label string="Module file successfully imported!" colspan="4"/>
</group>
</group>
@@ -20,6 +20,7 @@
##############################################################################
import pooler
import wizard
from osv import osv, fields
class base_module_upgrade(osv.osv_memory):
@@ -80,7 +81,7 @@ class base_module_upgrade(osv.osv_memory):
res = mod_obj.read(cr, uid, ids, ['name','state'], context)
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
def upgrade_module(self, cr, uid, ids, context):
def upgrade_module(self, cr, uid, ids, context=None):
pool = pooler.get_pool(cr.dbname)
mod_obj = self.pool.get('ir.module.module')
data_obj = self.pool.get('ir.model.data')
@@ -93,7 +94,7 @@ class base_module_upgrade(osv.osv_memory):
if dep_mod.state in ('unknown','uninstalled'):
unmet_packages.append(dep_mod.name)
if len(unmet_packages):
raise osv.except_osv('Unmet dependency !', 'Following modules are uninstalled or unknown. \n\n'+'\n'.join(unmet_packages))
raise osv.except_osv(_('Unmet dependency !'), _('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
mod_obj.download(cr, uid, ids, context=context)
cr.commit()
_, pool = pooler.restart_pool(cr.dbname, update_module=True)
@@ -35,7 +35,7 @@ class base_update_translations(osv.osv_memory):
lang_obj=pooler.get_pool(cr.dbname).get('res.lang')
ids=lang_obj.search(cr, uid, [('code', '=', lang_code)])
if not ids:
raise osv.except_osv(_('No language with code "%s" exists') % lang_code)
raise osv.except_osv(_('Error!'), _('No language with code "%s" exists') % lang_code)
lang = lang_obj.browse(cr, uid, ids[0])
return lang.name
def act_cancel(self, cr, uid, ids, context=None):
@@ -51,8 +51,14 @@ class base_update_translations(osv.osv_memory):
return {'type': 'ir.actions.act_window_close'}
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
res = super(base_update_translations, self).default_get(cr, uid, fields, context=context)
record_id = context and context.get('active_id', False) or False
if context.get('active_model') != "res.lang":
return res
record_id = context.get('active_id', False) or False
if record_id:
lang = self.pool.get('res.lang').browse(cr, uid, record_id).code
res.update(lang=lang)
-1
View File
@@ -23,7 +23,6 @@ import math
from osv import fields,osv
import tools
import ir
import pooler
from tools.translate import _
+3 -1
View File
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem icon="terp-partner" id="menu_base_partner" name="Sales" sequence="0"/>
<menuitem icon="terp-partner" id="menu_base_partner" name="Sales" sequence="0"
web_icon="data/sales.png"
web_icon_hover="data/sales-hover.png"/>
<menuitem id="menu_address_book" name="Address Book" parent="menu_base_partner" sequence="2"/>
+12 -12
View File
@@ -90,8 +90,8 @@ class res_company(osv.osv):
'account_no':fields.char('Account No.', size=64),
}
def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False):
def _search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False, access_rights_uid=None):
if context is None:
context = {}
@@ -106,8 +106,8 @@ class res_company(osv.osv):
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids]))
return cmp_ids
return super(res_company, self).search(cr, uid, args, offset=offset, limit=limit, order=order,
context=context, count=count)
return super(res_company, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
context=context, count=count, access_rights_uid=access_rights_uid)
def _company_default_get(self, cr, uid, object=False, field=False, context=None):
"""
@@ -203,16 +203,16 @@ class res_company(osv.osv):
return """
<header>
<pageTemplate>
<frame id="first" x1="22.0" y1="22.0" width="1080" height="700"/>
<frame id="first" x1="28.0" y1="28.0" width="786" height="525"/>
<pageGraphics>
<fill color="black"/>
<stroke color="black"/>
<setFont name="DejaVu Sans" size="8"/>
<drawString x="25" y="725"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
<drawString x="25" y="555"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
<setFont name="DejaVu Sans Bold" size="10"/>
<drawString x="490" y="725">[[ company.partner_id.name ]]</drawString>
<drawString x="382" y="555">[[ company.partner_id.name ]]</drawString>
<stroke color="#000000"/>
<lines>25 720 1085 720</lines>
<lines>25 550 818 550</lines>
</pageGraphics>
</pageTemplate>
</header>"""
@@ -220,16 +220,16 @@ class res_company(osv.osv):
return """
<header>
<pageTemplate>
<frame id="first" x1="1.3cm" y1="1.5cm" width="18.4cm" height="26.5cm"/>
<frame id="first" x1="28.0" y1="28.0" width="539" height="772"/>
<pageGraphics>
<fill color="black"/>
<stroke color="black"/>
<setFont name="DejaVu Sans" size="8"/>
<drawString x="1.3cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
<drawString x="1.0cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
<setFont name="DejaVu Sans Bold" size="10"/>
<drawString x="9.8cm" y="28.3cm">[[ company.partner_id.name ]]</drawString>
<drawString x="9.3cm" y="28.3cm">[[ company.partner_id.name ]]</drawString>
<stroke color="#000000"/>
<lines>1.3cm 28.1cm 20cm 28.1cm</lines>
<lines>1.0cm 28.1cm 20.1cm 28.1cm</lines>
</pageGraphics>
</pageTemplate>
</header>"""
+17 -39
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from operator import attrgetter
from osv import osv, fields
@@ -37,6 +37,7 @@ class res_config_configurable(osv.osv_memory):
_name = 'res.config'
_inherit = 'ir.wizard.screen'
logger = netsvc.Logger()
__logger = logging.getLogger(_name)
def get_current_progress(self, cr, uid, context=None):
'''Return a description the current progress of configuration:
@@ -63,14 +64,13 @@ class res_config_configurable(osv.osv_memory):
def _next_action(self, cr, uid, context=None):
todos = self.pool.get('ir.actions.todo')
self.logger.notifyChannel('actions', netsvc.LOG_INFO,
'getting next %s' % todos)
self.__logger.info('getting next %s', todos)
active_todos = todos.search(cr, uid, [('state','=','open')],
limit=1)
dont_skip_todo = True
if active_todos:
todo_obj = todos.browse(cr, uid, active_todos[0], context=None)
todo_groups = map(lambda x:x.id, todo_obj.groups_id)
dont_skip_todo = True
if todo_groups:
cr.execute("select 1 from res_groups_users_rel where uid=%s and gid IN %s",(uid, tuple(todo_groups),))
dont_skip_todo = bool(cr.fetchone())
@@ -84,30 +84,20 @@ class res_config_configurable(osv.osv_memory):
def _set_previous_todo(self, cr, uid, state, context=None):
""" lookup the previous (which is still the next at this point)
ir.actions.todo, set it to whatever state was provided.
Raises
`LookupError`: if we couldn't find *any* previous todo
`ValueError`: if no state is provided
anything ir_actions_todo.write can throw
"""
if context is None:
context = {}
# this is ultra brittle, but apart from storing the todo id
# into the res.config view, I'm not sure how to get the
# "previous" todo
previous_todo = self._next_action(cr, uid, context=context)
if not previous_todo:
raise LookupError(_("Couldn't find previous ir.actions.todo"))
if not state:
raise ValueError(_("Can't set an ir.actions.todo's state to an empty value."))
self.__logger.warn(_("Couldn't find previous ir.actions.todo"))
return
previous_todo.write({'state':state})
def _next(self, cr, uid, context=None):
self.logger.notifyChannel('actions', netsvc.LOG_INFO,
'getting next operation')
self.__logger.info('getting next operation')
next = self._next_action(cr, uid)
self.logger.notifyChannel('actions', netsvc.LOG_INFO,
'next action is %s' % next)
self.__logger.info('next action is %s', next)
if next:
action = next.action_id
return {
@@ -118,9 +108,7 @@ class res_config_configurable(osv.osv_memory):
'type': action.type,
'target': action.target,
}
self.logger.notifyChannel(
'actions', netsvc.LOG_INFO,
'all configuration actions have been executed')
self.__logger.info('all configuration actions have been executed')
current_user_menu = self.pool.get('res.users')\
.browse(cr, uid, uid).menu_id
@@ -174,10 +162,7 @@ class res_config_configurable(osv.osv_memory):
an action dictionary -- executes the action provided by calling
``next``.
"""
try:
self._set_previous_todo(cr, uid, state='done', context=context)
except Exception, e:
raise osv.except_osv(_('Error'), e.message)
self._set_previous_todo(cr, uid, state='done', context=context)
next = self.execute(cr, uid, ids, context=None)
if next: return next
return self.next(cr, uid, ids, context=context)
@@ -190,10 +175,7 @@ class res_config_configurable(osv.osv_memory):
an action dictionary -- executes the action provided by calling
``next``.
"""
try:
self._set_previous_todo(cr, uid, state='skip', context=context)
except Exception, e:
raise osv.except_osv(_('Error'), e.message)
self._set_previous_todo(cr, uid, state='skip', context=context)
next = self.cancel(cr, uid, ids, context=None)
if next: return next
return self.next(cr, uid, ids, context=context)
@@ -209,10 +191,7 @@ class res_config_configurable(osv.osv_memory):
an action dictionary -- executes the action provided by calling
``next``.
"""
try:
self._set_previous_todo(cr, uid, state='cancel', context=context)
except Exception, e:
raise osv.except_osv(_('Error'), e.message)
self._set_previous_todo(cr, uid, state='cancel', context=context)
next = self.cancel(cr, uid, ids, context=None)
if next: return next
return self.next(cr, uid, ids, context=context)
@@ -313,6 +292,7 @@ class res_config_installer(osv.osv_memory):
"""
_name = 'res.config.installer'
_inherit = 'res.config'
__logger = logging.getLogger(_name)
_install_if = {}
@@ -408,14 +388,12 @@ class res_config_installer(osv.osv_memory):
modules = self.pool.get('ir.module.module')
to_install = list(self.modules_to_install(
cr, uid, ids, context=context))
self.logger.notifyChannel(
'installer', netsvc.LOG_INFO,
'Selecting addons %s to install'%to_install)
self.__logger.info('Selecting addons %s to install', to_install)
modules.state_update(
cr, uid,
modules.search(cr, uid, [('name','in',to_install)]),
'to install', ['uninstalled'], context=context)
cr.commit() #TOFIX: after remove this statement, installation wizard is fail
cr.commit() #TOFIX: after remove this statement, installation wizard is fail
pooler.restart_pool(cr.dbname, update_module=True)
res_config_installer()
@@ -432,6 +410,7 @@ class ir_actions_configuration_wizard(osv.osv_memory):
'''
_name='ir.actions.configuration.wizard'
_inherit = 'res.config'
__logger = logging.getLogger(_name)
def _next_action_note(self, cr, uid, ids, context=None):
next = self._next_action(cr, uid)
@@ -451,8 +430,7 @@ class ir_actions_configuration_wizard(osv.osv_memory):
}
def execute(self, cr, uid, ids, context=None):
self.logger.notifyChannel(
'configuration', netsvc.LOG_WARNING, DEPRECATION_MESSAGE)
self.__logger.warn(DEPRECATION_MESSAGE)
ir_actions_configuration_wizard()
+2 -2
View File
@@ -7,8 +7,8 @@
<field name="arch" type="xml">
<form>
<group colspan="4" col="8">
<group colspan="3" width="220" height="250">
<field name="config_logo" widget="image" width="220" height="130" nolabel="1" colspan="1"/>
<group colspan="1" width="220" height="250">
<field name="config_logo" widget="image" width="220" height="130" nolabel="1"/>
<newline/>
<label align="0.0" string="description" width="200" colspan="2"/>
</group>
+4 -7
View File
@@ -48,7 +48,6 @@ class res_currency(osv.osv):
_description = "Currency"
_columns = {
'name': fields.char('Currency', size=32, required=True),
'code': fields.char('Code', size=3),
'symbol': fields.char('Symbol', size=3),
'rate': fields.function(_current_rate, method=True, string='Current Rate', digits=(12,6),
help='The rate of the currency to the currency of rate 1'),
@@ -65,7 +64,7 @@ class res_currency(osv.osv):
'active': lambda *a: 1,
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'res.currency', context=c)
}
_order = "code"
_order = "name"
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
res=super(osv.osv, self).read(cr, user, ids, fields, context, load)
@@ -102,12 +101,12 @@ class res_currency(osv.osv):
if from_currency['rate'] == 0 or to_currency['rate'] == 0:
date = context.get('date', time.strftime('%Y-%m-%d'))
if from_currency['rate'] == 0:
code = from_currency.code
currency_symbol = from_currency.symbol
else:
code = to_currency.code
currency_symbol = to_currency.symbol
raise osv.except_osv(_('Error'), _('No rate found \n' \
'for the currency: %s \n' \
'at the date: %s') % (code, date))
'at the date: %s') % (currency_symbol, date))
rate = to_currency.rate/from_currency.rate
if account and (account.currency_mode=='average') and account.currency_id:
q = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
@@ -133,9 +132,7 @@ class res_currency(osv.osv):
args2 = args[:]
if name:
args += [('name', operator, name)]
args2 += [('code', operator, name)]
ids = self.search(cr, uid, args, limit=limit)
ids += self.search(cr, uid, args2, limit=limit)
res = self.name_get(cr, uid, ids, context)
return res
res_currency()
@@ -9,7 +9,6 @@
<field name="arch" type="xml">
<tree string="Currencies">
<field name="name"/>
<field name="code"/>
<field name="company_id" select="2" />
<field name="rate_ids" invisible="1"/>
<field name="date"/>
@@ -28,7 +27,6 @@
<form string="Currency">
<group col="6" colspan="6">
<field name="name" select="1"/>
<field name="code" select="1"/>
<field name="rate"/>
<field name="company_id" select="2" groups="base.group_multi_company" />
<field name="symbol"/>
+2 -2
View File
@@ -36,8 +36,8 @@ class lang(osv.osv):
_columns = {
'name': fields.char('Name', size=64, required=True),
'code': fields.char('Locale Code', size=5, required=True, help='This field is used to set/get locales for user'),
'iso_code': fields.char('ISO code', size=5, required=False, help='This ISO code is the name of po files to use for translations'),
'code': fields.char('Locale Code', size=16, required=True, help='This field is used to set/get locales for user'),
'iso_code': fields.char('ISO code', size=16, required=False, help='This ISO code is the name of po files to use for translations'),
'translatable': fields.boolean('Translatable'),
'active': fields.boolean('Active'),
'direction': fields.selection([('ltr', 'Left-to-Right'), ('rtl', 'Right-to-Left')], 'Direction',required=True),
+1 -1
View File
@@ -76,7 +76,7 @@
</group>
<newline/>
<group colspan="4" col="4">
<separator string="Examples"/>
<separator string="Examples" colspan="4"/>
<newline/>
<label align="0.0" string="1. %%c ==> Fri Dec 5 18:25:20 2008"/>
<label align="0.0" string="2. %%a ,%%A ==> Fri, Friday"/>
+8
View File
@@ -50,6 +50,14 @@ class res_log(osv.osv):
cr.execute('CREATE INDEX %s ON res_log (user_id, read)' %
self._index_name)
def create(self, cr, uid, vals, context=None):
create_context = context and dict(context) or {}
if 'res_log_read' in create_context:
vals['read'] = create_context.pop('res_log_read')
if create_context and not vals.get('context'):
vals['context'] = create_context
return super(res_log, self).create(cr, uid, vals, context=context)
# TODO: do not return secondary log if same object than in the model (but unlink it)
def get(self, cr, uid, context=None):
unread_log_ids = self.search(cr, uid,
+61 -41
View File
@@ -33,6 +33,7 @@ import time
class groups(osv.osv):
_name = "res.groups"
_order = 'name'
_description = "Access Groups"
_columns = {
'name': fields.char('Group Name', size=64, required=True),
'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'),
@@ -45,7 +46,7 @@ class groups(osv.osv):
('name_uniq', 'unique (name)', 'The name of the group must be unique !')
]
def copy(self, cr, uid, id, default=None, context={}):
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)
@@ -82,14 +83,14 @@ class groups(osv.osv):
groups()
def _lang_get(self, cr, uid, context={}):
def _lang_get(self, cr, uid, context=None):
obj = self.pool.get('res.lang')
ids = obj.search(cr, uid, [('translatable','=',True)])
res = obj.read(cr, uid, ids, ['code', 'name'], context)
res = obj.read(cr, uid, ids, ['code', 'name'], context=context)
res = [(r['code'], r['name']) for r in res]
return res
def _tz_get(self,cr,uid, context={}):
def _tz_get(self,cr,uid, context=None):
return [(x, x) for x in pytz.all_timezones]
class users(osv.osv):
@@ -234,9 +235,10 @@ class users(osv.osv):
def on_change_company_id(self, cr, uid, ids, company_id):
return {
'value': {
'warning' : _("Please keep in mind that data currently displayed may not be relevant after switching to another company. If you have unsaved changes, please make sure to save and close the forms before switching to a different company (you can click on Cancel now)"),
}
'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 read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'):
@@ -263,7 +265,7 @@ class users(osv.osv):
]
_sql_constraints = [
('login_key', 'UNIQUE (login)', _('You can not have two users with the same login !'))
('login_key', 'UNIQUE (login)', 'You can not have two users with the same login !')
]
def _get_email_from(self, cr, uid, ids, context=None):
@@ -313,9 +315,9 @@ class users(osv.osv):
return result
_defaults = {
'password' : lambda *a : '',
'context_lang': lambda *args: 'en_US',
'active' : lambda *a: True,
'password' : '',
'context_lang': 'en_US',
'active' : True,
'menu_id': _get_menu,
'company_id': _get_company,
'company_ids': _get_companies,
@@ -371,7 +373,7 @@ class users(osv.osv):
ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
return self.name_get(cr, user, ids)
def copy(self, cr, uid, id, default=None, context={}):
def copy(self, cr, uid, id, default=None, context=None):
user2copy = self.read(cr, uid, [id], ['login','name'])[0]
if default is None:
default = {}
@@ -393,25 +395,28 @@ class users(osv.osv):
result[k[8:]] = res or False
return result
def action_get(self, cr, uid, 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_res_users_my')
return dataobj.browse(cr, uid, data_id, context).res_id
return dataobj.browse(cr, uid, data_id, context=context).res_id
def login(self, db, login, password):
if not password:
return False
cr = pooler.get_db(db).cursor()
cr.execute('select id from res_users where login=%s and password=%s and active', (tools.ustr(login), tools.ustr(password)))
res = cr.fetchone()
result = False
if res:
cr.execute("update res_users set date=%s where id=%s", (time.strftime('%Y-%m-%d %H:%M:%S'),res[0]))
try:
cr.execute('UPDATE res_users SET date=now() WHERE login=%s AND password=%s AND active RETURNING id',
(tools.ustr(login), tools.ustr(password)))
res = cr.fetchone()
cr.commit()
result = res[0]
cr.close()
return result
if res:
return res[0]
else:
return False
finally:
cr.close()
def check_super(self, passwd):
if passwd == tools.config['admin_passwd']:
return True
@@ -425,29 +430,34 @@ class users(osv.osv):
if (cached_pass is not None) and cached_pass == passwd:
return True
cr = pooler.get_db(db).cursor()
cr.execute('select count(1) from res_users where id=%s and password=%s and active=%s', (int(uid), passwd, True))
res = cr.fetchone()[0]
cr.close()
if not bool(res):
raise security.ExceptionNoTb('AccessDenied')
if res:
if self._uid_cache.has_key(db):
ulist = self._uid_cache[db]
ulist[uid] = passwd
else:
self._uid_cache[db] = {uid:passwd}
return bool(res)
try:
cr.execute('SELECT COUNT(1) FROM res_users WHERE id=%s AND password=%s AND active=%s',
(int(uid), passwd, True))
res = cr.fetchone()[0]
if not bool(res):
raise security.ExceptionNoTb('AccessDenied')
if res:
if self._uid_cache.has_key(db):
ulist = self._uid_cache[db]
ulist[uid] = passwd
else:
self._uid_cache[db] = {uid:passwd}
return bool(res)
finally:
cr.close()
def access(self, db, uid, passwd, sec_level, ids):
if not passwd:
return False
cr = pooler.get_db(db).cursor()
cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd))
res = cr.fetchone()
cr.close()
if not res:
raise security.ExceptionNoTb('Bad username or password')
return res[0]
try:
cr.execute('SELECT id FROM res_users WHERE id=%s AND password=%s', (uid, passwd))
res = cr.fetchone()
if not res:
raise security.ExceptionNoTb('Bad username or password')
return res[0]
finally:
cr.close()
users()
@@ -510,9 +520,19 @@ class groups2(osv.osv): ##FIXME: Is there a reason to inherit this object ?
}
def unlink(self, cr, uid, ids, context=None):
group_users = []
for record in self.read(cr, uid, ids, ['users'], context=context):
if record['users']:
raise osv.except_osv(_('Warning !'), _('Make sure you have no users linked with the group(s)!'))
group_users.extend(record['users'])
if group_users:
user_names = [user.name for user in self.pool.get('res.users').browse(cr, uid, group_users, context=context)]
if len(user_names) >= 5:
user_names = user_names[:5]
user_names += '...'
raise osv.except_osv(_('Warning !'),
_('Group(s) cannot be deleted, because some user(s) still belong to them: %s !') % \
', '.join(user_names))
return super(groups2, self).unlink(cr, uid, ids, context=context)
groups2()
+31 -9
View File
@@ -20,6 +20,8 @@
##############################################################################
from osv import fields,osv
class res_widget(osv.osv):
_name = "res.widget"
_rec_name = "title"
@@ -27,29 +29,49 @@ class res_widget(osv.osv):
'title' : fields.char('Title', size=64, required=True, translate=True),
'content': fields.text('Content', required=True),
}
res_widget()
class res_widget_user(osv.osv):
_name="res.widget.user"
_order = "sequence"
_columns = {
'sequence': fields.integer('Sequence'),
'user_id': fields.many2one('res.users','User', select=1),
'widget_id': fields.many2one('res.widget','Widget',required=1),
'widget_id': fields.many2one('res.widget','Widget',required=True),
}
def create(self, cr, uid, vals, context=None):
existing = self.search(cr, uid, [('user_id', '=', vals.get('user_id')), ('widget_id', '=', vals.get('widget_id'))], context=context)
if existing:
res = existing[0]
else:
res = super(res_widget_user, self).create(cr, uid, vals, context=context)
return res
res_widget_user()
class res_widget_wizard(osv.osv_memory):
_name = "res.widget.wizard"
_description = "Add a widget"
_description = "Add a widget for User"
_columns = {
'widget_id': fields.one2many("res.widget", 'Widget', required=True),
'widget_id': fields.many2many("res.widget",
"res_widget_user_rel", "uid", "wid",
"Widget"),
}
def widget_add(self, cr, uid, ids, context=None):
if context is None:
context = {}
wizard = self.read(cr, uid, ids)[0]
self.pool.get('res.widget.user').create(cr, uid, {'user_id':uid, 'widget_id':wizard['widget_id']})
return {}
def action_get(self, cr, uid, context=None):
return self.pool.get('ir.actions.act_window').for_xml_id(
cr, uid, 'base', 'action_res_widget_wizard', context=context)
def res_widget_add(self, cr, uid, ids, context=None):
wizard = self.read(cr, uid, ids, context=context)[0]
for wiz_id in wizard['widget_id']:
self.pool.get('res.widget.user').create(
cr, uid, {'user_id':uid, 'widget_id':wiz_id}, context=context)
return {'type': 'ir.actions.act_window_close'}
res_widget_wizard()
+190 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="openerp_twitter_favorites" model="res.widget">
<record id="openerp_favorites_twitter_widget" model="res.widget">
<field name="title">OpenERP Favorites</field>
<field name="content"><![CDATA[
<script type="text/javascript" src="http://widgets.twimg.com/j/2/widget.js"></script>
@@ -38,5 +38,194 @@ new TWTR.Widget({
}).render().setUser('openerp').start();
</script>]]> </field>
</record>
<record id="odony_twitter_widget" model="res.widget">
<field name="title">Olivier Dony's tweets</field>
<field name="content"><![CDATA[<script src="http://widgets.twimg.com/j/2/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: 'profile',
rpp: 10,
interval: 6000,
title: '',
subject: '',
width: 'auto',
height: 300,
theme: {
shell: {
background: '#F0EEEE',
color: '#444444'
},
tweets: {
background: '#F0EEEE',
color: '#444444',
links: '#9A0404'
}
},
features: {
scrollbar: false,
loop: false,
live: true,
hashtags: true,
timestamp: true,
avatars: false,
behavior: 'all'
}
}).render().setUser('odony').start();
</script>]]></field>
</record>
<record id="matrixise_twitter_widget" model="res.widget">
<field name="title">Stéphane Wirtel's tweets</field>
<field name="content"><![CDATA[<script src="http://widgets.twimg.com/j/2/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: 'profile',
rpp: 10,
interval: 6000,
title: '',
subject: '',
width: 'auto',
height: 300,
theme: {
shell: {
background: '#F0EEEE',
color: '#444444'
},
tweets: {
background: '#F0EEEE',
color: '#444444',
links: '#9A0404'
}
},
features: {
scrollbar: false,
loop: false,
live: true,
hashtags: true,
timestamp: true,
avatars: false,
behavior: 'all'
}
}).render().setUser('matrixise').start();
</script>]]></field>
</record>
<record id="rvalyi_twitter_widget" model="res.widget">
<field name="title">Raphaël Valyi's tweets</field>
<field name="content"><![CDATA[<script src="http://widgets.twimg.com/j/2/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: 'profile',
rpp: 10,
interval: 6000,
title: '',
subject: '',
width: 'auto',
height: 300,
theme: {
shell: {
background: '#F0EEEE',
color: '#444444'
},
tweets: {
background: '#F0EEEE',
color: '#444444',
links: '#9A0404'
}
},
features: {
scrollbar: false,
loop: false,
live: true,
hashtags: true,
timestamp: true,
avatars: false,
behavior: 'all'
}
}).render().setUser('rvalyi').start();
</script>]]></field>
</record>
<record id="albertnan_twitter_widget" model="res.widget">
<field name="title">Albert Cervera Areny's tweets</field>
<field name="content"><![CDATA[<script src="http://widgets.twimg.com/j/2/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: 'profile',
rpp: 10,
interval: 6000,
title: '',
subject: '',
width: 'auto',
height: 300,
theme: {
shell: {
background: '#F0EEEE',
color: '#444444'
},
tweets: {
background: '#F0EEEE',
color: '#444444',
links: '#9A0404'
}
},
features: {
scrollbar: false,
loop: false,
live: true,
hashtags: true,
timestamp: true,
avatars: false,
behavior: 'all'
}
}).render().setUser('albertnan').start();
</script>]]></field>
</record>
<record id="nhomar_twitter_widget" model="res.widget">
<field name="title">Nhomar Hernandez's tweets</field>
<field name="content"><![CDATA[<script src="http://widgets.twimg.com/j/2/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: 'profile',
rpp: 10,
interval: 6000,
title: '',
subject: '',
width: 'auto',
height: 300,
theme: {
shell: {
background: '#F0EEEE',
color: '#444444'
},
tweets: {
background: '#F0EEEE',
color: '#444444',
links: '#9A0404'
}
},
features: {
scrollbar: false,
loop: false,
live: true,
hashtags: true,
timestamp: true,
avatars: false,
behavior: 'all'
}
}).render().setUser('nhomar').start();
</script>]]></field>
</record>
<record id="openerp_favorites" model="res.widget.user">
<field name="widget_id" ref="openerp_favorites_twitter_widget"/>
</record>
</data>
</openerp>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="openerp_admin_widgets" model="res.widget.user">
<field name="widget_id" ref="odony_twitter_widget"/>
<field name="user_id" ref="base.user_root"/>
</record>
<record id="openerp_demo_widgets" model="res.widget.user">
<field name="widget_id" ref="albertnan_twitter_widget"/>
<field name="user_id" ref="base.user_demo"/>
</record>
</data>
</openerp>
+12 -7
View File
@@ -75,20 +75,25 @@
<menuitem action="res_widget_user_act_window" id="menu_res_widget_user_act_window" parent="base.next_id_2"
groups="base.group_no_one"/>
<!--
<record id="view_res_widget_wizard" model="ir.ui.view">
<field name="name">Res Widget Wizard</field>
<field name="name">Homepage Widgets Management</field>
<field name="model">res.widget.wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Res Widget" col="4">
<field name="res_widget"/>
<form string="Widget Wizard" col="4">
<field name="widget_id"/>
<newline/>
<button name="res_widget_add" string="Add Widget" type="object"/>
<separator string="" colspan="6"/>
<group colspan="6" col="4" >
<button special="cancel" string="Cancel" icon="gtk-cancel" />
<button name="res_widget_add" string="Add" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Res Widget Wizard" res_model="res.widget.wizard" src_model="res.widget.user" view_mode="form" target="new" key2="client_action_multi" id="action_res_widget_wizard"/>
-->
<act_window name="Homepage Widgets Management"
res_model="res.widget.wizard" src_model="res.widget.user"
view_mode="form" target="new" key2="client_action_multi"
id="action_res_widget_wizard"/>
</data>
</openerp>
+1
View File
@@ -248,6 +248,7 @@
</rng:optional>
<rng:optional><rng:attribute name="color"/></rng:optional>
<rng:optional><rng:attribute name="date_delay"/></rng:optional>
<rng:optional><rng:attribute name="day_length"/></rng:optional>
<rng:optional><rng:attribute name="date_start"/></rng:optional>
<rng:optional><rng:attribute name="date_stop"/></rng:optional>
<rng:optional><rng:attribute name="date_string"/></rng:optional>
+4 -7
View File
@@ -1,9 +1,6 @@
- |
Safe_Eval Scenario:
In order to check that common dangerous operations are
not allowed by the safe_eval mechanism, attempt to
evaluate some bad expressions, and verify that it triggers
an error.
To check that common dangerous operations are not allowed by the safe_eval mechanism, attempt to
evaluate unauthorized expressions, and verify that they trigger an error.
-
1. Try a few common expressions to verify they work with safe_eval
-
@@ -47,11 +44,11 @@
from tools.safe_eval import safe_eval
try:
safe_eval('open("/etc/passwd","r")')
assert False, "safe_eval should not allow arbitrary expressions"
assert False, "safe_eval should not allow calling open() builtin"
except NameError:
pass
except:
# NameError should be raised because open() builtin is not found,
# but other exceptions probably indicate that open() was executed!
assert False, "safe_eval should not allow arbitrary expressions"
assert False, "safe_eval should not allow calling open() builtin"
@@ -0,0 +1,46 @@
-
Testing that some domain expressions work
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','=','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator, for scalar value
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','in','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator for list value
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','in',['Agrolait','ASUStek'])])
assert len(ids) >= 1, ids
-
Check we can use "in" operator for plain fields.
-
!python {model: ir.ui.menu }: |
ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
assert len(ids) >= 1, ids
-
Test one2many operator with empty search list
-
!assert {model: res.partner, search: "[('address', 'in', [])]", count: 0, string: "Ids should be empty"}
-
Test one2many operator with False
-
!assert {model: res.partner, search: "[('address', '=', False)]"}:
- address in (False, None, [])
-
Test many2many operator with empty search list
-
!assert {model: res.partner, search: "[('category_id', 'in', [])]", count: 0, string: "Ids should be empty"}
-
Test many2many operator with False
-
!assert {model: res.partner, search: "[('category_id', '=', False)]"}:
- category_id in (False, None, [])
-
Filtering on invalid value across x2many relationship should return an empty set
-
!assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
+2 -1
View File
@@ -176,6 +176,8 @@
<rng:optional> <rng:attribute name="name"/></rng:optional>
<rng:optional> <rng:attribute name="parent"/> </rng:optional>
<rng:optional> <rng:attribute name="icon"/> </rng:optional>
<rng:optional> <rng:attribute name="web_icon"/> </rng:optional>
<rng:optional> <rng:attribute name="web_icon_hover"/> </rng:optional>
<rng:optional> <rng:attribute name="action"/> </rng:optional>
<rng:optional> <rng:attribute name="string"/> </rng:optional>
<rng:optional> <rng:attribute name="sequence"/> </rng:optional>
@@ -194,7 +196,6 @@
<rng:optional><rng:attribute name="domain" /> </rng:optional>
<rng:optional><rng:attribute name="src_model" /></rng:optional>
<rng:optional><rng:attribute name="context" /></rng:optional>
<rng:optional> <rng:attribute name="view"/> </rng:optional>
<rng:optional> <rng:attribute name="view_id"/> </rng:optional>
<rng:optional> <rng:attribute name="view_type"/> </rng:optional>
<rng:optional> <rng:attribute name="view_mode"/> </rng:optional>
+1 -1
View File
@@ -31,7 +31,7 @@ def ir_del(cr, uid, id):
obj = pooler.get_pool(cr.dbname).get('ir.values')
return obj.unlink(cr, uid, [id])
def ir_get(cr, uid, key, key2, models, meta=False, context={}, res_id_req=False):
def ir_get(cr, uid, key, key2, models, meta=False, context=None, res_id_req=False):
obj = pooler.get_pool(cr.dbname).get('ir.values')
res = obj.get(cr, uid, key, key2, models, meta=meta, context=context, res_id_req=res_id_req)
return res
+1 -1
View File
@@ -132,8 +132,8 @@ class ExportService(object):
LOG_NOTSET = 'notset'
LOG_DEBUG_SQL = 'debug_sql'
LOG_DEBUG_RPC = 'debug_rpc'
LOG_DEBUG_RPC_ANSWER = 'debug_rpc_answer'
LOG_DEBUG_RPC = 'debug_rpc'
LOG_DEBUG = 'debug'
LOG_TEST = 'test'
LOG_INFO = 'info'
+5 -3
View File
@@ -149,7 +149,9 @@ class expression(object):
right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context)
right1 = table.search(cr, uid, [(fargs[0],'in', right)], context=context)
self.__exp[i] = ('id', 'in', right1)
continue
if not isinstance(field,fields.property):
continue
if field._properties and ((not field.store) or field._fnct_search):
# this is a function field
@@ -183,7 +185,7 @@ class expression(object):
else:
call_null = True
if right:
if right is not False:
if isinstance(right, basestring):
ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context, limit=None)]
if ids2:
@@ -232,7 +234,7 @@ class expression(object):
self.__exp[i] = ('id', 'in', _rec_convert(ids2))
else:
call_null_m2m = True
if right:
if right is not False:
if isinstance(right, basestring):
res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context)]
if res_ids:
+26 -16
View File
@@ -63,8 +63,10 @@ class _column(object):
_symbol_set = (_symbol_c, _symbol_f)
_symbol_get = None
def __init__(self, string='unknown', required=False, readonly=False, domain=None, context={}, states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args):
if not context:
def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args):
if domain is None:
domain = []
if context is None:
context = {}
self.states = states or {}
self.string = string
@@ -76,7 +78,7 @@ class _column(object):
self.change_default = change_default
self.ondelete = ondelete
self.translate = translate
self._domain = domain or []
self._domain = domain
self._context = context
self.write = False
self.read = False
@@ -254,7 +256,13 @@ class binary(_column):
if v['id'] == i:
val = v[name]
break
if context.get('bin_size', False) and val:
# If client is requesting only the size of the field, we return it instead
# of the content. Presumably a separate request will be done to read the actual
# content if it's needed at some point.
# TODO: after 6.0 we should consider returning a dict with size and content instead of
# having an implicit convention for the value
if val and context.get('bin_size_%s' % name, context.get('bin_size')):
res[i] = tools.human_size(long(val))
else:
res[i] = val
@@ -333,8 +341,10 @@ class many2one(_column):
return result
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
context = context or {}
values = values or {}
if context is None:
context = {}
if values is None:
values = {}
res = {}
for r in values:
@@ -397,7 +407,7 @@ class one2many(_column):
assert(self.change_default != True)
def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if not context:
if context is None:
context = {}
if self._context:
context = context.copy()
@@ -446,12 +456,12 @@ class one2many(_column):
raise _('Not Implemented')
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if not context:
if context is None:
context = {}
if self._context:
context = context.copy()
context.update(self._context)
if not values:
if values is None:
values = {}
res = {}
@@ -732,9 +742,9 @@ class function(_column):
return self._fnct_search(obj, cr, uid, obj, name, args, context=context)
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
if not context:
if context is None:
context = {}
if not values:
if values is None:
values = {}
res = {}
if self._method:
@@ -780,7 +790,7 @@ class function(_column):
class related(function):
def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context={}):
def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None):
self._field_get2(cr, uid, obj, context)
i = len(self._arg)-1
sarg = name
@@ -797,7 +807,7 @@ class related(function):
return [(self._arg[0], 'in', sarg)]
def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
self._field_get2(cr, uid, obj, context)
self._field_get2(cr, uid, obj, context=context)
if type(ids) != type([]):
ids=[ids]
objlst = obj.browse(cr, uid, ids)
@@ -878,7 +888,7 @@ class related(function):
# TODO: improve here to change self.store = {...} according to related objects
pass
def _field_get2(self, cr, uid, obj, context={}):
def _field_get2(self, cr, uid, obj, context=None):
if self._relations:
return
obj_name = obj._name
@@ -898,10 +908,10 @@ class related(function):
# ---------------------------------------------------------
class dummy(function):
def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context={}):
def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None):
return []
def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
def _fnct_write(self, obj, cr, uid, ids, field_name, values, args, context=None):
return False
def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None):
+176 -120
View File
@@ -41,6 +41,7 @@ import calendar
import copy
import datetime
import logging
import warnings
import operator
import pickle
import re
@@ -126,12 +127,14 @@ class browse_record_list(list):
class browse_record(object):
logger = netsvc.Logger()
def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}):
def __init__(self, cr, uid, id, table, cache, context=None, list_class=None, fields_process=None):
'''
table : the object (inherited from orm)
context : dictionary with an optional context
'''
if not context:
if fields_process is None:
fields_process = {}
if context is None:
context = {}
self._list_class = list_class or browse_record_list
self._cr = cr
@@ -392,7 +395,6 @@ class orm_template(object):
'res_model': self._name,
'secondary': secondary,
'res_id': id,
'context': context,
},
context=context
)
@@ -401,10 +403,12 @@ class orm_template(object):
"""Override this method to do specific things when a view on the object is opened."""
pass
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
raise NotImplementedError(_('The read_group method is not implemented on this object !'))
def _field_create(self, cr, context={}):
def _field_create(self, cr, context=None):
if context is None:
context = {}
cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,))
if not cr.rowcount:
cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',))
@@ -486,11 +490,11 @@ class orm_template(object):
vals['relation'], bool(vals['view_load']),
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], vals['model'], vals['name']
))
continue
break
cr.commit()
def _auto_init(self, cr, context={}):
self._field_create(cr, context)
def _auto_init(self, cr, context=None):
self._field_create(cr, context=context)
def __init__(self, cr):
if not self._name and not hasattr(self, '_inherit'):
@@ -506,7 +510,7 @@ class orm_template(object):
if not self._table:
self._table = self._name.replace('.', '_')
def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}):
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
"""
Fetch records as objects allowing to use dot notation to browse fields and relations
@@ -517,8 +521,6 @@ class orm_template(object):
:rtype: object or list of objects requested
"""
if not context:
context = {}
self._list_class = list_class or browse_record_list
cache = {}
# need to accepts ints and longs because ids coming from a method
@@ -526,7 +528,7 @@ class orm_template(object):
if isinstance(select, (int, long)):
return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process)
elif isinstance(select, list):
return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context)
return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context=context)
else:
return browse_null()
@@ -554,8 +556,8 @@ class orm_template(object):
lines = []
data = map(lambda x: '', range(len(fields)))
done = []
for fpos in range(len(fields)):
done = []
f = fields[fpos]
if f:
r = row
@@ -600,7 +602,8 @@ class orm_template(object):
fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \
or [], fields)
if fields2 in done:
break
if [x for x in fields2 if x]:
break
done.append(fields2)
for row2 in r:
lines2 = self.__export_row(cr, uid, row2, fields2,
@@ -976,6 +979,10 @@ class orm_template(object):
for key in self.pool._sql_error.keys():
if key in e[0]:
msg = self.pool._sql_error[key]
if hasattr(msg, '__call__'):
msg = msg(cr, uid, [res_id,], context=context)
else:
msg = _(msg)
break
return (-1, res, 'Line ' + str(counter) +' : ' + msg, '')
if isinstance(e, osv.orm.except_orm):
@@ -1012,6 +1019,27 @@ class orm_template(object):
return (done, 0, 0, 0)
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
"""
Read records with given ids with the given fields
:param cr: database cursor
:param user: current user id
:param ids: id or list of the ids of the records to read
:param fields: optional list of field names to return (default: all fields would be returned)
:type fields: list (example ['field_name_1', ...])
:param context: optional context dictionary - it may contains keys for specifying certain options
like ``context_lang``, ``context_tz`` to alter the results of the call.
A special ``bin_size`` boolean flag may also be passed in the context to request the
value of all fields.binary columns to be returned as the size of the binary instead of its
contents. This can also be selectively overriden by passing a field-specific flag
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
:return: list of dictionaries((dictionary per record asked)) with requested field values
:rtype: [{‘name_of_the_field’: value, ...}, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
raise NotImplementedError(_('The read method is not implemented on this object !'))
def get_invalid_fields(self, cr, uid):
@@ -1028,9 +1056,12 @@ class orm_template(object):
# Check presence of __call__ directly instead of using
# callable() because it will be deprecated as of Python 3.0
if hasattr(msg, '__call__'):
txt_msg, params = msg(self, cr, uid, ids)
tmp_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=txt_msg) or txt_msg
translated_msg = tmp_msg % params
tmp_msg = msg(self, cr, uid, ids, context=context)
if isinstance(tmp_msg, tuple):
tmp_msg, params = tmp_msg
translated_msg = tmp_msg % params
else:
translated_msg = tmp_msg
else:
translated_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) or msg
error_msgs.append(
@@ -1049,8 +1080,15 @@ class orm_template(object):
:param fields_list: list of fields to get the default values for (example ['field1', 'field2',])
:type fields_list: list
:param context: usual context dictionary - it may contains keys in the form ``default_XXX``,
where XXX is a field name to set or override a default value.
:param context: optional context dictionary - it may contains keys for specifying certain options
like ``context_lang`` (language) or ``context_tz`` (timezone) to alter the results of the call.
It may contain keys in the form ``default_XXX`` (where XXX is a field name), to set
or override a default value for a field.
A special ``bin_size`` boolean flag may also be passed in the context to request the
value of all fields.binary columns to be returned as the size of the binary instead of its
contents. This can also be selectively overriden by passing a field-specific flag
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
:return: dictionary of the default values (set on the object model class, through user preferences, or in the context)
"""
# trigger view init hook
@@ -1167,46 +1205,49 @@ class orm_template(object):
if self._columns.keys():
for f in self._columns.keys():
field_col = self._columns[f]
if allfields and f not in allfields:
continue
res[f] = {'type': self._columns[f]._type}
res[f] = {'type': field_col._type}
# This additional attributes for M2M and function field is added
# because we need to display tooltip with this additional information
# when client is started in debug mode.
if isinstance(self._columns[f], fields.function):
res[f]['function'] = self._columns[f]._fnct and self._columns[f]._fnct.func_name or False
res[f]['store'] = self._columns[f].store
if isinstance(self._columns[f].store, dict):
res[f]['store'] = str(self._columns[f].store)
res[f]['fnct_search'] = self._columns[f]._fnct_search and self._columns[f]._fnct_search.func_name or False
res[f]['fnct_inv'] = self._columns[f]._fnct_inv and self._columns[f]._fnct_inv.func_name or False
res[f]['fnct_inv_arg'] = self._columns[f]._fnct_inv_arg or False
res[f]['func_obj'] = self._columns[f]._obj or False
res[f]['func_method'] = self._columns[f]._method
if isinstance(self._columns[f], fields.many2many):
res[f]['related_columns'] = list((self._columns[f]._id1, self._columns[f]._id2))
res[f]['third_table'] = self._columns[f]._rel
if isinstance(field_col, fields.function):
res[f]['function'] = field_col._fnct and field_col._fnct.func_name or False
res[f]['store'] = field_col.store
if isinstance(field_col.store, dict):
res[f]['store'] = str(field_col.store)
res[f]['fnct_search'] = field_col._fnct_search and field_col._fnct_search.func_name or False
res[f]['fnct_inv'] = field_col._fnct_inv and field_col._fnct_inv.func_name or False
res[f]['fnct_inv_arg'] = field_col._fnct_inv_arg or False
res[f]['func_obj'] = field_col._obj or False
res[f]['func_method'] = field_col._method
if isinstance(field_col, fields.many2many):
res[f]['related_columns'] = list((field_col._id1, field_col._id2))
res[f]['third_table'] = field_col._rel
for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator',
'change_default', 'translate', 'help', 'select', 'selectable'):
if getattr(self._columns[f], arg):
res[f][arg] = getattr(self._columns[f], arg)
if getattr(field_col, arg):
res[f][arg] = getattr(field_col, arg)
if not write_access:
res[f]['readonly'] = True
res[f]['states'] = {}
for arg in ('digits', 'invisible', 'filters'):
if getattr(self._columns[f], arg, None):
res[f][arg] = getattr(self._columns[f], arg)
if getattr(field_col, arg, None):
res[f][arg] = getattr(field_col, arg)
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US', self._columns[f].string)
if res_trans:
res[f]['string'] = res_trans
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
if help_trans:
res[f]['help'] = help_trans
if field_col.string:
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US', field_col.string)
if res_trans:
res[f]['string'] = res_trans
if field_col.help:
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
if help_trans:
res[f]['help'] = help_trans
if hasattr(self._columns[f], 'selection'):
if isinstance(self._columns[f].selection, (tuple, list)):
sel = self._columns[f].selection
if hasattr(field_col, 'selection'):
if isinstance(field_col.selection, (tuple, list)):
sel = field_col.selection
# translate each selection option
sel2 = []
for (key, val) in sel:
@@ -1218,12 +1259,11 @@ class orm_template(object):
res[f]['selection'] = sel
else:
# call the 'dynamic selection' function
res[f]['selection'] = self._columns[f].selection(self, cr,
user, context)
res[f]['selection'] = field_col.selection(self, cr, user, context)
if res[f]['type'] in ('one2many', 'many2many', 'many2one', 'one2one'):
res[f]['relation'] = self._columns[f]._obj
res[f]['domain'] = self._columns[f]._domain
res[f]['context'] = self._columns[f]._context
res[f]['relation'] = field_col._obj
res[f]['domain'] = field_col._domain
res[f]['context'] = field_col._context
else:
#TODO : read the fields from the database
pass
@@ -1246,7 +1286,7 @@ class orm_template(object):
context = {}
result = False
fields = {}
childs = True
children = True
def encode(s):
if isinstance(s, unicode):
@@ -1280,13 +1320,13 @@ class orm_template(object):
new_xml = etree.fromstring(encode(xml))
ctx = context.copy()
ctx['base_model_name'] = self._name
xarch, xfields = self.pool.get(node.get('object', False)).__view_look_dom_arch(cr, user, new_xml, view_id, ctx)
views[str(f.tag)] = {
xarch, xfields = self.pool.get(node.get('object')).__view_look_dom_arch(cr, user, new_xml, view_id, ctx)
views['form'] = {
'arch': xarch,
'fields': xfields
}
attrs = {'views': views}
fields = views.get('field', False) and views['field'].get('fields', False)
fields = xfields
if node.get('name'):
attrs = {}
try:
@@ -1294,13 +1334,13 @@ class orm_template(object):
column = self._columns[node.get('name')]
else:
column = self._inherit_fields[node.get('name')][2]
except:
except Exception:
column = False
if column:
relation = self.pool.get(column._obj)
childs = False
children = False
views = {}
for f in node:
if f.tag in ('form', 'tree', 'graph'):
@@ -1356,13 +1396,17 @@ class orm_template(object):
trans = self.pool.get('ir.translation')._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.get('string'))
if trans:
node.set('string', trans)
if node.get('confirm'):
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.get('confirm'))
if trans:
node.set('confirm', trans)
if node.get('sum'):
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.get('sum'))
if trans:
node.set('sum', trans)
for f in node:
if childs or (node.tag == 'field' and f.tag in ('filter','separator')):
if children or (node.tag == 'field' and f.tag in ('filter','separator')):
fields.update(self.__view_look_dom(cr, user, f, view_id, context))
return fields
@@ -1409,7 +1453,7 @@ class orm_template(object):
fields = self.fields_get(cr, user, fields_def.keys(), context)
for field in fields_def:
if field == 'id':
# sometime, the view may containt the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
# sometime, the view may contain the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
fields['id'] = {'readonly': True, 'type': 'integer', 'string': 'ID'}
elif field in fields:
fields[field].update(fields_def[field])
@@ -1419,7 +1463,7 @@ class orm_template(object):
model = res[0][1]
res.insert(0, ("Can't find field '%s' in the following view parts composing the view of object model '%s':" % (field, model), None))
msg = "\n * ".join([r[0] for r in res])
msg += "\n\nEither you wrongly customised this view, or some modules bringing those views are not compatible with your current data model"
msg += "\n\nEither you wrongly customized this view, or some modules bringing those views are not compatible with your current data model"
netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg)
raise except_orm('View error', msg)
return arch, fields
@@ -1470,14 +1514,14 @@ class orm_template(object):
return arch
def __get_default_search_view(self, cr, uid, context={}):
def __get_default_search_view(self, cr, uid, context=None):
def encode(s):
if isinstance(s, unicode):
return s.encode('utf8')
return s
view = self.fields_view_get(cr, uid, False, 'form', context)
view = self.fields_view_get(cr, uid, False, 'form', context=context)
root = etree.fromstring(encode(view['arch']))
res = etree.XML("""<search string="%s"></search>""" % root.get("string", ""))
@@ -2169,7 +2213,7 @@ class orm(orm_template):
_protected = ['read', 'write', 'create', 'default_get', 'perm_read', 'unlink', 'fields_get', 'fields_view_get', 'search', 'name_get', 'distinct_field_get', 'name_search', 'copy', 'import_data', 'search_count', 'exists']
__logger = logging.getLogger('orm')
__schema = logging.getLogger('orm.schema')
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
"""
Get the list of records in list view grouped by the given ``groupby`` fields
@@ -2182,6 +2226,9 @@ class orm(orm_template):
:param offset: optional number of records to skip
:param limit: optional max number of records to return
:param context: context arguments, like lang, time zone
:param order: optional ``order by`` specification, for overriding the natural
sort ordering of the groups, see also :py:meth:`~osv.osv.osv.search`
(supported only for many2one fields currently)
:return: list of dictionaries(one dictionary for each record) containing:
* the values of fields grouped by the fields in ``groupby`` argument
@@ -2212,7 +2259,7 @@ class orm(orm_template):
fget = self.fields_get(cr, uid, fields)
float_int_fields = filter(lambda x: fget[x]['type'] in ('float', 'integer'), fields)
flist = ''
group_by = groupby
group_count = group_by = groupby
if groupby:
if fget.get(groupby):
if fget[groupby]['type'] in ('date', 'datetime'):
@@ -2242,7 +2289,9 @@ class orm(orm_template):
where_clause = where_clause and ' WHERE ' + where_clause
limit_str = limit and ' limit %d' % limit or ''
offset_str = offset and ' offset %d' % offset or ''
cr.execute('SELECT min(%s.id) AS id,' % self._table + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params)
if len(groupby_list) < 2 and context.get('group_by_no_leaf'):
group_count = '_'
cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count, ' % (self._table, self._table, group_count) + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params)
alldata = {}
groupby = group_by
for r in cr.dictfetchall():
@@ -2251,7 +2300,7 @@ class orm(orm_template):
alldata[r['id']] = r
del r['id']
if groupby and fget[groupby]['type'] == 'many2one':
data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=groupby, context=context)
data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=orderby or groupby, context=context)
# the IDS of the records that has groupby field value = False or ''
# should be added too
data_ids += filter(lambda x:x not in data_ids, alldata.keys())
@@ -2259,6 +2308,7 @@ class orm(orm_template):
# restore order of the search as read() uses the default _order (this is only for groups, so the size of data_read shoud be small):
data.sort(lambda x,y: cmp(data_ids.index(x['id']), data_ids.index(y['id'])))
else:
#FIXME: handle orderby param for non-m2o too!
data = self.read(cr, uid, alldata.keys(), groupby and [groupby] or ['id'], context=context)
if groupby:
data.sort(lambda x,y:cmp(x[groupby],y[groupby]))
@@ -2327,8 +2377,7 @@ class orm(orm_template):
where += ' order by '+self._parent_order
cr.execute('SELECT id FROM '+self._table+' WHERE '+where)
pos2 = pos + 1
childs = cr.fetchall()
for id in childs:
for id in cr.fetchall():
pos2 = browse_rec(id[0], pos2)
cr.execute('update '+self._table+' set parent_left=%s, parent_right=%s where id=%s', (pos, pos2, root))
return pos2 + 1
@@ -2383,7 +2432,9 @@ class orm(orm_template):
self.__schema.debug("Table '%s': column '%s': dropped NOT NULL constraint",
self._table, column['attname'])
def _auto_init(self, cr, context={}):
def _auto_init(self, cr, context=None):
if context is None:
context = {}
store_compute = False
create = False
todo_end = []
@@ -2394,7 +2445,6 @@ class orm(orm_template):
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,))
cr.execute("COMMENT ON TABLE \"%s\" IS '%s'" % (self._table, self._description.replace("'", "''")))
create = True
self.__schema.debug("Table '%s': created", self._table)
cr.commit()
@@ -2447,13 +2497,22 @@ class orm(orm_template):
# iterate on the "object columns"
todo_update_store = []
update_custom_fields = context.get('update_custom_fields', False)
cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \
"FROM pg_class c,pg_attribute a,pg_type t " \
"WHERE c.relname=%s " \
"AND c.oid=a.attrelid " \
"AND a.atttypid=t.oid", (self._table,))
col_data = dict(map(lambda x: (x['attname'], x),cr.dictfetchall()))
for k in self._columns:
if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'):
continue
#raise _('Can not define a column %s. Reserved keyword !') % (k,)
#Not Updating Custom fields
if k.startswith('x_') and not update_custom_fields:
continue
f = self._columns[k]
if isinstance(f, fields.one2many):
@@ -2486,13 +2545,8 @@ class orm(orm_template):
self.__schema.debug("Create table '%s': relation between '%s' and '%s'",
f._rel, self._table, ref)
else:
cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \
"FROM pg_class c,pg_attribute a,pg_type t " \
"WHERE c.relname=%s " \
"AND a.attname=%s " \
"AND c.oid=a.attrelid " \
"AND a.atttypid=t.oid", (self._table, k))
res = cr.dictfetchall()
res = col_data.get(k, [])
res = res and [res] or []
if not res and hasattr(f, 'oldname'):
cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \
"FROM pg_class c,pg_attribute a,pg_type t " \
@@ -2561,7 +2615,7 @@ class orm(orm_template):
if not ok:
i = 0
while True:
newname = self._table + '_moved' + str(i)
newname = k + '_moved' + str(i)
cr.execute("SELECT count(1) FROM pg_class c,pg_attribute a " \
"WHERE c.relname=%s " \
"AND a.attname=%s " \
@@ -2938,21 +2992,6 @@ class orm(orm_template):
return super(orm, self).fields_get(cr, user, fields, context, write_access)
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
"""
Read records with given ids with the given fields
:param cr: database cursor
:param user: current user id
:param ids: id or list of the ids of the records to read
:param fields: optional list of field names to return (default: all fields would be returned)
:type fields: list (example ['field_name_1', ...])
:param context: (optional) context arguments, like lang, time zone
:return: list of dictionaries((dictionary per record asked)) with requested field values
:rtype: [{‘name_of_the_field’: value, ...}, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
if not context:
context = {}
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
@@ -3027,21 +3066,6 @@ class orm(orm_template):
else:
res = map(lambda x: {'id': x}, ids)
# if not res:
# res = map(lambda x: {'id': x}, ids)
# for record in res:
# for f in fields_to_read:
# field_val = False
# if f in self._columns.keys():
# ftype = self._columns[f]._type
# elif f in self._inherit_fields.keys():
# ftype = self._inherit_fields[f][2]._type
# else:
# continue
# if ftype in ('one2many', 'many2many'):
# field_val = []
# record.update({f:field_val})
for f in fields_pre:
if f == self.CONCURRENCY_CHECK_FIELD:
continue
@@ -3178,9 +3202,11 @@ class orm(orm_template):
for r in res:
for key in r:
r[key] = r[key] or False
if details and key in ('write_uid', 'create_uid'):
if r[key]:
if details and key in ('write_uid', 'create_uid') and r[key]:
try:
r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0]
except Exception:
pass # Leave the numeric uid there
r['xmlid'] = ("%(module)s.%(name)s" % r) if r['name'] else False
del r['name'], r['module']
if uniq:
@@ -3334,7 +3360,7 @@ class orm(orm_template):
fobj = None
if field in self._columns:
fobj = self._columns[field]
else:
elif field in self._inherit_fields:
fobj = self._inherit_fields[field][2]
if not fobj:
continue
@@ -4067,6 +4093,13 @@ class orm(orm_template):
if context is None:
context = {}
# avoid recursion through already copied records in case of circular relationship
seen_map = context.setdefault('__copy_data_seen',{})
if id in seen_map.setdefault(self._name,[]):
return
seen_map[self._name].append(id)
if default is None:
default = {}
if 'state' not in default:
@@ -4076,10 +4109,14 @@ class orm(orm_template):
else:
default['state'] = self._defaults['state']
context_wo_lang = context
context_wo_lang = context.copy()
if 'lang' in context:
del context_wo_lang['lang']
data = self.read(cr, uid, [id], context=context_wo_lang)[0]
data = self.read(cr, uid, [id,], context=context_wo_lang)
if data:
data = data[0]
else:
raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
fields = self.fields_get(cr, uid, context=context)
for f in fields:
@@ -4090,7 +4127,7 @@ class orm(orm_template):
if f in default:
data[f] = default[f]
elif ftype == 'function':
elif 'function' in fields[f]:
del data[f]
elif ftype == 'many2one':
try:
@@ -4110,7 +4147,8 @@ class orm(orm_template):
# parent but then are reassigned to the correct one thanks
# to the (0, 0, ...)
d = rel.copy_data(cr, uid, rel_id, context=context)
res.append((0, 0, d))
if d:
res.append((0, 0, d))
data[f] = res
elif ftype == 'many2many':
data[f] = [(6, 0, data[f])]
@@ -4127,6 +4165,15 @@ class orm(orm_template):
return data
def copy_translations(self, cr, uid, old_id, new_id, context=None):
if context is None:
context = {}
# avoid recursion through already copied records in case of circular relationship
seen_map = context.setdefault('__copy_translations_seen',{})
if old_id in seen_map.setdefault(self._name,[]):
return
seen_map[self._name].append(old_id)
trans_obj = self.pool.get('ir.translation')
fields = self.fields_get(cr, uid, context=context)
@@ -4138,10 +4185,9 @@ class orm(orm_template):
old_record, new_record = self.read(cr, uid, [old_id, new_id], [field_name], context=context)
# here we rely on the order of the ids to match the translations
# as foreseen in copy_data()
old_childs = sorted(old_record[field_name])
new_childs = sorted(new_record[field_name])
for (old_child, new_child) in zip(old_childs, new_childs):
# recursive copy of translations here
old_children = sorted(old_record[field_name])
new_children = sorted(new_record[field_name])
for (old_child, new_child) in zip(old_children, new_children):
target_obj.copy_translations(cr, uid, old_child, new_child, context=context)
# and for translatable fields we keep them for copy
elif field_def.get('translate'):
@@ -4177,6 +4223,9 @@ class orm(orm_template):
:return: True
"""
if context is None:
context = {}
context = context.copy()
data = self.copy_data(cr, uid, id, default, context)
new_id = self.create(cr, uid, data, context)
self.copy_translations(cr, uid, id, new_id, context)
@@ -4190,6 +4239,13 @@ class orm(orm_template):
return cr.fetchone()[0] == len(ids)
def check_recursion(self, cr, uid, ids, parent=None):
warnings.warn("You are using deprecated %s.check_recursion(). Please use the '_check_recursion()' instead!" % \
self._name, DeprecationWarning, stacklevel=3)
assert parent is None or parent in self._columns or parent in self._inherit_fields,\
"The 'parent' parameter passed to check_recursion() must be None or a valid field name"
return self._check_recursion(cr, uid, ids, parent)
def _check_recursion(self, cr, uid, ids, parent=None):
"""
Verifies that there is no loop in a hierarchical structure of records,
by following the parent relationship using the **parent** field until a loop
+65 -2
View File
@@ -32,7 +32,7 @@ import traceback
import logging
from psycopg2 import IntegrityError, errorcodes
from tools.func import wraps
from tools.translate import _
from tools.translate import translate
module_list = []
module_class_list = {}
@@ -51,6 +51,68 @@ class osv_pool(netsvc.Service):
def check(f):
@wraps(f)
def wrapper(self, dbname, *args, **kwargs):
""" Wraps around OSV functions and normalises a few exceptions
"""
def tr(src, ttype):
# We try to do the same as the _(), but without the frame
# inspection, since we aready are wrapping an osv function
# trans_obj = self.get('ir.translation') cannot work yet :(
ctx = {}
if not kwargs:
if args and isinstance(args[-1], dict):
ctx = args[-1]
elif isinstance(kwargs, dict):
ctx = kwargs.get('context', {})
uid = 1
if args and isinstance(args[0], (long, int)):
uid = args[0]
lang = ctx and ctx.get('lang')
if not (lang or hasattr(src, '__call__')):
return src
# We open a *new* cursor here, one reason is that failed SQL
# queries (as in IntegrityError) will invalidate the current one.
cr = False
if hasattr(src, '__call__'):
# callable. We need to find the right parameters to call
# the orm._sql_message(self, cr, uid, ids, context) function,
# or we skip..
# our signature is f(osv_pool, dbname [,uid, obj, method, args])
try:
if args and len(args) > 1:
obj = self.get(args[1])
if len(args) > 3 and isinstance(args[3], (long, int, list)):
ids = args[3]
else:
ids = []
cr = pooler.get_db_only(dbname).cursor()
return src(obj, cr, uid, ids, context=(ctx or {}))
except Exception:
pass
finally:
if cr: cr.close()
return False # so that the original SQL error will
# be returned, it is the best we have.
try:
cr = pooler.get_db_only(dbname).cursor()
res = translate(cr, name=False, source_type=ttype,
lang=lang, source=src)
if res:
return res
else:
return src
finally:
if cr: cr.close()
def _(src):
return tr(src, 'code')
try:
if not pooler.get_pool(dbname)._ready:
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
@@ -64,7 +126,8 @@ class osv_pool(netsvc.Service):
except IntegrityError, inst:
for key in self._sql_error.keys():
if key in inst[0]:
self.abortResponse(1, _('Constraint Error'), 'warning', _(self._sql_error[key]))
self.abortResponse(1, _('Constraint Error'), 'warning',
tr(self._sql_error[key], 'sql_constraint') or inst[0])
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
self.logger.debug("IntegrityError", exc_info=True)
+1 -1
View File
@@ -254,7 +254,7 @@ class document(object):
for el_cld in node:
self.parse_node(el_cld, el, v)
else:
# if there is no "type" attribute in the node, copy it to the xml data and parse its childs
# if there is no "type" attribute in the node, copy it to the xml data and parse its children
if not node.tag == etree.Comment:
if node.tag == parent.tag:
el = parent
-1
View File
@@ -139,7 +139,6 @@ class report_printscreen_list(report_int):
_append_node('header-date', str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")))
l = []
t = 0
rowcount = 0;
strmax = (pageSize[0]-40) * 2.8346
temp = []
tsum = []
+1 -1
View File
@@ -55,7 +55,7 @@ class rml2txt(render.render):
def __init__(self, rml, localcontext= None, datas={}):
super(rml2txt, self).__init__(datas)
self.rml = rml
self.localcontext = localcontext
self.localcontext = localcontext
self.output_type = 'txt'
def _render(self):
+105 -20
View File
@@ -3,6 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 P. Christeas, Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010 OpenERP SA. (http://www.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
@@ -19,8 +20,23 @@
#
##############################################################################
from reportlab import rl_config
import glob
import logging
import os
import platform
from reportlab import rl_config
from tools import config
"""This module allows the mapping of some system-available TTF fonts to
the reportlab engine.
This file could be customized per distro (although most Linux/Unix ones)
should have the same filenames, only need the code below).
Due to an awful configuration that ships with reportlab at many Linux
and Ubuntu distros, we have to override the search path, too.
"""
CustomTTFonts = [ ('Helvetica',"DejaVu Sans", "DejaVuSans.ttf", 'normal'),
('Helvetica',"DejaVu Sans Bold", "DejaVuSans-Bold.ttf", 'bold'),
@@ -34,32 +50,101 @@ CustomTTFonts = [ ('Helvetica',"DejaVu Sans", "DejaVuSans.ttf", 'normal'),
('Times-Roman',"Liberation Serif Bold", "LiberationSerif-Bold.ttf", 'bold'),
('Times-Roman',"Liberation Serif Italic", "LiberationSerif-Italic.ttf", 'italic'),
('Times-Roman',"Liberation Serif BoldItalic", "LiberationSerif-BoldItalic.ttf", 'bolditalic'),
('ZapfDingbats',"DejaVu Serif", "DejaVuSerif.ttf", 'normal'),
('ZapfDingbats',"DejaVu Serif Bold", "DejaVuSerif-Bold.ttf", 'bold'),
('ZapfDingbats',"DejaVu Serif Italic", "DejaVuSerif-Italic.ttf", 'italic'),
('ZapfDingbats',"DejaVu Serif BoldItalic", "DejaVuSerif-BoldItalic.ttf", 'bolditalic'),
('Courier',"FreeMono", "FreeMono.ttf", 'normal'),
('Courier',"FreeMono Bold", "FreeMonoBold.ttf", 'bold'),
('Courier',"FreeMono Oblique", "FreeMonoOblique.ttf", 'italic'),
('Courier',"FreeMono BoldOblique", "FreeMonoBoldOblique.ttf", 'bolditalic'),]
def SearchFontPath(font_file):
for dirname in rl_config.TTFSearchPath:
for root, dirs, files in os.walk(os.path.abspath(dirname)):
for file_name in files:
filename = os.path.join(root, file_name)
extension = os.path.splitext(filename)[1]
if extension.lower() in ['.ttf']:
if file_name==font_file:
return True
return False
TTFSearchPath_Linux = (
'/usr/share/fonts/truetype', # SuSE
'/usr/share/fonts/dejavu', '/usr/share/fonts/liberation', # Fedora, RHEL
'/usr/share/fonts/truetype/*', # Ubuntu,
'/usr/share/fonts/TTF/*', # at Mandriva/Mageia
)
TTFSearchPath_Windows = (
'c:/winnt/fonts',
'c:/windows/fonts'
)
TTFSearchPath_Darwin = (
#mac os X - from
#http://developer.apple.com/technotes/tn/tn2024.html
'~/Library/Fonts',
'/Library/Fonts',
'/Network/Library/Fonts',
'/System/Library/Fonts',
)
TTFSearchPathMap = {
'Darwin': TTFSearchPath_Darwin,
'Windows': TTFSearchPath_Windows,
'Linux': TTFSearchPath_Linux,
}
# ----- The code below is less distro-specific, please avoid editing! -------
__foundFonts = None
def FindCustomFonts():
"""Fill the __foundFonts list with those filenames, whose fonts
can be found in the reportlab ttf font path.
This process needs only be done once per loading of this module,
it is cached. But, if the system admin adds some font in the
meanwhile, the server must be restarted eventually.
"""
dirpath = []
log = logging.getLogger('report.fonts')
global __foundFonts
__foundFonts = {}
searchpath = []
if config.get('fonts_search_path'):
searchpath += map(str.strip, config.get('fonts_search_path').split(','))
local_platform = platform.system()
if local_platform in TTFSearchPathMap:
searchpath += TTFSearchPathMap[local_platform]
# Append the original search path of reportlab (at the end)
searchpath += rl_config.TTFSearchPath
# Perform the search for font files ourselves, as reportlab's
# TTFOpenFile is not very good at it.
for dirglob in searchpath:
dirglob = os.path.expanduser(dirglob)
for dirname in glob.iglob(dirglob):
abp = os.path.abspath(dirname)
if os.path.isdir(abp):
dirpath.append(abp)
for k, (name, font, filename, mode) in enumerate(CustomTTFonts):
if filename in __foundFonts:
continue
for d in dirpath:
abs_filename = os.path.join(d, filename)
if os.path.exists(abs_filename):
log.debug("Found font %s at %s", filename, abs_filename)
__foundFonts[filename] = abs_filename
break
def SetCustomFonts(rmldoc):
for name, font, fname, mode in CustomTTFonts:
if SearchFontPath(fname):
rmldoc.setTTFontMapping(name, font,filename, mode)
""" Map some font names to the corresponding TTF fonts
The ttf font may not even have the same name, as in
Times -> Liberation Serif.
This function is called once per report, so it should
avoid system-wide processing (cache it, instead).
"""
global __foundFonts
if __foundFonts is None:
FindCustomFonts()
for name, font, filename, mode in CustomTTFonts:
if os.path.isabs(filename) and os.path.exists(filename):
rmldoc.setTTFontMapping(name, font, filename, mode)
elif filename in __foundFonts:
rmldoc.setTTFontMapping(name, font, __foundFonts[filename], mode)
return True
#eof
+61 -22
View File
@@ -21,21 +21,27 @@
import sys
from StringIO import StringIO
import copy
import reportlab
import re
from reportlab.pdfgen import canvas
from reportlab import platypus
import cStringIO
import utils
import color
import os
import logging
from lxml import etree
import base64
from reportlab.platypus.doctemplate import ActionFlowable
from tools.safe_eval import safe_eval as eval
from reportlab.lib.units import inch,cm,mm
from reportlab.pdfbase import pdfmetrics
try:
from cStringIO import StringIO
_hush_pyflakes = [ StringIO ]
except ImportError:
from StringIO import StringIO
encoding = 'utf-8'
@@ -68,7 +74,7 @@ class NumberedCanvas(canvas.Canvas):
while not self.pages.get(key,False):
key = key + 1
self.setFont("Helvetica", 8)
self.drawRightString((self._pagesize[0]-35), (self._pagesize[1]-43),
self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40),
"Page %(this)i of %(total)i" % {
'this': self._pageNumber+1,
'total': self.pages.get(key,False),
@@ -222,7 +228,7 @@ class _rml_doc(object):
addMapping(name, 1, 0, name) #bold
addMapping(name, 1, 1, name) #italic and bold
def setTTFontMapping(self,face, fontname,filename, mode='all'):
def setTTFontMapping(self,face, fontname, filename, mode='all'):
from reportlab.lib.fonts import addMapping
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
@@ -411,7 +417,7 @@ class _rml_canvas(object):
if not node.get('file') :
if node.get('name'):
image_data = self.images[node.get('name')]
s = cStringIO.StringIO(image_data)
s = StringIO(image_data)
else:
if self.localcontext:
res = utils._regex.findall(node.text)
@@ -422,19 +428,19 @@ class _rml_canvas(object):
if node.text:
image_data = base64.decodestring(node.text)
if image_data:
s = cStringIO.StringIO(image_data)
s = StringIO(image_data)
else:
return False
else:
if node.get('file') in self.images:
s = cStringIO.StringIO(self.images[node.get('file')])
s = StringIO(self.images[node.get('file')])
else:
try:
u = urllib.urlopen(str(node.get('file')))
s = cStringIO.StringIO(u.read())
s = StringIO(u.read())
except Exception:
u = file(os.path.join(self.path,str(node.get('file'))), 'rb')
s = cStringIO.StringIO(u.read())
s = StringIO(u.read())
img = ImageReader(s)
(sx,sy) = img.getSize()
@@ -480,12 +486,18 @@ class _rml_canvas(object):
self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
def setFont(self, node):
from reportlab.pdfbase import pdfmetrics
fname = node.get('name')
#TODO : other fonts should be supported
if fname not in pdfmetrics.standardFonts:
fname = self.canvas._fontname
return self.canvas.setFont(fname, utils.unit_get(node.get('size')))
fontname = node.get('name')
if fontname not in pdfmetrics.getRegisteredFontNames()\
or fontname not in pdfmetrics.standardFonts:
# let reportlab attempt to find it
try:
pdfmetrics.getFont(fontname)
except Exception:
logging.getLogger('report.fonts').debug('Could not locate font %s, substituting default: %s',
fontname,
self.canvas._fontname)
fontname = self.canvas._fontname
return self.canvas.setFont(fontname, utils.unit_get(node.get('size')))
def render(self, node):
tags = {
@@ -552,8 +564,8 @@ class _rml_flowable(object):
return rc1
def _table(self, node):
childs = utils._child_get(node,self,'tr')
if not childs:
children = utils._child_get(node,self,'tr')
if not children:
return None
length = 0
colwidths = None
@@ -561,7 +573,7 @@ class _rml_flowable(object):
data = []
styles = []
posy = 0
for tr in childs:
for tr in children:
paraStyle = None
if tr.get('style'):
st = copy.deepcopy(self.styles.table_styles[tr.get('style')])
@@ -645,7 +657,30 @@ class _rml_flowable(object):
def _textual_image(self, node):
return base64.decodestring(node.text)
def _pto(self, node):
sub_story = []
pto_header = None
pto_trailer = None
for node in utils._child_get(node, self):
if node.tag == etree.Comment:
node.text = ''
continue
elif node.tag=='pto_header':
pto_header = self.render(node)
elif node.tag=='pto_trailer':
pto_trailer = self.render(node)
else:
flow = self._flowable(node)
if flow:
if isinstance(flow,list):
sub_story = sub_story + flow
else:
sub_story.append(flow)
return platypus.flowables.PTOContainer(sub_story, trailer=pto_trailer, header=pto_header)
def _flowable(self, node, extra_style=None):
if node.tag=='pto':
return self._pto(node)
if node.tag=='para':
style = self.styles.para_style_get(node)
if extra_style:
@@ -714,7 +749,7 @@ class _rml_flowable(object):
node.text = newtext
image_data = base64.decodestring(node.text)
if not image_data: return False
image = cStringIO.StringIO(image_data)
image = StringIO(image_data)
return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
else:
return platypus.Image(node.get('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
@@ -886,9 +921,13 @@ def parseNode(rml, localcontext = {},fout=None, images={}, path='.',title=None):
try:
from customfonts import SetCustomFonts
SetCustomFonts(r)
except Exception:
except ImportError:
# means there is no custom fonts mapping in this system.
pass
fp = cStringIO.StringIO()
except Exception:
logging.getLogger('report').warning('Cannot set font mapping', exc_info=True)
pass
fp = StringIO()
r.render(fp)
return fp.getvalue()
@@ -909,7 +948,7 @@ def parseString(rml, localcontext = {},fout=None, images={}, path='.',title=None
fp.close()
return fout
else:
fp = cStringIO.StringIO()
fp = StringIO()
r.render(fp)
return fp.getvalue()
+22 -20
View File
@@ -36,15 +36,16 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import re
import reportlab
from lxml import etree
import copy
import locale
import traceback, sys
import logging
import re
import reportlab
import tools
from tools.safe_eval import safe_eval as eval
import logging
from tools import ustr
_regex = re.compile('\[\[(.+?)\]\]')
@@ -67,7 +68,7 @@ def _child_get(node, self=None, tagname=None):
except GeneratorExit:
continue
except Exception, e:
logging.getLogger('report').exception(e)
logging.getLogger('report').warning('rml_except: "%s"',n.get('rml_except',''), exc_info=True)
continue
if n.get('rml_tag'):
try:
@@ -79,7 +80,7 @@ def _child_get(node, self=None, tagname=None):
except GeneratorExit:
yield n
except Exception, e:
logging.getLogger('report').exception(e)
logging.getLogger('report').warning('rml_tag: "%s"',n.get('rml_tag',''), exc_info=True)
yield n
else:
yield n
@@ -90,7 +91,7 @@ def _child_get(node, self=None, tagname=None):
except GeneratorExit:
continue
except Exception, e:
logging.getLogger('report').exception(e)
logging.getLogger('report').warning('rml_except: "%s"',n.get('rml_except',''), exc_info=True)
continue
if self and self.localcontext and n.get('rml_tag'):
try:
@@ -103,7 +104,7 @@ def _child_get(node, self=None, tagname=None):
except GeneratorExit:
pass
except Exception, e:
logging.getLogger('report').exception(e)
logging.getLogger('report').warning('rml_tag: "%s"',n.get('rml_tag',''), exc_info=True)
pass
if (tagname is None) or (n.tag==tagname):
yield n
@@ -117,24 +118,25 @@ def _process_text(self, txt):
sps = _regex.split(txt)
while sps:
# This is a simple text to translate
result += unicode(self.localcontext.get('translate', lambda x:x)(sps.pop(0)))
to_translate = tools.ustr(sps.pop(0))
result += tools.ustr(self.localcontext.get('translate', lambda x:x)(to_translate))
if sps:
try:
txt = None
expr = sps.pop(0)
txt = eval(expr,self.localcontext)
if txt and (isinstance(txt, unicode) or isinstance(txt, str)):
txt = unicode(txt)
except Exception,e:
tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
if type(txt)==type('') or type(txt)==type(u''):
txt2 = str2xml(txt)
result += unicode(txt2)
txt = eval(expr, self.localcontext)
if txt and isinstance(txt, basestring):
txt = tools.ustr(txt)
except Exception:
pass
if isinstance(txt, basestring):
result += str2xml(txt)
elif (txt is not None) and (txt is not False):
result += unicode(txt)
result += ustr(txt)
return result
def text_get(node):
return ''.join([unicode(n.text) for n in node])
return ''.join([ustr(n.text) for n in node])
units = [
(re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch),
+10 -5
View File
@@ -164,6 +164,7 @@ class rml_parse(object):
'lang' : user.company_id.partner_id.lang,
'translate' : self._translate,
'setHtmlImage' : self.set_html_image,
'strip_name' : self._strip_name,
'time' : time
}
self.localcontext.update(context)
@@ -184,6 +185,14 @@ class rml_parse(object):
def setTag(self, oldtag, newtag, attrs=None):
return newtag, attrs
def _ellipsis(self, char, size=100, truncation_str='...'):
if len(char) <= size:
return char
return char[:size-len(truncation_str)] + truncation_str
def _strip_name(self, name, maxlen=50):
return self._ellipsis(name, maxlen)
def format(self, text, oldtag=None):
return text.strip()
@@ -204,13 +213,10 @@ class rml_parse(object):
return res['datas']
else :
return ''
except Exception,e:
except Exception:
return ''
def setLang(self, lang):
if not lang or self.default_lang.has_key(lang):
if not lang:
key = 'en_US'
self.localcontext['lang'] = lang
self.lang_dict_called = False
for obj in self.objects:
@@ -519,7 +525,6 @@ class report_sxw(report_rml, preprocess.report):
xml_declaration=True)
rml_dom = etree.XML(rml)
body = rml_dom[-1]
elements = []
key1 = rml_parser.localcontext['name_space']["text"]+"p"
key2 = rml_parser.localcontext['name_space']["text"]+"drop-down"
-14
View File
@@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICNDCCAZ0CAQEwDQYJKoZIhvcNAQEEBQAweTEQMA4GA1UEChMHVGlueUVSUDEM
MAoGA1UECxMDRVJQMRkwFwYJKoZIhvcNAQkBFgpmcEB0aW55LmJlMRAwDgYDVQQH
EwdXYWxoYWluMQswCQYDVQQIEwJCVzELMAkGA1UEBhMCQkUxEDAOBgNVBAMTB1Rp
bnlFUlAwHhcNMDYwNTI0MDgzODUxWhcNMDcwNTI0MDgzODUxWjBMMQswCQYDVQQG
EwJCRTELMAkGA1UECBMCQlcxEDAOBgNVBAoTB1RpbnlFUlAxDDAKBgNVBAsTA0VS
UDEQMA4GA1UEAxMHVGlueUVSUDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
xzIDlU2PrczPsgXtxCskYxuwMPgNCNSCBfWsUZ9nJzlZfRAEXEq4LxaTPIgkzkIF
82bmJLgFz6/CyCFid4mkBLQBj30Opp2Vco39WRncNKHKxbk+/wZpZtQ0bSpvf+F4
MBqCLldYIqsoyenombVCb8X62IUu0ENF1wR22owvyKcCAwEAATANBgkqhkiG9w0B
AQQFAAOBgQB2yUqJ3gbQ8I6rcmaVJlcLDHfC5w1Jr1cUzcJevOPh3wygSZYYoUoe
yeYlzEag/DpPSHyRiJJVOKdiwU0yfmZPhfDNtDiBr47bz8qzIsYq5VeMmSeXrq/f
AA3iI4xE8YFzJHWtiBCqqyUok+j9pVad7iV7+UVIePHZLEkGGWIjDA==
-----END CERTIFICATE-----
-15
View File
@@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDHMgOVTY+tzM+yBe3EKyRjG7Aw+A0I1IIF9axRn2cnOVl9EARc
SrgvFpM8iCTOQgXzZuYkuAXPr8LIIWJ3iaQEtAGPfQ6mnZVyjf1ZGdw0ocrFuT7/
Bmlm1DRtKm9/4XgwGoIuV1giqyjJ6eiZtUJvxfrYhS7QQ0XXBHbajC/IpwIDAQAB
AoGAVwAxMHS/3FkoHckZICT3r5HYUosEpmaqo4+5w6yrkSYrP8RPI0A/UdG6XSXZ
bXzIvJakzkTRxPQvTtnF+A/V4rF9hxwB8cGXSywv5eDGmZ91qIsxY7Sv99VqSKNH
dNr9aZHloTvI51e/oramIJ/O3A+TbAS5i+u1DJC2IIFJcAECQQD8iRPTlPIqzjYD
Lg7KYGvwW9TE4ONAhC86kJbzV5o3amlV5duJgnkl/mNlfN1ihA7f3Gx9dfCjfRKp
V1rcjtCBAkEAye2aMw2v1m+MEqcPxyTUzVf5Y8BIXWbk15T43czXec9YclZSOBCX
Dgv4a3Fk+yxQUE0cZUH0U4FJq6mTgpuFJwJASFqZ9KATNlJ4xTZ4BGHV6zrUXkg0
tDJrObNdnID37XKulW7TFLXuMgWNwvEgmO5POLJ13whglubp5tzhapn8gQJAJz9Z
U0b7wFAaB54VAP31ppvMy0iaSB0xqX05CdNAplpYtJB2lpMS6RYGiMuXdwJb8d+q
/ztcg8aDTSw+kYoszQJBAPBrt694VkGT1k9Be6e5wyVDrE05bkHhFxPk/HMeWMDX
sZqHPs9vVaLBqu/uU84FdwRMOV71RG90g6eUEl7HWsg=
-----END RSA PRIVATE KEY-----
+4 -3
View File
@@ -67,7 +67,7 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer)
_send_traceback_header = False
i = 0
def __init__(self, addr, requestHandler,
def __init__(self, addr, requestHandler, proto='http',
logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
self.logRequests = logRequests
@@ -75,6 +75,7 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer)
HTTPServer.__init__(self, addr, requestHandler)
self.numThreads = 0
self.proto = proto
self.__threadno = 0
# [Bug #1222790] If possible, set close-on-exec flag; if a
@@ -143,7 +144,7 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server):
self.__interface = interface
try:
self.server = ThreadedHTTPServer((interface, port), handler)
self.server = ThreadedHTTPServer((interface, port), handler, proto=self._RealProto)
self.server.vdirs = []
self.server.logRequests = True
self.server.timeout = self._busywait_timeout
@@ -178,7 +179,7 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server):
def stats(self):
res = "%sd: " % self._RealProto + ((self.running and "running") or "stopped")
if self.server:
res += ", %d threads" % (self.server.numThreads,)
res += ", %d threads" % (self.server.numThreads,)
return res
def append_svc(self, service):
+11 -1
View File
@@ -36,6 +36,7 @@ import release
import sql_db
import tools
import locale
import logging
from cStringIO import StringIO
class db(netsvc.ExportService):
@@ -385,7 +386,7 @@ class common(_ObjectService):
'login_message','get_stats', 'check_connectivity',
'list_http_services']:
pass
elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel']:
elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel', 'get_os_time', 'get_sqlcount']:
passwd = params[0]
params = params[1:]
security.check_super(passwd)
@@ -567,6 +568,15 @@ GNU Public Licence.
def exp_check_connectivity(self):
return bool(sql_db.db_connect('template1'))
def exp_get_os_time(self):
return os.times()
def exp_get_sqlcount(self):
logger = logging.getLogger('db.cursor')
if not logger.isEnabledFor(logging.DEBUG_SQL):
logger.warning("Counters of SQL will not be reliable unless DEBUG_SQL is set at the server's config.")
return sql_db.sql_counter
common()
+37 -8
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
# A part of the code comes from the ganeti project: http://www.mail-archive.com/ganeti-devel@googlegroups.com/msg00713.html#
# Copyright P. Christeas <p_christ@hol.gr> 2008-2010
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
@@ -136,15 +135,25 @@ class HTTPDir:
return self.path
return False
class noconnection:
class noconnection(object):
""" a class to use instead of the real connection
"""
def __init__(self, realsocket=None):
self.__hidden_socket = realsocket
def makefile(self, mode, bufsize):
return None
def close(self):
pass
def getsockname(self):
""" We need to return info about the real socket that is used for the request
"""
if not self.__hidden_socket:
raise AttributeError("No-connection class cannot tell real socket")
return self.__hidden_socket.getsockname()
class dummyconn:
def shutdown(self, tru):
pass
@@ -191,6 +200,12 @@ class HttpOptions:
self.send_response(200)
self.send_header("Content-Length", 0)
if 'Microsoft' in self.headers.get('User-Agent', ''):
self.send_header('MS-Author-Via', 'DAV')
# Microsoft's webdav lib ass-umes that the server would
# be a FrontPage(tm) one, unless we send a non-standard
# header that we are not an elephant.
# http://www.ibm.com/developerworks/rational/library/2089.html
for key, value in opts.items():
if isinstance(value, basestring):
@@ -239,13 +254,21 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
fore.raw_requestline = "%s %s %s\n" % (self.command, path, self.version)
if not fore.parse_request(): # An error code has been sent, just exit
return
if fore.headers.status:
self.log_error("Parse error at headers: %s", fore.headers.status)
self.close_connection = 1
self.send_error(400,"Parse error at HTTP headers")
return
self.request_version = fore.request_version
if auth_provider and auth_provider.realm:
try:
self.sec_realms[auth_provider.realm].checkRequest(fore,path)
except AuthRequiredExc,ae:
if self.request_version != 'HTTP/1.1':
self.log_error("Cannot require auth at %s",self.request_version)
# Darwin 9.x.x webdav clients will report "HTTP/1.0" to us, while they support (and need) the
# authorisation features of HTTP/1.1
if self.request_version != 'HTTP/1.1' and ('Darwin/9.' not in fore.headers.get('User-Agent', '')):
self.log_error("Cannot require auth at %s", self.request_version)
self.send_error(403)
return
self._get_ignore_body(fore) # consume any body that came, not loose sync with input
@@ -390,15 +413,21 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
npath = '/' + npath
if not self.in_handlers.has_key(p):
self.in_handlers[p] = vdir.handler(noconnection(),self.client_address,self.server)
self.in_handlers[p] = vdir.handler(noconnection(self.request),self.client_address,self.server)
if vdir.auth_provider:
vdir.auth_provider.setupAuth(self, self.in_handlers[p])
hnd = self.in_handlers[p]
hnd.rfile = self.rfile
hnd.wfile = self.wfile
self.rlpath = self.raw_requestline
self._handle_one_foreign(hnd,npath, vdir.auth_provider)
# print "Handled, closing = ", self.close_connection
try:
self._handle_one_foreign(hnd,npath, vdir.auth_provider)
except IOError, e:
if e.errno == errno.EPIPE:
self.log_message("Could not complete request %s," \
"client closed connection", self.rlpath.rstrip())
else:
raise
return
# if no match:
self.send_error(404, "Path not found: %s" % self.path)
+22 -9
View File
@@ -28,6 +28,7 @@ from psycopg2.psycopg1 import cursor as psycopg1cursor
from psycopg2.pool import PoolError
import psycopg2.extensions
import warnings
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
@@ -51,11 +52,11 @@ psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,)
import tools
from tools.func import wraps
from tools.func import wraps, frame_codeinfo
from datetime import datetime as mdt
from datetime import timedelta
import threading
from inspect import stack
from inspect import currentframe
import re
re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$');
@@ -71,7 +72,7 @@ class Cursor(object):
@wraps(f)
def wrapper(self, *args, **kwargs):
if self.__closed:
raise psycopg2.ProgrammingError('Unable to use the cursor after having closed it')
raise psycopg2.OperationalError('Unable to use the cursor after having closed it')
return f(self, *args, **kwargs)
return wrapper
@@ -93,7 +94,10 @@ class Cursor(object):
self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor)
self.__closed = False # real initialisation value
self.autocommit(False)
self.__caller = tuple(stack()[2][1:3])
if self.sql_log:
self.__caller = frame_codeinfo(currentframe(),2)
else:
self.__caller = False
def __del__(self):
if not self.__closed:
@@ -102,9 +106,12 @@ class Cursor(object):
# but the database connection is not put back into the connection
# pool, preventing some operation on the database like dropping it.
# This can also lead to a server overload.
msg = "Cursor not closed explicitly\n" \
"Cursor was created at %s:%s"
self.__logger.warn(msg, *self.__caller)
msg = "Cursor not closed explicitly\n"
if self.__caller:
msg += "Cursor was created at %s:%s" % self.__caller
else:
msg += "Please enable sql debugging to trace the caller."
self.__logger.warn(msg)
self._close(True)
@check
@@ -290,7 +297,11 @@ class ConnectionPool(object):
# note: this code is called only if the for loop has completed (no break)
raise PoolError('The Connection Pool Is Full')
result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection)
try:
result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection)
except psycopg2.Error, e:
self.__logger.exception('Connection to the database failed')
raise
self._connections.append((result, True))
self._debug('Create new connection')
return result
@@ -337,10 +348,12 @@ class Connection(object):
def __nonzero__(self):
"""Check if connection is possible"""
try:
warnings.warn("You use an expensive function to test a connection.",
DeprecationWarning, stacklevel=1)
cr = self.cursor()
cr.close()
return True
except:
except Exception:
return False
+1
View File
@@ -20,3 +20,4 @@
##############################################################################
from test_osv import *
from test_translate import *
+48
View File
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010 OpenERP S.A. http://www.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
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import unittest
from tools.translate import quote, unquote
class TranslationToolsTestCase(unittest.TestCase):
def test_quote_unquote(self):
def test_string(str):
quoted = quote(str)
#print "\n1:", repr(str)
#print "2:", repr(quoted)
unquoted = unquote("".join(quoted.split('"\n"')))
#print "3:", repr(unquoted)
self.assertEquals(str, unquoted)
test_string("""test \nall kinds\n \n o\r
\\\\ nope\n\n"
""")
# The ones with 1+ backslashes directly followed by
# a newline or literal N can fail... we would need a
# state-machine parser to handle these, but this would
# be much slower so it's better to avoid them at the moment
self.assertRaises(AssertionError, quote, """test \nall kinds\n\no\r
\\\\nope\n\n"
""")
+4 -2
View File
@@ -450,11 +450,13 @@ class configmanager(object):
# try to create the directories and write the file
try:
if not os.path.exists(os.path.dirname(self.rcfile)):
rc_exists = os.path.exists(self.rcfile)
if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
os.makedirs(os.path.dirname(self.rcfile))
try:
p.write(file(self.rcfile, 'w'))
os.chmod(self.rcfile, 0600)
if not rc_exists:
os.chmod(self.rcfile, 0600)
except IOError:
sys.stderr.write("ERROR: couldn't write the config file\n")
+123 -115
View File
@@ -62,12 +62,24 @@ class ConvertError(Exception):
return 'Exception:\n\t%s\nUsing file:\n%s' % (self.orig, self.d)
def _ref(self, cr):
return lambda x: self.id_get(cr, False, x)
return lambda x: self.id_get(cr, x)
def _obj(pool, cr, uid, model_str, context=None):
model = pool.get(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,
timedelta=timedelta,
version=release.major_version,
ref=_ref(self, cr),
pytz=pytz)
if len(model_str):
idref2['obj'] = _obj(self.pool, cr, uid, model_str, context=context)
return idref2
def _fix_multiple_roots(node):
"""
Surround the children of the ``node`` element of an XML field with a
@@ -90,72 +102,68 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
if context is None:
context = {}
if node.tag in ('field','value'):
t = node.get('type','char')
f_model = node.get('model', '').encode('utf-8')
if node.get('search'):
f_search = node.get("search",'').encode('utf-8')
f_use = node.get("use",'id').encode('utf-8')
f_name = node.get("name",'').encode('utf-8')
q = unsafe_eval(f_search, idref)
ids = pool.get(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
if (f_name in _cols) and _cols[f_name]._type=='many2many':
return ids
f_val = False
if len(ids):
f_val = ids[0]
if isinstance(f_val, tuple):
f_val = f_val[0]
return f_val
a_eval = node.get('eval','')
if a_eval:
idref2 = dict(idref,
time=time,
DateTime=datetime,
timedelta=timedelta,
version=release.major_version,
ref=lambda x: self.id_get(cr, False, x),
pytz=pytz)
if len(f_model):
idref2['obj'] = _obj(self.pool, cr, uid, f_model, context=context)
try:
return unsafe_eval(a_eval, idref2)
except Exception:
logger = logging.getLogger('init')
logger.warning('could not eval(%s) for %s in %s' % (a_eval, node.get('name'), context), exc_info=True)
return ""
if t == 'xml':
def _process(s, idref):
m = re.findall('[^%]%\((.*?)\)[ds]', s)
for id in m:
if not id in idref:
idref[id]=self.id_get(cr, False, id)
return s % idref
_fix_multiple_roots(node)
return '<?xml version="1.0"?>\n'\
+_process("".join([etree.tostring(n, encoding='utf-8')
for n in node]),
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'):
res=[]
for n in node.findall('./value'):
res.append(_eval_xml(self,n,pool,cr,uid,idref))
if t=='tuple':
return tuple(res)
return res
t = node.get('type','char')
f_model = node.get('model', '').encode('utf-8')
if node.get('search'):
f_search = node.get("search",'').encode('utf-8')
f_use = node.get("use",'id').encode('utf-8')
f_name = node.get("name",'').encode('utf-8')
idref2 = {}
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)
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
if (f_name in _cols) and _cols[f_name]._type=='many2many':
return ids
f_val = False
if len(ids):
f_val = ids[0]
if isinstance(f_val, tuple):
f_val = f_val[0]
return f_val
a_eval = node.get('eval','')
idref2 = {}
if a_eval:
idref2 = _get_idref(self, cr, uid, f_model, context, idref)
try:
return unsafe_eval(a_eval, idref2)
except Exception:
logger = logging.getLogger('init')
logger.warning('could not eval(%s) for %s in %s' % (a_eval, node.get('name'), context), exc_info=True)
return ""
if t == 'xml':
def _process(s, idref):
m = re.findall('[^%]%\((.*?)\)[ds]', s)
for id in m:
if not id in idref:
idref[id]=self.id_get(cr, id)
return s % idref
_fix_multiple_roots(node)
return '<?xml version="1.0"?>\n'\
+_process("".join([etree.tostring(n, encoding='utf-8')
for n in node]),
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'):
res=[]
for n in node.findall('./value'):
res.append(_eval_xml(self,n,pool,cr,uid,idref))
if t=='tuple':
return tuple(res)
return res
elif node.tag == "getitem":
for n in node:
res=_eval_xml(self,n,pool,cr,uid,idref)
@@ -169,7 +177,7 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
args = []
a_eval = node.get('eval','')
if a_eval:
idref['ref'] = lambda x: self.id_get(cr, False, x)
idref['ref'] = lambda x: self.id_get(cr, x)
args = unsafe_eval(a_eval, idref)
for n in node:
return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
@@ -254,7 +262,7 @@ class xml_import(object):
def get_uid(self, cr, uid, data_node, node):
node_uid = node.get('uid','') or (len(data_node) and data_node.get('uid',''))
if node_uid:
return self.id_get(cr, None, node_uid)
return self.id_get(cr, node_uid)
return uid
def _test_xml_id(self, xml_id):
@@ -269,18 +277,20 @@ form: module.record_id""" % (xml_id,)
assert modcnt == 1, """The ID "%s" refers to an uninstalled module""" % (xml_id,)
if len(id) > 64:
self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'% (id,))
self.logger.error('id: %s is to long (max: 64)', id)
def _tag_delete(self, cr, rec, data_node=None):
d_model = rec.get("model",'')
d_search = rec.get("search",'')
d_search = rec.get("search",'').encode('utf-8')
d_id = rec.get("id",'')
ids = []
if d_search:
ids = self.pool.get(d_model).search(cr, self.uid, unsafe_eval(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))
if d_id:
try:
ids.append(self.id_get(cr, d_model, d_id))
ids.append(self.id_get(cr, d_id))
except:
# d_id cannot be found. doesn't matter in this case
pass
@@ -324,10 +334,10 @@ form: module.record_id""" % (xml_id,)
groups_value = []
for group in g_names:
if group.startswith('-'):
group_id = self.id_get(cr, 'res.groups', group[1:])
group_id = self.id_get(cr, group[1:])
groups_value.append((3, group_id))
else:
group_id = self.id_get(cr, 'res.groups', group)
group_id = self.id_get(cr, group)
groups_value.append((4, group_id))
res['groups_id'] = groups_value
@@ -367,10 +377,10 @@ form: module.record_id""" % (xml_id,)
groups_value = []
for group in g_names:
if group.startswith('-'):
group_id = self.id_get(cr, 'res.groups', group[1:])
group_id = self.id_get(cr, group[1:])
groups_value.append((3, group_id))
else:
group_id = self.id_get(cr, 'res.groups', group)
group_id = self.id_get(cr, group)
groups_value.append((4, group_id))
res['groups_id'] = groups_value
@@ -415,8 +425,8 @@ form: module.record_id""" % (xml_id,)
self._test_xml_id(xml_id)
type = rec.get('type','').encode('utf-8') or 'ir.actions.act_window'
view_id = False
if rec.get('view'):
view_id = self.id_get(cr, 'ir.actions.act_window', rec.get('view','').encode('utf-8'))
if rec.get('view_id'):
view_id = self.id_get(cr, rec.get('view_id','').encode('utf-8'))
domain = rec.get('domain','').encode('utf-8') or '{}'
res_model = rec.get('res_model','').encode('utf-8')
src_model = rec.get('src_model','').encode('utf-8')
@@ -428,7 +438,7 @@ form: module.record_id""" % (xml_id,)
uid = self.uid
active_id = str("active_id") # for further reference in client/bin/tools/__init__.py
def ref(str_id):
return self.id_get(cr, None, str_id)
return self.id_get(cr, str_id)
# Include all locals() in eval_context, for backwards compatibility
eval_context = {
@@ -479,10 +489,10 @@ form: module.record_id""" % (xml_id,)
groups_value = []
for group in g_names:
if group.startswith('-'):
group_id = self.id_get(cr, 'res.groups', group[1:])
group_id = self.id_get(cr, group[1:])
groups_value.append((3, group_id))
else:
group_id = self.id_get(cr, 'res.groups', group)
group_id = self.id_get(cr, group)
groups_value.append((4, group_id))
res['groups_id'] = groups_value
@@ -517,7 +527,7 @@ form: module.record_id""" % (xml_id,)
model = str(rec.get('model',''))
w_ref = rec.get('ref','')
if w_ref:
id = self.id_get(cr, model, w_ref)
id = self.id_get(cr, w_ref)
else:
number_children = len(rec)
assert number_children > 0,\
@@ -546,7 +556,7 @@ form: module.record_id""" % (xml_id,)
values = {'parent_id': False}
if rec.get('parent', False) is False and len(m_l) > 1:
# No parent attribute specified and the menu name has several menu components,
# No parent attribute specified and the menu name has several menu components,
# try to determine the ID of the parent according to menu path
pid = False
res = None
@@ -562,14 +572,14 @@ form: module.record_id""" % (xml_id,)
pid = res[0]
else:
# the menuitem does't exist but we are in branch (not a leaf)
self.logger.notifyChannel("init", netsvc.LOG_WARNING, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l)))
self.logger.warning('Warning no ID for submenu %s of menu %s !', menu_elem, str(m_l))
pid = self.pool.get('ir.ui.menu').create(cr, self.uid, {'parent_id' : pid, 'name' : menu_elem})
values['parent_id'] = pid
else:
# The parent attribute was specified, if non-empty determine its ID, otherwise
# explicitly make a top-level menu
if rec.get('parent'):
menu_parent_id = self.id_get(cr, 'ir.ui.menu', rec.get('parent',''))
menu_parent_id = self.id_get(cr, rec.get('parent',''))
else:
# we get here with <menuitem parent="">, explicit clear of parent, or
# if no parent attribute at all but menu name is not a menu path
@@ -578,7 +588,7 @@ form: module.record_id""" % (xml_id,)
if rec.get('name'):
values['name'] = rec.get('name')
try:
res = [ self.id_get(cr, 'ir.ui.menu', rec.get('id','')) ]
res = [ self.id_get(cr, rec.get('id','')) ]
except:
res = None
@@ -593,7 +603,7 @@ form: module.record_id""" % (xml_id,)
}
values['icon'] = icons.get(a_type,'STOCK_NEW')
if a_type=='act_window':
a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action)
a_id = self.id_get(cr, a_action)
cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%s', (int(a_id),))
rrres = cr.fetchone()
assert rrres, "No window action defined for this id %s !\n" \
@@ -618,7 +628,7 @@ form: module.record_id""" % (xml_id,)
if not values.get('name', False):
values['name'] = action_name
elif a_type=='wizard':
a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action)
a_id = self.id_get(cr, a_action)
cr.execute('select name from ir_act_wizard where id=%s', (int(a_id),))
resw = cr.fetchone()
if (not values.get('name', False)) and resw:
@@ -627,16 +637,20 @@ form: module.record_id""" % (xml_id,)
values['sequence'] = int(rec.get('sequence'))
if rec.get('icon'):
values['icon'] = str(rec.get('icon'))
if rec.get('web_icon'):
values['web_icon'] = "%s,%s" %(self.module, str(rec.get('web_icon')))
if rec.get('web_icon_hover'):
values['web_icon_hover'] = "%s,%s" %(self.module, str(rec.get('web_icon_hover')))
if rec.get('groups'):
g_names = rec.get('groups','').split(',')
groups_value = []
for group in g_names:
if group.startswith('-'):
group_id = self.id_get(cr, 'res.groups', group[1:])
group_id = self.id_get(cr, group[1:])
groups_value.append((3, group_id))
else:
group_id = self.id_get(cr, 'res.groups', group)
group_id = self.id_get(cr, group)
groups_value.append((4, group_id))
values['groups_id'] = groups_value
@@ -650,12 +664,12 @@ form: module.record_id""" % (xml_id,)
if rec.get('action') and pid:
a_action = rec.get('action').encode('utf8')
a_type = rec.get('type','').encode('utf8') or 'act_window'
a_id = self.id_get(cr, 'ir.actions.%s' % a_type, a_action)
a_id = self.id_get(cr, a_action)
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)
return ('ir.ui.menu', pid)
def _assert_equals(self, f1, f2, prec = 4):
def _assert_equals(self, f1, f2, prec=4):
return not round(f1 - f2, prec)
def _tag_assert(self, cr, rec, data_node=None):
@@ -678,7 +692,7 @@ form: module.record_id""" % (xml_id,)
context = self.get_context(data_node, rec, eval_dict)
uid = self.get_uid(cr, self.uid, data_node, rec)
if rec_id:
ids = [self.id_get(cr, rec_model, rec_id)]
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)
@@ -691,7 +705,7 @@ form: module.record_id""" % (xml_id,)
' expected count: %d\n' \
' obtained count: %d\n' \
% (rec_string, count, len(ids))
self.logger.notifyChannel('init', severity, msg)
self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
@@ -723,7 +737,7 @@ form: module.record_id""" % (xml_id,)
' expected value: %r\n' \
' obtained value: %r\n' \
% (rec_string, etree.tostring(test), expected_value, expression_value)
self.logger.notifyChannel('init', severity, msg)
self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
@@ -800,10 +814,10 @@ form: module.record_id""" % (xml_id,)
else:
if f_name in model._columns \
and model._columns[f_name]._type == 'reference':
val = self.model_id_get(cr, f_model, f_ref)
val = self.model_id_get(cr, f_ref)
f_val = val[0] + ',' + str(val[1])
else:
f_val = self.id_get(cr, f_model, f_ref)
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):
@@ -818,31 +832,27 @@ form: module.record_id""" % (xml_id,)
cr.commit()
return rec_model, id
def id_get(self, cr, model, id_str):
def id_get(self, cr, id_str):
if id_str in self.idref:
return self.idref[id_str]
res = self.model_id_get(cr, model, id_str)
res = self.model_id_get(cr, id_str)
if res and len(res)>1: res = res[1]
return res
def model_id_get(self, cr, model, id_str):
def model_id_get(self, cr, id_str):
model_data_obj = self.pool.get('ir.model.data')
mod = self.module
if '.' in id_str:
mod,id_str = id_str.split('.')
result = model_data_obj._get_id(cr, self.uid, mod, id_str)
res = model_data_obj.read(cr, self.uid, [result], ['model', 'res_id'])
if res and res[0] and res[0]['res_id']:
return res[0]['model'], int(res[0]['res_id'])
return False
return model_data_obj.get_object_reference(cr, self.uid, mod, id_str)
def parse(self, de):
if not de.tag in ['terp', 'openerp']:
self.logger.notifyChannel("init", netsvc.LOG_ERROR, "Mismatch xml format" )
self.logger.error("Mismatch xml format")
raise Exception( "Mismatch xml format: only terp or openerp as root tag" )
if de.tag == 'terp':
self.logger.notifyChannel("init", netsvc.LOG_WARNING, "The tag <terp/> is deprecated, use <openerp/>")
self.logger.warning("The tag <terp/> is deprecated, use <openerp/>")
for n in de.findall('./data'):
for rec in n:
@@ -860,7 +870,7 @@ form: module.record_id""" % (xml_id,)
def __init__(self, cr, module, idref, mode, report=None, noupdate=False):
self.logger = netsvc.Logger()
self.logger = logging.getLogger('init')
self.mode = mode
self.module = module
self.cr = cr
@@ -893,13 +903,14 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
encoding: utf-8'''
if not idref:
idref={}
logger = logging.getLogger('init')
model = ('.'.join(fname.split('.')[:-1]).split('-'))[0]
#remove folder path from model
head, model = os.path.split(model)
pool = pooler.get_pool(cr.dbname)
input = cStringIO.StringIO(csvcontent)
input = cStringIO.StringIO(csvcontent) #FIXME
reader = csv.reader(input, quotechar='"', delimiter=',')
fields = reader.next()
fname_partial = ""
@@ -917,9 +928,7 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
reader.next()
if not (mode == 'init' or 'id' in fields):
logger = netsvc.Logger()
logger.notifyChannel("init", netsvc.LOG_ERROR,
"Import specification does not contain 'id' and we are in init mode, Cannot continue.")
logger.error("Import specification does not contain 'id' and we are in init mode, Cannot continue.")
return
uid = 1
@@ -930,8 +939,7 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
try:
datas.append(map(lambda x: misc.ustr(x), line))
except:
logger = netsvc.Logger()
logger.notifyChannel("init", netsvc.LOG_ERROR, "Cannot import the line: %s" % line)
logger.error("Cannot import the line: %s", line)
pool.get(model).import_data(cr, uid, fields, datas,mode, module, noupdate, filename=fname_partial)
if config.get('import_partial'):
data = pickle.load(file(config.get('import_partial')))
+3 -1
View File
@@ -164,7 +164,7 @@ d[types.InstanceType] = _copy_inst
del d
def deepcopy(x, memo=None, _nil=[]):
def deepcopy(x, memo=None, _nil=None):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
@@ -172,6 +172,8 @@ def deepcopy(x, memo=None, _nil=[]):
if memo is None:
memo = {}
if _nil is None:
_nil = {}
d = id(x)
y = memo.get(d, _nil)
+42 -4
View File
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010 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
@@ -15,17 +16,17 @@
# 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 <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
__all__ = ['partial', 'wraps', 'update_wrapper']
__all__ = ['partial', 'wraps', 'update_wrapper', 'synchronized']
try:
from functools import partial, wraps, update_wrapper
except ImportError:
# The functools module doesn't exist in python < 2.5
# Code taken from python 2.5
# Code taken from python 2.5
# http://svn.python.org/view/python/tags/r254/Lib/functools.py?view=markup
def partial(fun, *args, **kwargs):
@@ -76,3 +77,40 @@ except ImportError:
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
def synchronized(lock_attr='_lock'):
def decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
lock = getattr(self, lock_attr)
try:
lock.acquire()
return func(self, *args, **kwargs)
finally:
lock.release()
return wrapper
return decorator
from inspect import getsourcefile
def frame_codeinfo(fframe, back=0):
""" Return a (filename, line) pair for a previous frame .
@return (filename, lineno) where lineno is either int or string==''
"""
try:
if not fframe:
return ("<unknown>", '')
for i in range(back):
fframe = fframe.f_back
try:
fname = getsourcefile(fframe)
except TypeError:
fname = '<builtin>'
lineno = fframe.f_lineno or ''
return (fname, lineno)
except Exception:
return ("<unknown>", '')
+115
View File
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
# taken from http://code.activestate.com/recipes/252524-length-limited-o1-lru-cache-implementation/
import threading
from func import synchronized
__all__ = ['LRU']
class LRUNode(object):
__slots__ = ['prev', 'next', 'me']
def __init__(self, prev, me):
self.prev = prev
self.me = me
self.next = None
class LRU:
"""
Implementation of a length-limited O(1) LRU queue.
Built for and used by PyPE:
http://pype.sourceforge.net
Copyright 2003 Josiah Carlson.
"""
def __init__(self, count, pairs=[]):
self._lock = threading.RLock()
self.count = max(count, 1)
self.d = {}
self.first = None
self.last = None
for key, value in pairs:
self[key] = value
@synchronized()
def __contains__(self, obj):
return obj in self.d
@synchronized()
def __getitem__(self, obj):
a = self.d[obj].me
self[a[0]] = a[1]
return a[1]
@synchronized()
def __setitem__(self, obj, val):
if obj in self.d:
del self[obj]
nobj = LRUNode(self.last, (obj, val))
if self.first is None:
self.first = nobj
if self.last:
self.last.next = nobj
self.last = nobj
self.d[obj] = nobj
if len(self.d) > self.count:
if self.first == self.last:
self.first = None
self.last = None
return
a = self.first
a.next.prev = None
self.first = a.next
a.next = None
del self.d[a.me[0]]
del a
@synchronized()
def __delitem__(self, obj):
nobj = self.d[obj]
if nobj.prev:
nobj.prev.next = nobj.next
else:
self.first = nobj.next
if nobj.next:
nobj.next.prev = nobj.prev
else:
self.last = nobj.prev
del self.d[obj]
@synchronized()
def __iter__(self):
cur = self.first
while cur != None:
cur2 = cur.next
yield cur.me[1]
cur = cur2
@synchronized()
def __len__(self):
return len(self.d)
@synchronized()
def iteritems(self):
cur = self.first
while cur != None:
cur2 = cur.next
yield cur.me
cur = cur2
@synchronized()
def iterkeys(self):
return iter(self.d)
@synchronized()
def itervalues(self):
for i,j in self.iteritems():
yield j
@synchronized()
def keys(self):
return self.d.keys()
@synchronized()
def pop(self,key):
v=self[key]
del self[key]
return v
+51 -44
View File
@@ -25,6 +25,7 @@ Miscelleanous tools used by OpenERP.
"""
import inspect
import subprocess
import logging
import os
import re
@@ -33,6 +34,7 @@ import socket
import sys
import threading
import time
import warnings
import zipfile
from datetime import datetime
from email.MIMEText import MIMEText
@@ -40,7 +42,6 @@ from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from email.Header import Header
from email.Utils import formatdate, COMMASPACE
from email.Utils import formatdate, COMMASPACE
from email import Encoders
from itertools import islice, izip
from which import which
@@ -51,6 +52,7 @@ else:
import netsvc
from config import config
from lru import LRU
_logger = logging.getLogger('tools')
@@ -74,20 +76,19 @@ def init_db(cr):
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))
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],))
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\')')
cr.execute('INSERT INTO ir_module_category \
(name, parent_id) \
VALUES (%s, %s) RETURNING id', (categs[0], p_id))
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
@@ -102,23 +103,23 @@ def init_db(cr):
state = 'uninstalled'
else:
state = 'uninstallable'
cr.execute('select nextval(\'ir_module_module_id_seq\')')
id = cr.fetchone()[0]
cr.execute('insert into ir_module_module \
(id, author, website, name, shortdesc, description, \
category_id, state, certificate, web) \
values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', (
id, info.get('author', ''),
cr.execute('INSERT INTO ir_module_module \
(author, website, name, shortdesc, description, \
category_id, state, certificate, web, license) \
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id', (
info.get('author', ''),
info.get('website', ''), i, info.get('name', False),
info.get('description', ''), p_id, state, info.get('certificate') or None,
info.get('web') or False))
cr.execute('insert into ir_model_data \
(name,model,module, res_id, noupdate) values (%s,%s,%s,%s,%s)', (
info.get('web') or False,
info.get('license') or 'AGPL-3'))
id = cr.fetchone()[0]
cr.execute('INSERT INTO ir_model_data \
(name,model,module, res_id, noupdate) VALUES (%s,%s,%s,%s,%s)', (
'module_meta_information', 'ir.module.module', i, id, True))
dependencies = info.get('depends', [])
for d in dependencies:
cr.execute('insert into ir_module_module_dependency \
(module_id,name) values (%s, %s)', (id, d))
cr.execute('INSERT INTO ir_module_module_dependency \
(module_id,name) VALUES (%s, %s)', (id, d))
cr.commit()
def find_in_path(name):
@@ -141,27 +142,24 @@ def exec_pg_command(name, *args):
if not prog:
raise Exception('Couldn\'t find %s' % name)
args2 = (os.path.basename(prog),) + args
return os.spawnv(os.P_WAIT, prog, args2)
return subprocess.call(args2, executable=prog)
def exec_pg_command_pipe(name, *args):
prog = find_pg_tool(name)
if not prog:
raise Exception('Couldn\'t find %s' % name)
if os.name == "nt":
cmd = '"' + prog + '" ' + ' '.join(args)
else:
cmd = prog + ' ' + ' '.join(args)
return os.popen2(cmd, 'b')
pop = subprocess.Popen(args, executable=prog, shell=True, bufsize= -1,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
return (pop.stdin, pop.stdout)
def exec_command_pipe(name, *args):
prog = find_in_path(name)
if not prog:
raise Exception('Couldn\'t find %s' % name)
if os.name == "nt":
cmd = '"'+prog+'" '+' '.join(args)
else:
cmd = prog+' '+' '.join(args)
return os.popen2(cmd, 'b')
pop = subprocess.Popen(args, executable=prog, shell=True, bufsize= -1,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
return (pop.stdin, pop.stdout)
#----------------------------------------------------------
# File paths
@@ -432,7 +430,7 @@ def _email_send(smtp_from, smtp_to_list, message, openobject_id=None, ssl=False,
from mailbox import Maildir
maildir_path = smtp_server[8:]
mdir = Maildir(maildir_path,factory=None, create = True)
mdir.add(msg.as_string(True))
mdir.add(message.as_string(True))
return True
oldstderr = smtplib.stderr
@@ -697,7 +695,7 @@ class cache(object):
__caches = []
def __init__(self, timeout=None, skiparg=2, multi=None):
def __init__(self, timeout=None, skiparg=2, multi=None, size=8192):
assert skiparg >= 2 # at least self and cr
if timeout is None:
self.timeout = config['cache_timeout']
@@ -706,7 +704,7 @@ class cache(object):
self.skiparg = skiparg
self.multi = multi
self.lasttime = time.time()
self.cache = {}
self.cache = LRU(size) # TODO take size from config
self.fun = None
cache.__caches.append(self)
@@ -864,14 +862,14 @@ def ustr(value, hint_encoding='utf-8'):
try:
return unicode(value)
except Exception:
raise UnicodeError('unable de to convert %r' % (value,))
raise UnicodeError('unable to convert %r' % (value,))
for ln in get_encodings(hint_encoding):
try:
return unicode(value, ln)
except Exception:
pass
raise UnicodeError('unable de to convert %r' % (value,))
raise UnicodeError('unable to convert %r' % (value,))
def exception_to_unicode(e):
@@ -914,6 +912,8 @@ def get_iso_codes(lang):
return lang
def get_languages():
# The codes below are those from Launchpad's Rosetta, with the exception
# of some trivial codes where the Launchpad code is xx and we have xx_XX.
languages={
'ab_RU': u'Abkhazian / аҧсуа',
'ar_AR': u'Arabic / الْعَرَبيّة',
@@ -980,7 +980,8 @@ def get_languages():
'sl_SI': u'Slovenian / slovenščina',
'sk_SK': u'Slovak / Slovenský jazyk',
'sq_AL': u'Albanian / Shqip',
'sr_RS': u'Serbian / српски језик',
'sr_RS': u'Serbian (Cyrillic) / српски',
'sr@latin': u'Serbian (Latin) / srpski',
'sv_SE': u'Swedish / svenska',
'te_IN': u'Telugu / తెలుగు',
'tr_TR': u'Turkish / Türkçe',
@@ -1114,6 +1115,8 @@ def debug(what):
--log-level=debug
"""
warnings.warn("The tools.debug() method is deprecated, please use logging.",
DeprecationWarning, stacklevel=2)
from inspect import stack
from pprint import pformat
st = stack()[1]
@@ -1122,10 +1125,10 @@ def debug(what):
what = pformat(what)
if param != what:
what = "%s = %s" % (param, what)
netsvc.Logger().notifyChannel(st[3], netsvc.LOG_DEBUG, what)
logging.getLogger(st[3]).debug(what)
icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
__icons_list = ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER',
'STOCK_CONNECT', 'STOCK_CONVERT', 'STOCK_COPY', 'STOCK_CUT', 'STOCK_DELETE',
'STOCK_DIALOG_AUTHENTICATION', 'STOCK_DIALOG_ERROR', 'STOCK_DIALOG_INFO',
@@ -1161,7 +1164,11 @@ icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_
'terp-gdu-smart-failing','terp-go-week','terp-gtk-select-all','terp-locked','terp-mail-forward',
'terp-mail-message-new','terp-mail-replied','terp-rating-rated','terp-stage','terp-stock_format-scientific',
'terp-dolar_ok!','terp-idea','terp-stock_format-default','terp-mail-','terp-mail_delete'
])
]
def icons(*a, **kw):
global __icons_list
return [(x, x) for x in __icons_list ]
def extract_zip_file(zip_file, outdirectory):
zf = zipfile.ZipFile(zip_file, 'r')
@@ -1408,7 +1415,7 @@ def upload_data(email, data, type='SURVEY'):
return True
# port of python 2.6's attrgetter with support for dotted notation
# port of python 2.6's attrgetter with support for dotted notation
def resolve_attr(obj, attr):
for name in attr.split("."):
obj = getattr(obj, name)

Some files were not shown because too many files have changed in this diff Show More