@@ -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)
|
||||
|
||||
@@ -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',
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
+603
-1029
File diff suppressed because it is too large
Load Diff
+1621
-1345
File diff suppressed because it is too large
Load Diff
+4303
-2462
File diff suppressed because it is too large
Load Diff
+1647
-1261
File diff suppressed because it is too large
Load Diff
+3139
-2076
File diff suppressed because it is too large
Load Diff
+4047
-2396
File diff suppressed because it is too large
Load Diff
+1640
-1451
File diff suppressed because it is too large
Load Diff
+4309
-2921
File diff suppressed because it is too large
Load Diff
+1838
-1499
File diff suppressed because it is too large
Load Diff
+4059
-2427
File diff suppressed because it is too large
Load Diff
+1695
-1588
File diff suppressed because it is too large
Load Diff
+4791
-2443
File diff suppressed because it is too large
Load Diff
+3956
-1965
File diff suppressed because it is too large
Load Diff
+4189
-2433
File diff suppressed because it is too large
Load Diff
+1324
-1244
File diff suppressed because it is too large
Load Diff
+3333
-2561
File diff suppressed because it is too large
Load Diff
+1307
-1218
File diff suppressed because it is too large
Load Diff
+3822
-2334
File diff suppressed because it is too large
Load Diff
+3891
-2013
File diff suppressed because it is too large
Load Diff
+3448
-2336
File diff suppressed because it is too large
Load Diff
+2957
-2268
File diff suppressed because it is too large
Load Diff
+1310
-1270
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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'),
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -23,7 +23,6 @@ import math
|
||||
|
||||
from osv import fields,osv
|
||||
import tools
|
||||
import ir
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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>"""
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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'
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -20,3 +20,4 @@
|
||||
##############################################################################
|
||||
|
||||
from test_osv import *
|
||||
from test_translate import *
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>", '')
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user